diff --git a/backend/src-common/pom.xml b/backend/src-common/pom.xml index fe223ceaf3..c2a23ca490 100644 --- a/backend/src-common/pom.xml +++ b/backend/src-common/pom.xml @@ -37,11 +37,24 @@ </plugins> </build> <dependencies> + <!-- https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple --> + <dependency> + <groupId>com.googlecode.json-simple</groupId> + <artifactId>json-simple</artifactId> + <version>1.1.1</version> + <scope>compile</scope> + </dependency> <dependency> <groupId>org.spdx</groupId> <artifactId>spdx-tools</artifactId> <scope>compile</scope> </dependency> + <dependency> + <groupId>org.spdx</groupId> + <artifactId>tools-java</artifactId> + <version>1.0.2</version> + <scope>compile</scope> + </dependency> <!-- Needed by spdx-tools --> <dependency> <groupId>org.apache.logging.log4j</groupId> diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/DocumentCreationInformationSummary.java b/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/DocumentCreationInformationSummary.java new file mode 100644 index 0000000000..7c1ea886eb --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/DocumentCreationInformationSummary.java @@ -0,0 +1,43 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.components.summary; + +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; + +import static org.eclipse.sw360.datahandler.thrift.ThriftUtils.copyField; + +/** + * Created by HieuPV on 22/07/21. + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class DocumentCreationInformationSummary extends DocumentSummary<DocumentCreationInformation> { + + @Override + protected DocumentCreationInformation summary(SummaryType type, DocumentCreationInformation document) { + // Copy required details + DocumentCreationInformation copy = new DocumentCreationInformation(); + + switch (type) { + case SUMMARY: + copyField(document, copy, DocumentCreationInformation._Fields.ID); + copyField(document, copy, DocumentCreationInformation._Fields.SPDXID); + copyField(document, copy, DocumentCreationInformation._Fields.NAME); + break; + default: + break; + } + + return copy; + } + + +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/FileInformationSummary.java b/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/FileInformationSummary.java new file mode 100644 index 0000000000..9b2b08968f --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/FileInformationSummary.java @@ -0,0 +1,43 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.components.summary; + +import org.eclipse.sw360.datahandler.thrift.spdx.fileinformation.*; + +import static org.eclipse.sw360.datahandler.thrift.ThriftUtils.copyField; + +/** + * Created by HieuPV on 22/07/21. + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class FileInformationSummary extends DocumentSummary<FileInformation> { + + @Override + protected FileInformation summary(SummaryType type, FileInformation document) { + // Copy required details + FileInformation copy = new FileInformation(); + + switch (type) { + case SUMMARY: + copyField(document, copy, FileInformation._Fields.ID); + copyField(document, copy, FileInformation._Fields.SPDXID); + copyField(document, copy, FileInformation._Fields.FILE_NAME); + break; + default: + break; + } + + return copy; + } + + +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/PackageInformationSummary.java b/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/PackageInformationSummary.java new file mode 100644 index 0000000000..5037f3fb50 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/PackageInformationSummary.java @@ -0,0 +1,42 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.components.summary; + +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; + +import static org.eclipse.sw360.datahandler.thrift.ThriftUtils.copyField; + +/** + * Created by HieuPV on 22/07/21. + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class PackageInformationSummary extends DocumentSummary<PackageInformation> { + + @Override + protected PackageInformation summary(SummaryType type, PackageInformation document) { + // Copy required details + PackageInformation copy = new PackageInformation(); + + switch (type) { + case SUMMARY: + copyField(document, copy, PackageInformation._Fields.ID); + copyField(document, copy, PackageInformation._Fields.SPDXID); + copyField(document, copy, PackageInformation._Fields.NAME); + break; + default: + break; + } + + return copy; + } + +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/SpdxDocumentSummary.java b/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/SpdxDocumentSummary.java new file mode 100644 index 0000000000..870382f8d4 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/components/summary/SpdxDocumentSummary.java @@ -0,0 +1,43 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.components.summary; + +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; + +import static org.eclipse.sw360.datahandler.thrift.ThriftUtils.copyField; + +/** + * Created by HieuPV on 22/07/21. + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentSummary extends DocumentSummary<SPDXDocument> { + + @Override + protected SPDXDocument summary(SummaryType type, SPDXDocument document) { + // Copy required details + SPDXDocument copy = new SPDXDocument(); + + switch (type) { + case SUMMARY: + copyField(document, copy, SPDXDocument._Fields.ID); + copyField(document, copy, SPDXDocument._Fields.SPDX_DOCUMENT_CREATION_INFO_ID); + copyField(document, copy, SPDXDocument._Fields.SPDX_PACKAGE_INFO_IDS); + copyField(document, copy, SPDXDocument._Fields.SPDX_FILE_INFO_IDS); + default: + break; + } + + return copy; + } + + +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsDatabaseHandler.java index d522197740..c0dec0aa0c 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ChangeLogsDatabaseHandler.java @@ -58,7 +58,6 @@ public List<ChangeLogs> getChangeLogsByDocumentId(User user, String docId) { changeLogsByDocId = changeLogsByDocId.stream().filter(Objects::nonNull).filter(changeLog -> isNotEmptyChangeLog(changeLog)) .collect(Collectors.toList()); Collections.sort(changeLogsByDocId, Comparator.comparing(ChangeLogs::getChangeTimestamp).reversed()); - changeLogsByDocId.stream().forEach(cl -> cl.setChangeTimestamp(cl.getChangeTimestamp().split(" ")[0])); return changeLogsByDocId; } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java index f12137c5b8..aa965edaa0 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ComponentDatabaseHandler.java @@ -25,6 +25,7 @@ import org.eclipse.sw360.datahandler.common.ThriftEnumUtils; import org.eclipse.sw360.datahandler.couchdb.AttachmentConnector; import org.eclipse.sw360.datahandler.couchdb.AttachmentStreamConnector; +import org.eclipse.sw360.datahandler.db.spdx.document.SpdxDocumentDatabaseHandler; import org.eclipse.sw360.datahandler.entitlement.ComponentModerator; import org.eclipse.sw360.datahandler.entitlement.ProjectModerator; import org.eclipse.sw360.datahandler.entitlement.ReleaseModerator; @@ -60,12 +61,25 @@ import org.apache.thrift.TException; import org.eclipse.sw360.spdx.SpdxBOMImporter; import org.eclipse.sw360.spdx.SpdxBOMImporterSink; +import org.eclipse.sw360.spdx.SpdxBOMExporter; +import org.eclipse.sw360.spdx.SpdxBOMExporterSink; import org.jetbrains.annotations.NotNull; import org.spdx.rdfparser.InvalidSPDXAnalysisException; - +import org.spdx.tools.SpdxConverter; +import org.spdx.tools.SpdxConverterException; +import org.spdx.tools.TagToRDF; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -111,6 +125,7 @@ public class ComponentDatabaseHandler extends AttachmentAwareDatabaseHandler { private DatabaseHandlerUtil dbHandlerUtil; private final AttachmentConnector attachmentConnector; + private final SpdxDocumentDatabaseHandler spdxDocumentDatabaseHandler; /** * Access to moderation */ @@ -160,6 +175,9 @@ public ComponentDatabaseHandler(Supplier<CloudantClient> httpClient, String dbNa attachmentConnector = new AttachmentConnector(httpClient, attachmentDbName, durationOf(30, TimeUnit.SECONDS)); DatabaseConnectorCloudant dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); + + // Create the spdx document database handler + this.spdxDocumentDatabaseHandler = new SpdxDocumentDatabaseHandler(httpClient, DatabaseSettings.COUCH_DB_SPDX); } public ComponentDatabaseHandler(Supplier<CloudantClient> httpClient, String dbName, String changeLogsDbName, String attachmentDbName, ComponentModerator moderator, ReleaseModerator releaseModerator, ProjectModerator projectModerator) throws MalformedURLException { @@ -512,12 +530,26 @@ public AddDocumentRequestSummary addRelease(Release release, User user) throws S } private boolean isDuplicate(Component component, boolean caseInsenstive){ - Set<String> duplicates = componentRepository.getComponentIdsByName(component.getName(), caseInsenstive); - return duplicates.size()>0; + return isDuplicate(component.getName(), caseInsenstive); } private boolean isDuplicate(Release release){ - List<Release> duplicates = releaseRepository.searchByNameAndVersion(release.getName(), release.getVersion()); + return isDuplicate(release.getName(), release.getVersion()); + } + + private boolean isDuplicate(String componentName, boolean caseInsenstive) { + if (isNullEmptyOrWhitespace(componentName)) { + return false; + } + Set<String> duplicates = componentRepository.getComponentIdsByName(componentName, caseInsenstive); + return duplicates.size()>0; + } + + private boolean isDuplicate(String releaseName, String releaseVersion) { + if (isNullEmptyOrWhitespace(releaseName)) { + return false; + } + List<Release> duplicates = releaseRepository.searchByNameAndVersion(releaseName, releaseVersion); return duplicates.size()>0; } @@ -1669,6 +1701,12 @@ public RequestStatus deleteRelease(String id, User user) throws SW360Exception { Component componentBefore = componentRepository.get(release.getComponentId()); // Remove release id from component removeReleaseId(id, release.componentId); + // Remove spdx if exist + String spdxId = release.getSpdxId(); + if (CommonUtils.isNotNullEmptyOrWhitespace(spdxId)) { + spdxDocumentDatabaseHandler.deleteSPDXDocument(spdxId, user); + release = releaseRepository.get(id); + } Component componentAfter=removeReleaseAndCleanUp(release); dbHandlerUtil.addChangeLogs(null, release, user.getEmail(), Operation.DELETE, attachmentConnector, Lists.newArrayList(), null, null); @@ -2352,21 +2390,208 @@ private void sendMailNotificationsForReleaseUpdate(Release release, String user) release.getName(), release.getVersion()); } - public RequestSummary importBomFromAttachmentContent(User user, String attachmentContentId) throws SW360Exception { + public ImportBomRequestPreparation prepareImportBom(User user, String attachmentContentId) throws SW360Exception { final AttachmentContent attachmentContent = attachmentConnector.getAttachmentContent(attachmentContentId); final Duration timeout = Duration.durationOf(30, TimeUnit.SECONDS); + String sourceFilePath = null; + String targetFilePath = null; try { final AttachmentStreamConnector attachmentStreamConnector = new AttachmentStreamConnector(timeout); try (final InputStream inputStream = attachmentStreamConnector.unsafeGetAttachmentStream(attachmentContent)) { final SpdxBOMImporterSink spdxBOMImporterSink = new SpdxBOMImporterSink(user, null, this); final SpdxBOMImporter spdxBOMImporter = new SpdxBOMImporter(spdxBOMImporterSink); - return spdxBOMImporter.importSpdxBOMAsRelease(inputStream, attachmentContent); + + InputStream spdxInputStream = null; + String fileType = getFileType(attachmentContent.getFilename()); + + if (!fileType.equals("rdf")) { + final String ext = "." + fileType; + final File sourceFile = DatabaseHandlerUtil.saveAsTempFile(user, inputStream, attachmentContentId, ext); + sourceFilePath = sourceFile.getAbsolutePath(); + targetFilePath = sourceFilePath.replace(ext, ".rdf"); + File targetFile = null; + try { + if (fileType.equals("spdx")) { + targetFile = convertTagToRdf(sourceFile, targetFilePath); + } else { + SpdxConverter.convert(sourceFilePath, targetFilePath); + targetFile = new File(targetFilePath); + } + spdxInputStream = new FileInputStream(targetFile); + } catch (SpdxConverterException e) { + log.error("Can not convert to RDF \n" + e); + ImportBomRequestPreparation importBomRequestPreparation = new ImportBomRequestPreparation(); + importBomRequestPreparation.setRequestStatus(RequestStatus.FAILURE); + importBomRequestPreparation.setMessage("error-convert"); + return importBomRequestPreparation; + + } finally { + Files.delete(Paths.get(sourceFilePath)); + } + } else { + final String ext = "." + fileType; + final File sourceFile = DatabaseHandlerUtil.saveAsTempFile(user, inputStream, attachmentContentId, ext); + sourceFilePath = sourceFile.getAbsolutePath(); + cutFileInformation(sourceFilePath); + File targetFile = new File (sourceFilePath); + spdxInputStream = new FileInputStream(targetFile); + targetFilePath = sourceFilePath; + } + + ImportBomRequestPreparation importBomRequestPreparation = spdxBOMImporter.prepareImportSpdxBOMAsRelease(spdxInputStream, attachmentContent); + if (RequestStatus.SUCCESS.equals(importBomRequestPreparation.getRequestStatus())) { + String name = importBomRequestPreparation.getName(); + String version = importBomRequestPreparation.getVersion(); + if (!isDuplicate(name, true)) { + importBomRequestPreparation.setIsComponentDuplicate(false); + importBomRequestPreparation.setIsReleaseDuplicate(false); + } else if (!isDuplicate(name, version)) { + importBomRequestPreparation.setIsComponentDuplicate(true); + importBomRequestPreparation.setIsReleaseDuplicate(false); + } else { + importBomRequestPreparation.setIsComponentDuplicate(true); + importBomRequestPreparation.setIsReleaseDuplicate(true); + } + importBomRequestPreparation.setMessage(targetFilePath); + } + + return importBomRequestPreparation; } } catch (InvalidSPDXAnalysisException | IOException e) { throw new SW360Exception(e.getMessage()); } } + private void cutFileInformation(String pathFile) { + try { + log.info("Run command cut File information from RDF file from line"); + String command = "file=\"" + pathFile + "\" " + + "&& start=$(cat $file | grep -nF \"spdx:hasFile>\" | head -1 | cut -d \":\" -f1) " + + "&& end=$(cat $file | grep -nF \"/spdx:hasFile>\" | tail -1 | cut -d \":\" -f1) " + + "&& echo $start to $end " + + "&& sed -i \"${start},${end}d\" $file "; + Process process = Runtime.getRuntime().exec(new String[] { "/bin/bash", "-c", command }); + printResults(process); + } catch (IOException e) { + log.error("Error when cut File information"); + e.printStackTrace(); + } + } + + public static void printResults(Process process) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line = ""; + while ((line = reader.readLine()) != null) { + log.info(line); + } + } + + private File convertTagToRdf(File sourceFile, String targetFilePath) { + FileInputStream spdxTagStream = null; + + try { + spdxTagStream = new FileInputStream(sourceFile); + } catch (FileNotFoundException e2) { + e2.printStackTrace(); + } + + File spdxRDFFile = new File(targetFilePath); + String outputFormat = "RDF/XML"; + FileOutputStream outStream = null; + try { + outStream = new FileOutputStream(spdxRDFFile); + } catch (FileNotFoundException e1) { + try { + spdxTagStream.close(); + } catch (IOException e) { + log.error("Warning: Unable to close input file on error."); + } + log.error("Could not write to the new SPDX RDF file " + spdxRDFFile.getPath() + "due to error " + e1.getMessage()); + } + + List<String> warnings = new ArrayList<String>(); + try { + TagToRDF.convertTagFileToRdf(spdxTagStream, outStream, outputFormat, warnings); + if (!warnings.isEmpty()) { + log.warn("The following warnings and or verification errors were found:"); + for (String warning:warnings) { + log.warn("\t" + warning); + } + } + } catch (Exception e) { + log.error("Error creating SPDX Analysis: " + e.getMessage()); + } finally { + if (outStream != null) { + try { + outStream.close(); + } catch (IOException e) { + log.error("Error closing RDF file: " + e.getMessage()); + } + } + if (spdxTagStream != null) { + try { + spdxTagStream.close(); + } catch (IOException e) { + log.error("Error closing Tag/Value file: " + e.getMessage()); + } + } + } + return spdxRDFFile; + } + + public RequestSummary exportSPDX(User user, String releaseId, String outputFormat) throws SW360Exception { + RequestSummary requestSummary = new RequestSummary(); + + try { + final SpdxBOMExporterSink spdxBOMExporterSink = new SpdxBOMExporterSink(user, null, this); + final SpdxBOMExporter spdxBOMExporter = new SpdxBOMExporter(spdxBOMExporterSink); + try { + return spdxBOMExporter.exportSPDXFile(releaseId, outputFormat); + } catch (InvalidSPDXAnalysisException e) { + e.printStackTrace(); + } + } catch (IOException e) { + throw new SW360Exception(e.getMessage()); + } + return requestSummary.setRequestStatus(RequestStatus.FAILURE); + } + + public RequestSummary importBomFromAttachmentContent(User user, String attachmentContentId, String newReleaseVersion, String releaseId, String rdfFilePath) throws SW360Exception { + final AttachmentContent attachmentContent = attachmentConnector.getAttachmentContent(attachmentContentId); + try { + final SpdxBOMImporterSink spdxBOMImporterSink = new SpdxBOMImporterSink(user, null, this); + final SpdxBOMImporter spdxBOMImporter = new SpdxBOMImporter(spdxBOMImporterSink); + InputStream spdxInputStream = null; + if (!isNullEmptyOrWhitespace(rdfFilePath)) { + spdxInputStream = new FileInputStream(new File(rdfFilePath)); + Files.delete(Paths.get(rdfFilePath)); + } else { + spdxInputStream = attachmentConnector.unsafeGetAttachmentStream(attachmentContent); + } + return spdxBOMImporter.importSpdxBOMAsRelease(spdxInputStream, attachmentContent, newReleaseVersion, releaseId); + } catch (IOException e) { + throw new SW360Exception(e.getMessage()); + } + } + + private String getFileType(String fileName) { + if (isNullEmptyOrWhitespace(fileName) || !fileName.contains(".")) { + log.error("Can not get file type from file name - no file extension"); + return null; + } + String ext = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); + if ("xml".equals(ext)) { + if (fileName.endsWith("rdf.xml")) { + ext = "rdf"; + } + } + return ext; + } + + private boolean isJSONFile(String fileType) { + return (!isNullEmptyOrWhitespace(fileType) && fileType.equals("json")); + } + private void removeLeadingTrailingWhitespace(Release release) { DatabaseHandlerUtil.trimStringFields(release, listOfStringFieldsInReleaseToTrim); diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/DatabaseHandlerUtil.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/DatabaseHandlerUtil.java index 70172254cf..f71939e425 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/DatabaseHandlerUtil.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/DatabaseHandlerUtil.java @@ -10,6 +10,7 @@ package org.eclipse.sw360.datahandler.db; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -44,6 +45,7 @@ import java.util.stream.Collectors; import org.apache.logging.log4j.Level; +import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LoggerContext; @@ -74,6 +76,7 @@ import org.eclipse.sw360.datahandler.couchdb.DatabaseMixInForChangeLog.ProjectReleaseRelationshipMixin; import org.eclipse.sw360.datahandler.couchdb.DatabaseMixInForChangeLog.RepositoryMixin; import org.eclipse.sw360.datahandler.couchdb.DatabaseMixInForChangeLog.VendorMixin; +import org.eclipse.sw360.datahandler.couchdb.DatabaseMixInForChangeLog.*; import org.eclipse.sw360.datahandler.thrift.ProjectReleaseRelationship; import org.eclipse.sw360.datahandler.thrift.RequestStatus; import org.eclipse.sw360.datahandler.thrift.SW360Exception; @@ -98,6 +101,19 @@ import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.Annotations; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.CheckSum; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.Creator; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.ExternalDocumentReferences; +import org.eclipse.sw360.datahandler.thrift.spdx.otherlicensinginformationdetected.OtherLicensingInformationDetected; +import org.eclipse.sw360.datahandler.thrift.spdx.relationshipsbetweenspdxelements.RelationshipsBetweenSPDXElements; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetRange; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.ExternalReference; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageVerificationCode; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -122,6 +138,7 @@ public class DatabaseHandlerUtil { private static final boolean IS_STORE_ATTACHMENT_TO_FILE_SYSTEM_ENABLED; private static final String ATTACHMENT_STORE_FILE_SYSTEM_LOCATION; private static final String ATTACHMENT_STORE_FILE_SYSTEM_PERMISSION; + // private static final String TEMPLE_FILE_LOCATION; private static ExecutorService ATTACHMENT_FILE_SYSTEM_STORE_THREAD_POOL = Executors.newFixedThreadPool(5); private static final String ATTACHMENT_DELETE_NO_OF_DAYS; private static final boolean IS_SW360CHANGELOG_ENABLED; @@ -137,6 +154,7 @@ public class DatabaseHandlerUtil { "/opt/sw360tempattachments"); ATTACHMENT_STORE_FILE_SYSTEM_PERMISSION = props.getProperty("attachment.store.file.system.permission", "rwx------"); + // TEMPLE_FILE_LOCATION = props.getProperty("temp.dir", "../temp") IS_STORE_ATTACHMENT_TO_FILE_SYSTEM_ENABLED = Boolean.parseBoolean(props.getProperty("enable.attachment.store.to.file.system", "false")); ATTACHMENT_DELETE_NO_OF_DAYS = props.getProperty("attachemnt.delete.no.of.days", "30"); @@ -402,10 +420,23 @@ public static <T, R> void trimStringFields(T obj, List<R> listOfStrFields) { changeLog.setDocumentId(newProjVer.getId()); changeLog.setDocumentType(newProjVer.getType()); changeLog.setDbName(DatabaseSettings.COUCH_DB_DATABASE); + } else if (newDocVersion instanceof SPDXDocument) { + SPDXDocument newProjVer = (SPDXDocument) newDocVersion; + changeLog.setDocumentId(newProjVer.getId()); + changeLog.setDocumentType(newProjVer.getType()); + changeLog.setDbName(DatabaseSettings.COUCH_DB_SPDX); + } else if (newDocVersion instanceof DocumentCreationInformation) { + DocumentCreationInformation newProjVer = (DocumentCreationInformation) newDocVersion; + changeLog.setDocumentId(newProjVer.getId()); + changeLog.setDocumentType(newProjVer.getType()); + changeLog.setDbName(DatabaseSettings.COUCH_DB_SPDX); + } else if (newDocVersion instanceof PackageInformation) { + PackageInformation newProjVer = (PackageInformation) newDocVersion; + changeLog.setDocumentId(newProjVer.getId()); + changeLog.setDocumentType(newProjVer.getType()); + changeLog.setDbName(DatabaseSettings.COUCH_DB_SPDX); } - log.info("Initialize ChangeLogs for Document Id : " + changeLog.getDocumentId()); - if (parentOperation != null) info.put("PARENT_OPERATION", parentOperation.name()); if (!info.isEmpty()) @@ -574,6 +605,12 @@ private static <T extends TBase> void changeLogsForNewlyCreatedOrDeleted(T newor fields = Release._Fields.values(); } else if (neworDeletedVersion instanceof ModerationRequest) { fields = ModerationRequest._Fields.values(); + } else if (neworDeletedVersion instanceof SPDXDocument) { + fields = SPDXDocument._Fields.values(); + } else if (neworDeletedVersion instanceof DocumentCreationInformation) { + fields = DocumentCreationInformation._Fields.values(); + } else if (neworDeletedVersion instanceof PackageInformation) { + fields = PackageInformation._Fields.values(); } else { return; } @@ -598,6 +635,12 @@ private static <T extends TBase> void changeLogsForNewlyCreatedOrDeleted(T newor fields = Release._Fields.values(); } else if (newVersion instanceof ModerationRequest) { fields = ModerationRequest._Fields.values(); + } else if (newVersion instanceof SPDXDocument) { + fields = SPDXDocument._Fields.values(); + } else if (newVersion instanceof DocumentCreationInformation) { + fields = DocumentCreationInformation._Fields.values(); + } else if (newVersion instanceof PackageInformation) { + fields = PackageInformation._Fields.values(); } else { return; } @@ -679,7 +722,7 @@ private static boolean isTwoCollectionSame(Collection<?> col1, Collection<?> col } private static String getTimeStamp() { - SimpleDateFormat timestampPattern = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat timestampPattern = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS"); Date timeNow = new Date(System.currentTimeMillis()); return timestampPattern.format(timeNow); } @@ -812,6 +855,16 @@ private static ObjectMapper initAndGetObjectMapper() { mapper.addMixInAnnotations(Repository.class, RepositoryMixin.class); mapper.addMixInAnnotations(ProjectProjectRelationship.class, ProjectProjectRelationshipMixin.class); mapper.addMixInAnnotations(ProjectReleaseRelationship.class, ProjectReleaseRelationshipMixin.class); + mapper.addMixInAnnotations(CheckSum.class, CheckSumMixin.class); + mapper.addMixInAnnotations(Annotations.class, AnnotationsMixin.class); + mapper.addMixInAnnotations(ExternalDocumentReferences.class, ExternalDocumentReferencesMixin.class); + mapper.addMixInAnnotations(Creator.class, CreatorMixin.class); + mapper.addMixInAnnotations(OtherLicensingInformationDetected.class, OtherLicensingInformationDetectedMixin.class); + mapper.addMixInAnnotations(PackageVerificationCode.class, PackageVerificationCodeMixin.class); + mapper.addMixInAnnotations(ExternalReference.class, ExternalReferenceMixin.class); + mapper.addMixInAnnotations(RelationshipsBetweenSPDXElements.class, RelationshipsBetweenSPDXElementsMixin.class); + mapper.addMixInAnnotations(SnippetInformation.class, SnippetInformationMixin.class); + mapper.addMixInAnnotations(SnippetRange.class, SnippetRangeMixin.class); } return mapper; } @@ -967,5 +1020,14 @@ private static void configureLog4J(String outputpath, String liferayhome) { .add( builder.newAppenderRef("ChangeLogFile"))); Configurator.reconfigure(builder.build()); } + public static File saveAsTempFile(User user, InputStream inputStream, String prefix, String suffix) throws IOException { + final File tempFile = File.createTempFile(prefix, suffix); + tempFile.deleteOnExit(); + // Set append to false, overwrite if file existed + try (FileOutputStream outputStream = new FileOutputStream(tempFile, false)) { + IOUtils.copy(inputStream, outputStream); + } + return tempFile; + } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentDatabaseHandler.java new file mode 100644 index 0000000000..95753798b6 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentDatabaseHandler.java @@ -0,0 +1,229 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.db.spdx.document; + +import com.cloudant.client.api.CloudantClient; + +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.common.DatabaseSettings; +import org.eclipse.sw360.datahandler.thrift.*; +import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.*; +import org.eclipse.sw360.datahandler.thrift.components.*; +import org.eclipse.sw360.datahandler.thrift.changelogs.*; +import org.eclipse.sw360.datahandler.db.ReleaseRepository; +import org.eclipse.sw360.datahandler.db.VendorRepository; +import org.eclipse.sw360.datahandler.db.DatabaseHandlerUtil; +import org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo.SpdxDocumentCreationInfoDatabaseHandler; +import org.eclipse.sw360.datahandler.db.spdx.packageinfo.SpdxPackageInfoDatabaseHandler; +import org.eclipse.sw360.datahandler.entitlement.SpdxDocumentModerator; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; +import org.eclipse.sw360.datahandler.common.CommonUtils; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +import java.net.MalformedURLException; +import java.util.*; +import java.util.function.Supplier; +import com.google.common.collect.Lists; + +import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotNull; +import static org.eclipse.sw360.datahandler.common.CommonUtils.*; +import static org.eclipse.sw360.datahandler.permissions.PermissionUtils.makePermission; +import static org.eclipse.sw360.datahandler.thrift.ThriftValidate.prepareSPDXDocument; + +/** + * Class for accessing SPDXDocument information from the database + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentDatabaseHandler { + + private static final Logger log = LogManager.getLogger(SpdxDocumentDatabaseHandler.class); + + /** + * Connection to the couchDB database + */ + private final DatabaseConnectorCloudant db; + private final DatabaseConnectorCloudant sw360db; + private final DatabaseConnectorCloudant dbChangeLogs; + + private final SpdxDocumentRepository SPDXDocumentRepository; + private final ReleaseRepository releaseRepository; + private final VendorRepository vendorRepository; + private DatabaseHandlerUtil dbHandlerUtil; + private final SpdxDocumentModerator moderator; + + private final SpdxDocumentCreationInfoDatabaseHandler creationInfoDatabaseHandler; + private final SpdxPackageInfoDatabaseHandler packageInfoDatabaseHandler; + + public SpdxDocumentDatabaseHandler(Supplier<CloudantClient> httpClient, String dbName) throws MalformedURLException { + db = new DatabaseConnectorCloudant(httpClient, dbName); + + // Create the repositories + SPDXDocumentRepository = new SpdxDocumentRepository(db); + + sw360db = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_DATABASE); + vendorRepository = new VendorRepository(sw360db); + releaseRepository = new ReleaseRepository(sw360db, vendorRepository); + // Create the moderator + moderator = new SpdxDocumentModerator(); + // Create the changelogs + dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); + this.creationInfoDatabaseHandler = new SpdxDocumentCreationInfoDatabaseHandler(httpClient, dbName); + this.packageInfoDatabaseHandler = new SpdxPackageInfoDatabaseHandler(httpClient, dbName); + } + + public List<SPDXDocument> getSPDXDocumentSummary(User user) { + List<SPDXDocument> spdxs = SPDXDocumentRepository.getSPDXDocumentSummary(); + return spdxs; + } + + public SPDXDocument getSPDXDocumentById(String id, User user) throws SW360Exception { + SPDXDocument spdx = SPDXDocumentRepository.get(id); + assertNotNull(spdx, "Could not find SPDX Document by id: " + id); + // Set permissions + if (user != null) { + makePermission(spdx, user).fillPermissions(); + } + return spdx; + } + + public SPDXDocument getSPDXDocumentForEdit(String id, User user) throws SW360Exception { + List<ModerationRequest> moderationRequestsForDocumentId = moderator.getModerationRequestsForDocumentId(id); + + SPDXDocument spdx = getSPDXDocumentById(id, user); + DocumentState documentState; + + if (moderationRequestsForDocumentId.isEmpty()) { + documentState = CommonUtils.getOriginalDocumentState(); + } else { + final String email = user.getEmail(); + Optional<ModerationRequest> moderationRequestOptional = CommonUtils.getFirstModerationRequestOfUser(moderationRequestsForDocumentId, email); + if (moderationRequestOptional.isPresent() + && isInProgressOrPending(moderationRequestOptional.get())){ + ModerationRequest moderationRequest = moderationRequestOptional.get(); + spdx = moderator.updateSPDXDocumentFromModerationRequest(spdx, moderationRequest.getSPDXDocumentAdditions(), moderationRequest.getSPDXDocumentDeletions()); + documentState = CommonUtils.getModeratedDocumentState(moderationRequest); + } else { + documentState = new DocumentState().setIsOriginalDocument(true).setModerationState(moderationRequestsForDocumentId.get(0).getModerationState()); + } + } + spdx.setPermissions(makePermission(spdx, user).getPermissionMap()); + spdx.setDocumentState(documentState); + return spdx; + } + + public AddDocumentRequestSummary addSPDXDocument(SPDXDocument spdx, User user) throws SW360Exception { + AddDocumentRequestSummary requestSummary= new AddDocumentRequestSummary(); + prepareSPDXDocument(spdx); + String releaseId = spdx.getReleaseId(); + Release release = releaseRepository.get(releaseId); + assertNotNull(release, "Could not find Release to add SPDX Document!"); + if (isNotNullEmptyOrWhitespace(release.getSpdxId())){ + log.error("SPDX Document existed in release!"); + return requestSummary.setRequestStatus(AddDocumentRequestStatus.DUPLICATE) + .setId(release.getSpdxId()); + } + spdx.setCreatedBy(user.getEmail()); + SPDXDocumentRepository.add(spdx); + String spdxId = spdx.getId(); + release.setSpdxId(spdxId); + releaseRepository.update(release); + dbHandlerUtil.addChangeLogs(spdx, null, user.getEmail(), Operation.CREATE, null, Lists.newArrayList(), null, null); + return requestSummary.setRequestStatus(AddDocumentRequestStatus.SUCCESS) + .setId(spdx.getId()); + } + + public RequestStatus updateSPDXDocument(SPDXDocument spdx, User user) throws SW360Exception { + prepareSPDXDocument(spdx); + SPDXDocument actual = SPDXDocumentRepository.get(spdx.getId()); + assertNotNull(actual, "Could not find SPDX Document to update!"); + if (!makePermission(spdx, user).isActionAllowed(RequestedAction.WRITE)) { + if (isChanged(actual, spdx)) { + return moderator.updateSPDXDocument(spdx, user); + } else { + return RequestStatus.SUCCESS; + } + } + SPDXDocumentRepository.update(spdx); + dbHandlerUtil.addChangeLogs(spdx, actual, user.getEmail(), Operation.UPDATE, null, Lists.newArrayList(), null, null); + return RequestStatus.SUCCESS; + } + + public RequestStatus updateSPDXDocumentFromModerationRequest(SPDXDocument spdxAdditions, SPDXDocument spdxDeletions, User user) throws SW360Exception { + try { + SPDXDocument spdx = getSPDXDocumentById(spdxAdditions.getId(), user); + spdx = moderator.updateSPDXDocumentFromModerationRequest(spdx, spdxAdditions, spdxDeletions); + return updateSPDXDocument(spdx, user); + } catch (SW360Exception e) { + log.error("Could not get original SPDX Document when updating from moderation request."); + return RequestStatus.FAILURE; + } + } + + public RequestStatus deleteSPDXDocument(String id, User user) throws SW360Exception { + SPDXDocument spdx = SPDXDocumentRepository.get(id); + assertNotNull(spdx, "Could not find SPDX Document to delete!"); + if (!makePermission(spdx, user).isActionAllowed(RequestedAction.WRITE)) { + return moderator.deleteSPDXDocument(spdx, user); + } + Set<String> packageInfoIds = spdx.getSpdxPackageInfoIds(); + if (packageInfoIds != null) { + for (String packageInfoId : packageInfoIds) { + packageInfoDatabaseHandler.deletePackageInformation(packageInfoId, user); + } + } + + Set<String> fileInfoIds = spdx.getSpdxFileInfoIds(); + if (fileInfoIds != null) { + return RequestStatus.IN_USE; + } + + String documentCreationId = spdx.getSpdxDocumentCreationInfoId(); + if (documentCreationId != null) { + creationInfoDatabaseHandler.deleteDocumentCreationInformation(documentCreationId, user); + } + + spdx.unsetSpdxPackageInfoIds(); + spdx.unsetSpdxDocumentCreationInfoId(); + + SPDXDocumentRepository.remove(id); + String releaseId = spdx.getReleaseId(); + if (isNotNullEmptyOrWhitespace(releaseId)) { + Release release = releaseRepository.get(releaseId); + assertNotNull(release, "Could not remove SPDX Document ID in Release!"); + Release oldRelease = release.deepCopy(); + release.unsetSpdxId(); + releaseRepository.update(release); + dbHandlerUtil.addChangeLogs(release, oldRelease, user.getEmail(), Operation.UPDATE, null, Lists.newArrayList(), spdx.getId(), Operation.SPDXDOCUMENT_DELETE); + } + return RequestStatus.SUCCESS; + } + + private boolean isChanged(SPDXDocument actual, SPDXDocument update) { + + for (SPDXDocument._Fields field : SPDXDocument._Fields.values()) { + if(update.getFieldValue(field) == null) { + continue; + } else if (actual.getFieldValue(field) == null) { + return true; + } else if (!actual.getFieldValue(field).equals(update.getFieldValue(field))) { + return true; + } + } + + return false; + } + +} \ No newline at end of file diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentRepository.java new file mode 100644 index 0000000000..a948506b60 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentRepository.java @@ -0,0 +1,45 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.db.spdx.document; + +import org.eclipse.sw360.components.summary.SpdxDocumentSummary; +import org.eclipse.sw360.components.summary.SummaryType; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.couchdb.SummaryAwareRepository; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; + +import com.cloudant.client.api.model.DesignDocument.MapReduce; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * CRUD access for the SPDXDocument class + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentRepository extends SummaryAwareRepository<SPDXDocument> { + + private static final String ALL = "function(doc) { if (doc.type == 'SPDXDocument') emit(null, doc._id) }"; + + public SpdxDocumentRepository(DatabaseConnectorCloudant db) { + super(SPDXDocument.class, db, new SpdxDocumentSummary()); + Map<String, MapReduce> views = new HashMap<String, MapReduce>(); + views.put("all", createMapReduce(ALL, null)); + initStandardDesignDocument(views, db); + } + + public List<SPDXDocument> getSPDXDocumentSummary() { + return makeSummary(SummaryType.SUMMARY, getAllIds()); + } + +} \ No newline at end of file diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java new file mode 100644 index 0000000000..0be9e71ae6 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/document/SpdxDocumentSearchHandler.java @@ -0,0 +1,53 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.db.spdx.document; + +import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.ektorp.http.HttpClient; + +import com.cloudant.client.api.CloudantClient; + +import java.io.IOException; +import java.util.List; +import java.util.function.Supplier; + +import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; + +/** + * Class for accessing the Lucene connector on the CouchDB database + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentSearchHandler { + + private static final LuceneSearchView luceneSearchView + = new LuceneSearchView("lucene", "SPDXDocument", + "function(doc) {" + + " if(doc.type == 'SPDXDocument') { " + + " var ret = new Document();" + + " ret.add(doc._id); " + + " return ret;" + + " }" + + "}"); + + private final LuceneAwareDatabaseConnector connector; + + public SpdxDocumentSearchHandler(Supplier<HttpClient> httpClient, Supplier<CloudantClient> cClient, String dbName) throws IOException { + connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); + connector.addView(luceneSearchView); + } + + public List<SPDXDocument> search(String searchText) { + return connector.searchView(SPDXDocument.class, luceneSearchView, prepareWildcardQuery(searchText)); + } +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoDatabaseHandler.java new file mode 100644 index 0000000000..ebfb0d531a --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoDatabaseHandler.java @@ -0,0 +1,196 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo; + +import com.cloudant.client.api.CloudantClient; + +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.db.spdx.document.SpdxDocumentRepository; +import org.eclipse.sw360.datahandler.thrift.*; +import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; +import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; +import org.eclipse.sw360.datahandler.thrift.changelogs.*; +import org.eclipse.sw360.datahandler.db.DatabaseHandlerUtil; +import org.eclipse.sw360.datahandler.entitlement.SpdxDocumentCreationInfoModerator; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; +import org.eclipse.sw360.datahandler.common.CommonUtils; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +import java.net.MalformedURLException; +import java.util.*; +import java.util.function.Supplier; +import com.google.common.collect.Lists; + +import org.eclipse.sw360.datahandler.common.DatabaseSettings; +import static org.eclipse.sw360.datahandler.common.CommonUtils.*; +import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotNull; +import static org.eclipse.sw360.datahandler.permissions.PermissionUtils.makePermission; +import static org.eclipse.sw360.datahandler.thrift.ThriftValidate.prepareSpdxDocumentCreationInfo; + +/** + * Class for accessing SPDX Document Creation Information from the database + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentCreationInfoDatabaseHandler { + + private static final Logger log = LogManager.getLogger(SpdxDocumentCreationInfoDatabaseHandler.class); + + /** + * Connection to the couchDB database + */ + private final DatabaseConnectorCloudant db; + private final DatabaseConnectorCloudant dbChangeLogs; + + private final SpdxDocumentCreationInfoRepository SPDXDocumentCreationInfoRepository; + private final SpdxDocumentRepository SPDXDocumentRepository; + private DatabaseHandlerUtil dbHandlerUtil; + private final SpdxDocumentCreationInfoModerator moderator; + + public SpdxDocumentCreationInfoDatabaseHandler(Supplier<CloudantClient> httpClient, String dbName) throws MalformedURLException { + db = new DatabaseConnectorCloudant(httpClient, dbName); + + // Create the repositories + SPDXDocumentCreationInfoRepository = new SpdxDocumentCreationInfoRepository(db); + SPDXDocumentRepository = new SpdxDocumentRepository(db); + // Create the moderator + moderator = new SpdxDocumentCreationInfoModerator(); + // Create the changelogs + dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); + } + + public List<DocumentCreationInformation> getDocumentCreationInformationSummary(User user) { + List<DocumentCreationInformation> documentCreationInfos = SPDXDocumentCreationInfoRepository.getDocumentCreationInformationSummary(); + return documentCreationInfos; + } + + public DocumentCreationInformation getDocumentCreationInformationById(String id, User user) throws SW360Exception { + DocumentCreationInformation documentCreationInfo = SPDXDocumentCreationInfoRepository.get(id); + assertNotNull(documentCreationInfo, "Could not find SPDX Document Creation Info by id: " + id); + // Set permissions + if (user != null) { + makePermission(documentCreationInfo, user).fillPermissions(); + } + return documentCreationInfo; + } + + public DocumentCreationInformation getDocumentCreationInfoForEdit(String id, User user) throws SW360Exception { + List<ModerationRequest> moderationRequestsForDocumentId = moderator.getModerationRequestsForDocumentId(id); + + DocumentCreationInformation documentCreationInfo = getDocumentCreationInformationById(id, user); + DocumentState documentState; + + if (moderationRequestsForDocumentId.isEmpty()) { + documentState = CommonUtils.getOriginalDocumentState(); + } else { + final String email = user.getEmail(); + Optional<ModerationRequest> moderationRequestOptional = CommonUtils.getFirstModerationRequestOfUser(moderationRequestsForDocumentId, email); + if (moderationRequestOptional.isPresent() + && isInProgressOrPending(moderationRequestOptional.get())){ + ModerationRequest moderationRequest = moderationRequestOptional.get(); + documentCreationInfo = moderator.updateSpdxDocumentCreationInfoFromModerationRequest(documentCreationInfo, moderationRequest.getDocumentCreationInfoAdditions(), moderationRequest.getDocumentCreationInfoDeletions()); + documentState = CommonUtils.getModeratedDocumentState(moderationRequest); + } else { + documentState = new DocumentState().setIsOriginalDocument(true).setModerationState(moderationRequestsForDocumentId.get(0).getModerationState()); + } + } + documentCreationInfo.setPermissions(makePermission(documentCreationInfo, user).getPermissionMap()); + documentCreationInfo.setDocumentState(documentState); + return documentCreationInfo; + } + + public AddDocumentRequestSummary addDocumentCreationInformation(DocumentCreationInformation documentCreationInfo, User user) throws SW360Exception { + AddDocumentRequestSummary requestSummary= new AddDocumentRequestSummary(); + prepareSpdxDocumentCreationInfo(documentCreationInfo); + String spdxDocumentId = documentCreationInfo.getSpdxDocumentId(); + SPDXDocument spdxDocument = SPDXDocumentRepository.get(spdxDocumentId); + assertNotNull(spdxDocument, "Could not find SPDX Document to add SPDX Document Creation Info!"); + if (isNotNullEmptyOrWhitespace(spdxDocument.getSpdxDocumentCreationInfoId())) { + log.error("SPDX Document Creation existed in SPDX Document!"); + return requestSummary.setRequestStatus(AddDocumentRequestStatus.DUPLICATE) + .setId(spdxDocumentId); + } + documentCreationInfo.setCreatedBy(user.getEmail()); + SPDXDocumentCreationInfoRepository.add(documentCreationInfo); + String documentCreationInfoId = documentCreationInfo.getId(); + spdxDocument.setSpdxDocumentCreationInfoId(documentCreationInfoId); + SPDXDocumentRepository.update(spdxDocument); + dbHandlerUtil.addChangeLogs(documentCreationInfo, null, user.getEmail(), Operation.CREATE, null, Lists.newArrayList(), null, null); + return requestSummary.setRequestStatus(AddDocumentRequestStatus.SUCCESS) + .setId(documentCreationInfoId); + } + + public RequestStatus updateDocumentCreationInformation(DocumentCreationInformation documentCreationInfo, User user) throws SW360Exception { + DocumentCreationInformation actual = SPDXDocumentCreationInfoRepository.get(documentCreationInfo.getId()); + assertNotNull(actual, "Could not find SPDX Document Creation Information to update!"); + prepareSpdxDocumentCreationInfo(documentCreationInfo); + if (!makePermission(documentCreationInfo, user).isActionAllowed(RequestedAction.WRITE)) { + if (isChanged(actual, documentCreationInfo)) { + return moderator.updateSpdxDocumentCreationInfo(documentCreationInfo, user); + } else { + return RequestStatus.SUCCESS; + } + } + SPDXDocumentCreationInfoRepository.update(documentCreationInfo); + dbHandlerUtil.addChangeLogs(documentCreationInfo, actual, user.getEmail(), Operation.UPDATE, null, Lists.newArrayList(), null, null); + return RequestStatus.SUCCESS; + } + + public RequestStatus updateDocumentCreationInfomationFromModerationRequest(DocumentCreationInformation documentCreationInfoAdditions, DocumentCreationInformation documentCreationInfoDeletions, User user) throws SW360Exception { + try { + DocumentCreationInformation documentCreationInfo = getDocumentCreationInformationById(documentCreationInfoAdditions.getId(), user); + documentCreationInfo = moderator.updateSpdxDocumentCreationInfoFromModerationRequest(documentCreationInfo, documentCreationInfoAdditions, documentCreationInfoDeletions); + return updateDocumentCreationInformation(documentCreationInfo, user); + } catch (SW360Exception e) { + log.error("Could not get original SPDX Document creation info when updating from moderation request."); + return RequestStatus.FAILURE; + } + } + + public RequestStatus deleteDocumentCreationInformation(String id, User user) throws SW360Exception { + DocumentCreationInformation documentCreationInfo = SPDXDocumentCreationInfoRepository.get(id); + assertNotNull(documentCreationInfo, "Could not find SPDX Document Creation Information to delete!"); + if (!makePermission(documentCreationInfo, user).isActionAllowed(RequestedAction.WRITE)) { + return moderator.deleteSpdxDocumentCreationInfo(documentCreationInfo, user); + } + SPDXDocumentCreationInfoRepository.remove(documentCreationInfo); + String spdxDocumentId = documentCreationInfo.getSpdxDocumentId(); + if (isNotNullEmptyOrWhitespace(spdxDocumentId)) { + SPDXDocument spdxDocument = SPDXDocumentRepository.get(spdxDocumentId); + assertNotNull(spdxDocument, "Could not remove SPDX Document Creation Info ID in SPDX Document!"); + SPDXDocument oldSpdxDocument = spdxDocument.deepCopy(); + spdxDocument.unsetSpdxDocumentCreationInfoId(); + SPDXDocumentRepository.update(spdxDocument); + dbHandlerUtil.addChangeLogs(spdxDocument, oldSpdxDocument, user.getEmail(), Operation.UPDATE, null, Lists.newArrayList(), documentCreationInfo.getId(), Operation.SPDX_DOCUMENT_CREATION_INFO_DELETE); + } + return RequestStatus.SUCCESS; + } + + private boolean isChanged(DocumentCreationInformation actual, DocumentCreationInformation update) { + + for (DocumentCreationInformation._Fields field : DocumentCreationInformation._Fields.values()) { + if(update.getFieldValue(field) == null) { + continue; + } else if (actual.getFieldValue(field) == null) { + return true; + } else if (!actual.getFieldValue(field).equals(update.getFieldValue(field))) { + return true; + } + } + + return false; + } + +} \ No newline at end of file diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoRepository.java new file mode 100644 index 0000000000..460eb21589 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoRepository.java @@ -0,0 +1,45 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo; + +import org.eclipse.sw360.components.summary.DocumentCreationInformationSummary; +import org.eclipse.sw360.components.summary.SummaryType; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.couchdb.SummaryAwareRepository; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; + +import com.cloudant.client.api.model.DesignDocument.MapReduce; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * CRUD access for the DocumentCreationInformation class + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentCreationInfoRepository extends SummaryAwareRepository<DocumentCreationInformation> { + + private static final String ALL = "function(doc) { if (doc.type == 'documentCreationInformation') emit(null, doc._id) }"; + + public SpdxDocumentCreationInfoRepository(DatabaseConnectorCloudant db) { + super(DocumentCreationInformation.class, db, new DocumentCreationInformationSummary()); + Map<String, MapReduce> views = new HashMap<String, MapReduce>(); + views.put("all", createMapReduce(ALL, null)); + initStandardDesignDocument(views, db); + } + + public List<DocumentCreationInformation> getDocumentCreationInformationSummary() { + return makeSummary(SummaryType.SUMMARY, getAllIds()); + } + +} \ No newline at end of file diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java new file mode 100644 index 0000000000..c3c47cd43b --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/documentcreationinfo/SpdxDocumentCreationInfoSearchHandler.java @@ -0,0 +1,53 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo; + +import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.ektorp.http.HttpClient; + +import com.cloudant.client.api.CloudantClient; + +import java.io.IOException; +import java.util.List; +import java.util.function.Supplier; + +import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; + +/** + * Class for accessing the Lucene connector on the CouchDB database + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentCreationInfoSearchHandler { + + private static final LuceneSearchView luceneSearchView + = new LuceneSearchView("lucene", "documentCreationInformation", + "function(doc) {" + + " if(doc.type == 'documentCreationInformation') { " + + " var ret = new Document();" + + " ret.add(doc._id); " + + " return ret;" + + " }" + + "}"); + + private final LuceneAwareDatabaseConnector connector; + + public SpdxDocumentCreationInfoSearchHandler(Supplier<HttpClient> httpClient, Supplier<CloudantClient> cClient, String dbName) throws IOException { + connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); + connector.addView(luceneSearchView); + } + + public List<DocumentCreationInformation> search(String searchText) { + return connector.searchView(DocumentCreationInformation.class, luceneSearchView, prepareWildcardQuery(searchText)); + } +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoDatabaseHandler.java new file mode 100644 index 0000000000..96752a94c2 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoDatabaseHandler.java @@ -0,0 +1,250 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.db.spdx.packageinfo; + +import com.cloudant.client.api.CloudantClient; + +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.db.spdx.document.SpdxDocumentRepository; +import org.eclipse.sw360.datahandler.thrift.*; +import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; +import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; +import org.eclipse.sw360.datahandler.thrift.changelogs.*; +import org.eclipse.sw360.datahandler.db.DatabaseHandlerUtil; +import org.eclipse.sw360.datahandler.entitlement.SpdxPackageInfoModerator; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; +import org.eclipse.sw360.datahandler.common.CommonUtils; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +import java.net.MalformedURLException; +import java.util.*; +import java.util.function.Supplier; +import com.google.common.collect.Lists; + +import org.eclipse.sw360.datahandler.common.DatabaseSettings; +import static org.eclipse.sw360.datahandler.common.CommonUtils.*; +import static org.eclipse.sw360.datahandler.common.SW360Assert.assertNotNull; +import static org.eclipse.sw360.datahandler.permissions.PermissionUtils.makePermission; +import static org.eclipse.sw360.datahandler.thrift.ThriftValidate.prepareSpdxPackageInfo; + +/** + * Class for accessing SPDX Package Information from the database + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxPackageInfoDatabaseHandler { + + private static final Logger log = LogManager.getLogger(SpdxPackageInfoDatabaseHandler.class); + private final DatabaseConnectorCloudant dbChangeLogs; + + /** + * Connection to the couchDB database + */ + private final DatabaseConnectorCloudant db; + + private final SpdxPackageInfoRepository PackageInfoRepository; + private final SpdxDocumentRepository SPDXDocumentRepository; + private DatabaseHandlerUtil dbHandlerUtil; + private final SpdxPackageInfoModerator moderator; + + public SpdxPackageInfoDatabaseHandler(Supplier<CloudantClient> httpClient, String dbName) throws MalformedURLException { + db = new DatabaseConnectorCloudant(httpClient, dbName); + + // Create the repositories + PackageInfoRepository = new SpdxPackageInfoRepository(db); + SPDXDocumentRepository = new SpdxDocumentRepository(db); + + // Create the moderator + moderator = new SpdxPackageInfoModerator(); + // Create the changelogs + dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); + this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); + } + + public List<PackageInformation> getPackageInformationSummary(User user) { + List<PackageInformation> packageInfos = PackageInfoRepository.getPackageInformationSummary(); + return packageInfos; + } + + public PackageInformation getPackageInformationById(String id, User user) throws SW360Exception { + PackageInformation packageInfo = PackageInfoRepository.get(id); + assertNotNull(packageInfo, "Could not find SPDX Package Info by id: " + id); + // Set permissions + if (user != null) { + makePermission(packageInfo, user).fillPermissions(); + } + return packageInfo; + } + + public PackageInformation getPackageInformationForEdit(String id, User user) throws SW360Exception { + List<ModerationRequest> moderationRequestsForDocumentId = moderator.getModerationRequestsForDocumentId(id); + + PackageInformation packageInfo = getPackageInformationById(id, user); + DocumentState documentState; + + if (moderationRequestsForDocumentId.isEmpty()) { + documentState = CommonUtils.getOriginalDocumentState(); + } else { + final String email = user.getEmail(); + Optional<ModerationRequest> moderationRequestOptional = CommonUtils.getFirstModerationRequestOfUser(moderationRequestsForDocumentId, email); + if (moderationRequestOptional.isPresent() + && isInProgressOrPending(moderationRequestOptional.get())){ + ModerationRequest moderationRequest = moderationRequestOptional.get(); + packageInfo = moderator.updateSpdxPackageInfoFromModerationRequest(packageInfo, moderationRequest.getPackageInfoAdditions(), moderationRequest.getPackageInfoDeletions()); + documentState = CommonUtils.getModeratedDocumentState(moderationRequest); + } else { + documentState = new DocumentState().setIsOriginalDocument(true).setModerationState(moderationRequestsForDocumentId.get(0).getModerationState()); + } + } + packageInfo.setPermissions(makePermission(packageInfo, user).getPermissionMap()); + packageInfo.setDocumentState(documentState); + return packageInfo; + } + + public AddDocumentRequestSummary addPackageInformation(PackageInformation packageInfo, User user) throws SW360Exception { + AddDocumentRequestSummary requestSummary = new AddDocumentRequestSummary(); + prepareSpdxPackageInfo(packageInfo); + packageInfo.setCreatedBy(user.getEmail()); + PackageInfoRepository.add(packageInfo); + String packageInfoId = packageInfo.getId(); + String spdxDocumentId = packageInfo.getSpdxDocumentId(); + SPDXDocument spdxDocument = SPDXDocumentRepository.get(spdxDocumentId); + assertNotNull(spdxDocument, "Could not find SPDX Document by id: " + spdxDocumentId); + Set<String> spdxPackageInfoIds = new HashSet<>(); + if (spdxDocument.getSpdxPackageInfoIds() != null) { + spdxPackageInfoIds = spdxDocument.getSpdxPackageInfoIds(); + } + spdxPackageInfoIds.add(packageInfoId); + spdxDocument.setSpdxPackageInfoIds(spdxPackageInfoIds); + SPDXDocumentRepository.update(spdxDocument); + dbHandlerUtil.addChangeLogs(packageInfo, null, user.getEmail(), Operation.CREATE, null, Lists.newArrayList(), null, null); + return requestSummary.setRequestStatus(AddDocumentRequestStatus.SUCCESS).setId(packageInfoId); + } + + public AddDocumentRequestSummary addPackageInformations(Set<PackageInformation> packageInfos, User user) throws SW360Exception { + AddDocumentRequestSummary requestSummary = new AddDocumentRequestSummary(); + String spdxDocumentId = packageInfos.iterator().next().getSpdxDocumentId(); + SPDXDocument spdxDocument = SPDXDocumentRepository.get(spdxDocumentId); + assertNotNull(spdxDocument, "Could not find SPDX Document by id: " + spdxDocumentId); + Set<String> packageInfoIds = new HashSet<>(); + if (spdxDocument.getSpdxPackageInfoIds() != null) { + packageInfoIds = spdxDocument.getSpdxPackageInfoIds(); + } + for (PackageInformation packageInfo : packageInfos) { + prepareSpdxPackageInfo(packageInfo); + packageInfo.setCreatedBy(user.getEmail()); + PackageInfoRepository.add(packageInfo); + packageInfoIds.add(packageInfo.getId()); + dbHandlerUtil.addChangeLogs(packageInfo, null, user.getEmail(), Operation.CREATE, null, Lists.newArrayList(), null, null); + } + spdxDocument.setSpdxPackageInfoIds(packageInfoIds); + SPDXDocumentRepository.update(spdxDocument); + return requestSummary.setRequestStatus(AddDocumentRequestStatus.SUCCESS).setId(spdxDocumentId); + } + + public RequestStatus updatePackageInformation(PackageInformation packageInfo, User user) throws SW360Exception { + prepareSpdxPackageInfo(packageInfo); + PackageInformation actual = PackageInfoRepository.get(packageInfo.getId()); + assertNotNull(actual, "Could not find SPDX Package Information to update!"); + if (!makePermission(packageInfo, user).isActionAllowed(RequestedAction.WRITE)) { + if (isChanged(actual, packageInfo)) { + return moderator.updateSpdxPackageInfo(packageInfo, user); + } else { + return RequestStatus.SUCCESS; + } + } + PackageInfoRepository.update(packageInfo); + dbHandlerUtil.addChangeLogs(packageInfo, actual, user.getEmail(), Operation.UPDATE, null, Lists.newArrayList(), null, null); + return RequestStatus.SUCCESS; + } + + public RequestSummary updatePackageInformations(Set<PackageInformation> packageInfos, User user) throws SW360Exception { + int countPackagesSendToModerator = 0; + for (PackageInformation packageInfo : packageInfos) { + PackageInformation actual = PackageInfoRepository.get(packageInfo.getId()); + assertNotNull(actual, "Could not find SPDX Package Information to update!"); + prepareSpdxPackageInfo(packageInfo); + if (!makePermission(packageInfos, user).isActionAllowed(RequestedAction.WRITE)) { + if (moderator.updateSpdxPackageInfo(packageInfo, user) == RequestStatus.SENT_TO_MODERATOR) { + countPackagesSendToModerator++; + } + } else { + PackageInfoRepository.update(packageInfo); + dbHandlerUtil.addChangeLogs(packageInfo, actual, user.getEmail(), Operation.UPDATE, null, Lists.newArrayList(), null, null); + } + } + RequestSummary requestSummary = new RequestSummary(); + if (countPackagesSendToModerator == packageInfos.size()) { + requestSummary.setRequestStatus(RequestStatus.SENT_TO_MODERATOR); + } else { + String message = "Send to moderator request " + countPackagesSendToModerator; + requestSummary.setMessage(message) + .setTotalAffectedElements(countPackagesSendToModerator) + .setTotalElements(packageInfos.size()) + .setRequestStatus(RequestStatus.SUCCESS); + } + return requestSummary; + } + + public RequestStatus updatePackageInfomationFromModerationRequest(PackageInformation packageInfoAdditions, PackageInformation packageInfoDeletions, User user) throws SW360Exception { + try { + PackageInformation packageInformation = getPackageInformationById(packageInfoAdditions.getId(), user); + packageInformation = moderator.updateSpdxPackageInfoFromModerationRequest(packageInformation, packageInfoAdditions, packageInfoDeletions); + return updatePackageInformation(packageInformation, user); + } catch (SW360Exception e) { + log.error("Could not get original SPDX Package info when updating from moderation request."); + return RequestStatus.FAILURE; + } + } + + public RequestStatus deletePackageInformation(String id, User user) throws SW360Exception { + PackageInformation packageInfo = PackageInfoRepository.get(id); + assertNotNull(packageInfo, "Could not find SPDX Package Information to delete!"); + if (!makePermission(packageInfo, user).isActionAllowed(RequestedAction.WRITE)) { + return moderator.deleteSpdxPackageInfo(packageInfo, user); + } + PackageInfoRepository.remove(packageInfo); + String spdxDocumentId = packageInfo.getSpdxDocumentId(); + SPDXDocument spdxDocument = SPDXDocumentRepository.get(spdxDocumentId); + assertNotNull(spdxDocument, "Could not find SPDX Document to remove Package Info!"); + SPDXDocument oldSpdxDocument = spdxDocument.deepCopy(); + Set<String> packageInfoIds = spdxDocument.getSpdxPackageInfoIds(); + if (packageInfoIds != null) { + packageInfoIds.remove(id); + spdxDocument.setSpdxPackageInfoIds(packageInfoIds); + SPDXDocumentRepository.update(spdxDocument); + dbHandlerUtil.addChangeLogs(spdxDocument, oldSpdxDocument, user.getEmail(), Operation.UPDATE, null, Lists.newArrayList(), packageInfo.getId(), Operation.SPDX_PACKAGE_INFO_DELETE); + } else { + log.warn("Could not remove Package Id from SPDX Documnet"); + } + + return RequestStatus.SUCCESS; + } + + private boolean isChanged(PackageInformation actual, PackageInformation update) { + + for (PackageInformation._Fields field : PackageInformation._Fields.values()) { + if(update.getFieldValue(field) == null) { + continue; + } else if (actual.getFieldValue(field) == null) { + return true; + } else if (!actual.getFieldValue(field).equals(update.getFieldValue(field))) { + return true; + } + } + + return false; + } + +} \ No newline at end of file diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoRepository.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoRepository.java new file mode 100644 index 0000000000..91a3a343c3 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoRepository.java @@ -0,0 +1,45 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.db.spdx.packageinfo; + +import org.eclipse.sw360.components.summary.PackageInformationSummary; +import org.eclipse.sw360.components.summary.SummaryType; +import org.eclipse.sw360.datahandler.cloudantclient.DatabaseConnectorCloudant; +import org.eclipse.sw360.datahandler.couchdb.SummaryAwareRepository; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; + +import com.cloudant.client.api.model.DesignDocument.MapReduce; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * CRUD access for the PackageInformation class + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxPackageInfoRepository extends SummaryAwareRepository<PackageInformation> { + + private static final String ALL = "function(doc) { if (doc.type == 'packageInformation') emit(null, doc._id) }"; + + public SpdxPackageInfoRepository(DatabaseConnectorCloudant db) { + super(PackageInformation.class, db, new PackageInformationSummary()); + Map<String, MapReduce> views = new HashMap<String, MapReduce>(); + views.put("all", createMapReduce(ALL, null)); + initStandardDesignDocument(views, db); + } + + public List<PackageInformation> getPackageInformationSummary() { + return makeSummary(SummaryType.SUMMARY, getAllIds()); + } + +} \ No newline at end of file diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java new file mode 100644 index 0000000000..03b65fadc5 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/spdx/packageinfo/SpdxPackageInfoSearchHandler.java @@ -0,0 +1,53 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.db.spdx.packageinfo; + +import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector; +import org.eclipse.sw360.datahandler.couchdb.lucene.LuceneSearchView; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; +import org.ektorp.http.HttpClient; + +import com.cloudant.client.api.CloudantClient; + +import java.io.IOException; +import java.util.List; +import java.util.function.Supplier; + +import static org.eclipse.sw360.datahandler.couchdb.lucene.LuceneAwareDatabaseConnector.prepareWildcardQuery; + +/** + * Class for accessing the Lucene connector on the CouchDB database + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxPackageInfoSearchHandler { + + private static final LuceneSearchView luceneSearchView + = new LuceneSearchView("lucene", "packageInformation", + "function(doc) {" + + " if(doc.type == 'packageInformation') { " + + " var ret = new Document();" + + " ret.add(doc._id); " + + " return ret;" + + " }" + + "}"); + + private final LuceneAwareDatabaseConnector connector; + + public SpdxPackageInfoSearchHandler(Supplier<HttpClient> httpClient, Supplier<CloudantClient> cClient, String dbName) throws IOException { + connector = new LuceneAwareDatabaseConnector(httpClient, cClient, dbName); + connector.addView(luceneSearchView); + } + + public List<PackageInformation> search(String searchText) { + return connector.searchView(PackageInformation.class, luceneSearchView, prepareWildcardQuery(searchText)); + } +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/entitlement/SpdxDocumentCreationInfoModerator.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/entitlement/SpdxDocumentCreationInfoModerator.java new file mode 100644 index 0000000000..2c362a1605 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/entitlement/SpdxDocumentCreationInfoModerator.java @@ -0,0 +1,162 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.entitlement; + +import org.eclipse.sw360.datahandler.common.Moderator; +import org.eclipse.sw360.datahandler.thrift.RequestStatus; +import org.eclipse.sw360.datahandler.thrift.ThriftClients; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.Creator; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.ExternalDocumentReferences; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationService; +import org.eclipse.sw360.datahandler.thrift.users.User; +import org.apache.logging.log4j.Logger; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.thrift.TException; + +/** + * Moderation for the SPDX DocumentCreationInformation service + * + * @author hieu1.phamvan@toshiba.co.jp + */ + +public class SpdxDocumentCreationInfoModerator + extends Moderator<DocumentCreationInformation._Fields, DocumentCreationInformation> { + + private static final Logger log = LogManager.getLogger(SpdxDocumentCreationInfoModerator.class); + + public SpdxDocumentCreationInfoModerator(ThriftClients thriftClients) { + super(thriftClients); + } + + public SpdxDocumentCreationInfoModerator() { + super(new ThriftClients()); + } + + public RequestStatus updateSpdxDocumentCreationInfo(DocumentCreationInformation documentCreationInfo, User user) { + + try { + ModerationService.Iface client = thriftClients.makeModerationClient(); + client.createSpdxDocumentCreationInfoRequest(documentCreationInfo, user); + return RequestStatus.SENT_TO_MODERATOR; + } catch (TException e) { + log.error("Could not moderate SPDX Document Creation Info " + documentCreationInfo.getId() + " for User " + + user.getEmail(), e); + return RequestStatus.FAILURE; + } + } + + public RequestStatus deleteSpdxDocumentCreationInfo(DocumentCreationInformation documentCreationInfo, User user) { + try { + ModerationService.Iface client = thriftClients.makeModerationClient(); + client.createSpdxDocumentCreationInfoDeleteRequest(documentCreationInfo, user); + return RequestStatus.SENT_TO_MODERATOR; + } catch (TException e) { + log.error("Could not moderate delete SPDX document creation information" + documentCreationInfo.getId() + " for User " + + user.getEmail(), e); + return RequestStatus.FAILURE; + } + } + + public DocumentCreationInformation updateSpdxDocumentCreationInfoFromModerationRequest( + DocumentCreationInformation documentCreationInfo, DocumentCreationInformation documentCreationInfoAdditions, + DocumentCreationInformation documentCreationInfoDeletions) { + for (DocumentCreationInformation._Fields field : DocumentCreationInformation._Fields.values()) { + if (documentCreationInfoAdditions.getFieldValue(field) == null + && documentCreationInfoDeletions.getFieldValue(field) == null) { + continue; + } + switch (field) { + case ID: + case REVISION: + case TYPE: + break; + case EXTERNAL_DOCUMENT_REFS: + documentCreationInfo = updateExternalDocumentRefs(documentCreationInfo, documentCreationInfoAdditions, documentCreationInfoDeletions); + break; + case CREATOR: + documentCreationInfo = updateCreator(documentCreationInfo, documentCreationInfoAdditions, documentCreationInfoDeletions); + break; + default: + documentCreationInfo = updateBasicField(field, DocumentCreationInformation.metaDataMap.get(field), + documentCreationInfo, documentCreationInfoAdditions, documentCreationInfoDeletions); + } + + } + return documentCreationInfo; + } + + private DocumentCreationInformation updateExternalDocumentRefs(DocumentCreationInformation documentCreationInfo, + DocumentCreationInformation documentCreationInfoAdditions, + DocumentCreationInformation documentCreationInfoDeletions) { + Set<ExternalDocumentReferences> actuals = documentCreationInfo.getExternalDocumentRefs(); + Iterator<ExternalDocumentReferences> additionsIterator = documentCreationInfoAdditions.getExternalDocumentRefsIterator(); + Iterator<ExternalDocumentReferences> deletionsIterator = documentCreationInfoDeletions.getExternalDocumentRefsIterator(); + if (additionsIterator == null && deletionsIterator == null) { + return documentCreationInfo; + } + if (actuals == null) { + actuals = new HashSet<>(); + } + while (additionsIterator.hasNext()) { + ExternalDocumentReferences additions = additionsIterator.next(); + ExternalDocumentReferences actual = new ExternalDocumentReferences(); + for (ExternalDocumentReferences._Fields field : ExternalDocumentReferences._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + actuals.add(actual); + } + while (deletionsIterator.hasNext()) { + ExternalDocumentReferences deletions = deletionsIterator.next(); + actuals.remove(deletions); + } + documentCreationInfo.setExternalDocumentRefs(actuals); + return documentCreationInfo; + } + + private DocumentCreationInformation updateCreator(DocumentCreationInformation documentCreationInfo, + DocumentCreationInformation documentCreationInfoAdditions, + DocumentCreationInformation documentCreationInfoDeletions) { + Set<Creator> actuals = documentCreationInfo.getCreator(); + Iterator<Creator> additionsIterator = documentCreationInfoAdditions.getCreatorIterator(); + Iterator<Creator> deletionsIterator = documentCreationInfoDeletions.getCreatorIterator(); + if (additionsIterator == null && deletionsIterator == null) { + return documentCreationInfo; + } + if (actuals == null) { + actuals = new HashSet<>(); + } + while (additionsIterator.hasNext()) { + Creator additions = additionsIterator.next(); + Creator actual = new Creator(); + for (Creator._Fields field : Creator._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + actuals.add(actual); + } + while (deletionsIterator.hasNext()) { + Creator deletions = deletionsIterator.next(); + actuals.remove(deletions); + } + documentCreationInfo.setCreator(actuals); + return documentCreationInfo; + } + +} \ No newline at end of file diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/entitlement/SpdxDocumentModerator.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/entitlement/SpdxDocumentModerator.java new file mode 100644 index 0000000000..3795fec517 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/entitlement/SpdxDocumentModerator.java @@ -0,0 +1,215 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.entitlement; + +import org.eclipse.sw360.datahandler.common.Moderator; +import org.eclipse.sw360.datahandler.thrift.RequestStatus; +import org.eclipse.sw360.datahandler.thrift.ThriftClients; +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.Annotations; +import org.eclipse.sw360.datahandler.thrift.spdx.otherlicensinginformationdetected.OtherLicensingInformationDetected; +import org.eclipse.sw360.datahandler.thrift.spdx.relationshipsbetweenspdxelements.RelationshipsBetweenSPDXElements; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationService; +import org.eclipse.sw360.datahandler.thrift.users.User; +import org.apache.logging.log4j.Logger; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.thrift.TException; + +/** + * Moderation for the SPDX Document service + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentModerator extends Moderator<SPDXDocument._Fields, SPDXDocument> { + + private static final Logger log = LogManager.getLogger(SpdxDocumentModerator.class); + + public SpdxDocumentModerator(ThriftClients thriftClients) { + super(thriftClients); + } + + public SpdxDocumentModerator() { + super(new ThriftClients()); + } + + public RequestStatus updateSPDXDocument(SPDXDocument spdx, User user) { + + try { + ModerationService.Iface client = thriftClients.makeModerationClient(); + client.createSPDXDocumentRequest(spdx, user); + return RequestStatus.SENT_TO_MODERATOR; + } catch (TException e) { + log.error("Could not moderate SPDX Document " + spdx.getId() + " for User " + user.getEmail(), e); + return RequestStatus.FAILURE; + } + } + + public RequestStatus deleteSPDXDocument(SPDXDocument spdx, User user) { + try { + ModerationService.Iface client = thriftClients.makeModerationClient(); + client.createSPDXDocumentDeleteRequest(spdx, user); + return RequestStatus.SENT_TO_MODERATOR; + } catch (TException e) { + log.error("Could not moderate delete SPDX document " + spdx.getId() + " for User " + user.getEmail(), e); + return RequestStatus.FAILURE; + } + } + + public SPDXDocument updateSPDXDocumentFromModerationRequest(SPDXDocument spdx, SPDXDocument spdxAdditions, SPDXDocument spdxDeletions) { + for (SPDXDocument._Fields field : SPDXDocument._Fields.values()) { + if(spdxAdditions.getFieldValue(field) == null && spdxDeletions.getFieldValue(field) == null) { + continue; + } + switch (field) { + case ID: + case REVISION: + case TYPE: + break; + case OTHER_LICENSING_INFORMATION_DETECTEDS: + spdx = updateOtherLicensingInformationDetecteds(spdx, spdxAdditions, spdxDeletions); + break; + case RELATIONSHIPS: + spdx = updateRelationships(spdx, spdxAdditions, spdxDeletions); + break; + case ANNOTATIONS: + spdx = updateAnnotaions(spdx, spdxAdditions, spdxDeletions); + break; + case SNIPPETS: + spdx = updateSnippets(spdx, spdxAdditions, spdxDeletions); + break; + default: + spdx = updateBasicField(field, SPDXDocument.metaDataMap.get(field), spdx, spdxAdditions, spdxDeletions); + } + + } + return spdx; + } + + private SPDXDocument updateOtherLicensingInformationDetecteds(SPDXDocument spdx, SPDXDocument spdxAdditions, SPDXDocument spdxDeletions) { + Set<OtherLicensingInformationDetected> actuals = spdx.getOtherLicensingInformationDetecteds(); + Iterator<OtherLicensingInformationDetected> additionsIterator = spdxAdditions.getOtherLicensingInformationDetectedsIterator(); + Iterator<OtherLicensingInformationDetected> deletionsIterator = spdxDeletions.getOtherLicensingInformationDetectedsIterator(); + if (additionsIterator == null && deletionsIterator == null) { + return spdx; + } + if (actuals == null) { + actuals = new HashSet<>(); + } + while (additionsIterator.hasNext()) { + OtherLicensingInformationDetected additions = additionsIterator.next(); + OtherLicensingInformationDetected actual = new OtherLicensingInformationDetected(); + for (OtherLicensingInformationDetected._Fields field : OtherLicensingInformationDetected._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + actuals.add(actual); + } + while(deletionsIterator.hasNext()) { + OtherLicensingInformationDetected deletions = deletionsIterator.next(); + actuals.remove(deletions); + } + + spdx.setOtherLicensingInformationDetecteds(actuals); + return spdx; + } + + private SPDXDocument updateRelationships(SPDXDocument spdx, SPDXDocument spdxAdditions, SPDXDocument spdxDeletions) { + Set<RelationshipsBetweenSPDXElements> actuals = spdx.getRelationships(); + Iterator<RelationshipsBetweenSPDXElements> additionsIterator = spdxAdditions.getRelationshipsIterator(); + Iterator<RelationshipsBetweenSPDXElements> deletionsIterator = spdxDeletions.getRelationshipsIterator(); + if (additionsIterator == null && deletionsIterator == null) { + return spdx; + } + if (actuals == null) { + actuals = new HashSet<>(); + } + while (additionsIterator.hasNext()) { + RelationshipsBetweenSPDXElements additions = additionsIterator.next(); + RelationshipsBetweenSPDXElements actual = new RelationshipsBetweenSPDXElements(); + for (RelationshipsBetweenSPDXElements._Fields field : RelationshipsBetweenSPDXElements._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + actuals.add(actual); + } + while (deletionsIterator.hasNext()) { + RelationshipsBetweenSPDXElements deletions = deletionsIterator.next(); + actuals.remove(deletions); + } + spdx.setRelationships(actuals); + return spdx; + } + + private SPDXDocument updateAnnotaions(SPDXDocument spdx, SPDXDocument spdxAdditions, SPDXDocument spdxDeletions) { + Set<Annotations> actuals = spdx.getAnnotations(); + Iterator<Annotations> additionsIterator = spdxAdditions.getAnnotationsIterator(); + Iterator<Annotations> deletionsIterator = spdxDeletions.getAnnotationsIterator(); + if (additionsIterator == null && deletionsIterator == null) { + return spdx; + } + if (actuals == null) { + actuals = new HashSet<>(); + } + while (additionsIterator.hasNext()) { + Annotations additions = additionsIterator.next(); + Annotations actual = new Annotations(); + for (Annotations._Fields field : Annotations._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + actuals.add(actual); + } + while (deletionsIterator.hasNext()) { + Annotations deletions = deletionsIterator.next(); + actuals.remove(deletions); + } + spdx.setAnnotations(actuals); + return spdx; + } + + private SPDXDocument updateSnippets(SPDXDocument spdx, SPDXDocument spdxAdditions, SPDXDocument spdxDeletions) { + Set<SnippetInformation> actuals = spdx.getSnippets(); + Iterator<SnippetInformation> additionsIterator = spdxAdditions.getSnippetsIterator(); + Iterator<SnippetInformation> deletionsIterator = spdxDeletions.getSnippetsIterator(); + if (additionsIterator == null && deletionsIterator == null) { + return spdx; + } + if (actuals == null) { + actuals = new HashSet<>(); + } + while (additionsIterator.hasNext()) { + SnippetInformation additions = additionsIterator.next(); + SnippetInformation actual = new SnippetInformation(); + for (SnippetInformation._Fields field : SnippetInformation._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + actuals.add(actual); + } + while (deletionsIterator.hasNext()) { + SnippetInformation deletions = deletionsIterator.next(); + actuals.remove(deletions); + } + spdx.setSnippets(actuals); + return spdx; + } + +} \ No newline at end of file diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/entitlement/SpdxPackageInfoModerator.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/entitlement/SpdxPackageInfoModerator.java new file mode 100644 index 0000000000..ef55f79886 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/entitlement/SpdxPackageInfoModerator.java @@ -0,0 +1,213 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.entitlement; + +import org.eclipse.sw360.datahandler.common.Moderator; +import org.eclipse.sw360.datahandler.thrift.RequestStatus; +import org.eclipse.sw360.datahandler.thrift.ThriftClients; +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.Annotations; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.CheckSum; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.ExternalReference; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageVerificationCode; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationService; +import org.eclipse.sw360.datahandler.thrift.users.User; + +import org.apache.logging.log4j.Logger; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.thrift.TException; + +/** + * Moderation for the SPDX PackageInformation service + * + * @author hieu1.phamvan@toshiba.co.jp + */ + +public class SpdxPackageInfoModerator extends Moderator<PackageInformation._Fields, PackageInformation> { + + private static final Logger log = LogManager.getLogger(SpdxPackageInfoModerator.class); + + + public SpdxPackageInfoModerator(ThriftClients thriftClients) { + super(thriftClients); + } + + public SpdxPackageInfoModerator() { + super(new ThriftClients()); + } + + public RequestStatus updateSpdxPackageInfo(PackageInformation packageInfo, User user) { + + try { + ModerationService.Iface client = thriftClients.makeModerationClient(); + client.createSpdxPackageInfoRequest(packageInfo, user); + return RequestStatus.SENT_TO_MODERATOR; + } catch (TException e) { + log.error("Could not moderate SPDX Package Info " + packageInfo.getId() + " for User " + user.getEmail(), e); + return RequestStatus.FAILURE; + } + } + + public RequestStatus deleteSpdxPackageInfo(PackageInformation packageInfo, User user) { + try { + ModerationService.Iface client = thriftClients.makeModerationClient(); + client.createSpdxPackageInfoDeleteRequest(packageInfo, user); + return RequestStatus.SENT_TO_MODERATOR; + } catch (TException e) { + log.error("Could not moderate delete SPDX document " + packageInfo.getId() + " for User " + user.getEmail(), e); + return RequestStatus.FAILURE; + } + } + + public PackageInformation updateSpdxPackageInfoFromModerationRequest(PackageInformation packageInfo, + PackageInformation packageInfoAdditions, + PackageInformation packageInfoDeletions) { + for (PackageInformation._Fields field : PackageInformation._Fields.values()) { + if(packageInfoAdditions.getFieldValue(field) == null && packageInfoDeletions.getFieldValue(field) == null){ + continue; + } + switch (field) { + case ID: + case REVISION: + case TYPE: + break; + case CHECKSUMS: + packageInfo = updateCheckSums(packageInfo, packageInfoAdditions, packageInfoDeletions); + break; + case EXTERNAL_REFS: + packageInfo = updateExternalReference(packageInfo, packageInfoAdditions, packageInfoDeletions); + break; + case ANNOTATIONS: + packageInfo = updateAnnotaions(packageInfo, packageInfoAdditions, packageInfoDeletions); + break; + case PACKAGE_VERIFICATION_CODE: + packageInfo = updatePackageVerificationCode(packageInfo, packageInfoAdditions, packageInfoDeletions); + break; + default: + packageInfo = updateBasicField(field, PackageInformation.metaDataMap.get(field), packageInfo, packageInfoAdditions, packageInfoDeletions); + } + + } + return packageInfo; + } + + private PackageInformation updateAnnotaions(PackageInformation packageInfo, PackageInformation packageInfoAdditions, PackageInformation packageInfoDeletions) { + Set<Annotations> actuals = packageInfo.getAnnotations(); + Iterator<Annotations> additionsIterator = packageInfoAdditions.getAnnotationsIterator(); + Iterator<Annotations> deletionsIterator = packageInfoDeletions.getAnnotationsIterator(); + if (additionsIterator == null && deletionsIterator == null) { + return packageInfo; + } + if (actuals == null) { + actuals = new HashSet<>(); + } + while (additionsIterator.hasNext()) { + Annotations additions = additionsIterator.next(); + Annotations actual = new Annotations(); + for (Annotations._Fields field : Annotations._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + actuals.add(actual); + } + while (deletionsIterator.hasNext()) { + Annotations deletions = deletionsIterator.next(); + actuals.remove(deletions); + } + packageInfo.setAnnotations(actuals); + return packageInfo; + } + + private PackageInformation updateCheckSums(PackageInformation packageInfo, PackageInformation packageInfoAdditions, PackageInformation packageInfoDeletions) { + Set<CheckSum> actuals = packageInfo.getChecksums(); + Iterator<CheckSum> additionsIterator = packageInfoAdditions.getChecksumsIterator(); + Iterator<CheckSum> deletionsIterator = packageInfoDeletions.getChecksumsIterator(); + if (additionsIterator == null && deletionsIterator == null) { + return packageInfo; + } + if (actuals == null) { + actuals = new HashSet<>(); + } + while (additionsIterator.hasNext()) { + CheckSum additions = additionsIterator.next(); + CheckSum actual = new CheckSum(); + for (CheckSum._Fields field : CheckSum._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + actuals.add(actual); + } + while (deletionsIterator.hasNext()) { + CheckSum deletions = deletionsIterator.next(); + actuals.remove(deletions); + } + packageInfo.setChecksums(actuals); + return packageInfo; + } + + private PackageInformation updateExternalReference(PackageInformation packageInfo, PackageInformation packageInfoAdditions, PackageInformation packageInfoDeletions) { + Set<ExternalReference> actuals = packageInfo.getExternalRefs(); + Iterator<ExternalReference> additionsIterator = packageInfoAdditions.getExternalRefsIterator(); + Iterator<ExternalReference> deletionsIterator = packageInfoDeletions.getExternalRefsIterator(); + if (additionsIterator == null && deletionsIterator == null) { + return packageInfo; + } + if (actuals == null) { + actuals = new HashSet<>(); + } + while (additionsIterator.hasNext()) { + ExternalReference additions = additionsIterator.next(); + ExternalReference actual = new ExternalReference(); + for (ExternalReference._Fields field : ExternalReference._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + actuals.add(actual); + } + while (deletionsIterator.hasNext()) { + ExternalReference deletions = deletionsIterator.next(); + actuals.remove(deletions); + } + packageInfo.setExternalRefs(actuals); + return packageInfo; + } + + private PackageInformation updatePackageVerificationCode(PackageInformation packageInfo, PackageInformation packageInfoAdditions, PackageInformation packageInfoDeletions) { + PackageVerificationCode actual = packageInfo.getPackageVerificationCode(); + PackageVerificationCode additions = packageInfoAdditions.getPackageVerificationCode(); + PackageVerificationCode deletions = packageInfoDeletions.getPackageVerificationCode(); + + if (additions == null && deletions == null) { + return packageInfo; + } + if (actual == null) { + actual = new PackageVerificationCode(); + } + + for (PackageVerificationCode._Fields field : PackageVerificationCode._Fields.values()) { + if (additions.isSet(field)) { + actual.setFieldValue(field, additions.getFieldValue(field)); + } + } + + packageInfo.setPackageVerificationCode(actual); + return packageInfo; + } + +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMExporter.java b/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMExporter.java new file mode 100644 index 0000000000..a9ae807b94 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMExporter.java @@ -0,0 +1,281 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.spdx; + +import org.eclipse.sw360.datahandler.thrift.*; +import org.eclipse.sw360.datahandler.thrift.components.Release; +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.*; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; +import org.eclipse.sw360.datahandler.thrift.spdx.otherlicensinginformationdetected.*; +import org.eclipse.sw360.datahandler.thrift.spdx.relationshipsbetweenspdxelements.*; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.*; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; +import org.spdx.rdfparser.InvalidSPDXAnalysisException; +import org.spdx.rdfparser.license.ExtractedLicenseInfo; +import org.spdx.tools.SpdxConverter; +import org.spdx.tools.SpdxConverterException; +import org.spdx.tools.RdfToTag; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.net.MalformedURLException; +import java.util.*; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.eclipse.sw360.datahandler.couchdb.DatabaseMixInForSPDXDocument.*; +public class SpdxBOMExporter { + private static final Logger log = LogManager.getLogger(SpdxBOMExporter.class); + private final SpdxBOMExporterSink sink; + private Set<ExtractedLicenseInfo> licenses = new HashSet<>(); + + public SpdxBOMExporter(SpdxBOMExporterSink sink) { + this.sink = sink; + } + + public RequestSummary exportSPDXFile(String releaseId, String outputFormat) throws SW360Exception, MalformedURLException, InvalidSPDXAnalysisException { + RequestSummary requestSummary = new RequestSummary(); + String verifyMessage = ""; + final String targetFileName = releaseId + "." + outputFormat.toLowerCase(); + log.info("Export to file: " + targetFileName); + + if (createSPDXJsonFomatFromSW360SPDX(releaseId)) { + if (outputFormat.equals("JSON")) { + //verifyMessage = convertJSONtoOutputFormat(targetFileName, releaseId + ".RDF"); + requestSummary.setMessage("Export to JSON format successfully !"); + return requestSummary.setRequestStatus(RequestStatus.SUCCESS); + } else { + verifyMessage = convertJSONtoOutputFormat(releaseId + ".json", targetFileName); + if (verifyMessage.isEmpty()) { + log.info("Export to " + targetFileName + " sucessfully"); + requestSummary.setMessage("Export to " + outputFormat + " format successfully !"); + return requestSummary.setRequestStatus(RequestStatus.SUCCESS); + } else { + log.error("Export to " + targetFileName + " error"); + requestSummary.setMessage(verifyMessage); + return requestSummary.setRequestStatus(RequestStatus.FAILURE); + } + } + } else { + log.error("Export to " + targetFileName + " error !!!"); + requestSummary.setMessage("Export to " + targetFileName + " error !!!"); + return requestSummary.setRequestStatus(RequestStatus.FAILURE); + } + } + + private String convertJSONtoOutputFormat(String jsonFileName, String outputFileName ) { + try { + log.info("Convert " + jsonFileName + " to " + outputFileName); + if (outputFileName.split("\\.")[1].equals("spdx")) { + SpdxConverter.convert(jsonFileName, "tmp.rdf"); + RdfToTag.main(new String[] {"tmp.rdf", outputFileName}); + } else { + SpdxConverter.convert(jsonFileName, outputFileName); + } + } catch (SpdxConverterException e) { + log.error("Convert to " + outputFileName + " file error !!!"); + e.printStackTrace(); + return e.getMessage(); + } + return ""; + } + + private boolean createSPDXJsonFomatFromSW360SPDX(String releaseId) throws SW360Exception { + final SPDXDocument sw360SPDXDocument = getSpdxDocumentFromRelease(releaseId); + final Set<PackageInformation> sw360PackageInformations = getPackagesInformationFromSpdxDocument(sw360SPDXDocument.getId()); + final DocumentCreationInformation sw360CreationInfo = getDocCreationInfoFromSpdxDocument(sw360SPDXDocument.getId()); + + // creating JSONObject + JSONObject SPDXJson = new JSONObject(); + Map<String, String> m = new LinkedHashMap<>(); + JSONParser parser = new JSONParser(); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + objectMapper.addMixInAnnotations(Annotations.class, AnnotationsMixin.class); + objectMapper.addMixInAnnotations(CheckSum.class, CheckSumMixin.class); + objectMapper.addMixInAnnotations(ExternalReference.class, ExternalReferenceMixin.class); + objectMapper.addMixInAnnotations(PackageInformation.class, PackageInformationMixin.class); + objectMapper.addMixInAnnotations(ExternalDocumentReferences.class, ExternalDocumentReferencesMixin.class); + objectMapper.addMixInAnnotations(SnippetInformation.class, SnippetInformationMixin.class); + objectMapper.addMixInAnnotations(SnippetRange.class, SnippetRangeMixin.class); + objectMapper.addMixInAnnotations(RelationshipsBetweenSPDXElements.class, RelationshipsBetweenSPDXElementsMixin.class); + objectMapper.addMixInAnnotations(OtherLicensingInformationDetected.class, OtherLicensingInformationDetectedMixin.class); + objectMapper.addMixInAnnotations(PackageVerificationCode.class, PackageVerificationCodeMixin.class); + + try { + // put package infomation to SPDX json + JSONArray SPDXPackageInfo = new JSONArray(); + JSONArray SDPXRelationships = (JSONArray) parser.parse(objectMapper.writeValueAsString(sw360SPDXDocument.getRelationships())); + for (PackageInformation sw360PackageInfo : sw360PackageInformations) { + log.info("Export Package Infomation: " + sw360PackageInfo.getName()); + JSONObject SW360SPDXPackageInfo = (JSONObject) parser.parse(objectMapper.writeValueAsString(sw360PackageInfo)); + + if (sw360PackageInfo.getPackageVerificationCode() != null) { + JSONObject packageVerificationCode = new JSONObject(); + JSONObject sw360packageVerificationCode = (JSONObject) parser.parse(objectMapper.writeValueAsString(sw360PackageInfo.getPackageVerificationCode())); + packageVerificationCode.put("packageVerificationCodeExcludedFiles", sw360packageVerificationCode.get("excludedFiles")); + packageVerificationCode.put("packageVerificationCodeValue", sw360packageVerificationCode.get("value")); + SW360SPDXPackageInfo.remove("packageVerificationCode"); + SW360SPDXPackageInfo.put("packageVerificationCode", packageVerificationCode); + } + + if (!sw360PackageInfo.getRelationships().isEmpty()) { + for (RelationshipsBetweenSPDXElements relationship : sw360PackageInfo.getRelationships()) { + JSONObject packageReleationship = (JSONObject) parser.parse(objectMapper.writeValueAsString(relationship)); + SDPXRelationships.add(packageReleationship); + } + } + + SW360SPDXPackageInfo.remove("relationships"); + SPDXPackageInfo.add(SW360SPDXPackageInfo); + } + SPDXJson.put("packages", SPDXPackageInfo); + + // put document creation infomation to SPDX json + JSONObject SW360SPDXCreationInfo = (JSONObject) parser.parse(objectMapper.writeValueAsString(sw360CreationInfo)); + Set<String> keys = new HashSet<>(Arrays.asList("spdxVersion", "dataLicense", "SPDXID", "name", "documentNamespace", "externalDocumentRefs", + "documentComment")); + + for (String key : keys) { + if (key.equals("documentNamespace")) { + String documentNamespace = SW360SPDXCreationInfo.get(key).toString(); + if (documentNamespace.substring(documentNamespace.length() - 1).equals("#")) { + SPDXJson.put(key, documentNamespace.substring(0, documentNamespace.length() - 1)); + } else { + SPDXJson.put(key, documentNamespace); + } + } else { + SPDXJson.put(key, SW360SPDXCreationInfo.get(key)); + } + } + + JSONObject creationInfo = new JSONObject(); + creationInfo.put("comment", SW360SPDXCreationInfo.get("creatorComment")); + creationInfo.put("created", SW360SPDXCreationInfo.get("created")); + creationInfo.put("licenseListVersion", SW360SPDXCreationInfo.get("licenseListVersion")); + + JSONArray SW360SPDXCreationInfoCreator = (JSONArray) parser.parse(objectMapper.writeValueAsString(sw360CreationInfo.getCreator())); + JSONArray creators = new JSONArray(); + SW360SPDXCreationInfoCreator.forEach(c -> { + JSONObject obj = (JSONObject) c; + String type = (String) obj.get("type"); + String value = (String) obj.get("value"); + creators.add(type + ": " +value); + }); + creationInfo.put("creators", creators); + SPDXJson.put("creationInfo", creationInfo); + + + // put spdx document to SPDX json + JSONArray files = new JSONArray(); + for (SnippetInformation snippet : sw360SPDXDocument.getSnippets()) { + if (!snippet.getSnippetFromFile().isEmpty()) { + JSONObject file = new JSONObject(); + file.put("SPDXID", snippet.getSnippetFromFile()); + file.put("fileName", snippet.getSnippetFromFile()); + file.put("comment", "File information is generated from snippet and only SPDXID is correct information"); + files.add(file); + } + } + SPDXJson.put("files", files); + + JSONArray snippets = (JSONArray) parser.parse(objectMapper.writeValueAsString(sw360SPDXDocument.getSnippets())); + snippets.forEach(s -> { + JSONObject snippet = (JSONObject) s; + JSONArray ranges = new JSONArray(); + + JSONArray snippetRanges = (JSONArray) snippet.get("snippetRanges"); + snippetRanges.forEach(r -> { + JSONObject rangeElement = (JSONObject) r; + JSONObject range = new JSONObject(); + if (rangeElement.get("rangeType").equals("LINE")) { + JSONObject startPointer = new JSONObject(); + startPointer.put("lineNumber", rangeElement.get("startPointer")); + startPointer.put("reference", rangeElement.get("reference")); + range.put("startPointer", startPointer); + + JSONObject endPointer = new JSONObject(); + endPointer.put("lineNumber", rangeElement.get("endPointer")); + endPointer.put("reference", rangeElement.get("reference")); + range.put("endPointer", endPointer); + } else { + JSONObject startPointer = new JSONObject(); + startPointer.put("offset", rangeElement.get("startPointer")); + startPointer.put("reference", rangeElement.get("reference")); + range.put("startPointer", startPointer); + + JSONObject endPointer = new JSONObject(); + endPointer.put("offset", rangeElement.get("endPointer")); + endPointer.put("reference", rangeElement.get("reference")); + range.put("endPointer", endPointer); + } + ranges.add(range); + }); + snippet.remove("snippetRanges"); + snippet.put("ranges", ranges); + }); + SPDXJson.put("snippets", snippets); + + SPDXJson.put("relationships", SDPXRelationships); + SPDXJson.put("annotations", (JSONArray) parser.parse(objectMapper.writeValueAsString(sw360SPDXDocument.getAnnotations()))); + SPDXJson.put("hasExtractedLicensingInfos", (JSONArray) parser.parse(objectMapper.writeValueAsString(sw360SPDXDocument.getOtherLicensingInformationDetecteds()))); + + PrintWriter pw = new PrintWriter(releaseId + ".json"); + pw.write(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(SPDXJson)); + pw.flush(); + pw.close(); + } catch (ParseException | JsonProcessingException | FileNotFoundException e) { + log.error("Can not convert SW360 SPDX Document to Json"); + e.printStackTrace(); + return false; + } + + return true; + } + + private SPDXDocument getSpdxDocumentFromRelease(String releaseId) throws SW360Exception { + SPDXDocument spdxDoc; + spdxDoc = null; + final Release release = sink.getRelease(releaseId); + if (release.isSetSpdxId()) { + spdxDoc = sink.getSPDXDocument(release.getSpdxId()); + } + return spdxDoc; + } + + private DocumentCreationInformation getDocCreationInfoFromSpdxDocument(String spdxDocId) throws SW360Exception { + DocumentCreationInformation info = null; + final SPDXDocument spdxDoc = sink.getSPDXDocument(spdxDocId); + if (spdxDoc.isSetSpdxDocumentCreationInfoId()) { + info = sink.getDocumentCreationInfo(spdxDoc.getSpdxDocumentCreationInfoId()); + } + return info; + } + + private Set<PackageInformation> getPackagesInformationFromSpdxDocument(String spdxDocId) throws SW360Exception { + Set<PackageInformation> infos = new HashSet<>(); + final SPDXDocument spdxDoc = sink.getSPDXDocument(spdxDocId); + for (String packageId : spdxDoc.getSpdxPackageInfoIds()) { + PackageInformation info = sink.getPackageInfo(packageId); + infos.add(info); + } + return infos; + } + +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMExporterSink.java b/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMExporterSink.java new file mode 100644 index 0000000000..177fe1f056 --- /dev/null +++ b/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMExporterSink.java @@ -0,0 +1,66 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.spdx; + +import org.eclipse.sw360.datahandler.common.DatabaseSettings; +import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; +import org.eclipse.sw360.datahandler.db.ProjectDatabaseHandler; +import org.eclipse.sw360.datahandler.thrift.*; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; + +import org.eclipse.sw360.datahandler.db.spdx.document.SpdxDocumentDatabaseHandler; +import org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo.SpdxDocumentCreationInfoDatabaseHandler; +import org.eclipse.sw360.datahandler.db.spdx.packageinfo.SpdxPackageInfoDatabaseHandler; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.sw360.datahandler.thrift.components.Release; +import org.eclipse.sw360.datahandler.thrift.users.User; + +import java.net.MalformedURLException; + +public class SpdxBOMExporterSink { + private static final Logger log = LogManager.getLogger(SpdxBOMExporterSink.class); + + private final ProjectDatabaseHandler projectDatabaseHandler; + private final ComponentDatabaseHandler componentDatabaseHandler; + private final SpdxDocumentDatabaseHandler spdxDocumentDatabaseHandler; + private final SpdxDocumentCreationInfoDatabaseHandler creationInfoDatabaseHandler; + private final SpdxPackageInfoDatabaseHandler packageInfoDatabaseHandler; + private final User user; + + public SpdxBOMExporterSink(User user, ProjectDatabaseHandler projectDatabaseHandler, ComponentDatabaseHandler componentDatabaseHandler) throws MalformedURLException { + this.projectDatabaseHandler = projectDatabaseHandler; + this.componentDatabaseHandler = componentDatabaseHandler; + this.spdxDocumentDatabaseHandler = new SpdxDocumentDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + this.creationInfoDatabaseHandler = new SpdxDocumentCreationInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + this.packageInfoDatabaseHandler = new SpdxPackageInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + this.user = user; + } + + public SPDXDocument getSPDXDocument(String id) throws SW360Exception { + return spdxDocumentDatabaseHandler.getSPDXDocumentById(id, user); + } + + public Release getRelease(String id) throws SW360Exception { + return componentDatabaseHandler.getRelease(id, user); + } + + public DocumentCreationInformation getDocumentCreationInfo(String id) throws SW360Exception { + return creationInfoDatabaseHandler.getDocumentCreationInformationById(id, user); + } + + public PackageInformation getPackageInfo(String id) throws SW360Exception { + return packageInfoDatabaseHandler.getPackageInformationById(id, user); + } +} diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMImporter.java b/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMImporter.java index d8695e3450..a75333bf71 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMImporter.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMImporter.java @@ -1,5 +1,7 @@ /* * Copyright Siemens AG, 2019. Part of the SW360 Portal Project. + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -14,20 +16,36 @@ import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentType; +import org.eclipse.sw360.datahandler.thrift.attachments.CheckStatus; import org.eclipse.sw360.datahandler.thrift.components.Component; import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.projects.Project; +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.*; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; +import org.eclipse.sw360.datahandler.thrift.spdx.fileinformation.*; +import org.eclipse.sw360.datahandler.thrift.spdx.otherlicensinginformationdetected.*; +import org.eclipse.sw360.datahandler.thrift.spdx.relationshipsbetweenspdxelements.*; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.*; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.*; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; import org.spdx.rdfparser.InvalidSPDXAnalysisException; import org.spdx.rdfparser.SPDXDocumentFactory; import org.spdx.rdfparser.model.*; +import org.spdx.rdfparser.model.pointer.*; +import org.spdx.rdfparser.license.ExtractedLicenseInfo; +import org.spdx.rdfparser.SpdxPackageVerificationCode; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.commons.lang3.ArrayUtils; import java.io.InputStream; +import java.net.MalformedURLException; import java.util.*; import java.util.stream.Collectors; +import static org.eclipse.sw360.datahandler.common.CommonUtils.isNotNullEmptyOrWhitespace; + public class SpdxBOMImporter { private static final Logger log = LogManager.getLogger(SpdxBOMImporter.class); private final SpdxBOMImporterSink sink; @@ -36,9 +54,41 @@ public SpdxBOMImporter(SpdxBOMImporterSink sink) { this.sink = sink; } - public RequestSummary importSpdxBOMAsRelease(InputStream inputStream, AttachmentContent attachmentContent) + public ImportBomRequestPreparation prepareImportSpdxBOMAsRelease(InputStream inputStream, AttachmentContent attachmentContent) throws InvalidSPDXAnalysisException, SW360Exception { - return importSpdxBOM(inputStream, attachmentContent, SW360Constants.TYPE_RELEASE); + final ImportBomRequestPreparation requestPreparation = new ImportBomRequestPreparation(); + final SpdxDocument spdxDocument = openAsSpdx(inputStream); + final List<SpdxItem> describedPackages = Arrays.stream(spdxDocument.getDocumentDescribes()) + .filter(item -> item instanceof SpdxPackage) + .collect(Collectors.toList()); + + if (describedPackages.size() == 0) { + requestPreparation.setMessage("The provided BOM did not contain any top level packages."); + requestPreparation.setRequestStatus(RequestStatus.FAILURE); + return requestPreparation; + } else if (describedPackages.size() > 1) { + requestPreparation.setMessage("The provided BOM file contained multiple described top level packages. This is not allowed here."); + requestPreparation.setRequestStatus(RequestStatus.FAILURE); + return requestPreparation; + } + + final SpdxItem spdxItem = describedPackages.get(0); + if (spdxItem instanceof SpdxPackage) { + final SpdxPackage spdxPackage = (SpdxPackage) spdxItem; + + requestPreparation.setName(spdxPackage.getName()); + requestPreparation.setVersion(spdxPackage.getVersionInfo()); + requestPreparation.setRequestStatus(RequestStatus.SUCCESS); + } else { + requestPreparation.setMessage("Failed to get spdx package from the provided BOM file."); + requestPreparation.setRequestStatus(RequestStatus.FAILURE); + } + return requestPreparation; + } + + public RequestSummary importSpdxBOMAsRelease(InputStream inputStream, AttachmentContent attachmentContent, String newReleaseVersion, String releaseId) + throws SW360Exception { + return importSpdxBOM(inputStream, attachmentContent, SW360Constants.TYPE_RELEASE, newReleaseVersion, releaseId); } public RequestSummary importSpdxBOMAsProject(InputStream inputStream, AttachmentContent attachmentContent) @@ -48,11 +98,22 @@ public RequestSummary importSpdxBOMAsProject(InputStream inputStream, Attachment private RequestSummary importSpdxBOM(InputStream inputStream, AttachmentContent attachmentContent, String type) throws InvalidSPDXAnalysisException, SW360Exception { + return importSpdxBOM(inputStream, attachmentContent, type, null, null); + } + + private RequestSummary importSpdxBOM(InputStream inputStream, AttachmentContent attachmentContent, String type, String newReleaseVersion, String releaseId) + throws SW360Exception { final RequestSummary requestSummary = new RequestSummary(); - final SpdxDocument spdxDocument = openAsSpdx(inputStream); - final List<SpdxItem> describedPackages = Arrays.stream(spdxDocument.getDocumentDescribes()) - .filter(item -> item instanceof SpdxPackage) - .collect(Collectors.toList()); + SpdxDocument spdxDocument = null; + List<SpdxItem> describedPackages = new ArrayList<>(); + try { + spdxDocument = openAsSpdx(inputStream); + describedPackages = Arrays.stream(spdxDocument.getDocumentDescribes()) + .filter(item -> item instanceof SpdxPackage) + .collect(Collectors.toList()); + } catch (InvalidSPDXAnalysisException e) { + log.error("Can not open file to SpdxDocument " +e); + } if (describedPackages.size() == 0) { requestSummary.setTotalAffectedElements(0); @@ -73,7 +134,7 @@ private RequestSummary importSpdxBOM(InputStream inputStream, AttachmentContent if (SW360Constants.TYPE_PROJECT.equals(type)) { response = importAsProject(spdxItem, attachmentContent); } else if (SW360Constants.TYPE_RELEASE.equals(type)) { - response = importAsRelease(spdxItem, attachmentContent); + response = importAsRelease(spdxItem, attachmentContent, spdxDocument, newReleaseVersion, releaseId); } else { throw new SW360Exception("Unsupported type=[" + type + "], can not import BOM"); } @@ -121,29 +182,497 @@ private Release createReleaseFromSpdxPackage(SpdxPackage spdxPackage) { return release; } + private SPDXDocument createSPDXDocumentFromSpdxDocument(String releaseId, SpdxDocument spdxDocument) throws SW360Exception, MalformedURLException { + final SPDXDocument doc = getSpdxDocumentFromRelease(releaseId); + doc.setReleaseId(releaseId); + try { + final SpdxSnippet[] spdxSnippets = spdxDocument.getDocumentContainer().findAllSnippets().toArray(new SpdxSnippet[0]); + final Relationship[] spdxRelationships = spdxDocument.getRelationships(); + final Annotation[] spdxAnnotations = spdxDocument.getAnnotations(); + final ExtractedLicenseInfo[] extractedLicenseInfos = spdxDocument.getExtractedLicenseInfos(); + + final Set<SnippetInformation> snippetInfos = createSnippetsFromSpdxSnippets(spdxSnippets); + final Set<RelationshipsBetweenSPDXElements> relationships = createRelationshipsFromSpdxRelationships(spdxRelationships, spdxDocument.getId()); + final Set<Annotations> annotations = createAnnotationsFromSpdxAnnotations(spdxAnnotations); + final Set<OtherLicensingInformationDetected> otherLicenses = createOtherLicensesFromSpdxExtractedLicenses(extractedLicenseInfos); + + doc.setSnippets(snippetInfos) + .setRelationships(relationships) + .setAnnotations(annotations) + .setOtherLicensingInformationDetecteds(otherLicenses); + } catch (InvalidSPDXAnalysisException e) { + log.error(e); + } + + return doc; + } + + private Set<Annotations> createAnnotationsFromSpdxAnnotations(Annotation[] spdxAnnotations) { + Set<Annotations> annotations = new HashSet<Annotations>(); + int index = 0; + + for(Annotation spdxAnn : spdxAnnotations) { + String annotator = spdxAnn.getAnnotator(); + String date = spdxAnn.getAnnotationDate(); + String type = spdxAnn.getAnnotationTypeTag(); + String comment = spdxAnn.getComment(); + + Annotations ann = new Annotations(); + ann.setAnnotator(verifyOrSetDefault(annotator)) + .setAnnotationDate(verifyOrSetDefault(date)) + .setAnnotationType(verifyOrSetDefault(type)) + .setAnnotationComment(verifyOrSetDefault(comment)) + .setIndex(index); + + annotations.add(ann); + index++; + } + + return annotations; + } + + private Set<SnippetInformation> createSnippetsFromSpdxSnippets(SpdxSnippet[] spdxSnippets) { + Set<SnippetInformation> snippets = new HashSet<SnippetInformation>(); + int index = 0; + + try { + for (SpdxSnippet spdxSnippet : spdxSnippets) { + String id = spdxSnippet.getId(); + String snippetFromFile = spdxSnippet.getSnippetFromFile().getId(); + Set<SnippetRange> ranges = createSnippetRangesFromSpdxSnippet(spdxSnippet); + String licenseConcluded = spdxSnippet.getLicenseConcluded().toString(); + Set<String> licenseInfoInFile = Arrays.stream(spdxSnippet.getLicenseInfoFromFiles()) + .map(license -> verifyOrSetDefault(license.toString())) + .collect(Collectors.toSet()); + String licenseComment = spdxSnippet.getLicenseComment(); + String copyrightText = spdxSnippet.getCopyrightText(); + String comment = spdxSnippet.getComment(); + String name = spdxSnippet.getName(); + String attributionText = String.join("|", spdxSnippet.getAttributionText()); + + SnippetInformation snippet = new SnippetInformation(); + snippet.setSPDXID(verifyOrSetDefault(id)) + .setSnippetFromFile(verifyOrSetDefault(snippetFromFile)) + .setSnippetRanges(ranges) + .setLicenseConcluded(verifyOrSetDefault(licenseConcluded)) + .setLicenseInfoInSnippets(licenseInfoInFile) + .setLicenseComments(verifyOrSetDefault(licenseComment)) + .setCopyrightText(verifyOrSetDefault(copyrightText)) + .setComment(verifyOrSetDefault(comment)) + .setName(verifyOrSetDefault(name)) + .setSnippetAttributionText(verifyOrSetDefault(attributionText)) + .setIndex(index); + + snippets.add(snippet); + index++; + } + } catch (InvalidSPDXAnalysisException e) { + log.error(e); + } + + return snippets; + } + + private Set<SnippetRange> createSnippetRangesFromSpdxSnippet(SpdxSnippet spdxSnippet) throws InvalidSPDXAnalysisException { + StartEndPointer spdxByteRange = spdxSnippet.getByteRange(); + String[] byteRanges = rangeToStrs(spdxByteRange); + SnippetRange snippetByteRange = new SnippetRange(); + snippetByteRange.setRangeType("BYTE") + .setStartPointer(byteRanges[0]) + .setEndPointer(byteRanges[1]) + .setReference(spdxByteRange.getStartPointer().getReference().getId()) + .setIndex(0); + + StartEndPointer spdxLineRange = spdxSnippet.getLineRange(); + String[] lineRanges = rangeToStrs(spdxLineRange); + SnippetRange snippetLineRange = new SnippetRange(); + snippetLineRange.setRangeType("LINE") + .setStartPointer(lineRanges[0]) + .setEndPointer(lineRanges[1]) + .setReference(spdxLineRange.getStartPointer().getReference().getId()) + .setIndex(1); + + return new HashSet<SnippetRange>(Arrays.asList(snippetByteRange, snippetLineRange)); + } + + // refer to rangeToStr function of spdx-tools + private String[] rangeToStrs(StartEndPointer rangePointer) throws InvalidSPDXAnalysisException { + SinglePointer startPointer = rangePointer.getStartPointer(); + if (startPointer == null) { + throw new InvalidSPDXAnalysisException("Missing start pointer"); + } + SinglePointer endPointer = rangePointer.getEndPointer(); + if (endPointer == null) { + throw new InvalidSPDXAnalysisException("Missing end pointer"); + } + String start = null; + if (startPointer instanceof ByteOffsetPointer) { + start = String.valueOf(((ByteOffsetPointer)startPointer).getOffset()); + } else if (startPointer instanceof LineCharPointer) { + start = String.valueOf(((LineCharPointer)startPointer).getLineNumber()); + } else { + log.error("Unknown pointer type for start pointer "+startPointer.toString()); + throw new InvalidSPDXAnalysisException("Unknown pointer type for start pointer"); + } + String end = null; + if (endPointer instanceof ByteOffsetPointer) { + end = String.valueOf(((ByteOffsetPointer)endPointer).getOffset()); + } else if (endPointer instanceof LineCharPointer) { + end = String.valueOf(((LineCharPointer)endPointer).getLineNumber()); + } else { + log.error("Unknown pointer type for start pointer "+startPointer.toString()); + throw new InvalidSPDXAnalysisException("Unknown pointer type for start pointer"); + } + return new String[] { start, end }; + } + + private Set<RelationshipsBetweenSPDXElements> createRelationshipsFromSpdxRelationships(Relationship[] spdxRelationships, String spdxElementId) { + Set<RelationshipsBetweenSPDXElements> relationships = new HashSet<RelationshipsBetweenSPDXElements>(); + int index = 0; + + for (Relationship spdxRelationship : spdxRelationships) { + if (!(spdxRelationship.getRelatedSpdxElement() instanceof SpdxFile)) { + String type = spdxRelationship.getRelationshipType().toTag(); + String relatedSpdxElement = spdxRelationship.getRelatedSpdxElement().getId(); + String comment = spdxRelationship.getComment(); + + RelationshipsBetweenSPDXElements relationship = new RelationshipsBetweenSPDXElements(); + relationship.setSpdxElementId(verifyOrSetDefault(spdxElementId)) + .setRelationshipType(verifyOrSetDefault(type)) + .setRelatedSpdxElement(verifyOrSetDefault(relatedSpdxElement)) + .setRelationshipComment(verifyOrSetDefault(comment)) + .setIndex(index); + + relationships.add(relationship); + index++; + } + } + + return relationships; + } + + private Set<OtherLicensingInformationDetected> createOtherLicensesFromSpdxExtractedLicenses(ExtractedLicenseInfo[] spdxExtractedLicenses) { + Set<OtherLicensingInformationDetected> otherLicenses = new HashSet<OtherLicensingInformationDetected>(); + int index = 0; + + for (ExtractedLicenseInfo spdxExtractedLicense : spdxExtractedLicenses) { + String licenseId = spdxExtractedLicense.getLicenseId(); + String extractedText = spdxExtractedLicense.getExtractedText(); + String name = spdxExtractedLicense.getName(); + Set<String> crossRef = new HashSet<String>(Arrays.asList(verifyOrSetDefault(spdxExtractedLicense.getCrossRef()))); + String comment = spdxExtractedLicense.getComment(); + + OtherLicensingInformationDetected otherLicense = new OtherLicensingInformationDetected(); + otherLicense.setLicenseId(verifyOrSetDefault(licenseId)) + .setExtractedText(verifyOrSetDefault(extractedText)) + .setLicenseName(verifyOrSetDefault(name)) + .setLicenseCrossRefs(crossRef) + .setLicenseComment(verifyOrSetDefault(comment)) + .setIndex(index); + + otherLicenses.add(otherLicense); + index++; + } + + return otherLicenses; + } + + private DocumentCreationInformation createDocumentCreationInfoFromSpdxDocument(String spdxDocId, SpdxDocument spdxDocument) throws SW360Exception, MalformedURLException { + final DocumentCreationInformation info = getDocCreationInfoFromSpdxDocument(spdxDocId); + info.setSpdxDocumentId(spdxDocId); + + try { + final String spdxVersion = spdxDocument.getSpecVersion(); + final String dataLicense = spdxDocument.getDataLicense().toString(); + final String spdxId = spdxDocument.getId(); + final String name = spdxDocument.getName(); + final String documentNamespace = spdxDocument.getDocumentContainer().getDocumentNamespace(); + final Set<ExternalDocumentReferences> refs = createExternalDocumentRefsFromSpdxDocument(spdxDocument); + final String licenseListVersion = spdxDocument.getCreationInfo().getLicenseListVersion(); + final Set<Creator> creators = createCreatorFromSpdxDocument(spdxDocument); + final String createdDate = spdxDocument.getCreationInfo().getCreated(); + final String creatorComment = spdxDocument.getCreationInfo().getComment(); + final String documentComment = spdxDocument.getDocumentComment(); + + info.setSpdxVersion(verifyOrSetDefault(spdxVersion)) + .setDataLicense(verifyOrSetDefault(dataLicense)) + .setSPDXID(verifyOrSetDefault(spdxId)) + .setName(verifyOrSetDefault(name)) + .setDocumentNamespace(verifyOrSetDefault(documentNamespace)) + .setExternalDocumentRefs(refs) + .setLicenseListVersion(verifyOrSetDefault(licenseListVersion)) + .setCreator(creators) + .setCreated(verifyOrSetDefault(createdDate)) + .setCreatorComment(verifyOrSetDefault(creatorComment)) + .setDocumentComment(verifyOrSetDefault(documentComment)); + } catch (InvalidSPDXAnalysisException e) { + log.error(e); + } + + return info; + } + + private Set<ExternalDocumentReferences> createExternalDocumentRefsFromSpdxDocument(SpdxDocument spdxDocument) { + Set<ExternalDocumentReferences> refs = new HashSet<ExternalDocumentReferences>(); + int index = 0; + + try { + ExternalDocumentRef[] externalDocumentRefs = spdxDocument.getDocumentContainer().getExternalDocumentRefs(); + + for (ExternalDocumentRef externalDocumentRef : externalDocumentRefs) { + Checksum spdxChecksum = externalDocumentRef.getChecksum(); + + String externalDocumentId = externalDocumentRef.getExternalDocumentId(); + String spdxDocumentNamespace = externalDocumentRef.getSpdxDocumentNamespace(); + CheckSum checksum = new CheckSum(); + checksum.setAlgorithm(org.spdx.rdfparser.model.Checksum.CHECKSUM_ALGORITHM_TO_TAG.get(spdxChecksum.getAlgorithm()).replace(":", "")) + .setChecksumValue(spdxChecksum.getValue()); + + ExternalDocumentReferences ref = new ExternalDocumentReferences(); + ref.setExternalDocumentId(verifyOrSetDefault(externalDocumentId)) + .setChecksum(checksum) + .setSpdxDocument(verifyOrSetDefault(spdxDocumentNamespace)) + .setIndex(index); + + refs.add(ref); + index++; + } + } catch (InvalidSPDXAnalysisException e) { + log.error(e); + } + + return refs; + } + + private Set<Creator> createCreatorFromSpdxDocument(SpdxDocument spdxDocument) { + Set<Creator> creators = new HashSet<Creator>(); + int index = 0; + + try { + String[] spdxCreators = spdxDocument.getCreationInfo().getCreators(); + + for (String spdxCreator : spdxCreators) { + String[] data = spdxCreator.split(":"); + if (data.length < 2) { + log.error("Failed to get SPDX creator from " + spdxCreator + "!"); + continue; + } + String type = data[0].trim(); + String value = spdxCreator.substring(data[0].length()+1).trim(); + + Creator creator = new Creator(); + creator.setType(verifyOrSetDefault(type)); + creator.setValue(verifyOrSetDefault(value)); + creator.setIndex(index); + + creators.add(creator); + index++; + } + } catch (InvalidSPDXAnalysisException e) { + log.error(e); + } + + return creators; + } + + private PackageInformation createPackageInfoFromSpdxPackage(String spdxDocId, SpdxPackage spdxPackage) throws SW360Exception, MalformedURLException { + PackageVerificationCode PVC = new PackageVerificationCode(); + try { + PVC = createPVCFromSpdxPackage(spdxPackage); + } catch (NullPointerException e) { + PVC.setExcludedFiles(Collections.emptySet()) + .setValue(verifyOrSetDefault("")); + log.error("Can not get PVC " + e); + } + + Set<CheckSum> checksums = new HashSet<>(); + try { + checksums = createCheckSumsFromSpdxChecksums(spdxPackage); + } catch (NullPointerException e) { + Collections.emptySet(); + log.error("Can not get checksums " + e); + } + + String licenseDeclared = ""; + try { + licenseDeclared = createLicenseDeclaredFromSpdxLicenseDeclared(spdxPackage); + } catch (NullPointerException e) { + log.error("Can not get licenseDeclared " + e); + } + + Set<ExternalReference> externalRefs = new HashSet<>(); + try { + externalRefs = createExternalReferenceFromSpdxPackage(spdxPackage); + } catch (NullPointerException e) { + log.error("Can not get externalRefs " + e); + externalRefs = Collections.emptySet(); + } + + PackageInformation pInfo = getPackageInformationFromSpdxDocument(spdxDocId, spdxPackage.getName()); + pInfo.setSpdxDocumentId(spdxDocId); + + try { + final String name = spdxPackage.getName(); + final String spdxId = spdxPackage.getId(); + final String versionInfo = spdxPackage.getVersionInfo(); + final String packageFileName = spdxPackage.getPackageFileName(); + final String supplier = spdxPackage.getSupplier(); + final String originator = spdxPackage.getOriginator(); + final String downloadLocation = spdxPackage.getDownloadLocation(); + final boolean fileAnalyzed = spdxPackage.isFilesAnalyzed(); + final String homepage = spdxPackage.getHomepage(); + final String sourceInfo = spdxPackage.getSourceInfo(); + final String licenseConcluded = spdxPackage.getLicenseConcluded().toString(); + final Set<String> licenseInfosFromFiles = Arrays.stream(spdxPackage.getLicenseInfoFromFiles()) + .map(license -> license.toString()) + .collect(Collectors.toSet()); + final String licenseComment = spdxPackage.getLicenseComment(); + final String copyrightText = spdxPackage.getCopyrightText(); + final String summary = spdxPackage.getSummary(); + final String description = spdxPackage.getDescription(); + final String comment = spdxPackage.getComment(); + final Set<String> attributionText = new HashSet<String>(Arrays.asList(verifyOrSetDefault(spdxPackage.getAttributionText()))); + final Set<Annotations> annotations = createAnnotationsFromSpdxAnnotations(spdxPackage.getAnnotations()); + + pInfo.setName(verifyOrSetDefault(name)) + .setSPDXID(verifyOrSetDefault(spdxId)) + .setVersionInfo(verifyOrSetDefault(versionInfo)) + .setPackageFileName(verifyOrSetDefault(packageFileName)) + .setSupplier(verifyOrSetDefault(supplier)) + .setOriginator(verifyOrSetDefault(originator)) + .setDownloadLocation(verifyOrSetDefault(downloadLocation)) + .setFilesAnalyzed(fileAnalyzed) + .setPackageVerificationCode(PVC) + .setChecksums(checksums) + .setHomepage(verifyOrSetDefault(homepage)) + .setSourceInfo(verifyOrSetDefault(sourceInfo)) + .setLicenseConcluded(verifyOrSetDefault(licenseConcluded)) + .setLicenseInfoFromFiles(licenseInfosFromFiles) + .setLicenseDeclared(verifyOrSetDefault(licenseDeclared)) + .setLicenseComments(verifyOrSetDefault(licenseComment)) + .setCopyrightText(verifyOrSetDefault(copyrightText)) + .setSummary(verifyOrSetDefault(summary)) + .setDescription(verifyOrSetDefault(description)) + .setPackageComment(verifyOrSetDefault(comment)) + .setExternalRefs(externalRefs) + .setAttributionText(attributionText) + .setAnnotations(annotations); + } catch (InvalidSPDXAnalysisException e) { + log.error("createPackageInfoFromSpdxPackage error " + e); + } + + return pInfo; + } + + private PackageVerificationCode createPVCFromSpdxPackage(SpdxPackage spdxPackage) { + try { + PackageVerificationCode PVC = new PackageVerificationCode(); + SpdxPackageVerificationCode spdxPVC = spdxPackage.getPackageVerificationCode(); + Set<String> excludedFileNames = new HashSet<String>(Arrays.asList(verifyOrSetDefault(spdxPVC.getExcludedFileNames()))); + String value = spdxPVC.getValue(); + + PVC.setExcludedFiles(excludedFileNames) + .setValue(verifyOrSetDefault(value)); + return PVC; + } catch (InvalidSPDXAnalysisException e) { + log.error("Error get PVC " + e); + return null; + } + } + + private Set<ExternalReference> createExternalReferenceFromSpdxPackage(SpdxPackage spdxPackage) { + Set<ExternalReference> refs = new HashSet<ExternalReference>(); + int index = 0; + + try { + ExternalRef[] spdxExternalRefs = spdxPackage.getExternalRefs(); + for (ExternalRef spdxRef : spdxExternalRefs) { + String category = spdxRef.getReferenceCategory().getTag(); + String locator = spdxRef.getReferenceLocator(); + String type = spdxRef.getReferenceType().toString(); + String comment = spdxRef.getComment(); + + ExternalReference ref = new ExternalReference(); + ref.setReferenceCategory(verifyOrSetDefault(category)) + .setReferenceLocator(verifyOrSetDefault(locator)) + .setReferenceType(verifyOrSetDefault(type)) + .setComment(verifyOrSetDefault(comment)) + .setIndex(index); + + refs.add(ref); + index++; + } + } catch (InvalidSPDXAnalysisException e) { + log.error(e); + } + + return refs; + } + + private Set<CheckSum> createCheckSumsFromSpdxChecksums(SpdxPackage spdxPackage) { + Set<CheckSum> checksums = new HashSet<CheckSum>(); + int index = 0; + try { + Checksum[] spdxChecksums = spdxPackage.getChecksums(); + for (Checksum spdxChecksum : spdxChecksums) { + String algorithm = org.spdx.rdfparser.model.Checksum.CHECKSUM_ALGORITHM_TO_TAG.get(spdxChecksum.getAlgorithm()).replace(":", ""); + String value = spdxChecksum.getValue(); + CheckSum checksum = new CheckSum(); + checksum.setAlgorithm(verifyOrSetDefault(algorithm)) + .setChecksumValue(verifyOrSetDefault(value)) + .setIndex(index); + checksums.add(checksum); + index++; + } + } catch (InvalidSPDXAnalysisException e) { + checksums = Collections.emptySet(); + } + return checksums; + } + + private String createLicenseDeclaredFromSpdxLicenseDeclared(SpdxPackage spdxPackage) { + try { + return spdxPackage.getLicenseDeclared().toString(); + } catch (InvalidSPDXAnalysisException e) { + log.error("Can not get licenseDeclared " + e); + } + return null; + } + private Attachment makeAttachmentFromContent(AttachmentContent attachmentContent) { Attachment attachment = new Attachment(); attachment.setAttachmentContentId(attachmentContent.getId()); - attachment.setAttachmentType(AttachmentType.OTHER); + attachment.setAttachmentType(AttachmentType.SBOM); attachment.setCreatedComment("Used for SPDX Bom import"); attachment.setFilename(attachmentContent.getFilename()); + attachment.setCheckStatus(CheckStatus.NOTCHECKED); return attachment; } private Optional<SpdxBOMImporterSink.Response> importAsRelease(SpdxElement relatedSpdxElement) throws SW360Exception { - return importAsRelease(relatedSpdxElement, null); + return importAsRelease(relatedSpdxElement, null, null, null, null); } - private Optional<SpdxBOMImporterSink.Response> importAsRelease(SpdxElement relatedSpdxElement, AttachmentContent attachmentContent) throws SW360Exception { + private Optional<SpdxBOMImporterSink.Response> importAsRelease(SpdxElement relatedSpdxElement, AttachmentContent attachmentContent, + SpdxDocument spdxDocument, String newReleaseVersion, String releaseId) throws SW360Exception { if (relatedSpdxElement instanceof SpdxPackage) { final SpdxPackage spdxPackage = (SpdxPackage) relatedSpdxElement; - SpdxBOMImporterSink.Response component = importAsComponent(spdxPackage); - final String componentId = component.getId(); - - final Release release = createReleaseFromSpdxPackage(spdxPackage); - release.setComponentId(componentId); + final Release release; + SpdxBOMImporterSink.Response component; + if (isNotNullEmptyOrWhitespace(releaseId)) { + release = sink.getRelease(releaseId); + component = new SpdxBOMImporterSink.Response(release.getComponentId(), true); + } else { + component = importAsComponent(spdxPackage); + final String componentId = component.getId(); + + release = createReleaseFromSpdxPackage(spdxPackage); + if (isNotNullEmptyOrWhitespace(newReleaseVersion)) + release.setVersion(newReleaseVersion); + release.setComponentId(componentId); + } final Relationship[] relationships = spdxPackage.getRelationships(); List<SpdxBOMImporterSink.Response> releases = importAsReleases(relationships); @@ -157,6 +686,13 @@ private Optional<SpdxBOMImporterSink.Response> importAsRelease(SpdxElement relat final SpdxBOMImporterSink.Response response = sink.addRelease(release); + + try { + importSpdxDocument(response.getId(), spdxDocument, spdxPackage); + } catch (MalformedURLException e) { + log.error(e); + } + response.addChild(component); return Optional.of(response); } else { @@ -165,6 +701,83 @@ private Optional<SpdxBOMImporterSink.Response> importAsRelease(SpdxElement relat } } + private void importSpdxDocument(String releaseId, SpdxDocument spdxDocument, SpdxPackage spdxPackage) throws SW360Exception, MalformedURLException { + final SPDXDocument spdxDoc = createSPDXDocumentFromSpdxDocument(releaseId, spdxDocument); + final SpdxBOMImporterSink.Response spdxDocRes = sink.addOrUpdateSpdxDocument(spdxDoc); + final String spdxDocId = spdxDocRes.getId(); + + final DocumentCreationInformation docCreationInfo = createDocumentCreationInfoFromSpdxDocument(spdxDocId, spdxDocument); + final SpdxBOMImporterSink.Response docCreationInfoRes = sink.addOrUpdateDocumentCreationInformation(docCreationInfo); + final String docCreationInfoId = docCreationInfoRes.getId(); + + List<SpdxPackage> packages = new ArrayList<>(); + try { + packages = spdxDocument.getDocumentContainer().findAllPackages(); + } catch (InvalidSPDXAnalysisException e) { + log.error("Can not get list package from SpdxDocument"); + e.printStackTrace(); + packages = Collections.emptyList(); + } + + int index = 1; + for (SpdxPackage packageElement : packages) { + log.info("Import package: " + packageElement.toString()); + PackageInformation packageInfo = createPackageInfoFromSpdxPackage(spdxDocId, packageElement); + if (ArrayUtils.isNotEmpty(packageElement.getRelationships())) { + Set<RelationshipsBetweenSPDXElements> packageReleaseRelationship = createRelationshipsFromSpdxRelationships(packageElement.getRelationships(), packageElement.getId()); + packageInfo.setRelationships(packageReleaseRelationship); + } else { + packageInfo.setRelationships(Collections.emptySet()); + } + + if (packageElement.getName().equals(spdxPackage.getName())) { + packageInfo.setIndex(0); + } else { + packageInfo.setIndex(index); + index ++; + } + SpdxBOMImporterSink.Response packageInfoRes = sink.addOrUpdatePackageInformation(packageInfo); + } + } + + private SPDXDocument getSpdxDocumentFromRelease(String releaseId) throws SW360Exception, MalformedURLException { + SPDXDocument spdxDoc; + final Release release = sink.getRelease(releaseId); + if (release.isSetSpdxId()) { + spdxDoc = sink.getSPDXDocument(release.getSpdxId()); + } else { + spdxDoc = new SPDXDocument(); + } + return spdxDoc; + } + + private DocumentCreationInformation getDocCreationInfoFromSpdxDocument(String spdxDocId) throws SW360Exception, MalformedURLException { + DocumentCreationInformation info; + final SPDXDocument spdxDoc = sink.getSPDXDocument(spdxDocId); + if (spdxDoc.isSetSpdxDocumentCreationInfoId()) { + info = sink.getDocumentCreationInfo(spdxDoc.getSpdxDocumentCreationInfoId()); + } else { + info = new DocumentCreationInformation(); + } + return info; + } + + private PackageInformation getPackageInformationFromSpdxDocument(String spdxDocId, String packageName) throws SW360Exception, MalformedURLException { + PackageInformation info; + final SPDXDocument spdxDoc = sink.getSPDXDocument(spdxDocId); + if (spdxDoc.getSpdxPackageInfoIdsSize() > 0) { + for (String packageId : spdxDoc.getSpdxPackageInfoIds()) { + if (sink.getPackageInfo(packageId).getName().equals(packageName)) { + info = sink.getPackageInfo(packageId); + return info; + } + } + return new PackageInformation(); + } else { + return new PackageInformation(); + } + } + private Map<String, ReleaseRelationship> makeReleaseIdToRelationship(List<SpdxBOMImporterSink.Response> releases) { return releases.stream() .collect(Collectors.toMap(SpdxBOMImporterSink.Response::getId, SpdxBOMImporterSink.Response::getReleaseRelationship)); @@ -237,4 +850,13 @@ private Optional<SpdxBOMImporterSink.Response> importAsProject(SpdxElement spdxE return Optional.empty(); } } + + + private String verifyOrSetDefault(String value) { + return (isNotNullEmptyOrWhitespace(value)) ? value : ""; + } + + private String[] verifyOrSetDefault(String[] values) { + return (values != null && values.length > 0) ? values : new String[0]; + } } diff --git a/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMImporterSink.java b/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMImporterSink.java index 8a1f07ee13..c28295d0eb 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMImporterSink.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/spdx/SpdxBOMImporterSink.java @@ -9,6 +9,7 @@ */ package org.eclipse.sw360.spdx; +import org.eclipse.sw360.datahandler.common.DatabaseSettings; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; import org.eclipse.sw360.datahandler.db.ProjectDatabaseHandler; @@ -16,12 +17,21 @@ import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; import org.eclipse.sw360.datahandler.thrift.components.Component; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; + +import org.eclipse.sw360.datahandler.db.spdx.document.SpdxDocumentDatabaseHandler; +import org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo.SpdxDocumentCreationInfoDatabaseHandler; +import org.eclipse.sw360.datahandler.db.spdx.packageinfo.SpdxPackageInfoDatabaseHandler; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.projects.Project; import org.eclipse.sw360.datahandler.thrift.users.User; +import java.net.MalformedURLException; import java.util.*; import java.util.stream.Collectors; @@ -30,11 +40,17 @@ public class SpdxBOMImporterSink { private final ProjectDatabaseHandler projectDatabaseHandler; private final ComponentDatabaseHandler componentDatabaseHandler; + private final SpdxDocumentDatabaseHandler spdxDocumentDatabaseHandler; + private final SpdxDocumentCreationInfoDatabaseHandler creationInfoDatabaseHandler; + private final SpdxPackageInfoDatabaseHandler packageInfoDatabaseHandler; private final User user; - public SpdxBOMImporterSink(User user, ProjectDatabaseHandler projectDatabaseHandler, ComponentDatabaseHandler componentDatabaseHandler) { + public SpdxBOMImporterSink(User user, ProjectDatabaseHandler projectDatabaseHandler, ComponentDatabaseHandler componentDatabaseHandler) throws MalformedURLException { this.projectDatabaseHandler = projectDatabaseHandler; this.componentDatabaseHandler = componentDatabaseHandler; + this.spdxDocumentDatabaseHandler = new SpdxDocumentDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + this.creationInfoDatabaseHandler = new SpdxDocumentCreationInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + this.packageInfoDatabaseHandler = new SpdxPackageInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); this.user = user; } @@ -68,6 +84,67 @@ public Response addRelease(Release release) throws SW360Exception { return new Response(releaseId, AddDocumentRequestStatus.SUCCESS.equals(addDocumentRequestSummary.getRequestStatus())); } + public Response addOrUpdateSpdxDocument(SPDXDocument spdxDocument) throws SW360Exception { + log.debug("create or update SPDXDocument"); + // SpdxDocumentDatabaseHandler handler = new SpdxDocumentDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + RequestStatus requestStatus; + String spdxDocId; + if (spdxDocument.isSetId()) { + requestStatus = spdxDocumentDatabaseHandler.updateSPDXDocument(spdxDocument, user); + spdxDocId = spdxDocument.getId(); + } else { + AddDocumentRequestSummary addDocumentRequestSummary = spdxDocumentDatabaseHandler.addSPDXDocument(spdxDocument, user); + requestStatus = RequestStatus.findByValue(addDocumentRequestSummary.getRequestStatus().getValue()); + spdxDocId = addDocumentRequestSummary.getId(); + } + + // final String spdxDocId = addDocumentRequestSummary.getId(); + if(spdxDocId == null || spdxDocId.isEmpty()) { + throw new SW360Exception("Id of spdx document should not be empty. " + requestStatus.toString()); + } + return new Response(spdxDocId, RequestStatus.SUCCESS.equals(requestStatus)); + } + + public Response addOrUpdateDocumentCreationInformation(DocumentCreationInformation documentCreationInfo) throws SW360Exception { + log.debug("create or update DocumentCreationInformation { name='" + documentCreationInfo.getName() + "' }"); + // SpdxDocumentCreationInfoDatabaseHandler handler = new SpdxDocumentCreationInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + RequestStatus requestStatus; + String docCreationInfoId; + if (documentCreationInfo.isSetId()) { + requestStatus = creationInfoDatabaseHandler.updateDocumentCreationInformation(documentCreationInfo, user); + docCreationInfoId = documentCreationInfo.getId(); + } else { + AddDocumentRequestSummary addDocumentRequestSummary = creationInfoDatabaseHandler.addDocumentCreationInformation(documentCreationInfo, user); + requestStatus = RequestStatus.findByValue(addDocumentRequestSummary.getRequestStatus().getValue()); + docCreationInfoId = addDocumentRequestSummary.getId(); + } + // final AddDocumentRequestSummary addDocumentRequestSummary = handler.addDocumentCreationInformation(documentCreationInfo, user); + + // final String docCreationInfoId = addDocumentRequestSummary.getId(); + if(docCreationInfoId == null || docCreationInfoId.isEmpty()) { + throw new SW360Exception("Id of added document creation information should not be empty. " + requestStatus.toString()); + } + return new Response(docCreationInfoId, RequestStatus.SUCCESS.equals(requestStatus)); + } + + public Response addOrUpdatePackageInformation(PackageInformation packageInfo) throws SW360Exception { + log.debug("create or update PackageInfomation { name='" + packageInfo.getName() + "' }"); + RequestStatus requestStatus; + String packageInfoId; + if (packageInfo.isSetId()) { + requestStatus = packageInfoDatabaseHandler.updatePackageInformation(packageInfo, user); + packageInfoId = packageInfo.getId(); + } else { + AddDocumentRequestSummary addDocumentRequestSummary = packageInfoDatabaseHandler.addPackageInformation(packageInfo, user); + requestStatus = RequestStatus.findByValue(addDocumentRequestSummary.getRequestStatus().getValue()); + packageInfoId = addDocumentRequestSummary.getId(); + } + if (packageInfoId == null || packageInfoId.isEmpty()) { + throw new SW360Exception("Id of added package information should not be empty. " + requestStatus.toString()); + } + return new Response(packageInfoId, RequestStatus.SUCCESS.equals(requestStatus)); + } + public Response addProject(Project project) throws SW360Exception { log.debug("create Project { name='" + project.getName() + "', version='" + project.getVersion() + "' }"); @@ -91,6 +168,25 @@ public Response addProject(Project project) throws SW360Exception { return new Response(projectId, AddDocumentRequestStatus.SUCCESS.equals(addDocumentRequestSummary.getRequestStatus())); } + public Release getRelease(String id) throws SW360Exception { + return componentDatabaseHandler.getRelease(id, user); + } + + public SPDXDocument getSPDXDocument(String id) throws SW360Exception { + // SpdxDocumentDatabaseHandler handler = new SpdxDocumentDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + return spdxDocumentDatabaseHandler.getSPDXDocumentById(id, user); + } + + public DocumentCreationInformation getDocumentCreationInfo(String id) throws SW360Exception { + // SpdxDocumentCreationInfoDatabaseHandler handler = new SpdxDocumentCreationInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + return creationInfoDatabaseHandler.getDocumentCreationInformationById(id, user); + } + + public PackageInformation getPackageInfo(String id) throws SW360Exception { + // SpdxPackageInfoDatabaseHandler handler = new SpdxPackageInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + return packageInfoDatabaseHandler.getPackageInformationById(id, user); + } + public static class Response { private final String id; private final List<Response> childs; diff --git a/backend/src-common/src/test/java/org/eclipse/sw360/spdx/SpdxBOMImporterTest.java b/backend/src-common/src/test/java/org/eclipse/sw360/spdx/SpdxBOMImporterTest.java index 7a26e3adea..8d00129808 100644 --- a/backend/src-common/src/test/java/org/eclipse/sw360/spdx/SpdxBOMImporterTest.java +++ b/backend/src-common/src/test/java/org/eclipse/sw360/spdx/SpdxBOMImporterTest.java @@ -83,7 +83,7 @@ public void testProject() throws Exception { @Test public void testRelease() throws Exception { - final RequestSummary requestSummary = spdxBOMImporter.importSpdxBOMAsRelease(inputStream, attachmentContent); + final RequestSummary requestSummary = spdxBOMImporter.importSpdxBOMAsRelease(inputStream, attachmentContent, null, null); assertNotNull(requestSummary); verify(spdxBOMImporterSink, times(4)).addComponent(Matchers.any()); diff --git a/backend/src/pom.xml b/backend/src/pom.xml index d759dec221..bef4ad19e3 100644 --- a/backend/src/pom.xml +++ b/backend/src/pom.xml @@ -42,6 +42,9 @@ <module>src-wsimport</module> <module>src-changelogs</module> <module>src-health</module> + <module>src-spdxdocument</module> + <module>src-spdxdocumentcreationinfo</module> + <module>src-spdxpackageinfo</module> </modules> <dependencies> diff --git a/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java b/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java index 684d1e29ca..7b2634d24c 100644 --- a/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java +++ b/backend/src/src-components/src/main/java/org/eclipse/sw360/components/ComponentHandler.java @@ -617,10 +617,25 @@ public String getCyclicLinkedReleasePath(Release release, User user) throws TExc } @Override - public RequestSummary importBomFromAttachmentContent(User user, String attachmentContentId) throws TException { + public ImportBomRequestPreparation prepareImportBom(User user, String attachmentContentId) throws TException { assertNotNull(attachmentContentId); assertUser(user); - return handler.importBomFromAttachmentContent(user, attachmentContentId); + return handler.prepareImportBom(user, attachmentContentId); + } + + @Override + public RequestSummary importBomFromAttachmentContent(User user, String attachmentContentId, String newReleaseVersion, String releaseId, String rdfFilePath) throws TException { + assertNotNull(attachmentContentId); + assertUser(user); + return handler.importBomFromAttachmentContent(user, attachmentContentId, newReleaseVersion, releaseId, rdfFilePath); + } + + + @Override + public RequestSummary exportSPDX(User user, String releaseId, String outputFormat) throws TException { + assertNotNull(releaseId); + assertUser(user); + return handler.exportSPDX(user, releaseId, outputFormat); } @Override diff --git a/backend/src/src-fossology/src/main/java/org/eclipse/sw360/fossology/rest/FossologyRestClient.java b/backend/src/src-fossology/src/main/java/org/eclipse/sw360/fossology/rest/FossologyRestClient.java index 90a003fdcb..7eb6276970 100644 --- a/backend/src/src-fossology/src/main/java/org/eclipse/sw360/fossology/rest/FossologyRestClient.java +++ b/backend/src/src-fossology/src/main/java/org/eclipse/sw360/fossology/rest/FossologyRestClient.java @@ -215,6 +215,7 @@ public int startScanning(int uploadId) { headers.set("Authorization", "Bearer " + token); headers.set("folderId", folderId); headers.set("uploadId", uploadId + ""); + headers.set("Content-Type", "application/json"); ObjectNode analysis = objectMapper.createObjectNode(); analysis.put("bucket", true); diff --git a/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParser.java b/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParser.java index 23fd02cba0..98ece9a9da 100644 --- a/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParser.java +++ b/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParser.java @@ -15,6 +15,7 @@ import org.eclipse.sw360.datahandler.thrift.SW360Exception; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; +import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentType; import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseInfoParsingResult; import org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseInfoRequestStatus; import org.eclipse.sw360.datahandler.thrift.users.User; @@ -60,16 +61,16 @@ public List<String> getApplicableFileExtensions() { @Override public <T> List<LicenseInfoParsingResult> getLicenseInfos(Attachment attachment, User user, T context) throws TException { - return Collections.singletonList(getLicenseInfo(attachment, true, user, context)); + return Collections.singletonList(getLicenseInfo(attachment, true, false, user, context)); } @Override public <T> List<LicenseInfoParsingResult> getLicenseInfosIncludeConcludedLicense(Attachment attachment, boolean includeConcludedLicense, User user, T context) throws TException { - return Collections.singletonList(getLicenseInfo(attachment, includeConcludedLicense, user, context)); + return Collections.singletonList(getLicenseInfo(attachment, includeConcludedLicense, false, user, context)); } - public <T> LicenseInfoParsingResult getLicenseInfo(Attachment attachment, boolean includeConcludedLicense, User user, + public <T> LicenseInfoParsingResult getLicenseInfo(Attachment attachment, boolean includeConcludedLicense, boolean includeFileInformation, User user, T context) throws TException { AttachmentContent attachmentContent = attachmentContentProvider.getAttachmentContent(attachment); @@ -78,8 +79,10 @@ public <T> LicenseInfoParsingResult getLicenseInfo(Attachment attachment, boolea return new LicenseInfoParsingResult() .setStatus(LicenseInfoRequestStatus.FAILURE); } - - return getLicenseInfoFromSpdx(attachmentContent, includeConcludedLicense, spdxDocument.get()); + if (attachment.getAttachmentType().equals(AttachmentType.INITIAL_SCAN_REPORT)) { + return getLicenseInfoFromSpdx(attachmentContent, includeConcludedLicense, true, spdxDocument.get()); + } + return getLicenseInfoFromSpdx(attachmentContent, includeConcludedLicense, false, spdxDocument.get()); } protected String getUriOfAttachment(AttachmentContent attachmentContent) throws URISyntaxException { diff --git a/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParserTools.java b/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParserTools.java index 6a810b3e0f..6d7b39e9dc 100644 --- a/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParserTools.java +++ b/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParserTools.java @@ -35,6 +35,7 @@ import org.apache.jena.rdf.model.impl.Util; +import static org.eclipse.sw360.datahandler.common.CommonUtils.isNotNullEmptyOrWhitespace; import static org.eclipse.sw360.datahandler.common.CommonUtils.isNullEmptyOrWhitespace; public class SPDXParserTools { @@ -57,6 +58,7 @@ public class SPDXParserTools { // SPDX Class private static final String SPDX_FILE = "File"; + private static final String SPDX_FILE_NAME = "fileName"; private static final String SPDX_LICENSE = "License"; private static final String SPDX_PACKAGE = "Package"; private static final String SPDX_EXTRACTED_LICENSING_INFO = "ExtractedLicensingInfo"; @@ -324,11 +326,20 @@ private static Node getLicenseDeclared(Node spdxPackage) { return licenseDeclareds.length > 0 ? licenseDeclareds[0] : null; } + private static LicenseNameWithText getLicenseObject(Element e) { + String licenseName = extractLicenseName(e); + String licenseID = getLicenseId(e); + String licenseText = getExtractedText(e); + if (isNullEmptyOrWhitespace(licenseText)) { + licenseText = getLicenseText(e); + } + return new LicenseNameWithText().setLicenseName(licenseName).setLicenseText(licenseText).setLicenseSpdxId(licenseID); + } /* * Get all licenses with text in spdx:ExtractedLicensingInfo and spdx:License * with their uri. */ - private static HashMap<String, LicenseNameWithText> getLicenseTextFromMetadata(Document doc) { + private static HashMap<String, LicenseNameWithText> getLicenseTextFromMetadata(Document doc, boolean includeFileInformation) { HashMap<String, LicenseNameWithText> uriLicenseMap = new HashMap<String, LicenseNameWithText>(); String[] licenseTags = { SPDX_EXTRACTED_LICENSING_INFO, SPDX_LICENSE }; @@ -346,17 +357,49 @@ private static HashMap<String, LicenseNameWithText> getLicenseTextFromMetadata(D if (isNullEmptyOrWhitespace(uri)) continue; - String licenseName = extractLicenseName(e); - String licenseID = getLicenseId(e); - String licenseText = getExtractedText(e); - if (isNullEmptyOrWhitespace(licenseText)) - licenseText = getLicenseText(e); - uriLicenseMap.put(uri, new LicenseNameWithText().setLicenseName(licenseName) - .setLicenseText(licenseText).setLicenseSpdxId(licenseID)); + uriLicenseMap.put(uri, getLicenseObject(e)); + } + } + } + if (includeFileInformation) { + Node[] files = findMultipleSpdxNodes(doc, SPDX_FILE); + // To link license with files -> adding source files into LicenseNameWithText + for (Node file : files) { + NodeList childNodes = file.getChildNodes(); + String sourceFileName = ""; + for (Node child : iterable(childNodes)) { + if (isNotNullEmptyOrWhitespace(child.getLocalName())) { + if (child.getLocalName().equals(SPDX_FILE_NAME)) { + sourceFileName = child.getTextContent(); + } else if (child.getLocalName().equals(SPDX_LICENSE_INFO_IN_FILE)) { + if (child instanceof Element) { + Element e = (Element) child; + String uri = e.getAttributeNS(RDF_NAMESPACE, RDF_RESOURCE); + + if (isNullEmptyOrWhitespace(uri)) { + continue; + } + LicenseNameWithText lnwt = uriLicenseMap.containsKey(uri) ? uriLicenseMap.get(uri) : getLicenseObject(e); + lnwt.addToSourceFiles(sourceFileName); + uriLicenseMap.put(uri, lnwt); + } + } else if (child.getLocalName().equals(SPDX_LICENSE_CONCLUDED)) { + if (child instanceof Element) { + Element e = (Element) child; + String uri = e.getAttributeNS(RDF_NAMESPACE, RDF_RESOURCE); + + if (isNullEmptyOrWhitespace(uri)) { + continue; + } + LicenseNameWithText lnwt = uriLicenseMap.containsKey(uri) ? uriLicenseMap.get(uri) : getLicenseObject(e); + lnwt.addToSourceFiles(sourceFileName); + uriLicenseMap.put(uri, lnwt); + } + } + } } } } - return uriLicenseMap; } @@ -607,13 +650,13 @@ private static Stream<String> getAllCopyrights(Node spdxItem) { } protected static LicenseInfoParsingResult getLicenseInfoFromSpdx(AttachmentContent attachmentContent, - boolean includeConcludedLicense, Document doc) { + boolean includeConcludedLicense, boolean includeFileInformation, Document doc) { LicenseInfo licenseInfo = new LicenseInfo().setFilenames(Arrays.asList(attachmentContent.getFilename())); licenseInfo.setLicenseNamesWithTexts(new HashSet<>()); licenseInfo.setCopyrights(new HashSet<>()); Set<String> concludedLicenseIds = Sets.newHashSet(); - uriLicenseMap = getLicenseTextFromMetadata(doc); + uriLicenseMap = getLicenseTextFromMetadata(doc, includeFileInformation); nodeIDFileMap = getFileFromMetadata(doc); for (Node spdxItem : getDocumentDescribes(doc)) { diff --git a/backend/src/src-licenseinfo/src/test/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParserTest.java b/backend/src/src-licenseinfo/src/test/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParserTest.java index 08b38a1916..56cdb17aab 100644 --- a/backend/src/src-licenseinfo/src/test/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParserTest.java +++ b/backend/src/src-licenseinfo/src/test/java/org/eclipse/sw360/licenseinfo/parsers/SPDXParserTest.java @@ -139,7 +139,7 @@ public void testAddSPDXContentToCLI(String exampleFile, List<String> expectedLic Document spdxDocument = dBuilder.parse(input); spdxDocument.getDocumentElement().normalize(); - LicenseInfoParsingResult result = SPDXParserTools.getLicenseInfoFromSpdx(attachmentContent, true, spdxDocument); + LicenseInfoParsingResult result = SPDXParserTools.getLicenseInfoFromSpdx(attachmentContent, true, false, spdxDocument); assertIsResultOfExample(result.getLicenseInfo(), exampleFile, expectedLicenses, numberOfCoyprights, exampleCopyright, exampleConcludedLicenseIds); } diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/ModerationHandler.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/ModerationHandler.java index 0ba92b6f73..91e854463e 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/ModerationHandler.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/ModerationHandler.java @@ -27,6 +27,9 @@ import org.eclipse.sw360.datahandler.thrift.projects.ClearingRequest; import org.eclipse.sw360.datahandler.thrift.projects.Project; import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; import org.eclipse.sw360.moderation.db.ModerationDatabaseHandler; import java.io.IOException; @@ -125,6 +128,54 @@ public void createProjectDeleteRequest(Project project, User user) throws TExcep handler.createRequest(project, user, true); } + @Override + public RequestStatus createSPDXDocumentRequest(SPDXDocument spdx, User user) throws TException { + assertUser(user); + assertNotNull(spdx); + + return handler.createRequest(spdx, user, false); + } + + @Override + public void createSPDXDocumentDeleteRequest(SPDXDocument spdx, User user) throws TException { + assertUser(user); + assertNotNull(spdx); + + handler.createRequest(spdx, user, true); + } + + @Override + public RequestStatus createSpdxDocumentCreationInfoRequest(DocumentCreationInformation documentCreationInfo, User user) throws TException { + assertUser(user); + assertNotNull(documentCreationInfo); + + return handler.createRequest(documentCreationInfo, user, false); + } + + @Override + public void createSpdxDocumentCreationInfoDeleteRequest(DocumentCreationInformation documentCreationInfo, User user) throws TException { + assertUser(user); + assertNotNull(documentCreationInfo); + + handler.createRequest(documentCreationInfo, user, true); + } + + @Override + public RequestStatus createSpdxPackageInfoRequest(PackageInformation packageInfo, User user) throws TException { + assertUser(user); + assertNotNull(packageInfo); + + return handler.createRequest(packageInfo, user, false); + } + + @Override + public void createSpdxPackageInfoDeleteRequest(PackageInformation packageInfo, User user) throws TException { + assertUser(user); + assertNotNull(packageInfo); + + handler.createRequest(packageInfo, user, true); + } + @Override public List<ModerationRequest> getModerationRequestByDocumentId(String documentId) throws TException { assertId(documentId); diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationDatabaseHandler.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationDatabaseHandler.java index 36fa89e849..9e787f49b0 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationDatabaseHandler.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ModerationDatabaseHandler.java @@ -24,6 +24,9 @@ import org.eclipse.sw360.datahandler.db.ComponentDatabaseHandler; import org.eclipse.sw360.datahandler.db.DatabaseHandlerUtil; import org.eclipse.sw360.datahandler.db.ProjectDatabaseHandler; +import org.eclipse.sw360.datahandler.db.spdx.document.SpdxDocumentDatabaseHandler; +import org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo.SpdxDocumentCreationInfoDatabaseHandler; +import org.eclipse.sw360.datahandler.db.spdx.packageinfo.SpdxPackageInfoDatabaseHandler; import org.eclipse.sw360.datahandler.permissions.PermissionUtils; import org.eclipse.sw360.datahandler.thrift.ClearingRequestEmailTemplate; import org.eclipse.sw360.datahandler.thrift.ClearingRequestState; @@ -43,6 +46,9 @@ import org.eclipse.sw360.datahandler.thrift.projects.ClearingRequest; import org.eclipse.sw360.datahandler.thrift.projects.Project; import org.eclipse.sw360.datahandler.thrift.projects.ProjectClearingState; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.users.UserGroup; @@ -90,6 +96,9 @@ public class ModerationDatabaseHandler { private final LicenseDatabaseHandler licenseDatabaseHandler; private final ProjectDatabaseHandler projectDatabaseHandler; private final ComponentDatabaseHandler componentDatabaseHandler; + private final SpdxDocumentDatabaseHandler spdxDocumentDatabaseHandler; + private final SpdxDocumentCreationInfoDatabaseHandler spdxDocumentCreationInfoDatabaseHandler; + private final SpdxPackageInfoDatabaseHandler spdxPackageInfoDatabaseHandler; private final DatabaseConnectorCloudant db; private DatabaseHandlerUtil dbHandlerUtil; @@ -105,6 +114,9 @@ public ModerationDatabaseHandler(Supplier<CloudantClient> httpClient, String dbN licenseDatabaseHandler = new LicenseDatabaseHandler(httpClient, dbName); projectDatabaseHandler = new ProjectDatabaseHandler(httpClient, dbName, attachmentDbName); componentDatabaseHandler = new ComponentDatabaseHandler(httpClient, dbName, attachmentDbName); + spdxDocumentDatabaseHandler = new SpdxDocumentDatabaseHandler(httpClient, DatabaseSettings.COUCH_DB_SPDX); + spdxDocumentCreationInfoDatabaseHandler = new SpdxDocumentCreationInfoDatabaseHandler(httpClient, DatabaseSettings.COUCH_DB_SPDX); + spdxPackageInfoDatabaseHandler = new SpdxPackageInfoDatabaseHandler(httpClient, DatabaseSettings.COUCH_DB_SPDX); DatabaseConnectorCloudant dbChangeLogs = new DatabaseConnectorCloudant(httpClient, DatabaseSettings.COUCH_DB_CHANGE_LOGS); this.dbHandlerUtil = new DatabaseHandlerUtil(dbChangeLogs); } @@ -583,6 +595,80 @@ public void createRequest(User user) { addOrUpdate(request, user); } + private Set<String> getSPDXDocumentModerators(String department, String createdBy) { + // Define moderators + Set<String> moderators = new HashSet<>(); + CommonUtils.add(moderators, createdBy); + CommonUtils.addAll(moderators, getUsersAtLeast(UserGroup.ECC_ADMIN, department, false, true)); + CommonUtils.addAll(moderators, getUsersAtLeast(UserGroup.ADMIN)); + return moderators; + } + + public RequestStatus createRequest(SPDXDocument spdx, User user, Boolean isDeleteRequest) { + SPDXDocument dbSpdx; + try{ + dbSpdx = spdxDocumentDatabaseHandler.getSPDXDocumentById(spdx.getId(), user); + } catch (SW360Exception e) { + log.error("Could not get original SPDX Document from database. Could not generate moderation request.", e); + return RequestStatus.FAILURE; + } + // Define moderators + Set<String> moderators = getSPDXDocumentModerators(user.getDepartment(), dbSpdx.getCreatedBy()); + ModerationRequest request = createStubRequest(user, isDeleteRequest, spdx.getId(), moderators); + + // Set meta-data + request.setDocumentType(DocumentType.SPDXDOCUMENT); + request.setDocumentName(SW360Utils.printName(spdx)); + + // Fill the request + ModerationRequestGenerator generator = new SpdxDocumentModerationRequestGenerator(); + request = generator.setAdditionsAndDeletions(request, spdx, dbSpdx); + addOrUpdate(request, user); + return RequestStatus.SENT_TO_MODERATOR; + } + + public RequestStatus createRequest(DocumentCreationInformation documentCreationInfo, User user, Boolean isDeleteRequest) { + DocumentCreationInformation dbDocumentCreationInfo; + try{ + dbDocumentCreationInfo = spdxDocumentCreationInfoDatabaseHandler.getDocumentCreationInformationById(documentCreationInfo.getId(), user); + } catch (SW360Exception e) { + log.error("Could not get original SPDX Document Creation Info from database. Could not generate moderation request.", e); + return RequestStatus.FAILURE; + } + // Define moderators + Set<String> moderators = getSPDXDocumentModerators(user.getDepartment(), dbDocumentCreationInfo.getCreatedBy()); + ModerationRequest request = createStubRequest(user, isDeleteRequest, documentCreationInfo.getId(), moderators); + // Set meta-data + request.setDocumentType(DocumentType.SPDX_DOCUMENT_CREATION_INFO); + request.setDocumentName(SW360Utils.printName(documentCreationInfo)); + // Fill the request + ModerationRequestGenerator generator = new SpdxDocumentCreationInfoModerationRequestGenerator(); + request = generator.setAdditionsAndDeletions(request, documentCreationInfo, dbDocumentCreationInfo); + addOrUpdate(request, user); + return RequestStatus.SENT_TO_MODERATOR; + } + + public RequestStatus createRequest(PackageInformation packageInfo, User user, Boolean isDeleteRequest) { + PackageInformation dbPackageInfo; + try{ + dbPackageInfo = spdxPackageInfoDatabaseHandler.getPackageInformationById(packageInfo.getId(), user); + } catch (SW360Exception e) { + log.error("Could not get original SPDX Package Info from database. Could not generate moderation request.", e); + return RequestStatus.FAILURE; + } + // Define moderators + Set<String> moderators = getSPDXDocumentModerators(user.getDepartment(), dbPackageInfo.getCreatedBy()); + ModerationRequest request = createStubRequest(user, isDeleteRequest, packageInfo.getId(), moderators); + // Set meta-data + request.setDocumentType(DocumentType.SPDX_PACKAGE_INFO); + request.setDocumentName(SW360Utils.printName(packageInfo)); + // Fill the request + ModerationRequestGenerator generator = new SpdxPackageInfoModerationRequestGenerator(); + request = generator.setAdditionsAndDeletions(request, packageInfo, dbPackageInfo); + addOrUpdate(request, user); + return RequestStatus.SENT_TO_MODERATOR; + } + private String getDepartmentByUserEmail(String userEmail) throws TException { UserService.Iface client = (new ThriftClients()).makeUserClient(); return client.getDepartmentByEmail(userEmail); diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ReleaseModerationRequestGenerator.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ReleaseModerationRequestGenerator.java index b868bfe792..0b61604f4e 100644 --- a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ReleaseModerationRequestGenerator.java +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/ReleaseModerationRequestGenerator.java @@ -24,8 +24,6 @@ */ public class ReleaseModerationRequestGenerator extends ModerationRequestGenerator<Release._Fields, Release> { - private static final String DUMMY_VALUE = "Dummy_Value"; - @Override public ModerationRequest setAdditionsAndDeletions(ModerationRequest request, Release updateRelease, Release actualRelease){ updateDocument = updateRelease; @@ -34,13 +32,13 @@ public ModerationRequest setAdditionsAndDeletions(ModerationRequest request, Rel documentAdditions = new Release(); documentDeletions = new Release(); //required fields: - documentAdditions.setName(DUMMY_VALUE); + documentAdditions.setName(updateRelease.getName()); documentAdditions.setId(updateRelease.getId()); - documentAdditions.setVersion(DUMMY_VALUE); + documentAdditions.setVersion(updateRelease.getVersion()); documentAdditions.setComponentId(updateRelease.getComponentId()); - documentDeletions.setName(DUMMY_VALUE); + documentDeletions.setName(actualRelease.getName()); documentDeletions.setId(actualRelease.getId()); - documentDeletions.setVersion(DUMMY_VALUE); + documentDeletions.setVersion(actualRelease.getVersion()); documentDeletions.setComponentId(actualRelease.getComponentId()); for (Release._Fields field : Release._Fields.values()) { diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/SpdxDocumentCreationInfoModerationRequestGenerator.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/SpdxDocumentCreationInfoModerationRequestGenerator.java new file mode 100644 index 0000000000..cb9d379b37 --- /dev/null +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/SpdxDocumentCreationInfoModerationRequestGenerator.java @@ -0,0 +1,55 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.moderation.db; + +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; + +/** + * Class for comparing a document with its counterpart in the database + * Writes the difference (= additions and deletions) to the moderation request + * + * @author hieu1.phamvan@toshiba.co.jp + */ + +public class SpdxDocumentCreationInfoModerationRequestGenerator extends ModerationRequestGenerator<DocumentCreationInformation._Fields, DocumentCreationInformation> { + @Override + public ModerationRequest setAdditionsAndDeletions(ModerationRequest request, DocumentCreationInformation updateDocumentCreationInfo, DocumentCreationInformation actualDocumentCreationInfo){ + updateDocument = updateDocumentCreationInfo; + actualDocument = actualDocumentCreationInfo; + + documentAdditions = new DocumentCreationInformation(); + documentDeletions = new DocumentCreationInformation(); + //required fields: + documentAdditions.setId(updateDocument.getId()); + documentDeletions.setId(actualDocument.getId()); + for (DocumentCreationInformation._Fields field : DocumentCreationInformation._Fields.values()) { + + if(actualDocument.getFieldValue(field) == null){ + documentAdditions.setFieldValue(field, updateDocument.getFieldValue(field)); + } else if (updateDocument.getFieldValue(field) == null){ + documentDeletions.setFieldValue(field, actualDocument.getFieldValue(field)); + } else if(!actualDocument.getFieldValue(field).equals(updateDocument.getFieldValue(field))) { + switch (field) { + case PERMISSIONS: + case DOCUMENT_STATE: + case CREATED_BY: + break; + default: + dealWithBaseTypes(field, DocumentCreationInformation.metaDataMap.get(field)); + } + } + } + request.setDocumentCreationInfoAdditions(documentAdditions); + request.setDocumentCreationInfoDeletions(documentDeletions); + return request; + } +} diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/SpdxDocumentModerationRequestGenerator.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/SpdxDocumentModerationRequestGenerator.java new file mode 100644 index 0000000000..c69470d7b8 --- /dev/null +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/SpdxDocumentModerationRequestGenerator.java @@ -0,0 +1,56 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.moderation.db; + +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.*; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; + +/** + * Class for comparing a document with its counterpart in the database + * Writes the difference (= additions and deletions) to the moderation request + * + * @author hieu1.phamvan@toshiba.co.jp + */ + +public class SpdxDocumentModerationRequestGenerator extends ModerationRequestGenerator<SPDXDocument._Fields, SPDXDocument> { + @Override + public ModerationRequest setAdditionsAndDeletions(ModerationRequest request, SPDXDocument updateSpdx, SPDXDocument actualSpdx){ + updateDocument = updateSpdx; + actualDocument = actualSpdx; + + documentAdditions = new SPDXDocument(); + documentDeletions = new SPDXDocument(); + //required fields: + documentAdditions.setId(updateSpdx.getId()); + documentDeletions.setId(actualSpdx.getId()); + + for (SPDXDocument._Fields field : SPDXDocument._Fields.values()) { + + if(actualSpdx.getFieldValue(field) == null){ + documentAdditions.setFieldValue(field, updateSpdx.getFieldValue(field)); + } else if (updateSpdx.getFieldValue(field) == null){ + documentDeletions.setFieldValue(field, actualSpdx.getFieldValue(field)); + } else if(!actualSpdx.getFieldValue(field).equals(updateSpdx.getFieldValue(field))) { + switch (field) { + case PERMISSIONS: + case DOCUMENT_STATE: + case CREATED_BY: + break; + default: + dealWithBaseTypes(field, SPDXDocument.metaDataMap.get(field)); + } + } + } + request.setSPDXDocumentAdditions(documentAdditions); + request.setSPDXDocumentDeletions(documentDeletions); + return request; + } +} diff --git a/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/SpdxPackageInfoModerationRequestGenerator.java b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/SpdxPackageInfoModerationRequestGenerator.java new file mode 100644 index 0000000000..2933674f1b --- /dev/null +++ b/backend/src/src-moderation/src/main/java/org/eclipse/sw360/moderation/db/SpdxPackageInfoModerationRequestGenerator.java @@ -0,0 +1,60 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.moderation.db; + +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; +import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; +import org.apache.thrift.protocol.TType; + +/** + * Class for comparing a document with its counterpart in the database + * Writes the difference (= additions and deletions) to the moderation request + * + * @author hieu1.phamvan@toshiba.co.jp + */ + +public class SpdxPackageInfoModerationRequestGenerator extends ModerationRequestGenerator<PackageInformation._Fields, PackageInformation> { + @Override + public ModerationRequest setAdditionsAndDeletions(ModerationRequest request, PackageInformation updatePackageInfo, PackageInformation actualPackageInfo){ + updateDocument = updatePackageInfo; + actualDocument = actualPackageInfo; + + documentAdditions = new PackageInformation(); + documentDeletions = new PackageInformation(); + //required fields: + documentAdditions.setId(updateDocument.getId()); + documentDeletions.setId(actualDocument.getId()); + + for (PackageInformation._Fields field : PackageInformation._Fields.values()) { + if(PackageInformation.metaDataMap.get(field).valueMetaData.type == TType.BOOL && + actualDocument.getFieldValue(field) != updateDocument.getFieldValue(field)) { + documentAdditions.setFieldValue(field, updateDocument.getFieldValue(field)); + documentDeletions.setFieldValue(field, actualDocument.getFieldValue(field)); + } else if(actualDocument.getFieldValue(field) == null){ + documentAdditions.setFieldValue(field, updateDocument.getFieldValue(field)); + } else if (updateDocument.getFieldValue(field) == null){ + documentDeletions.setFieldValue(field, actualDocument.getFieldValue(field)); + } else if(!actualDocument.getFieldValue(field).equals(updateDocument.getFieldValue(field))) { + switch (field) { + case PERMISSIONS: + case DOCUMENT_STATE: + case CREATED_BY: + break; + default: + dealWithBaseTypes(field, PackageInformation.metaDataMap.get(field)); + } + } + } + request.setPackageInfoAdditions(documentAdditions); + request.setPackageInfoDeletions(documentDeletions); + return request; + } +} diff --git a/backend/src/src-spdxdocument/pom.xml b/backend/src/src-spdxdocument/pom.xml new file mode 100644 index 0000000000..bff06e891e --- /dev/null +++ b/backend/src/src-spdxdocument/pom.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + ~ + ~ Author : hieu1.phamvan@toshiba.co.jp + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.sw360</groupId> + <artifactId>backend-src</artifactId> + <version>${revision}</version> + </parent> + + <artifactId>src-spdxdocument</artifactId> + <packaging>jar</packaging> + <name>src-spdxdocument</name> + + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-1.2-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + </dependencies> +</project> diff --git a/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentHandler.java b/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentHandler.java new file mode 100644 index 0000000000..cbd01d6874 --- /dev/null +++ b/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentHandler.java @@ -0,0 +1,95 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.spdxdocument; + +import org.eclipse.sw360.datahandler.common.DatabaseSettings; +import org.eclipse.sw360.datahandler.db.spdx.document.*; +import org.eclipse.sw360.datahandler.thrift.RequestStatus; +import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.*; +import org.eclipse.sw360.datahandler.thrift.users.User; + +import com.cloudant.client.api.CloudantClient; + +import org.apache.thrift.TException; + +import java.net.MalformedURLException; +import java.util.List; +import java.util.function.Supplier; + +import static org.eclipse.sw360.datahandler.common.SW360Assert.*; + +/** + * Implementation of the Thrift service + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SPDXDocumentHandler implements SPDXDocumentService.Iface { + + SpdxDocumentDatabaseHandler handler; + + SPDXDocumentHandler() throws MalformedURLException { + handler = new SpdxDocumentDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + } + + SPDXDocumentHandler(Supplier<CloudantClient> httpClient, String dbName) throws MalformedURLException { + handler = new SpdxDocumentDatabaseHandler(httpClient, dbName); + } + + @Override + public List<SPDXDocument> getSPDXDocumentSummary(User user) throws TException { + assertUser(user); + return handler.getSPDXDocumentSummary(user); + } + + @Override + public SPDXDocument getSPDXDocumentById(String id, User user) throws TException { + assertNotEmpty(id); + assertUser(user); + return handler.getSPDXDocumentById(id, user); + } + + @Override + public SPDXDocument getSPDXDocumentForEdit(String id, User user) throws TException { + assertNotEmpty(id); + assertUser(user); + return handler.getSPDXDocumentForEdit(id, user); + } + + @Override + public AddDocumentRequestSummary addSPDXDocument(SPDXDocument spdx, User user) throws TException { + assertNotNull(spdx); + assertUser(user); + return handler.addSPDXDocument(spdx, user); + } + + @Override + public RequestStatus updateSPDXDocument(SPDXDocument spdx, User user) throws TException { + assertNotNull(spdx); + assertUser(user); + return handler.updateSPDXDocument(spdx, user); + } + + @Override + public RequestStatus updateSPDXDocumentFromModerationRequest(SPDXDocument spdxAdditions, SPDXDocument spdxDeletions, User user) throws TException { + assertUser(user); + return handler.updateSPDXDocumentFromModerationRequest(spdxAdditions, spdxDeletions, user); + } + + @Override + public RequestStatus deleteSPDXDocument(String id, User user) throws TException { + assertId(id); + assertUser(user); + + return handler.deleteSPDXDocument(id, user); + } + +} \ No newline at end of file diff --git a/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentServlet.java b/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentServlet.java new file mode 100644 index 0000000000..3dff4b9683 --- /dev/null +++ b/backend/src/src-spdxdocument/src/main/java/org/eclipse/sw360/spdxdocument/SPDXDocumentServlet.java @@ -0,0 +1,31 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.spdxdocument; + +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocumentService; +import org.apache.thrift.protocol.TCompactProtocol; +import org.eclipse.sw360.projects.Sw360ThriftServlet; + +import java.net.MalformedURLException; + +/** + * Thrift Servlet instantiation + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SPDXDocumentServlet extends Sw360ThriftServlet { + + public SPDXDocumentServlet() throws MalformedURLException { + // Create a service processor using the provided handler + super(new SPDXDocumentService.Processor<>(new SPDXDocumentHandler()), new TCompactProtocol.Factory()); + } + +} diff --git a/backend/src/src-spdxdocumentcreationinfo/pom.xml b/backend/src/src-spdxdocumentcreationinfo/pom.xml new file mode 100644 index 0000000000..c4a3250215 --- /dev/null +++ b/backend/src/src-spdxdocumentcreationinfo/pom.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + ~ + ~ Author : hieu1.phamvan@toshiba.co.jp + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.sw360</groupId> + <artifactId>backend-src</artifactId> + <version>${revision}</version> + </parent> + + <artifactId>src-spdxdocumentcreationinfo</artifactId> + <packaging>jar</packaging> + <name>src-spdxdocumentcreationinfo</name> + + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-1.2-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.sw360</groupId> + <artifactId>src-spdxdocument</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationHandler.java b/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationHandler.java new file mode 100644 index 0000000000..69a05ad27a --- /dev/null +++ b/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationHandler.java @@ -0,0 +1,95 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.spdxdocumentcreationinfo; + +import org.eclipse.sw360.datahandler.common.DatabaseSettings; +import org.eclipse.sw360.datahandler.db.spdx.documentcreationinfo.*; +import org.eclipse.sw360.datahandler.thrift.RequestStatus; +import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; +import org.eclipse.sw360.datahandler.thrift.users.User; + +import com.cloudant.client.api.CloudantClient; + +import org.apache.thrift.TException; + +import java.net.MalformedURLException; +import java.util.List; +import java.util.function.Supplier; + +import static org.eclipse.sw360.datahandler.common.SW360Assert.*; + +/** + * Implementation of the Thrift service + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class DocumentCreationInformationHandler implements DocumentCreationInformationService.Iface { + + SpdxDocumentCreationInfoDatabaseHandler handler; + + DocumentCreationInformationHandler() throws MalformedURLException { + handler = new SpdxDocumentCreationInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + } + + DocumentCreationInformationHandler(Supplier<CloudantClient> httpClient, String dbName) throws MalformedURLException { + handler = new SpdxDocumentCreationInfoDatabaseHandler(httpClient, dbName); + } + + @Override + public List<DocumentCreationInformation> getDocumentCreationInformationSummary(User user) throws TException { + assertUser(user); + return handler.getDocumentCreationInformationSummary(user); + } + + @Override + public DocumentCreationInformation getDocumentCreationInformationById(String id, User user) throws TException { + assertNotEmpty(id); + assertUser(user); + return handler.getDocumentCreationInformationById(id, user); + } + + @Override + public DocumentCreationInformation getDocumentCreationInfoForEdit(String id, User user) throws TException { + assertNotEmpty(id); + assertUser(user); + return handler.getDocumentCreationInfoForEdit(id, user); + } + + @Override + public AddDocumentRequestSummary addDocumentCreationInformation(DocumentCreationInformation documentCreationInformation, User user) throws TException { + assertNotNull(documentCreationInformation); + assertUser(user); + return handler.addDocumentCreationInformation(documentCreationInformation, user); + } + + @Override + public RequestStatus updateDocumentCreationInformation(DocumentCreationInformation documentCreationInformation, User user) throws TException { + assertNotNull(documentCreationInformation); + assertUser(user); + return handler.updateDocumentCreationInformation(documentCreationInformation, user); + } + + @Override + public RequestStatus updateDocumentCreationInfomationFromModerationRequest(DocumentCreationInformation documentCreationInfoAdditions, DocumentCreationInformation documentCreationInfoDeletions, User user) throws TException { + assertUser(user); + return handler.updateDocumentCreationInfomationFromModerationRequest(documentCreationInfoAdditions, documentCreationInfoDeletions, user); + } + + @Override + public RequestStatus deleteDocumentCreationInformation(String id, User user) throws TException { + assertId(id); + assertUser(user); + + return handler.deleteDocumentCreationInformation(id, user); + } + +} \ No newline at end of file diff --git a/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationServlet.java b/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationServlet.java new file mode 100644 index 0000000000..1700a5ec1f --- /dev/null +++ b/backend/src/src-spdxdocumentcreationinfo/src/main/java/org/eclipse/sw360/spdxdocumentcreationinfo/DocumentCreationInformationServlet.java @@ -0,0 +1,31 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.spdxdocumentcreationinfo; + +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformationService; +import org.apache.thrift.protocol.TCompactProtocol; +import org.eclipse.sw360.projects.Sw360ThriftServlet; + +import java.net.MalformedURLException; + +/** + * Thrift Servlet instantiation + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class DocumentCreationInformationServlet extends Sw360ThriftServlet { + + public DocumentCreationInformationServlet() throws MalformedURLException { + // Create a service processor using the provided handler + super(new DocumentCreationInformationService.Processor<>(new DocumentCreationInformationHandler()), new TCompactProtocol.Factory()); + } + +} \ No newline at end of file diff --git a/backend/src/src-spdxpackageinfo/pom.xml b/backend/src/src-spdxpackageinfo/pom.xml new file mode 100644 index 0000000000..63f1c302a1 --- /dev/null +++ b/backend/src/src-spdxpackageinfo/pom.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + ~ + ~ Author : hieu1.phamvan@toshiba.co.jp + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.sw360</groupId> + <artifactId>backend-src</artifactId> + <version>${revision}</version> + </parent> + + <artifactId>src-spdxpackageinfo</artifactId> + <packaging>jar</packaging> + <name>src-spdxpackageinfo</name> + + <dependencies> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-1.2-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.sw360</groupId> + <artifactId>src-spdxdocument</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationHandler.java b/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationHandler.java new file mode 100644 index 0000000000..3fd31eb010 --- /dev/null +++ b/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationHandler.java @@ -0,0 +1,107 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.spdxpackageinfo; + +import org.eclipse.sw360.datahandler.common.DatabaseSettings; +import org.eclipse.sw360.datahandler.db.spdx.packageinfo.*; +import org.eclipse.sw360.datahandler.thrift.RequestStatus; +import org.eclipse.sw360.datahandler.thrift.RequestSummary; +import org.eclipse.sw360.datahandler.thrift.AddDocumentRequestSummary; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; +import org.eclipse.sw360.datahandler.thrift.users.User; + +import com.cloudant.client.api.CloudantClient; + +import org.apache.thrift.TException; + +import java.net.MalformedURLException; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +import static org.eclipse.sw360.datahandler.common.SW360Assert.*; + +/** + * Implementation of the Thrift service + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class PackageInformationHandler implements PackageInformationService.Iface { + + SpdxPackageInfoDatabaseHandler handler; + + PackageInformationHandler() throws MalformedURLException { + handler = new SpdxPackageInfoDatabaseHandler(DatabaseSettings.getConfiguredClient(), DatabaseSettings.COUCH_DB_SPDX); + } + + PackageInformationHandler(Supplier<CloudantClient> httpClient, String dbName) throws MalformedURLException { + handler = new SpdxPackageInfoDatabaseHandler(httpClient, dbName); + } + + @Override + public List<PackageInformation> getPackageInformationSummary(User user) throws TException { + assertUser(user); + return handler.getPackageInformationSummary(user); + } + + @Override + public PackageInformation getPackageInformationById(String id, User user) throws TException { + assertNotEmpty(id); + assertUser(user); + return handler.getPackageInformationById(id, user); + } + + @Override + public PackageInformation getPackageInformationForEdit(String id, User user) throws TException { + assertNotEmpty(id); + assertUser(user); + return handler.getPackageInformationForEdit(id, user); + } + + @Override + public AddDocumentRequestSummary addPackageInformation(PackageInformation packageInformation, User user) throws TException { + assertNotNull(packageInformation); + assertUser(user); + return handler.addPackageInformation(packageInformation, user); + } + + @Override + public AddDocumentRequestSummary addPackageInformations(Set<PackageInformation> packageInformations, User user) throws TException { + return handler.addPackageInformations(packageInformations, user); + } + + @Override + public RequestStatus updatePackageInformation(PackageInformation packageInformation, User user) throws TException { + assertNotNull(packageInformation); + assertUser(user); + return handler.updatePackageInformation(packageInformation, user); + } + + @Override + public RequestSummary updatePackageInformations(Set<PackageInformation> packageInformations, User user) throws TException { + assertUser(user); + return handler.updatePackageInformations(packageInformations, user); + } + + @Override + public RequestStatus updatePackageInfomationFromModerationRequest(PackageInformation packageInfoAdditions, PackageInformation packageInfoDeletions, User user) throws TException { + assertUser(user); + return handler.updatePackageInfomationFromModerationRequest(packageInfoAdditions, packageInfoDeletions, user); + } + @Override + public RequestStatus deletePackageInformation(String id, User user) throws TException { + assertId(id); + assertUser(user); + + return handler.deletePackageInformation(id, user); + } + +} \ No newline at end of file diff --git a/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationServlet.java b/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationServlet.java new file mode 100644 index 0000000000..080c5b8aac --- /dev/null +++ b/backend/src/src-spdxpackageinfo/src/main/java/org/eclipse/sw360/spdxpackageinfo/PackageInformationServlet.java @@ -0,0 +1,31 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.spdxpackageinfo; + +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformationService; +import org.apache.thrift.protocol.TCompactProtocol; +import org.eclipse.sw360.projects.Sw360ThriftServlet; + +import java.net.MalformedURLException; + +/** + * Thrift Servlet instantiation + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class PackageInformationServlet extends Sw360ThriftServlet { + + public PackageInformationServlet() throws MalformedURLException { + // Create a service processor using the provided handler + super(new PackageInformationService.Processor<>(new PackageInformationHandler()), new TCompactProtocol.Factory()); + } + +} \ No newline at end of file diff --git a/backend/svc/pom.xml b/backend/svc/pom.xml index dece8b6a20..e28c66a908 100644 --- a/backend/svc/pom.xml +++ b/backend/svc/pom.xml @@ -41,6 +41,9 @@ <module>svc-wsimport</module> <module>svc-changelogs</module> <module>svc-health</module> + <module>svc-spdxdocument</module> + <module>svc-spdxdocumentcreationinfo</module> + <module>svc-spdxpackageinfo</module> </modules> <build> diff --git a/backend/svc/svc-spdxdocument/pom.xml b/backend/svc/svc-spdxdocument/pom.xml new file mode 100644 index 0000000000..ff6d1a37c9 --- /dev/null +++ b/backend/svc/svc-spdxdocument/pom.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + ~ + ~ Author : hieu1.phamvan@toshiba.co.jp + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.sw360</groupId> + <artifactId>backend-svc</artifactId> + <version>${revision}</version> + </parent> + + <artifactId>svc-spdxdocument</artifactId> + <packaging>war</packaging> + <name>svc-spdxdocument</name> + + <properties> + <artifact.deploy.dir>${backend.deploy.dir}</artifact.deploy.dir> + </properties> + + <build> + <finalName>spdxdocument</finalName> + + <plugins> + <plugin> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>add-build-configuration-resources</id> + <phase>generate-resources</phase> + <goals> + <goal>unpack</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.eclipse.sw360</groupId> + <artifactId>src-${project.build.finalName}</artifactId> + <version>${project.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${project.build.outputDirectory}</outputDirectory> + <excludes>**/*.java,**/*.class</excludes> + </artifactItem> + </artifactItems> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.eclipse.sw360</groupId> + <artifactId>src-${project.build.finalName}</artifactId> + <version>${project.version}</version> + <type>jar</type> + </dependency> + </dependencies> +</project> diff --git a/backend/svc/svc-spdxdocument/src/main/webapp/WEB-INF/web.xml b/backend/svc/svc-spdxdocument/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..d523767be0 --- /dev/null +++ b/backend/svc/svc-spdxdocument/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + ~ + ~ Author : hieu1.phamvan@toshiba.co.jp + --> +<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" + version="3.1"> + <display-name>SPDX Document Service</display-name> + <welcome-file-list> + <welcome-file>index.jsp</welcome-file> + </welcome-file-list> + + <listener> + <listener-class>org.eclipse.sw360.SW360ServiceContextListener</listener-class> + </listener> + + <servlet> + <servlet-name>SPDXDocumentService</servlet-name> + <servlet-class>org.eclipse.sw360.spdxdocument.SPDXDocumentServlet</servlet-class> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>SPDXDocumentService</servlet-name> + <url-pattern>/thrift</url-pattern> + </servlet-mapping> + +</web-app> \ No newline at end of file diff --git a/backend/svc/svc-spdxdocument/src/main/webapp/index.jsp b/backend/svc/svc-spdxdocument/src/main/webapp/index.jsp new file mode 100644 index 0000000000..5ee91cfd26 --- /dev/null +++ b/backend/svc/svc-spdxdocument/src/main/webapp/index.jsp @@ -0,0 +1,23 @@ +<!-- +Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. +Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + +This program and the accompanying materials are made +available under the terms of the Eclipse Public License 2.0 +which is available at https://www.eclipse.org/legal/epl-2.0/ + +SPDX-License-Identifier: EPL-2.0 + +Author : hieu1.phamvan@toshiba.co.jp + +Description: Welcome file for SPDX Document Service +--> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<html> + <head> + <title>Welcome to the SW360 SPDX Document Service</title> + </head> + <body> + <h2>Welcome to the SW360 SPDX Document Service!</h2> + </body> +</html> diff --git a/backend/svc/svc-spdxdocumentcreationinfo/pom.xml b/backend/svc/svc-spdxdocumentcreationinfo/pom.xml new file mode 100644 index 0000000000..b5924e9bc3 --- /dev/null +++ b/backend/svc/svc-spdxdocumentcreationinfo/pom.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + ~ + ~ Author : hieu1.phamvan@toshiba.co.jp + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.sw360</groupId> + <artifactId>backend-svc</artifactId> + <version>${revision}</version> + </parent> + + <artifactId>svc-spdxdocumentcreationinfo</artifactId> + <packaging>war</packaging> + <name>svc-spdxdocumentcreationinfo</name> + + <properties> + <artifact.deploy.dir>${backend.deploy.dir}</artifact.deploy.dir> + </properties> + + <build> + <finalName>spdxdocumentcreationinfo</finalName> + + <plugins> + <plugin> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>add-build-configuration-resources</id> + <phase>generate-resources</phase> + <goals> + <goal>unpack</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.eclipse.sw360</groupId> + <artifactId>src-${project.build.finalName}</artifactId> + <version>${project.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${project.build.outputDirectory}</outputDirectory> + <excludes>**/*.java,**/*.class</excludes> + </artifactItem> + </artifactItems> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.eclipse.sw360</groupId> + <artifactId>src-${project.build.finalName}</artifactId> + <version>${project.version}</version> + <type>jar</type> + </dependency> + </dependencies> +</project> diff --git a/backend/svc/svc-spdxdocumentcreationinfo/src/main/webapp/WEB-INF/web.xml b/backend/svc/svc-spdxdocumentcreationinfo/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..924067d4fe --- /dev/null +++ b/backend/svc/svc-spdxdocumentcreationinfo/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + ~ + ~ Author : hieu1.phamvan@toshiba.co.jp + --> +<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" + version="3.1"> + <display-name>SPDX Document Creation Information Service</display-name> + <welcome-file-list> + <welcome-file>index.jsp</welcome-file> + </welcome-file-list> + + <listener> + <listener-class>org.eclipse.sw360.SW360ServiceContextListener</listener-class> + </listener> + + <servlet> + <servlet-name>DocumentCreationInformationService</servlet-name> + <servlet-class>org.eclipse.sw360.spdxdocumentcreationinfo.DocumentCreationInformationServlet</servlet-class> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>DocumentCreationInformationService</servlet-name> + <url-pattern>/thrift</url-pattern> + </servlet-mapping> + +</web-app> \ No newline at end of file diff --git a/backend/svc/svc-spdxdocumentcreationinfo/src/main/webapp/index.jsp b/backend/svc/svc-spdxdocumentcreationinfo/src/main/webapp/index.jsp new file mode 100644 index 0000000000..0675354785 --- /dev/null +++ b/backend/svc/svc-spdxdocumentcreationinfo/src/main/webapp/index.jsp @@ -0,0 +1,23 @@ +<!-- +Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. +Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + +This program and the accompanying materials are made +available under the terms of the Eclipse Public License 2.0 +which is available at https://www.eclipse.org/legal/epl-2.0/ + +SPDX-License-Identifier: EPL-2.0 + +Author : hieu1.phamvan@toshiba.co.jp + +Description: Welcome file for SPDX Document Creation Information Service +--> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<html> + <head> + <title>Welcome to the SW360 SPDX Document Creation Information Service</title> + </head> + <body> + <h2>Welcome to the SW360 SPDX Document Creation Information Service!</h2> + </body> +</html> diff --git a/backend/svc/svc-spdxpackageinfo/pom.xml b/backend/svc/svc-spdxpackageinfo/pom.xml new file mode 100644 index 0000000000..6c91775eef --- /dev/null +++ b/backend/svc/svc-spdxpackageinfo/pom.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + ~ + ~ Author : hieu1.phamvan@toshiba.co.jp + ~ + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.sw360</groupId> + <artifactId>backend-svc</artifactId> + <version>${revision}</version> + </parent> + + <artifactId>svc-spdxpackageinfo</artifactId> + <packaging>war</packaging> + <name>svc-spdxpackageinfo</name> + + <properties> + <artifact.deploy.dir>${backend.deploy.dir}</artifact.deploy.dir> + </properties> + + <build> + <finalName>spdxpackageinfo</finalName> + + <plugins> + <plugin> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>add-build-configuration-resources</id> + <phase>generate-resources</phase> + <goals> + <goal>unpack</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.eclipse.sw360</groupId> + <artifactId>src-${project.build.finalName}</artifactId> + <version>${project.version}</version> + <type>jar</type> + <overWrite>true</overWrite> + <outputDirectory>${project.build.outputDirectory}</outputDirectory> + <excludes>**/*.java,**/*.class</excludes> + </artifactItem> + </artifactItems> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.eclipse.sw360</groupId> + <artifactId>src-${project.build.finalName}</artifactId> + <version>${project.version}</version> + <type>jar</type> + </dependency> + </dependencies> +</project> diff --git a/backend/svc/svc-spdxpackageinfo/src/main/webapp/WEB-INF/web.xml b/backend/svc/svc-spdxpackageinfo/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..1b518e745a --- /dev/null +++ b/backend/svc/svc-spdxpackageinfo/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + ~ + ~ Author : hieu1.phamvan@toshiba.co.jp + ~ + --> +<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" + version="3.1"> + <display-name>SPDX Package Information Service</display-name> + <welcome-file-list> + <welcome-file>index.jsp</welcome-file> + </welcome-file-list> + + <listener> + <listener-class>org.eclipse.sw360.SW360ServiceContextListener</listener-class> + </listener> + + <servlet> + <servlet-name>PackageInformationService</servlet-name> + <servlet-class>org.eclipse.sw360.spdxpackageinfo.PackageInformationServlet</servlet-class> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>PackageInformationService</servlet-name> + <url-pattern>/thrift</url-pattern> + </servlet-mapping> + +</web-app> \ No newline at end of file diff --git a/backend/svc/svc-spdxpackageinfo/src/main/webapp/index.jsp b/backend/svc/svc-spdxpackageinfo/src/main/webapp/index.jsp new file mode 100644 index 0000000000..db531ba332 --- /dev/null +++ b/backend/svc/svc-spdxpackageinfo/src/main/webapp/index.jsp @@ -0,0 +1,23 @@ +<!-- +Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. +Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + +This program and the accompanying materials are made +available under the terms of the Eclipse Public License 2.0 +which is available at https://www.eclipse.org/legal/epl-2.0/ + +SPDX-License-Identifier: EPL-2.0 + +Author : hieu1.phamvan@toshiba.co.jp + +Description: Welcome file for SPDX Service +--> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<html> + <head> + <title>Welcome to the SW360 SPDX Package Information Service</title> + </head> + <body> + <h2>Welcome to the SW360 SPDX Package Information Service!</h2> + </body> +</html> diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/ChangeLogsPortletUtils.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/ChangeLogsPortletUtils.java index 3da089e1a9..a3176b23d2 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/ChangeLogsPortletUtils.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/ChangeLogsPortletUtils.java @@ -22,6 +22,7 @@ import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.Set; import javax.portlet.PortletRequest; import javax.portlet.ResourceRequest; @@ -38,6 +39,10 @@ import org.eclipse.sw360.datahandler.thrift.changelogs.ChangeLogs; import org.eclipse.sw360.datahandler.thrift.changelogs.ChangeLogsService; import org.eclipse.sw360.datahandler.thrift.changelogs.ChangeLogsService.Iface; +import org.eclipse.sw360.datahandler.thrift.components.ComponentService; +import org.eclipse.sw360.datahandler.thrift.components.Release; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocumentService; import org.eclipse.sw360.datahandler.thrift.changelogs.ChangedFields; import org.eclipse.sw360.datahandler.thrift.changelogs.ReferenceDocData; import org.eclipse.sw360.datahandler.thrift.users.User; @@ -128,8 +133,12 @@ private JSONObject serveChangeLogsList(ResourceRequest request, ResourceResponse } } - List<ChangeLogs> changeLogsList = getFilteredChangeLogList(request, changeLogsClient); + List<ChangeLogs> changeLogsList = getFilteredChangeLogList(request, changeLogsClient, null); + if (isReleaseChangesLog(changeLogsList)) { + changeLogsList = getChangesLogsForSPDX(request, changeLogsList, changeLogsClient); + } + changeLogsList.stream().forEach(cl -> cl.setChangeTimestamp(cl.getChangeTimestamp().split(" ")[0])); JSONArray jsonProjects = getChangeLogData(changeLogsList, paginationParameters, request); JSONObject jsonResult = createJSONObject(); jsonResult.put(DATATABLE_RECORDS_TOTAL, changeLogsList.size()); @@ -139,6 +148,49 @@ private JSONObject serveChangeLogsList(ResourceRequest request, ResourceResponse return jsonResult; } + private boolean isReleaseChangesLog(List<ChangeLogs> changeLogsList) { + for (ChangeLogs changeLogs : changeLogsList) { + if (changeLogs.documentType.equals("release")){ + return true; + } + } + return false; + } + + private List<ChangeLogs> getChangesLogsForSPDX(ResourceRequest request, List<ChangeLogs> SPDXChangeLogsList, ChangeLogsService.Iface changeLogsClient) { + ComponentService.Iface componentClient = thriftClients.makeComponentClient(); + SPDXDocumentService.Iface SPDXClient = thriftClients.makeSPDXClient(); + User user = UserCacheHolder.getUserFromRequest(request); + String releaseId = request.getParameter(PortalConstants.DOCUMENT_ID); + try { + Release release = componentClient.getReleaseById(releaseId, user); + String spdxId = release.getSpdxId(); + if (!isNullOrEmpty(spdxId)) { + SPDXDocument spdxDocument = SPDXClient.getSPDXDocumentById(spdxId, user); + String spdxDocumentCreationInfoId = spdxDocument.getSpdxDocumentCreationInfoId(); + Set<String> packageInfoIds = spdxDocument.getSpdxPackageInfoIds(); + List<ChangeLogs> spdxChangeLogsList = getFilteredChangeLogList(request, changeLogsClient, spdxId); + SPDXChangeLogsList.addAll(spdxChangeLogsList); + if (!isNullOrEmpty(spdxDocumentCreationInfoId)) { + List<ChangeLogs> spdxDocumentChangeLogsList = getFilteredChangeLogList(request, changeLogsClient, spdxDocumentCreationInfoId); + SPDXChangeLogsList.addAll(spdxDocumentChangeLogsList); + } + if (packageInfoIds != null) { + List<ChangeLogs> packagesChangeLogsList = Lists.newArrayList(); + for (String packageInfoId : packageInfoIds) { + List<ChangeLogs> packageChangeLogsList = getFilteredChangeLogList(request, changeLogsClient, packageInfoId); + packagesChangeLogsList.addAll(packageChangeLogsList); + } + SPDXChangeLogsList.addAll(packagesChangeLogsList); + } + Collections.sort(SPDXChangeLogsList, Comparator.comparing(ChangeLogs::getChangeTimestamp).reversed()); + } + } catch (TException e) { + log.error("Error while getting change logs for SPDX" + e); + } + return SPDXChangeLogsList; + } + private LiferayPortletURL getModerationPortletUrl(PortletRequest request) { Portlet portlet = PortletLocalServiceUtil.getPortletById(PortalConstants.MODERATION_PORTLET_NAME); Optional<Layout> layout = LayoutLocalServiceUtil.getLayouts(portlet.getCompanyId()).stream() @@ -154,9 +206,11 @@ private LiferayPortletURL getModerationPortletUrl(PortletRequest request) { } private List<ChangeLogs> getFilteredChangeLogList(ResourceRequest request, - ChangeLogsService.Iface changeLogsClient) { + ChangeLogsService.Iface changeLogsClient, String docId) { final User user = UserCacheHolder.getUserFromRequest(request); - String docId = request.getParameter(PortalConstants.DOCUMENT_ID); + if (docId == null) { + docId = request.getParameter(PortalConstants.DOCUMENT_ID); + } try { return changeLogsClient.getChangeLogsByDocumentId(user, docId); } catch (TException exp) { diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/PortalConstants.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/PortalConstants.java index f8b4a53c35..e50e51defb 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/PortalConstants.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/common/PortalConstants.java @@ -65,6 +65,8 @@ public class PortalConstants { //! Standard keys for Lists and their size public static final String KEY_SUMMARY = "documents"; + public static final String RDF_FILE_EXTENSION = ".rdf"; + public static final String XML_FILE_EXTENSION = ".xml"; public static final String KEY_LIST_SIZE = "documentssize"; @@ -113,6 +115,7 @@ public class PortalConstants { public static final String LICENSE_TYPE_GLOBAL = "global"; public static final String LICENSE_TYPE_OTHERS = "Others"; public static final String LICENSE_IDS = "licenseIds"; + public static final String MAIN_LICENSE_FILES = "LICENSE.*|license|license.txt|license.html|COPYING.*|copying|copying.txt|copying.html"; //! Specialized keys for moderation public static final String MODERATION_PORTLET_NAME = PORTLET_NAME_PREFIX + "moderations"; @@ -297,7 +300,6 @@ public class PortalConstants { public static final String PROJECT_OBLIGATIONS_INFO_BY_RELEASE = "projectObligationsInfoByRelease"; public static final String LINKED_OBLIGATIONS = "linkedObligations"; public static final String APPROVED_OBLIGATIONS_COUNT = "approvedObligationsCount"; - public static final String OBLIGATION_FROM_README_OSS = "obligationFromReadmeOSS"; public static final String EXCLUDED_RELEASES = "excludedReleases"; public static final String RELATIONSHIPS = "relations"; public static final String PROJECT_RELEASE_TO_RELATION = "projectReleaseToRelation"; @@ -493,7 +495,11 @@ public class PortalConstants { public static final String PARENT_SCOPE_GROUP_ID = "parentScopeGroupId"; // bom import + public static final String PREPARE_IMPORT_BOM = "prepareImportBom"; public static final String IMPORT_BOM = "importBom"; + public static final String IMPORT_BOM_AS_NEW = "importBomAsNew"; + public static final String NEW_RELEASE_VERSION = "newRleaseVersion"; + public static final String RDF_FILE_PATH = "rdfFilePath"; // project actions public static final String VIEW_LINKED_PROJECTS = "view_linked_projects"; @@ -649,6 +655,18 @@ public class PortalConstants { public static final boolean SSO_LOGIN_ENABLED; public static final boolean IS_COMPONENT_VISIBILITY_RESTRICTION_ENABLED; + //! Specialized keys for SPDX + public static final String SPDXDOCUMENT = "spdxDocument"; + public static final String SPDX_DOCUMENT_CREATION_INFO = "spdxDocumentCreationInfo"; + public static final String SPDX_PACKAGE_INFO = "spdxPackageInfo"; + public static final String SPDX_DOCUMENT_ID = "spdxDocumentId"; + public static final String SPDX_DOCUMENT_CREATION_INFO_ID = "spdxDocumentCreationInfoId"; + public static final String SPDX_PACKAGE_INFO_ID = "spdxPackageInfoId"; + public static final String ACTUAL_SPDXDOCUMENT = "actual_SPDXDocument"; + public static final String ACTUAL_DOCUMENT_CREATION_INFO = "actual_DocumentCreationInfo"; + public static final String ACTUAL_PACKAGE_INFO = "actual_PackageInfo"; + public static final Set<String> SET_RELATIONSHIP_TYPE; + static { Properties props = CommonUtils.loadProperties(PortalConstants.class, PROPERTIES_FILE_PATH); @@ -687,6 +705,7 @@ public class PortalConstants { CLEARING_REPORT_TEMPLATE_FORMAT = props.getProperty("org.eclipse.sw360.licensinfo.projectclearing.templateformat", "docx"); PREDEFINED_TAGS = props.getProperty("project.tag", "[]"); SSO_LOGIN_ENABLED = Boolean.parseBoolean(props.getProperty("sso.login.enabled", "false")); + SET_RELATIONSHIP_TYPE = CommonUtils.splitToSet(props.getProperty("relationship.type", "DESCRIBES,DESCRIBED_BY,CONTAINS,CONTAINED_BY,DEPENDS_ON,DEPENDENCY_OF,DEPENDENCY_MANIFEST_OF,BUILD_DEPENDENCY_OF,DEV_DEPENDENCY_OF,OPTIONAL_DEPENDENCY_OF,PROVIDED_DEPENDENCY_OF,TEST_DEPENDENCY_OF,RUNTIME_DEPENDENCY_OF,EXAMPLE_OF,GENERATES,GENERATED_FROM,ANCESTOR_OF,DESCENDANT_OF,VARIANT_OF,DISTRIBUTION_ARTIFACT,PATCH_FOR,PATCH_APPLIED,COPY_OF,FILE_ADDED,FILE_DELETED,FILE_MODIFIED,EXPANDED_FROM_ARCHIVE,DYNAMIC_LINK,STATIC_LINK,DATA_FILE_OF,TEST_CASE_OF,BUILD_TOOL_OF,DEV_TOOL_OF,TEST_OF,TEST_TOOL_OF,DOCUMENTATION_OF,OPTIONAL_COMPONENT_OF,METAFILE_OF,PACKAGE_OF,AMENDS,PREREQUISITE_FOR,HAS_PREREQUISITE,OTHER")); IS_COMPONENT_VISIBILITY_RESTRICTION_ENABLED = Boolean.parseBoolean( System.getProperty("RunComponentVisibilityRestrictionTest", props.getProperty("component.visibility.restriction.enabled", "false"))); DISABLE_CLEARING_REQUEST_FOR_PROJECT_WITH_GROUPS = props.getProperty("org.eclipse.sw360.disable.clearing.request.for.project.group", ""); diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/FossologyAwarePortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/FossologyAwarePortlet.java index cb498d2fb6..257479970e 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/FossologyAwarePortlet.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/FossologyAwarePortlet.java @@ -122,7 +122,7 @@ protected void serveLicenseToSourceFileMapping(ResourceRequest request, Resource Predicate<Attachment> isApprovedCLI = attachment -> CheckStatus.ACCEPTED.equals(attachment.getCheckStatus()); filteredAttachments = filteredAttachments.stream().filter(isApprovedCLI).collect(Collectors.toList()); } - if (filteredAttachments.size() == 1 && filteredAttachments.get(0).getFilename().endsWith(".xml")) { + if (filteredAttachments.size() == 1 && filteredAttachments.get(0).getFilename().endsWith(PortalConstants.XML_FILE_EXTENSION)) { final Attachment filteredAttachment = filteredAttachments.get(0); final String attachmentContentId = filteredAttachment.getAttachmentContentId(); diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java index 4e6e4b3684..dba636fbc4 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java @@ -152,6 +152,27 @@ protected void renderRequestSummary(PortletRequest request, MimeResponse respons } } + protected void renderRequestPreparation(PortletRequest request, MimeResponse response, ImportBomRequestPreparation requestPreparation) { + JSONObject jsonObject = JSONFactoryUtil.createJSONObject(); + jsonObject.put(PortalConstants.RESULT, requestPreparation.requestStatus.toString()); + if (requestPreparation.isSetIsComponentDuplicate()) + jsonObject.put("isComponentDuplicate", requestPreparation.isComponentDuplicate); + if (requestPreparation.isSetIsReleaseDuplicate()) + jsonObject.put("isReleaseDuplicate", requestPreparation.isReleaseDuplicate); + if (requestPreparation.isSetName()) + jsonObject.put("name", requestPreparation.name); + if (requestPreparation.isSetVersion()) + jsonObject.put("version", requestPreparation.version); + if (requestPreparation.isSetMessage()) + jsonObject.put("message", requestPreparation.message); + + try { + writeJSON(request, response, jsonObject); + } catch (IOException e) { + log.error("Problem rendering RequestStatus", e); + } + } + protected void renderRequestStatus(PortletRequest request, MimeResponse response, RequestStatus requestStatus) { JSONObject jsonObject = JSONFactoryUtil.createJSONObject(); jsonObject.put(PortalConstants.RESULT, requestStatus.toString()); diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java index fe5abb9ce5..de18c3aebf 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; @@ -58,11 +59,16 @@ import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.datahandler.thrift.vendors.VendorService; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.*; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.*; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation._Fields; import org.eclipse.sw360.exporter.ComponentExporter; import org.eclipse.sw360.portal.common.*; import org.eclipse.sw360.portal.common.datatables.PaginationParser; import org.eclipse.sw360.portal.common.datatables.data.PaginationParameters; import org.eclipse.sw360.portal.portlets.FossologyAwarePortlet; +import org.eclipse.sw360.portal.portlets.components.spdx.SpdxPortlet; import org.eclipse.sw360.portal.users.LifeRayUserSession; import org.eclipse.sw360.portal.users.UserCacheHolder; import org.eclipse.sw360.portal.users.UserUtils; @@ -71,10 +77,12 @@ import org.apache.thrift.TEnum; import org.apache.thrift.TException; import org.apache.thrift.TSerializer; -import org.apache.thrift.protocol.TSimpleJSONProtocol; import org.osgi.service.component.annotations.ConfigurationPolicy; import org.apache.commons.lang.StringUtils; +import java.nio.file.Files; +import java.nio.file.Paths; + import javax.portlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -101,24 +109,26 @@ import static org.eclipse.sw360.portal.common.PortletUtils.getVerificationState; import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.protocol.TType; +import org.apache.xmlbeans.impl.common.SniffedXmlInputStream; @org.osgi.service.component.annotations.Component( - immediate = true, - properties = { - "/org/eclipse/sw360/portal/portlets/base.properties", - "/org/eclipse/sw360/portal/portlets/default.properties" - }, - property = { - "javax.portlet.name=" + COMPONENT_PORTLET_NAME, - - "javax.portlet.display-name=Components", - "javax.portlet.info.short-title=Components", - "javax.portlet.info.title=Components", - "javax.portlet.resource-bundle=content.Language", - "javax.portlet.init-param.view-template=/html/components/view.jsp", - }, - service = Portlet.class, - configurationPolicy = ConfigurationPolicy.REQUIRE + immediate = true, + properties = { + "/org/eclipse/sw360/portal/portlets/base.properties", + "/org/eclipse/sw360/portal/portlets/default.properties" + }, + property = { + "javax.portlet.name=" + COMPONENT_PORTLET_NAME, + + "javax.portlet.display-name=Components", + "javax.portlet.info.short-title=Components", + "javax.portlet.info.title=Components", + "javax.portlet.resource-bundle=content.Language", + "javax.portlet.init-param.view-template=/html/components/view.jsp", + }, + service = Portlet.class, + configurationPolicy = ConfigurationPolicy.REQUIRE ) public class ComponentPortlet extends FossologyAwarePortlet { @@ -180,6 +190,8 @@ protected Set<Attachment> getAttachments(String documentId, String documentType, private static final String CONFIG_KEY_URL = "url"; + private _Fields field; + //! Serve resource and helpers @Override public void serveResource(ResourceRequest request, ResourceResponse response) throws IOException, PortletException { @@ -209,13 +221,13 @@ public void serveResource(ResourceRequest request, ResourceResponse response) th serveLinkedReleases(request, response); } else if (PortalConstants.PROJECT_SEARCH.equals(action)) { serveProjectSearch(request, response); - } else if (PortalConstants.UPDATE_VULNERABILITIES_RELEASE.equals(action)){ - updateVulnerabilitiesRelease(request,response); - } else if (PortalConstants.UPDATE_VULNERABILITIES_COMPONENT.equals(action)){ - updateVulnerabilitiesComponent(request,response); + } else if (PortalConstants.UPDATE_VULNERABILITIES_RELEASE.equals(action)) { + updateVulnerabilitiesRelease(request, response); + } else if (PortalConstants.UPDATE_VULNERABILITIES_COMPONENT.equals(action)) { + updateVulnerabilitiesComponent(request, response); } else if (PortalConstants.UPDATE_ALL_VULNERABILITIES.equals(action)) { updateAllVulnerabilities(request, response); - } else if (PortalConstants.UPDATE_VULNERABILITY_VERIFICATION.equals(action)){ + } else if (PortalConstants.UPDATE_VULNERABILITY_VERIFICATION.equals(action)) { updateVulnerabilityVerification(request, response); } else if (PortalConstants.EXPORT_TO_EXCEL.equals(action)) { exportExcel(request, response); @@ -225,10 +237,14 @@ public void serveResource(ResourceRequest request, ResourceResponse response) th loadSpdxLicenseInfo(request, response); } else if (PortalConstants.WRITE_SPDX_LICENSE_INFO_INTO_RELEASE.equals(action)) { writeSpdxLicenseInfoIntoRelease(request, response); + } else if (PortalConstants.PREPARE_IMPORT_BOM.equals(action)) { + prepareImportBom(request, response); } else if (PortalConstants.IMPORT_BOM.equals(action)) { importBom(request, response); } else if (PortalConstants.LICENSE_TO_SOURCE_FILE.equals(action)) { serveLicenseToSourceFileMapping(request, response); + } else if (PortalConstants.IMPORT_BOM_AS_NEW.equals(action)) { + importBomAsNew(request, response); } else if (isGenericAction(action)) { dealWithGenericAction(request, response, action); } else if (PortalConstants.LOAD_CHANGE_LOGS.equals(action) || PortalConstants.VIEW_CHANGE_LOGS.equals(action)) { @@ -237,8 +253,55 @@ public void serveResource(ResourceRequest request, ResourceResponse response) th JSONObject dataForChangeLogs = changeLogsPortletUtilsPortletUtils.serveResourceForChangeLogs(request, response, action); writeJSON(request, response, dataForChangeLogs); - } else if (PortalConstants.EVALUATE_CLI_ATTACHMENTS.equals(action)) { - evaluateCLIAttachments(request, response); + } else if (action.equals("export-spdx")) { + exportSPDX(request, response); + } else if (action.equals("download-export-spdx")) { + downloadSPDX(request, response); + } + } + + private void downloadSPDX(ResourceRequest request, ResourceResponse response) { + final ComponentService.Iface componentClient = thriftClients.makeComponentClient(); + String releaseId = request.getParameter(PortalConstants.RELEASE_ID); + String outputFormat = request.getParameter(PortalConstants.WHAT); + User user = UserCacheHolder.getUserFromRequest(request); + String filename = releaseId + "." + outputFormat.toLowerCase(); + String exportFileName = null; + + if (PortalConstants.ACTION_CANCEL.equals(request.getParameter(PortalConstants.ACTION_CANCEL))) { + deleteFileExport(outputFormat, releaseId, filename); + return; + } + + try { + log.info("Download SPDX file"); + exportFileName = componentClient.getReleaseById(releaseId, user).getName().replaceAll("[\\/:*?\"<>|\\s]", "_") + + "_" + componentClient.getReleaseById(releaseId, user).getVersion().replaceAll("[\\/:*?\"<>|\\s]", "_") + + "_" + SW360Utils.getCreatedOn() + "." + outputFormat.toLowerCase(); + InputStream inputStream = new FileInputStream(filename); + PortletResponseUtil.sendFile(request, response, exportFileName, inputStream, CONTENT_TYPE_OPENXML_SPREADSHEET); + log.info("Download SPDX file success !!!"); + } catch (IOException | TException e) { + e.printStackTrace(); + } + deleteFileExport(outputFormat, releaseId, filename); + } + + // delete file after user download + private void deleteFileExport(String outputFormat, String releaseId, String filename) { + try { + if (!outputFormat.equals("JSON")) { + Files.delete(Paths.get(releaseId + ".json")); + } + if (outputFormat.equals("SPDX")) { + Files.delete(Paths.get("tmp.rdf")); + } + if (Files.exists(Paths.get(filename))) { + Files.delete(Paths.get(filename)); + } + } catch (IOException e) { + e.printStackTrace(); + log.error("Failed to delete files."); } } @@ -262,13 +325,28 @@ private void evaluateCLIAttachments(ResourceRequest request, ResourceResponse re writeJSON(request, response, jsonObject); } + private void exportSPDX(ResourceRequest request, ResourceResponse response) { + final ComponentService.Iface componentClient = thriftClients.makeComponentClient(); + String releaseId = request.getParameter(PortalConstants.RELEASE_ID); + String outputFormat = request.getParameter(PortalConstants.WHAT); + User user = UserCacheHolder.getUserFromRequest(request); + + try { + final RequestSummary requestSummary = componentClient.exportSPDX(user, releaseId, outputFormat); + renderRequestSummary(request, response, requestSummary); + } catch (TException e) { + log.error("Failed to export SPDX file.", e); + } + } + private void importBom(ResourceRequest request, ResourceResponse response) { final ComponentService.Iface componentClient = thriftClients.makeComponentClient(); User user = UserCacheHolder.getUserFromRequest(request); String attachmentContentId = request.getParameter(ATTACHMENT_CONTENT_ID); + String rdfFilePath = request.getParameter(RDF_FILE_PATH); try { - final RequestSummary requestSummary = componentClient.importBomFromAttachmentContent(user, attachmentContentId); + final RequestSummary requestSummary = componentClient.importBomFromAttachmentContent(user, attachmentContentId, null, null, rdfFilePath); LiferayPortletURL releaseUrl = createDetailLinkTemplate(request); releaseUrl.setParameter(PortalConstants.PAGENAME, PortalConstants.PAGENAME_RELEASE_DETAIL); @@ -283,6 +361,43 @@ private void importBom(ResourceRequest request, ResourceResponse response) { } } + private void importBomAsNew(ResourceRequest request, ResourceResponse response) { + final ComponentService.Iface componentClient = thriftClients.makeComponentClient(); + User user = UserCacheHolder.getUserFromRequest(request); + String attachmentContentId = request.getParameter(ATTACHMENT_CONTENT_ID); + String newReleaseVersion = request.getParameter(NEW_RELEASE_VERSION); + String rdfFilePath = request.getParameter(RDF_FILE_PATH); + + try { + final RequestSummary requestSummary = componentClient.importBomFromAttachmentContent(user, attachmentContentId, newReleaseVersion, null, rdfFilePath); + + LiferayPortletURL releaseUrl = createDetailLinkTemplate(request); + releaseUrl.setParameter(PortalConstants.PAGENAME, PortalConstants.PAGENAME_RELEASE_DETAIL); + releaseUrl.setParameter(RELEASE_ID, requestSummary.getMessage()); + JSONObject jsonObject = JSONFactoryUtil.createJSONObject(); + jsonObject.put("redirectUrl", releaseUrl.toString()); + + renderRequestSummary(request, response, requestSummary, jsonObject); + } catch (TException e) { + log.error("Failed to import BOM.", e); + response.setProperty(ResourceResponse.HTTP_STATUS_CODE, Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); + } + } + + private void prepareImportBom(ResourceRequest request, ResourceResponse response) { + final ComponentService.Iface componentClient = thriftClients.makeComponentClient(); + User user = UserCacheHolder.getUserFromRequest(request); + String attachmentContentId = request.getParameter(ATTACHMENT_CONTENT_ID); + + try { + final ImportBomRequestPreparation importBomRequestPreparation = componentClient.prepareImportBom(user, attachmentContentId); + renderRequestPreparation(request, response, importBomRequestPreparation); + } catch (TException e) { + log.error("Failed to import BOM.", e); + response.setProperty(ResourceResponse.HTTP_STATUS_CODE, Integer.toString(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); + } + } + @Override protected void dealWithFossologyAction(ResourceRequest request, ResourceResponse response, String action) throws IOException, PortletException { @@ -392,7 +507,7 @@ private void serveCheckComponentName(ResourceRequest request, ResourceResponse r } private void respondSimilarComponentsResponseJson(ResourceRequest request, ResourceResponse response, - List<Component> similarComponents, List<String> errors) throws IOException { + List<Component> similarComponents, List<String> errors) throws IOException { response.setContentType(ContentTypes.APPLICATION_JSON); JsonGenerator jsonGenerator = JSON_FACTORY.createGenerator(response.getWriter()); @@ -573,35 +688,54 @@ private void loadSpdxLicenseInfo(ResourceRequest request, ResourceResponse respo String releaseId = request.getParameter(PortalConstants.RELEASE_ID); String attachmentContentId = request.getParameter(PortalConstants.ATTACHMENT_ID); String attachmentName = request.getParameter(PortalConstants.ATTACHMENT_NAME); + Map<String, Set<String>> licenseToSrcFilesMap = new LinkedHashMap<>(); boolean includeConcludedLicense = new Boolean(request.getParameter(PortalConstants.INCLUDE_CONCLUDED_LICENSE)); ComponentService.Iface componentClient = thriftClients.makeComponentClient(); LicenseInfoService.Iface licenseInfoClient = thriftClients.makeLicenseInfoClient(); - Set<String> concludedLicenseIds = new HashSet<>(); - Set<String> mainLicenseNames = new HashSet<String>(); - Set<String> otherLicenseNames = new HashSet<String>(); + final Set<String> concludedLicenseIds = new TreeSet<String>(); + Set<String> mainLicenseNames = new TreeSet<String>(); + Set<String> otherLicenseNames = new TreeSet<String>(); + AttachmentType attachmentType = AttachmentType.OTHER; + long totalFileCount = 0; try { Release release = componentClient.getReleaseById(releaseId, user); List<LicenseInfoParsingResult> licenseInfoResult = licenseInfoClient.getLicenseInfoForAttachment(release, attachmentContentId, includeConcludedLicense, user); + attachmentType = release.getAttachments().stream().filter(att -> attachmentContentId.equals(att.getAttachmentContentId())).map(Attachment::getAttachmentType).findFirst().orElse(null); List<LicenseNameWithText> licenseWithTexts = licenseInfoResult.stream() .flatMap(result -> result.getLicenseInfo().getLicenseNamesWithTexts().stream()) .filter(license -> !license.getLicenseName().equalsIgnoreCase(SW360Constants.LICENSE_NAME_UNKNOWN) && !license.getLicenseName().equalsIgnoreCase(SW360Constants.NA) && !license.getLicenseName().equalsIgnoreCase(SW360Constants.NO_ASSERTION)) // exclude unknown, n/a and noassertion .collect(Collectors.toList()); - if (attachmentName.endsWith(".rdf")) { - concludedLicenseIds = licenseInfoResult.stream() - .flatMap(singleResult -> singleResult.getLicenseInfo().getConcludedLicenseIds().stream()) - .collect(Collectors.toSet()); - otherLicenseNames = licenseWithTexts.stream().map(LicenseNameWithText::getLicenseName).collect(Collectors.toSet()); + + if (attachmentName.endsWith(PortalConstants.RDF_FILE_EXTENSION)) { + if (AttachmentType.INITIAL_SCAN_REPORT.equals(attachmentType)) { + totalFileCount = licenseWithTexts.stream().map(LicenseNameWithText::getSourceFiles).filter(src -> src != null).mapToInt(Set::size).sum(); + licenseToSrcFilesMap = licenseWithTexts.stream().collect(Collectors.toMap(LicenseNameWithText::getLicenseName, + LicenseNameWithText::getSourceFiles, (oldValue, newValue) -> oldValue)); + licenseWithTexts.forEach(lwt -> { + lwt.getSourceFiles().forEach(sf -> { + if (sf.replaceAll(".*/", "").matches(MAIN_LICENSE_FILES)) { + concludedLicenseIds.add(lwt.getLicenseName()); + } + }); + }); + } else { + concludedLicenseIds.addAll(licenseInfoResult.stream().flatMap(singleResult -> singleResult.getLicenseInfo().getConcludedLicenseIds().stream()).collect(Collectors.toCollection(TreeSet::new))); + } + otherLicenseNames = licenseWithTexts.stream().map(LicenseNameWithText::getLicenseName).collect(Collectors.toCollection(TreeSet::new)); otherLicenseNames.removeAll(concludedLicenseIds); - } else if (attachmentName.endsWith(".xml")) { - mainLicenseNames = licenseWithTexts.stream().filter(license -> license.getType().equals(LICENSE_TYPE_GLOBAL)).map(LicenseNameWithText::getLicenseName).collect(Collectors.toSet()); - otherLicenseNames = licenseWithTexts.stream().filter(license -> !license.getType().equals(LICENSE_TYPE_GLOBAL)).map(LicenseNameWithText::getLicenseName).collect(Collectors.toSet()); + } else if (attachmentName.endsWith(PortalConstants.XML_FILE_EXTENSION)) { + mainLicenseNames = licenseWithTexts.stream() + .filter(license -> license.getType().equals(LICENSE_TYPE_GLOBAL)) + .map(LicenseNameWithText::getLicenseName).collect(Collectors.toCollection(TreeSet::new)); + otherLicenseNames = licenseWithTexts.stream() + .filter(license -> !license.getType().equals(LICENSE_TYPE_GLOBAL)) + .map(LicenseNameWithText::getLicenseName).collect(Collectors.toCollection(TreeSet::new)); } - } catch (TException e) { log.error("Cannot retrieve license information for attachment id " + attachmentContentId + " in release " + releaseId + ".", e); @@ -612,21 +746,35 @@ private void loadSpdxLicenseInfo(ResourceRequest request, ResourceResponse respo JsonGenerator jsonGenerator = JSON_FACTORY.createGenerator(response.getWriter()); jsonGenerator.writeStartObject(); if (concludedLicenseIds.size() > 0) { - jsonGenerator.writeStringField(LICENSE_PREFIX, LanguageUtil.get(resourceBundle,"concluded.license.ids")); + jsonGenerator.writeStringField(LICENSE_PREFIX, LanguageUtil.get(resourceBundle, "concluded.license.ids")); jsonGenerator.writeArrayFieldStart(LICENSE_IDS); - concludedLicenseIds.forEach(licenseId -> wrapException(() -> { jsonGenerator.writeString(licenseId); })); + concludedLicenseIds.forEach(licenseId -> wrapException(() -> { + jsonGenerator.writeString(licenseId); + })); jsonGenerator.writeEndArray(); } else if (CommonUtils.isNotEmpty(mainLicenseNames)) { - jsonGenerator.writeStringField(LICENSE_PREFIX, LanguageUtil.get(resourceBundle,"main.license.id")); + jsonGenerator.writeStringField(LICENSE_PREFIX, LanguageUtil.get(resourceBundle, "main.license.id")); jsonGenerator.writeArrayFieldStart(LICENSE_IDS); - mainLicenseNames.forEach(licenseId -> wrapException(() -> { jsonGenerator.writeString(licenseId); })); + mainLicenseNames.forEach(licenseId -> wrapException(() -> { + jsonGenerator.writeString(licenseId); + })); jsonGenerator.writeEndArray(); } - jsonGenerator.writeStringField("otherLicense", LanguageUtil.get(resourceBundle,"other.license.id")); + jsonGenerator.writeStringField("otherLicense", LanguageUtil.get(resourceBundle, "other.license.id")); jsonGenerator.writeArrayFieldStart("otherLicenseIds"); - otherLicenseNames.forEach(licenseId -> wrapException(() -> { jsonGenerator.writeString(licenseId); })); + otherLicenseNames.forEach(licenseId -> wrapException(() -> { + jsonGenerator.writeString(licenseId); + })); jsonGenerator.writeEndArray(); - + if (AttachmentType.INITIAL_SCAN_REPORT.equals(attachmentType)) { + jsonGenerator.writeStringField(LICENSE_PREFIX, LanguageUtil.get(resourceBundle, "possible.main.license.ids")); + jsonGenerator.writeStringField("totalFileCount", Long.toString(totalFileCount)); + } + for (Map.Entry<String, Set<String>> entry : licenseToSrcFilesMap.entrySet()) { + jsonGenerator.writeArrayFieldStart(entry.getKey()); + entry.getValue().forEach(srcFile -> wrapException(() -> { jsonGenerator.writeString(srcFile); })); + jsonGenerator.writeEndArray(); + } jsonGenerator.writeEndObject(); jsonGenerator.close(); @@ -640,6 +788,7 @@ private void loadSpdxLicenseInfo(ResourceRequest request, ResourceResponse respo private void writeSpdxLicenseInfoIntoRelease(ResourceRequest request, ResourceResponse response) { User user = UserCacheHolder.getUserFromRequest(request); String releaseId = request.getParameter(PortalConstants.RELEASE_ID); + String attachmentContentId = request.getParameter(PortalConstants.ATTACHMENT_ID); ComponentService.Iface componentClient = thriftClients.makeComponentClient(); RequestStatus result = null; @@ -667,6 +816,8 @@ private void writeSpdxLicenseInfoIntoRelease(ResourceRequest request, ResourceRe } } result = componentClient.updateRelease(release, user); + + componentClient.importBomFromAttachmentContent(user, attachmentContentId, null, releaseId, null); } catch (TException | IOException e) { log.error("Cannot write license info into release " + releaseId + ".", e); response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "500"); @@ -763,7 +914,7 @@ private void prepareComponentEdit(RenderRequest request) { PortletUtils.setCustomFieldsEdit(request, user, component); setUsingDocs(request, user, null, component.getReleaseIds()); setAttachmentsInRequest(request, component); - SessionMessages.add(request, "request_processed", LanguageUtil.get(resourceBundle,"new.component")); + SessionMessages.add(request, "request_processed", LanguageUtil.get(resourceBundle, "new.component")); } } } @@ -785,7 +936,10 @@ private void prepareReleaseEdit(RenderRequest request, RenderResponse response) ComponentService.Iface client = thriftClients.makeComponentClient(); Component component; Release release; - + SPDXDocument spdxDocument = new SPDXDocument(); + DocumentCreationInformation documentCreationInfo = new DocumentCreationInformation(); + Set<PackageInformation> packageInfos = new HashSet<>(); + PackageInformation packageInfo = new PackageInformation(); if (!isNullOrEmpty(releaseId)) { release = client.getAccessibleReleaseByIdForEdit(releaseId, user); Map<String, String> sortedAdditionalData = getSortedMap(release.getAdditionalData(), true); @@ -805,10 +959,30 @@ private void prepareReleaseEdit(RenderRequest request, RenderResponse response) } component = client.getAccessibleComponentById(id, user); + String spdxDocumentId = release.getSpdxId(); + if (!isNullOrEmpty(spdxDocumentId)) { + SPDXDocumentService.Iface SPDXDocumentClient = thriftClients.makeSPDXClient(); + spdxDocument = SPDXDocumentClient.getSPDXDocumentForEdit(spdxDocumentId, user); + String spdxDocumentCreationInfoId = spdxDocument.getSpdxDocumentCreationInfoId(); + Set<String> spdxPackageInfoIds = spdxDocument.getSpdxPackageInfoIds(); + if (!isNullOrEmpty(spdxDocumentCreationInfoId)) { + DocumentCreationInformationService.Iface doClient = thriftClients.makeSPDXDocumentInfoClient(); + documentCreationInfo = doClient.getDocumentCreationInfoForEdit(spdxDocumentCreationInfoId, user); + } + if (spdxPackageInfoIds != null && !spdxPackageInfoIds.isEmpty()) { + PackageInformationService.Iface paClient = thriftClients.makeSPDXPackageInfoClient(); + for (String spdxPackageInfoId : spdxPackageInfoIds) { + packageInfo = paClient.getPackageInformationForEdit(spdxPackageInfoId, user); + packageInfos.add(packageInfo); + } + } + } + + } else { component = client.getAccessibleComponentById(id, user); release = (Release) request.getAttribute(RELEASE); - if(release == null) { + if (release == null) { release = new Release(); release.setComponentId(id); release.setClearingState(ClearingState.NEW_CLEARING); @@ -818,7 +992,7 @@ private void prepareReleaseEdit(RenderRequest request, RenderResponse response) putDirectlyLinkedReleaseRelationsWithAccessibilityInRequest(request, release, user); setAttachmentsInRequest(request, release); setUsingDocs(request, null, user, client); - SessionMessages.add(request, "request_processed", LanguageUtil.get(resourceBundle,"new.license")); + SessionMessages.add(request, "request_processed", LanguageUtil.get(resourceBundle, "new.license")); } } @@ -842,7 +1016,41 @@ private void prepareReleaseEdit(RenderRequest request, RenderResponse response) request.setAttribute(COMPONENT, component); request.setAttribute(IS_USER_AT_LEAST_ECC_ADMIN, PermissionUtils.isUserAtLeast(UserGroup.ECC_ADMIN, user) || PermissionUtils.isUserAtLeastDesiredRoleInSecondaryGroup(UserGroup.ECC_ADMIN, allSecRoles) ? "Yes" : "No"); - + request.setAttribute(SPDXDOCUMENT, spdxDocument); + request.setAttribute(SPDX_DOCUMENT_CREATION_INFO, documentCreationInfo); + request.setAttribute(SPDX_PACKAGE_INFO, packageInfos); + + ObjectMapper objectMapper = new ObjectMapper(); + try { + if (!spdxDocument.isSetId()) { + spdxDocument = generateSpdxDocument(); + } + String spdxDocumentJson = objectMapper.writeValueAsString(spdxDocument); + request.setAttribute("spdxDocumentJson", spdxDocumentJson); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + try { + if (!documentCreationInfo.isSetId()) { + documentCreationInfo = generateDocumentCreationInformation(); + } + String documentCreationInfoJson = objectMapper.writeValueAsString(documentCreationInfo); + request.setAttribute("documentCreationInfoJson", documentCreationInfoJson); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + try { + JSONArray packageArray = JSONFactoryUtil.createJSONArray(); + Set<String> setPackage = new HashSet<>(); + for (PackageInformation pack : packageInfos) { + String packageInfoJson = objectMapper.writeValueAsString(pack); + setPackage.add(packageInfoJson); + packageArray.put(packageInfoJson); + } + request.setAttribute("packageInfoJson", setPackage); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } } catch (TException e) { if (e instanceof SW360Exception) { SW360Exception sw360Exp = (SW360Exception)e; @@ -860,6 +1068,72 @@ private void prepareReleaseEdit(RenderRequest request, RenderResponse response) } } + private SPDXDocument generateSpdxDocument() { + SPDXDocument spdxDocument = new SPDXDocument(); + for (SPDXDocument._Fields field : SPDXDocument._Fields.values()) { + switch (SPDXDocument.metaDataMap.get(field).valueMetaData.type) { + case TType.SET: + spdxDocument.setFieldValue(field, new HashSet<>()); + break; + case TType.STRING: + spdxDocument.setFieldValue(field, ""); + break; + default: + break; + } + } + return spdxDocument; + } + + private DocumentCreationInformation generateDocumentCreationInformation() { + DocumentCreationInformation documentCreationInfo = new DocumentCreationInformation(); + for (DocumentCreationInformation._Fields field : DocumentCreationInformation._Fields.values()) { + switch (DocumentCreationInformation.metaDataMap.get(field).valueMetaData.type) { + case TType.SET: + documentCreationInfo.setFieldValue(field, new HashSet<>()); + break; + case TType.STRING: + documentCreationInfo.setFieldValue(field, ""); + break; + default: + break; + } + } + return documentCreationInfo; + } + + private PackageInformation generatePackageInfomation() { + PackageInformation packageInfo = new PackageInformation(); + + for (PackageInformation._Fields field : PackageInformation._Fields.values()) { + + switch (field) { + case PACKAGE_VERIFICATION_CODE: { + PackageVerificationCode packageVerificationCode = new PackageVerificationCode(); + packageInfo.setPackageVerificationCode(packageVerificationCode); + break; + } + default: { + this.field = field; + switch (PackageInformation.metaDataMap.get(field).valueMetaData.type) { + case TType.SET: + packageInfo.setFieldValue(field, new HashSet<>()); + break; + case TType.STRING: + packageInfo.setFieldValue(field, ""); + break; + case TType.BOOL: + packageInfo.setFieldValue(field, true); + default: + break; + } + break; + } + } + } + return packageInfo; + } + private void prepareReleaseDuplicate(RenderRequest request, RenderResponse response) throws PortletException { String id = request.getParameter(COMPONENT_ID); String releaseId = request.getParameter(RELEASE_ID); @@ -993,7 +1267,7 @@ private void generateComponentMergeWizardStep0Response(ActionRequest request, Js jsonGenerator.writeStartObject(); jsonGenerator.writeArrayFieldStart("components"); - componentSummary.stream().filter( component -> !component.getId().equals(srcId)).forEach(component -> { + componentSummary.stream().filter(component -> !component.getId().equals(srcId)).forEach(component -> { try { jsonGenerator.writeStartObject(); jsonGenerator.writeStringField("id", component.getId()); @@ -1037,7 +1311,7 @@ private void generateComponentMergeWizardStep2Response(ActionRequest request, Js jsonGenerator.writeStartObject(); // adding common title - jsonGenerator.writeRaw("\""+ COMPONENT_SELECTION +"\":" + JSON_THRIFT_SERIALIZER.toString(componentSelection) + ","); + jsonGenerator.writeRaw("\"" + COMPONENT_SELECTION + "\":" + JSON_THRIFT_SERIALIZER.toString(componentSelection) + ","); jsonGenerator.writeStringField(COMPONENT_SOURCE_ID, componentSourceId); jsonGenerator.writeEndObject(); @@ -1063,7 +1337,7 @@ private void generateComponentMergeWizardStep3Response(ActionRequest request, Js // write response JSON jsonGenerator.writeStartObject(); jsonGenerator.writeStringField("redirectUrl", componentUrl.toString()); - if (status == RequestStatus.IN_USE){ + if (status == RequestStatus.IN_USE) { jsonGenerator.writeStringField("error", "Cannot merge when one of the components has an active moderation request."); } else if (status == RequestStatus.ACCESS_DENIED) { jsonGenerator.writeStringField("error", "You do not have sufficient permissions."); @@ -1171,11 +1445,11 @@ private void generateReleaseMergeWizardStep0Response(ActionRequest request, Json ComponentService.Iface cClient = thriftClients.makeComponentClient(); List<Release> releases = cClient.getReleasesByComponentId(componentId, sessionUser); - + jsonGenerator.writeStartObject(); jsonGenerator.writeArrayFieldStart("releases"); - releases.stream().filter( release -> !release.getId().equals(targetId) ).forEach(release -> { + releases.stream().filter(release -> !release.getId().equals(targetId)).forEach(release -> { try { jsonGenerator.writeStartObject(); jsonGenerator.writeStringField("id", release.getId()); @@ -1205,15 +1479,15 @@ private void generateReleaseMergeWizardStep1Response(ActionRequest request, Json boolean matchingPair = false; boolean foundSourceAttachments = false; Set<String> attachmentHashes = new HashSet<>(); - for(Attachment attachment : nullToEmptySet(releaseTarget.getAttachments())) { - if(attachment.getAttachmentType().equals(AttachmentType.SOURCE) || attachment.getAttachmentType().equals(AttachmentType.SOURCE_SELF)) { + for (Attachment attachment : nullToEmptySet(releaseTarget.getAttachments())) { + if (attachment.getAttachmentType().equals(AttachmentType.SOURCE) || attachment.getAttachmentType().equals(AttachmentType.SOURCE_SELF)) { attachmentHashes.add(attachment.getSha1()); foundSourceAttachments = true; } } - for(Attachment attachment : nullToEmptySet(releaseSource.getAttachments())) { - if(attachment.getAttachmentType().equals(AttachmentType.SOURCE) || attachment.getAttachmentType().equals(AttachmentType.SOURCE_SELF)) { - if(attachmentHashes.contains(attachment.getSha1())) { + for (Attachment attachment : nullToEmptySet(releaseSource.getAttachments())) { + if (attachment.getAttachmentType().equals(AttachmentType.SOURCE) || attachment.getAttachmentType().equals(AttachmentType.SOURCE_SELF)) { + if (attachmentHashes.contains(attachment.getSha1())) { matchingPair = true; break; } @@ -1221,7 +1495,7 @@ private void generateReleaseMergeWizardStep1Response(ActionRequest request, Json } } - if(foundSourceAttachments && !matchingPair) { + if (foundSourceAttachments && !matchingPair) { jsonGenerator.writeStartObject(); jsonGenerator.writeStringField("error", "Both releases must have at least one pair of same source attachments or no source attachments at all. Otherwise a merge is not possible."); jsonGenerator.writeEndObject(); @@ -1229,17 +1503,17 @@ private void generateReleaseMergeWizardStep1Response(ActionRequest request, Json } Map<String, Map<String, String>> displayInformation = new HashMap<>(); - + addToMap(displayInformation, "mainlineState", releaseTarget.getMainlineState()); addToMap(displayInformation, "mainlineState", releaseSource.getMainlineState()); addToMap(displayInformation, "repositorytype", releaseTarget.getRepository() != null ? releaseTarget.getRepository().getRepositorytype() : null); addToMap(displayInformation, "repositorytype", releaseSource.getRepository() != null ? releaseSource.getRepository().getRepositorytype() : null); addToMap(displayInformation, "eccStatus", releaseTarget.getEccInformation().getEccStatus()); addToMap(displayInformation, "eccStatus", releaseSource.getEccInformation().getEccStatus()); - for(Attachment attachment : nullToEmptySet(releaseSource.getAttachments())) { + for (Attachment attachment : nullToEmptySet(releaseSource.getAttachments())) { addToMap(displayInformation, "attachmentType", attachment.getAttachmentType()); } - for(Attachment attachment : nullToEmptySet(releaseTarget.getAttachments())) { + for (Attachment attachment : nullToEmptySet(releaseTarget.getAttachments())) { addToMap(displayInformation, "attachmentType", attachment.getAttachmentType()); } @@ -1248,11 +1522,11 @@ private void generateReleaseMergeWizardStep1Response(ActionRequest request, Json releaseIds.addAll(nullToEmptyMap(releaseTarget.getReleaseIdToRelationship()).keySet()); List<Release> releases = cClient.getReleasesById(releaseIds, sessionUser); Map<String, String> releaseToNameMap = new HashMap<String, String>(); - for(Release release : releases) { + for (Release release : releases) { releaseToNameMap.put(release.getId(), release.getName() + " (" + release.getVersion() + ")"); } displayInformation.put("release", releaseToNameMap); - + jsonGenerator.writeStartObject(); // adding common title @@ -1266,7 +1540,7 @@ private void generateReleaseMergeWizardStep1Response(ActionRequest request, Json private <T> void addToMap(Map<String, Map<String, String>> map, String key, TEnum value) { Map<String, String> subMap = map.getOrDefault(key, new HashMap<String, String>()); - if(value != null) { + if (value != null) { subMap.put(value.getValue() + "", ThriftEnumUtils.enumToString(value)); } map.put(key, subMap); @@ -1292,7 +1566,7 @@ private Map<String, Integer> getUsageInformationForReleaseMerge(String releaseSo usageInformation.put("releaseVulnerabilities", releaseVulnerabilities.size()); List<ProjectVulnerabilityRating> projectRatings = vulnerabilityClient.getProjectVulnerabilityRatingsByReleaseId(releaseSourceId, sessionUser); usageInformation.put("projectRatings", projectRatings.size()); - + return usageInformation; } @@ -1306,7 +1580,7 @@ private void generateReleaseMergeWizardStep2Response(ActionRequest request, Json jsonGenerator.writeStartObject(); // adding common title - jsonGenerator.writeRaw("\""+ RELEASE_SELECTION +"\":" + JSON_THRIFT_SERIALIZER.toString(releaseSelection) + ","); + jsonGenerator.writeRaw("\"" + RELEASE_SELECTION + "\":" + JSON_THRIFT_SERIALIZER.toString(releaseSelection) + ","); jsonGenerator.writeStringField(RELEASE_SOURCE_ID, releaseSourceId); jsonGenerator.writeEndObject(); @@ -1333,7 +1607,7 @@ private void generateReleaseMergeWizardStep3Response(ActionRequest request, Json // write response JSON jsonGenerator.writeStartObject(); jsonGenerator.writeStringField("redirectUrl", releaseUrl.toString()); - if (status == RequestStatus.IN_USE){ + if (status == RequestStatus.IN_USE) { jsonGenerator.writeStringField("error", "Cannot merge when one of the releases has an active moderation request."); } else if (status == RequestStatus.ACCESS_DENIED) { jsonGenerator.writeStringField("error", "You do not have sufficient permissions."); @@ -1471,7 +1745,7 @@ private void prepareReleaseDetailView(RenderRequest request, RenderResponse resp } Map<RequestedAction, Boolean> permissions = release.getPermissions(); - + request.setAttribute(PortalConstants.WRITE_ACCESS_USER, permissions.get(RequestedAction.WRITE)); if (isNullOrEmpty(id)) { id = release.getComponentId(); @@ -1492,6 +1766,61 @@ private void prepareReleaseDetailView(RenderRequest request, RenderResponse resp if (release != null) { addReleaseBreadcrumb(request, response, release); } + String spdxDocumentId = release.getSpdxId(); + SPDXDocument spdxDocument = new SPDXDocument(); + DocumentCreationInformation documentCreationInfo = new DocumentCreationInformation(); + PackageInformation packageInfo = new PackageInformation(); + Set<PackageInformation> packageInfos = new HashSet<>(); + if (!isNullOrEmpty(spdxDocumentId)) { + SPDXDocumentService.Iface SPDXDocumentClient = thriftClients.makeSPDXClient(); + spdxDocument = SPDXDocumentClient.getSPDXDocumentById(spdxDocumentId, user); + String spdxDocumentCreationInfoId = spdxDocument.getSpdxDocumentCreationInfoId(); + Set<String> spdxPackageInfoIds = spdxDocument.getSpdxPackageInfoIds(); + if (!isNullOrEmpty(spdxDocumentCreationInfoId)) { + DocumentCreationInformationService.Iface doClient = thriftClients.makeSPDXDocumentInfoClient(); + documentCreationInfo = doClient.getDocumentCreationInformationById(spdxDocumentCreationInfoId, user); + } + if (spdxPackageInfoIds != null) { + PackageInformationService.Iface paClient = thriftClients.makeSPDXPackageInfoClient(); + for (String spdxPackageInfoId : spdxPackageInfoIds) { + packageInfo = paClient.getPackageInformationById(spdxPackageInfoId, user); + packageInfos.add(packageInfo); + } + } + request.setAttribute(SPDXDOCUMENT, spdxDocument); + request.setAttribute(SPDX_DOCUMENT_CREATION_INFO, documentCreationInfo); + request.setAttribute(SPDX_PACKAGE_INFO, packageInfos); + } + + ObjectMapper objectMapper = new ObjectMapper(); + try { + if (!spdxDocument.isSetId()) { + spdxDocument = generateSpdxDocument(); + } + String spdxDocumentJson = objectMapper.writeValueAsString(spdxDocument); + request.setAttribute("spdxDocumentJson", spdxDocumentJson); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + try { + if (!documentCreationInfo.isSetId()) { + documentCreationInfo = generateDocumentCreationInformation(); + } + String documentCreationInfoJson = objectMapper.writeValueAsString(documentCreationInfo); + request.setAttribute("documentCreationInfoJson", documentCreationInfoJson); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + try { + Set<String> setPackage = new HashSet<>(); + for (PackageInformation pack : packageInfos) { + String packageInfoJson = objectMapper.writeValueAsString(pack); + setPackage.add(packageInfoJson); + } + request.setAttribute("packageInfoJson", setPackage); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } } catch (TException e) { if (e instanceof SW360Exception) { @@ -1511,7 +1840,7 @@ private void prepareReleaseDetailView(RenderRequest request, RenderResponse resp } private String createFossologyJobViewLink(ExternalToolProcessStep processStep, - Map<String, Set<String>> configKeyToValues, String fossologyJobsViewLink) { + Map<String, Set<String>> configKeyToValues, String fossologyJobsViewLink) { String uploadId = null; if (processStep != null) { uploadId = processStep.getResult(); @@ -1525,7 +1854,7 @@ private String createFossologyJobViewLink(ExternalToolProcessStep processStep, URI fossologyRestURI = new URI(url); fossologyHostName = fossologyRestURI.getHost(); fossologyPath = fossologyRestURI.getPath(); - fossologyPath = fossologyPath.substring(0,fossologyPath.indexOf("/api/v")); + fossologyPath = fossologyPath.substring(0, fossologyPath.indexOf("/api/v")); protocol = fossologyRestURI.getScheme(); int port = fossologyRestURI.getPort(); portStr = port == -1 ? StringUtils.EMPTY : ":" + port; @@ -1544,14 +1873,22 @@ private String createFossologyJobViewLink(ExternalToolProcessStep processStep, private void setSpdxAttachmentsInRequest(RenderRequest request, Release release) { Set<Attachment> attachments = CommonUtils.nullToEmptySet(release.getAttachments()); - Set<Attachment> spdxAttachments = attachments.stream() - .filter(a -> AttachmentType.COMPONENT_LICENSE_INFO_COMBINED.equals(a.getAttachmentType()) - || AttachmentType.COMPONENT_LICENSE_INFO_XML.equals(a.getAttachmentType())) - .collect(Collectors.toSet()); + Set<AttachmentType> attTypes = attachments.stream().map(Attachment::getAttachmentType).collect(Collectors.toUnmodifiableSet()); + Set<Attachment> spdxAttachments = Sets.newHashSet(); + if (attTypes.contains(AttachmentType.COMPONENT_LICENSE_INFO_COMBINED) || attTypes.contains(AttachmentType.COMPONENT_LICENSE_INFO_XML)) { + spdxAttachments = attachments.stream() + .filter(a -> AttachmentType.COMPONENT_LICENSE_INFO_COMBINED.equals(a.getAttachmentType()) + || AttachmentType.COMPONENT_LICENSE_INFO_XML.equals(a.getAttachmentType())) + .collect(Collectors.toSet()); + } else if (attTypes.contains(AttachmentType.INITIAL_SCAN_REPORT)) { + spdxAttachments = attachments.stream() + .filter(a -> AttachmentType.INITIAL_SCAN_REPORT.equals(a.getAttachmentType())) + .collect(Collectors.toSet()); + } request.setAttribute(PortalConstants.SPDX_ATTACHMENTS, spdxAttachments); } - private String formatedMessageForVul(List<VerificationStateInfo> infoHistory){ + private String formatedMessageForVul(List<VerificationStateInfo> infoHistory) { return CommonVulnerabilityPortletUtils.formatedMessageForVul(infoHistory, e -> e.getVerificationState().name(), e -> e.getCheckedOn(), @@ -1572,7 +1909,7 @@ private void putVulnerabilitiesInRequestRelease(RenderRequest request, String re putVulnerabilitiesInRequest(request, vuls, user); } - private void putVulnerabilitiesInRequestComponent(RenderRequest request, String componentId, User user, boolean isVulEditable) throws TException{ + private void putVulnerabilitiesInRequestComponent(RenderRequest request, String componentId, User user, boolean isVulEditable) throws TException { VulnerabilityService.Iface vulClient = thriftClients.makeVulnerabilityClient(); List<VulnerabilityDTO> vuls; if (isVulEditable) { @@ -1592,14 +1929,14 @@ private void putVulnerabilitiesInRequest(RenderRequest request, List<Vulnerabili private void addToVulnerabilityVerifications(Map<String, Map<String, VerificationState>> vulnerabilityVerifications, Map<String, Map<String, String>> vulnerabilityTooltips, - VulnerabilityDTO vulnerability){ + VulnerabilityDTO vulnerability) { String vulnerabilityId = vulnerability.getExternalId(); String releaseId = vulnerability.getIntReleaseId(); Map<String, VerificationState> vulnerabilityVerification = vulnerabilityVerifications.computeIfAbsent(vulnerabilityId, k -> new HashMap<>()); Map<String, String> vulnerabilityTooltip = vulnerabilityTooltips.computeIfAbsent(vulnerabilityId, k -> new HashMap<>()); ReleaseVulnerabilityRelation relation = vulnerability.getReleaseVulnerabilityRelation(); - if (! relation.isSetVerificationStateInfo()) { + if (!relation.isSetVerificationStateInfo()) { vulnerabilityVerification.put(releaseId, VerificationState.NOT_CHECKED); vulnerabilityTooltip.put(releaseId, "Not checked yet."); } else { @@ -1618,7 +1955,7 @@ private void putVulnerabilityMetadatasInRequest(RenderRequest request, List<Vuln } long numberOfCorrectVuls = vuls.stream() - .filter(vul -> ! VerificationState.INCORRECT.equals(getVerificationState(vul))) + .filter(vul -> !VerificationState.INCORRECT.equals(getVerificationState(vul))) .map(VulnerabilityDTO::getExternalId) .collect(Collectors.toSet()) .size(); @@ -1636,8 +1973,8 @@ private void putVulnerabilityMetadatasInRequest(RenderRequest request, List<Vuln request.setAttribute(NUMBER_OF_INCORRECT_VULNERABILITIES, numberOfIncorrectVuls); } - request.setAttribute(PortalConstants.VULNERABILITY_VERIFICATIONS,vulnerabilityVerifications); - request.setAttribute(PortalConstants.VULNERABILITY_VERIFICATION_TOOLTIPS,vulnerabilityTooltips); + request.setAttribute(PortalConstants.VULNERABILITY_VERIFICATIONS, vulnerabilityVerifications); + request.setAttribute(PortalConstants.VULNERABILITY_VERIFICATION_TOOLTIPS, vulnerabilityTooltips); } @@ -1726,21 +2063,21 @@ private Map<String, Set<String>> getComponentFilterMap(PortletRequest request) { String query = new StringBuilder("[%s ").append(PortalConstants.TO).append(" %s]").toString(); DateRange range = ThriftEnumUtils.stringToEnum(dateRange, DateRange.class); switch (range) { - case EQUAL: - break; - case LESS_THAN_OR_EQUAL_TO: - parameter = String.format(query, PortalConstants.EPOCH_DATE, parameter); - break; - case GREATER_THAN_OR_EQUAL_TO: - parameter = String.format(query, parameter, upperLimit); - break; - case BETWEEN: - String endDate = request.getParameter(PortalConstants.END_DATE); - if (isNullEmptyOrWhitespace(endDate)) { - endDate = upperLimit; - } - parameter = String.format(query, parameter, endDate); - break; + case EQUAL: + break; + case LESS_THAN_OR_EQUAL_TO: + parameter = String.format(query, PortalConstants.EPOCH_DATE, parameter); + break; + case GREATER_THAN_OR_EQUAL_TO: + parameter = String.format(query, parameter, upperLimit); + break; + case BETWEEN: + String endDate = request.getParameter(PortalConstants.END_DATE); + if (isNullEmptyOrWhitespace(endDate)) { + endDate = upperLimit; + } + parameter = String.format(query, parameter, endDate); + break; } } Set<String> values = CommonUtils.splitToSet(parameter); @@ -1796,7 +2133,7 @@ public void updateComponent(ActionRequest request, ActionResponse response) thro setSessionMessage(request, requestStatus, "Component", "update", component.getName()); if (RequestStatus.DUPLICATE.equals(requestStatus) || RequestStatus.DUPLICATE_ATTACHMENT.equals(requestStatus) || RequestStatus.NAMINGERROR.equals(requestStatus)) { - if(RequestStatus.DUPLICATE.equals(requestStatus)) + if (RequestStatus.DUPLICATE.equals(requestStatus)) setSW360SessionError(request, ErrorMessages.COMPONENT_DUPLICATE); else if (RequestStatus.NAMINGERROR.equals(requestStatus)) setSW360SessionError(request, ErrorMessages.COMPONENT_NAMING_ERROR); @@ -1820,7 +2157,7 @@ else if (RequestStatus.NAMINGERROR.equals(requestStatus)) AddDocumentRequestSummary summary = client.addComponent(component, user); AddDocumentRequestStatus status = summary.getRequestStatus(); - switch(status){ + switch (status) { case SUCCESS: String successMsg = "Component " + component.getName() + " added successfully"; SessionMessages.add(request, "request_processed", successMsg); @@ -1889,7 +2226,7 @@ public void updateRelease(ActionRequest request, ActionResponse response) throws setSessionMessage(request, requestStatus, "Release", "update", printName(release)); if (RequestStatus.DUPLICATE.equals(requestStatus) || RequestStatus.DUPLICATE_ATTACHMENT.equals(requestStatus) || RequestStatus.NAMINGERROR.equals(requestStatus)) { - if(RequestStatus.DUPLICATE.equals(requestStatus)) + if (RequestStatus.DUPLICATE.equals(requestStatus)) setSW360SessionError(request, ErrorMessages.RELEASE_DUPLICATE); else if (RequestStatus.NAMINGERROR.equals(requestStatus)) setSW360SessionError(request, ErrorMessages.RELEASE_NAME_VERSION_ERROR); @@ -1918,6 +2255,7 @@ else if (RequestStatus.NAMINGERROR.equals(requestStatus)) request.setAttribute(WebKeys.REDIRECT, redirectUrl.toString()); sendRedirect(request, response); + SpdxPortlet.updateSPDX(request, response, user, releaseId, false); } } else { release = new Release(); @@ -1937,8 +2275,29 @@ else if (RequestStatus.NAMINGERROR.equals(requestStatus)) AddDocumentRequestSummary summary = client.addRelease(release, user); AddDocumentRequestStatus status = summary.getRequestStatus(); - switch(status){ + switch (status) { case SUCCESS: + ObjectMapper objectMapper = new ObjectMapper(); + try { + String spdxDocumentJson = objectMapper.writeValueAsString(generateSpdxDocument()); + request.setAttribute(SPDXDocument._Fields.TYPE.toString(), spdxDocumentJson); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + try { + String documentCreationInfoJson = objectMapper.writeValueAsString(generateDocumentCreationInformation()); + request.setAttribute(SPDXDocument._Fields.SPDX_DOCUMENT_CREATION_INFO_ID.toString(), documentCreationInfoJson); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + try { + String packageInfoJson = objectMapper.writeValueAsString(generatePackageInfomation()); + packageInfoJson = "[" + packageInfoJson + "]"; + request.setAttribute(SPDXDocument._Fields.SPDX_PACKAGE_INFO_IDS.toString(), packageInfoJson); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + SpdxPortlet.updateSPDX(request, response, user, summary.getId(), true); response.setRenderParameter(RELEASE_ID, summary.getId()); String successMsg = "Release " + printName(release) + " added successfully"; SessionMessages.add(request, "request_processed", successMsg); @@ -1978,7 +2337,7 @@ private void prepareRequestForReleaseEditAfterDuplicateError(ActionRequest reque } private void fillVendor(Release release) throws TException { - if(!isNullOrEmpty(release.getVendorId()) && release.isSetVendorId()) { + if (!isNullOrEmpty(release.getVendorId()) && release.isSetVendorId()) { VendorService.Iface client = thriftClients.makeVendorClient(); Vendor vendor = client.getByID(release.getVendorId()); release.setVendor(vendor); @@ -2025,7 +2384,7 @@ private void updateVulnerabilitiesRelease(ResourceRequest request, ResourceRespo JSONObject responseData = PortletUtils.importStatusToJSON(importStatus); PrintWriter writer = response.getWriter(); writer.write(responseData.toString()); - } catch (TException e){ + } catch (TException e) { log.error("Error updating CVEs for release in backend.", e); } } diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/spdx/SpdxPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/spdx/SpdxPortlet.java new file mode 100644 index 0000000000..09df2a0722 --- /dev/null +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/spdx/SpdxPortlet.java @@ -0,0 +1,420 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package org.eclipse.sw360.portal.portlets.components.spdx; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.liferay.portal.kernel.json.JSONArray; +import com.liferay.portal.kernel.json.JSONException; +import com.liferay.portal.kernel.json.JSONFactoryUtil; +import com.liferay.portal.kernel.json.JSONObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.thrift.TException; +import org.eclipse.sw360.datahandler.thrift.ThriftClients; +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.Annotations; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; +import org.eclipse.sw360.datahandler.thrift.spdx.otherlicensinginformationdetected.OtherLicensingInformationDetected; +import org.eclipse.sw360.datahandler.thrift.spdx.relationshipsbetweenspdxelements.RelationshipsBetweenSPDXElements; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetRange; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocumentService; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.ExternalReference; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformationService; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageVerificationCode; +import org.eclipse.sw360.datahandler.thrift.users.User; + +import javax.portlet.ActionRequest; +import javax.portlet.ActionResponse; +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.base.Strings.isNullOrEmpty; + +/** + * SPDX portlet implementation + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public abstract class SpdxPortlet { + + private SpdxPortlet() { + // Utility class with only static functions + } + + private static final Logger log = LogManager.getLogger(SpdxPortlet.class); + private static final ObjectMapper mapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + private static SPDXDocument parseSPDXDocumentFromRequest(String jsonData) { + SPDXDocument spdx = new SPDXDocument(); + if (jsonData == null) { + return null; + } + try { + JSONObject json = JSONFactoryUtil.createJSONObject(jsonData); + Set<SnippetInformation> snippets = parseSnippets(json); + json.remove("snippets"); + Set<RelationshipsBetweenSPDXElements> relationships = parseRelationships(json); + json.remove("relationships"); + Set<Annotations> annotations = parseAnnotations(json); + json.remove("annotations"); + Set<OtherLicensingInformationDetected> licensingInfo = parseLicensingInfo(json); + json.remove("otherLicensingInformationDetecteds"); + json.remove("documentState"); + json.remove("permissions"); + try { + spdx = mapper.readValue(json.toJSONString(), SPDXDocument.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + spdx.setSnippets(snippets); + spdx.setRelationships(relationships); + spdx.setAnnotations(annotations); + spdx.setOtherLicensingInformationDetecteds(licensingInfo); + } catch (JSONException e) { + e.printStackTrace(); + } + return spdx; + } + + private static DocumentCreationInformation parseDocumentCreationInfoFromRequest(String jsonData) { + DocumentCreationInformation documentCreationInfo = new DocumentCreationInformation(); + if (jsonData == null) { + return null; + } + try { + JSONObject json = JSONFactoryUtil.createJSONObject(jsonData); + Set<ExternalDocumentReferences> externalDocumentRefs = parseExternalDocumentReferences(json); + json.remove("externalDocumentRefs"); + Set<Creator> creator = parseCreator(json); + json.remove("creator"); + json.remove("documentState"); + json.remove("permissions"); + try { + documentCreationInfo = mapper.readValue(json.toJSONString(), DocumentCreationInformation.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + documentCreationInfo.setExternalDocumentRefs(externalDocumentRefs); + documentCreationInfo.setCreator(creator); + } catch (JSONException e) { + e.printStackTrace(); + } + return documentCreationInfo; + } + + private static PackageInformation parsePackageInfoFromRequest(String jsonData) { + PackageInformation packageInfo = new PackageInformation(); + if (jsonData == null) { + return null; + } + try { + JSONObject json = JSONFactoryUtil.createJSONObject(jsonData); + PackageVerificationCode packageVerificationCode = parsePackageVerificationCode(json); + json.remove("packageVerificationCode"); + Set<CheckSum> checksums = parseChecksum(json); + json.remove("checksums"); + Set<ExternalReference> externalRefs = parseExternalReference(json); + json.remove("externalReferences"); + Set<Annotations> annotations = parseAnnotations(json); + json.remove("annotations"); + json.remove("documentState"); + json.remove("permissions"); + try { + packageInfo = mapper.readValue(json.toJSONString(), PackageInformation.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + packageInfo.setPackageVerificationCode(packageVerificationCode); + packageInfo.setChecksums(checksums); + packageInfo.setExternalRefs(externalRefs); + packageInfo.setAnnotations(annotations); + } catch (JSONException e) { + e.printStackTrace(); + } + return packageInfo; + } + + private static Set<PackageInformation> parsePackageInfosFromRequest(String jsonData) { + Set<PackageInformation> packageInfos = new HashSet<>(); + if (jsonData == null) { + return null; + } + try { + JSONArray arrayPackages = JSONFactoryUtil.createJSONArray(jsonData); + if (arrayPackages == null) { + return packageInfos; + } + for (int i = 0; i < arrayPackages.length(); i++) { + PackageInformation packageInfo = parsePackageInfoFromRequest(arrayPackages.getJSONObject(i).toJSONString()); + if (packageInfo != null) { + packageInfos.add(packageInfo); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + return packageInfos; + } + + public static void updateSPDX(ActionRequest request, ActionResponse response, User user, String releaseId, boolean addNew) throws TException { + String spdxDocumentData; + String documentCreationInfoData; + String packageInfoData; + String spdxDocumentId = ""; + + if (addNew) { + spdxDocumentData = (String) request.getAttribute(SPDXDocument._Fields.TYPE.toString()); + documentCreationInfoData = (String) request.getAttribute(SPDXDocument._Fields.SPDX_DOCUMENT_CREATION_INFO_ID.toString()); + packageInfoData = (String) request.getAttribute(SPDXDocument._Fields.SPDX_PACKAGE_INFO_IDS.toString()); + } else { + spdxDocumentData = request.getParameter(SPDXDocument._Fields.TYPE.toString()); + documentCreationInfoData = request.getParameter(SPDXDocument._Fields.SPDX_DOCUMENT_CREATION_INFO_ID.toString()); + packageInfoData = request.getParameter(SPDXDocument._Fields.SPDX_PACKAGE_INFO_IDS.toString()); + } + if (!isNullOrEmpty(spdxDocumentData)) { + SPDXDocument spdx = parseSPDXDocumentFromRequest(spdxDocumentData); + SPDXDocumentService.Iface spdxClient = new ThriftClients().makeSPDXClient(); + if (spdx != null) { + if (isNullOrEmpty(spdx.getReleaseId()) && !isNullOrEmpty(releaseId)) { + spdx.setReleaseId(releaseId); + } + if (isNullOrEmpty(spdx.getId())) { + spdx.unsetId(); + spdx.unsetRevision(); + spdxDocumentId = spdxClient.addSPDXDocument(spdx, user).getId(); + } else { + spdxClient.updateSPDXDocument(spdx, user); + spdxDocumentId = spdx.getId(); + } + } + } + if (!isNullOrEmpty(documentCreationInfoData)) { + DocumentCreationInformation document = parseDocumentCreationInfoFromRequest(documentCreationInfoData); + if (document != null) { + DocumentCreationInformationService.Iface documentClient = new ThriftClients().makeSPDXDocumentInfoClient(); + if (isNullOrEmpty(document.getSpdxDocumentId())) { + document.setSpdxDocumentId(spdxDocumentId); + } + if (isNullOrEmpty(document.getId())) { + document.unsetId(); + document.unsetRevision(); + documentClient.addDocumentCreationInformation(document, user); + } else { + documentClient.updateDocumentCreationInformation(document, user); + } + } + } + if (!isNullOrEmpty(packageInfoData)) { + Set<PackageInformation> packageInfos = parsePackageInfosFromRequest(packageInfoData); + SPDXDocumentService.Iface SPDXClient = new ThriftClients().makeSPDXClient(); + SPDXDocument spdxDocument = SPDXClient.getSPDXDocumentById(spdxDocumentId, user); + idDeletePackageInfo(packageInfos, spdxDocument, user); + if (packageInfos != null) { + PackageInformationService.Iface packageClient = new ThriftClients().makeSPDXPackageInfoClient(); + for (PackageInformation packageInfo : packageInfos) { + if (isNullOrEmpty(packageInfo.getSpdxDocumentId())) { + packageInfo.setSpdxDocumentId(spdxDocumentId); + } + if (isNullOrEmpty(packageInfo.getId())) { + packageInfo.unsetId(); + packageInfo.unsetRevision(); + packageClient.addPackageInformation(packageInfo, user); + } else { + packageClient.updatePackageInformation(packageInfo, user); + } + } + } + } + } + + private static void idDeletePackageInfo(Set<PackageInformation> packageInfos, SPDXDocument spdxDocument, User user) { + Set<String> spdxDocumentId = spdxDocument.getSpdxPackageInfoIds(); + Set<String> listIdPackageInfos = new HashSet<>(); + packageInfos.forEach(pInfo -> listIdPackageInfos.add(pInfo.getId())); + for (String s : spdxDocumentId) { + if (!listIdPackageInfos.contains(s)) { + PackageInformationService.Iface packageClient = new ThriftClients().makeSPDXPackageInfoClient(); + try { + packageClient.deletePackageInformation(s, user); + } catch (Exception e) { + log.error("Could not delete SDPX Package Info {}", e.getMessage()); + } + } + } + } + + private static Set<SnippetInformation> parseSnippets(JSONObject json) { + Set<SnippetInformation> snippets = new HashSet<>(); + JSONArray arraySnippets = json.getJSONArray("snippets"); + if (arraySnippets == null) { + return snippets; + } + for (int i = 0; i < arraySnippets.length(); i++) { + try { + JSONObject objectSnippet = arraySnippets.getJSONObject(i); + JSONArray arraySnippet = objectSnippet.getJSONArray("snippetRanges"); + objectSnippet.remove("snippetRanges"); + SnippetInformation snippet = mapper.readValue(objectSnippet.toJSONString(), SnippetInformation.class); + Set<SnippetRange> snippetRanges = new HashSet<>(); + for (int j = 0; j < arraySnippet.length(); j++) { + snippetRanges.add(mapper.readValue(arraySnippet.getString(j), SnippetRange.class)); + } + snippet.setSnippetRanges(snippetRanges); + snippets.add(snippet); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + return snippets; + } + + private static Set<RelationshipsBetweenSPDXElements> parseRelationships(JSONObject json) { + Set<RelationshipsBetweenSPDXElements> relationships = new HashSet<>(); + JSONArray arrayRelationships = json.getJSONArray("relationships"); + if (arrayRelationships == null) { + return relationships; + } + for (int i = 0; i < arrayRelationships.length(); i++) { + try { + relationships + .add(mapper.readValue(arrayRelationships.getString(i), RelationshipsBetweenSPDXElements.class)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + return relationships; + } + + private static Set<Annotations> parseAnnotations(JSONObject json) { + Set<Annotations> annotations = new HashSet<>(); + JSONArray arrayAnnotations = json.getJSONArray("annotations"); + if (arrayAnnotations == null) { + return annotations; + } + for (int i = 0; i < arrayAnnotations.length(); i++) { + try { + annotations.add(mapper.readValue(arrayAnnotations.getString(i), Annotations.class)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + return annotations; + } + + private static Set<OtherLicensingInformationDetected> parseLicensingInfo(JSONObject json) { + Set<OtherLicensingInformationDetected> licensingInfo = new HashSet<>(); + JSONArray arrayLicensingInfo = json.getJSONArray("otherLicensingInformationDetecteds"); + if (arrayLicensingInfo == null) { + return licensingInfo; + } + for (int i = 0; i < arrayLicensingInfo.length(); i++) { + try { + licensingInfo.add( + mapper.readValue(arrayLicensingInfo.getString(i), OtherLicensingInformationDetected.class)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + return licensingInfo; + } + + private static Set<ExternalDocumentReferences> parseExternalDocumentReferences(JSONObject json) { + Set<ExternalDocumentReferences> externalDocumentRefs = new HashSet<>(); + JSONArray arrayExternalDocumentRefs = json.getJSONArray("externalDocumentRefs"); + if (arrayExternalDocumentRefs == null) { + return externalDocumentRefs; + } + for (int i = 0; i < arrayExternalDocumentRefs.length(); i++) { + try { + JSONObject objectExternalDocumentRef = arrayExternalDocumentRefs.getJSONObject(i); + JSONObject objectChecksum = objectExternalDocumentRef.getJSONObject("checksum"); + objectExternalDocumentRef.remove("checksum"); + ExternalDocumentReferences externalDocumentRef = mapper.readValue(objectExternalDocumentRef.toJSONString(), ExternalDocumentReferences.class); + CheckSum checksum = mapper.readValue(objectChecksum.toJSONString(), CheckSum.class); + externalDocumentRef.setChecksum(checksum); + externalDocumentRefs.add(externalDocumentRef); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + return externalDocumentRefs; + } + + private static Set<Creator> parseCreator(JSONObject json) { + Set<Creator> creator = new HashSet<>(); + JSONArray arrayCreator = json.getJSONArray("creator"); + if (arrayCreator == null) { + return creator; + } + for (int i = 0; i < arrayCreator.length(); i++) { + try { + creator.add(mapper.readValue(arrayCreator.getString(i), Creator.class)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + return creator; + } + + private static PackageVerificationCode parsePackageVerificationCode(JSONObject json) { + PackageVerificationCode packageVerificationCode = new PackageVerificationCode(); + JSONObject objectPackageVerificationCode = json.getJSONObject("packageVerificationCode"); + if (objectPackageVerificationCode == null) { + return packageVerificationCode; + } + try { + packageVerificationCode = mapper.readValue(objectPackageVerificationCode.toJSONString(), + PackageVerificationCode.class); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return packageVerificationCode; + } + + private static Set<CheckSum> parseChecksum(JSONObject json) { + Set<CheckSum> checkSums = new HashSet<>(); + JSONArray arrayCheckSums = json.getJSONArray("checksums"); + if (arrayCheckSums == null) { + return checkSums; + } + for (int i = 0; i < arrayCheckSums.length(); i++) { + try { + checkSums.add(mapper.readValue(arrayCheckSums.getString(i), CheckSum.class)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + return checkSums; + } + + private static Set<ExternalReference> parseExternalReference(JSONObject json) { + Set<ExternalReference> externalRefs = new HashSet<>(); + JSONArray arrayExternalRefs = json.getJSONArray("externalRefs"); + if (arrayExternalRefs == null) { + return externalRefs; + } + for (int i = 0; i < arrayExternalRefs.length(); i++) { + try { + externalRefs.add(mapper.readValue(arrayExternalRefs.getString(i), ExternalReference.class)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + return externalRefs; + } + +} \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/moderation/ModerationPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/moderation/ModerationPortlet.java index ff03150805..c2df5efd66 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/moderation/ModerationPortlet.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/moderation/ModerationPortlet.java @@ -56,6 +56,12 @@ import org.eclipse.sw360.datahandler.thrift.projects.Project; import org.eclipse.sw360.datahandler.thrift.projects.ProjectLink; import org.eclipse.sw360.datahandler.thrift.projects.ProjectService; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformationService; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocumentService; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformationService; import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.users.UserGroup; @@ -596,6 +602,42 @@ private void acceptModerationRequest(User user, User requestingUser, ModerationR UserUtils.activateLiferayUser(request, moderationRequest.getUser()); } break; + case SPDXDOCUMENT: { + SPDXDocumentService.Iface SpdxDocumentClient = thriftClients.makeSPDXClient(); + if (moderationRequest.isRequestDocumentDelete()) { + SpdxDocumentClient.deleteSPDXDocument(moderationRequest.getDocumentId(), user); + } else { + SpdxDocumentClient.updateSPDXDocumentFromModerationRequest( + moderationRequest.getSPDXDocumentAdditions(), + moderationRequest.getSPDXDocumentDeletions(), + user); + } + } + break; + case SPDX_DOCUMENT_CREATION_INFO: { + DocumentCreationInformationService.Iface documentCreationInfoClient = thriftClients.makeSPDXDocumentInfoClient(); + if (moderationRequest.isRequestDocumentDelete()) { + documentCreationInfoClient.deleteDocumentCreationInformation(moderationRequest.getDocumentId(), user); + } else { + documentCreationInfoClient.updateDocumentCreationInfomationFromModerationRequest( + moderationRequest.getDocumentCreationInfoAdditions(), + moderationRequest.getDocumentCreationInfoDeletions(), + user); + } + } + break; + case SPDX_PACKAGE_INFO: { + PackageInformationService.Iface packageInfoClient = thriftClients.makeSPDXPackageInfoClient(); + if (moderationRequest.isRequestDocumentDelete()) { + packageInfoClient.deletePackageInformation(moderationRequest.getDocumentId(), user); + } else { + packageInfoClient.updatePackageInfomationFromModerationRequest( + moderationRequest.getPackageInfoAdditions(), + moderationRequest.getPackageInfoDeletions(), + user); + } + } + break; } } @@ -765,6 +807,15 @@ private void renderEditViewForId(RenderRequest request, RenderResponse response, case USER: renderUserModeration(request, response, moderationRequest, user); break; + case SPDXDOCUMENT: + renderSPDXDocumentModeration(request, response, moderationRequest, user); + break; + case SPDX_DOCUMENT_CREATION_INFO: + renderDocumentCreationInfoModeration(request, response, moderationRequest, user); + break; + case SPDX_PACKAGE_INFO: + renderPackageInfoModeration(request, response, moderationRequest, user); + break; } } } @@ -998,6 +1049,93 @@ public void renderUserModeration(RenderRequest request, RenderResponse response include("/html/moderation/users/merge.jsp", request, response); } + public void renderSPDXDocumentModeration(RenderRequest request, RenderResponse response, ModerationRequest moderationRequest, User user) throws IOException, PortletException, TException { + final boolean requestDocumentDelete = moderationRequest.isRequestDocumentDelete(); + Boolean is_used = false; + SPDXDocument actual_SPDXDocuemnt = null; + try { + SPDXDocumentService.Iface client = thriftClients.makeSPDXClient(); + actual_SPDXDocuemnt = client.getSPDXDocumentForEdit(moderationRequest.getDocumentId(), user); + request.setAttribute(PortalConstants.ACTUAL_SPDXDOCUMENT, actual_SPDXDocuemnt); + } catch (TException e) { + log.error("Could not retrieve SPDX Document", e); + } + + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (actual_SPDXDocuemnt == null) { + renderNextModeration(request, response, user, LanguageUtil.get(resourceBundle,"ignored.unretrievable.target"), thriftClients.makeModerationClient(), moderationRequest); + return; + } + + if (refuseToDeleteUsedDocument(request, response, moderationRequest, user, requestDocumentDelete, is_used)) + return; + + request.setAttribute(DOCUMENT_TYPE, SW360Constants.TYPE_SPDX_DOCUMENT); + if (moderationRequest.isRequestDocumentDelete()) { + include("/html/moderation/spdx/spdxdocument/delete.jsp", request, response); + } else { + include("/html/moderation/spdx/spdxdocument/merge.jsp", request, response); + } + } + + public void renderDocumentCreationInfoModeration(RenderRequest request, RenderResponse response, ModerationRequest moderationRequest, User user) throws IOException, PortletException, TException { + final boolean requestDocumentDelete = moderationRequest.isRequestDocumentDelete(); + Boolean is_used = false; + DocumentCreationInformation actual_DocuemntCreationInfo = null; + try { + DocumentCreationInformationService.Iface client = thriftClients.makeSPDXDocumentInfoClient(); + actual_DocuemntCreationInfo = client.getDocumentCreationInfoForEdit(moderationRequest.getDocumentId(), user); + request.setAttribute(PortalConstants.ACTUAL_DOCUMENT_CREATION_INFO, actual_DocuemntCreationInfo); + } catch (TException e) { + log.error("Could not retrieve Document Creation Information", e); + } + + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (actual_DocuemntCreationInfo == null) { + renderNextModeration(request, response, user, LanguageUtil.get(resourceBundle,"ignored.unretrievable.target"), thriftClients.makeModerationClient(), moderationRequest); + return; + } + + if (refuseToDeleteUsedDocument(request, response, moderationRequest, user, requestDocumentDelete, is_used)) + return; + + request.setAttribute(DOCUMENT_TYPE, SW360Constants.TYPE_SPDX_DOCUMENT_CREATION_INFO); + if (moderationRequest.isRequestDocumentDelete()) { + include("/html/moderation/spdx/documentcreationinfo/delete.jsp", request, response); + } else { + include("/html/moderation/spdx/documentcreationinfo/merge.jsp", request, response); + } + } + + public void renderPackageInfoModeration(RenderRequest request, RenderResponse response, ModerationRequest moderationRequest, User user) throws IOException, PortletException, TException { + final boolean requestDocumentDelete = moderationRequest.isRequestDocumentDelete(); + Boolean is_used = false; + PackageInformation actual_PackageInfo = null; + try { + PackageInformationService.Iface client = thriftClients.makeSPDXPackageInfoClient(); + actual_PackageInfo = client.getPackageInformationForEdit(moderationRequest.getDocumentId(), user); + request.setAttribute(PortalConstants.ACTUAL_PACKAGE_INFO, actual_PackageInfo); + } catch (TException e) { + log.error("Could not retrieve Package Information", e); + } + + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (actual_PackageInfo == null) { + renderNextModeration(request, response, user, LanguageUtil.get(resourceBundle,"ignored.unretrievable.target"), thriftClients.makeModerationClient(), moderationRequest); + return; + } + + if (refuseToDeleteUsedDocument(request, response, moderationRequest, user, requestDocumentDelete, is_used)) + return; + + request.setAttribute(DOCUMENT_TYPE, SW360Constants.TYPE_SPDX_PACKAGE_INFO); + if (moderationRequest.isRequestDocumentDelete()) { + include("/html/moderation/spdx/packageinfo/delete.jsp", request, response); + } else { + include("/html/moderation/spdx/packageinfo/merge.jsp", request, response); + } + } + @UsedAsLiferayAction public void applyFilters(ActionRequest request, ActionResponse response) throws PortletException, IOException { for (ModerationRequest._Fields moderationFilteredField : MODERATION_FILTERED_FIELDS) { diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java index 97465049d7..62808cea64 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/projects/ProjectPortlet.java @@ -95,6 +95,7 @@ import static org.eclipse.sw360.datahandler.common.CommonUtils.*; import static org.eclipse.sw360.datahandler.common.SW360Constants.CONTENT_TYPE_OPENXML_SPREADSHEET; import static org.eclipse.sw360.datahandler.common.SW360Utils.printName; +import static org.eclipse.sw360.datahandler.common.WrappedException.wrapException; import static org.eclipse.sw360.datahandler.common.WrappedException.wrapTException; import static org.eclipse.sw360.portal.common.PortalConstants.*; import static org.eclipse.sw360.portal.portlets.projects.ProjectPortletUtils.isUsageEquivalent; @@ -244,6 +245,8 @@ public void serveResource(ResourceRequest request, ResourceResponse response) th serveLicenseToSourceFileMapping(request, response); } else if (PortalConstants.ADD_LICENSE_TO_RELEASE.equals(action)) { addLicenseToLinkedReleases(request, response); + } else if (PortalConstants.LOAD_SPDX_LICENSE_INFO.equals(action)) { + loadSpdxLicenseInfo(request, response); } else if (isGenericAction(action)) { dealWithGenericAction(request, response, action); } else if (PortalConstants.LOAD_CHANGE_LOGS.equals(action) || PortalConstants.VIEW_CHANGE_LOGS.equals(action)) { @@ -2583,8 +2586,6 @@ private Map<String, ObligationStatusInfo> setLicenseInfoWithObligations(PortletR obligationStatusMap = licenseObligation.getObligationStatusMap(); request.setAttribute(APPROVED_OBLIGATIONS_COUNT, getFulfilledObligationsCount(obligationStatusMap)); - request.setAttribute(OBLIGATION_FROM_README_OSS, getObligationsFromReadmeOSSCount(obligationStatusMap)); - request.setAttribute(EXCLUDED_RELEASES, excludedReleases); request.setAttribute(PROJECT_OBLIGATIONS_INFO_BY_RELEASE, filterAndSortLicenseInfo(licenseObligation.getLicenseInfoResults())); } catch (TException e) { @@ -2627,17 +2628,8 @@ private List<LicenseInfoParsingResult> filterAndSortLicenseInfo(List<LicenseInfo private int getFulfilledObligationsCount(Map<String, ObligationStatusInfo> obligationStatusMap) { if (CommonUtils.isNotEmpty(obligationStatusMap.keySet())) { - return Math.toIntExact( - obligationStatusMap.values().stream().filter(obligation -> obligation.getStatus() != null - && !ObligationStatus.OPEN.equals(obligation.getStatus())).count()); - } - return 0; - } - - private int getObligationsFromReadmeOSSCount(Map<String, ObligationStatusInfo> obligationStatusMap) { - if (CommonUtils.isNotEmpty(obligationStatusMap.keySet())) { - return Math.toIntExact( - obligationStatusMap.values().stream().filter(obligation -> obligation.getObligationLevel() == null).count()); + return Math.toIntExact(obligationStatusMap.values().stream() + .filter(obligation -> ObligationStatus.ACKNOWLEDGED_OR_FULFILLED.equals(obligation.getStatus())).count()); } return 0; } @@ -2706,14 +2698,14 @@ private void addLicenseToLinkedReleases(ResourceRequest request, ResourceRespons && !license.getLicenseName().equalsIgnoreCase(SW360Constants.NA) && !license.getLicenseName().equalsIgnoreCase(SW360Constants.NO_ASSERTION)) // exclude unknown, n/a and noassertion .collect(Collectors.toList()); - if (attachmentName.endsWith(".rdf")) { + if (attachmentName.endsWith(PortalConstants.RDF_FILE_EXTENSION)) { mainLicenses.addAll(licenseInfoResult.stream() .filter(filterConcludedLicense) .flatMap(singleResult -> singleResult.getLicenseInfo().getConcludedLicenseIds().stream()) .collect(Collectors.toSet())); otherLicenses.addAll(licenseWithTexts.stream().map(LicenseNameWithText::getLicenseName).collect(Collectors.toSet())); otherLicenses.removeAll(mainLicenses); - } else if (attachmentName.endsWith(".xml")) { + } else if (attachmentName.endsWith(PortalConstants.XML_FILE_EXTENSION)) { mainLicenses.addAll(licenseWithTexts.stream() .filter(filterLicense) .map(LicenseNameWithText::getLicenseName).collect(Collectors.toSet())); @@ -2746,6 +2738,92 @@ private void addLicenseToLinkedReleases(ResourceRequest request, ResourceRespons } } + private void loadSpdxLicenseInfo(ResourceRequest request, ResourceResponse response) throws IOException, PortletException { + final User user = UserCacheHolder.getUserFromRequest(request); + final String releaseId = request.getParameter(PortalConstants.RELEASE_ID); + final ComponentService.Iface componentClient = thriftClients.makeComponentClient(); + final LicenseInfoService.Iface licenseInfoClient = thriftClients.makeLicenseInfoClient(); + final JSONObject jsonResult = createJSONObject(); + final ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + final Predicate<Attachment> isISR = attachment -> AttachmentType.INITIAL_SCAN_REPORT.equals(attachment.getAttachmentType()); + + Set<LicenseNameWithText> licenseNameWithTexts = new HashSet<LicenseNameWithText>(); + String attachmentContentId = ""; + String attachmentName = ""; + Set<String> concludedLicenseIds = new TreeSet<String>(); + Set<String> otherLicenseNames = new TreeSet<String>(); + AttachmentType attachmentType = AttachmentType.OTHER; + long totalFileCount = 0; + try { + Release release = componentClient.getReleaseById(releaseId, user); + Set<Attachment> attachments = CommonUtils.nullToEmptySet(release.getAttachments()); + attachments = attachments.stream().filter(isISR).collect(Collectors.toSet()); + if (attachments.size() == 1) { + Attachment attachment = attachments.iterator().next(); + attachmentType = attachment.getAttachmentType(); + attachmentContentId = attachment.getAttachmentContentId(); + attachmentName = attachment.getFilename(); + List<LicenseInfoParsingResult> licenseInfoResult = licenseInfoClient.getLicenseInfoForAttachment(release, + attachmentContentId, true, user); + List<LicenseNameWithText> licenseWithTexts = licenseInfoResult.stream() + .flatMap(result -> result.getLicenseInfo().getLicenseNamesWithTexts().stream()) + .filter(license -> !license.getLicenseName().equalsIgnoreCase(SW360Constants.LICENSE_NAME_UNKNOWN) + && !license.getLicenseName().equalsIgnoreCase(SW360Constants.NA) + && !license.getLicenseName().equalsIgnoreCase(SW360Constants.NO_ASSERTION)) // exclude unknown, n/a and noassertion + .collect(Collectors.toList()); + if (attachmentName.endsWith(PortalConstants.RDF_FILE_EXTENSION)) { + totalFileCount = licenseWithTexts.stream().map(LicenseNameWithText::getSourceFiles).filter(src -> src != null).mapToInt(Set::size).sum(); + concludedLicenseIds = licenseInfoResult.stream() + .flatMap(singleResult -> singleResult.getLicenseInfo().getConcludedLicenseIds().stream()) + .collect(Collectors.toCollection(TreeSet::new)); + otherLicenseNames = licenseWithTexts.stream().map(LicenseNameWithText::getLicenseName) + .collect(Collectors.toCollection(TreeSet::new)); + otherLicenseNames.removeAll(concludedLicenseIds); + } + } else { + jsonResult.put(SW360Constants.STATUS, SW360Constants.FAILURE); + if (attachments.size() > 1) { + jsonResult.put(SW360Constants.MESSAGE, LanguageUtil.get(resourceBundle, "multiple.isr.are.found.in.the.release")); + } else if (attachments.isEmpty()) { + jsonResult.put(SW360Constants.MESSAGE, LanguageUtil.get(resourceBundle, "isr.attachment.not.found.in.the.release")); + } else { + jsonResult.put(SW360Constants.MESSAGE, LanguageUtil.get(resourceBundle, "license.information.not.found.in.isr")); + } + } + } catch (TException e) { + log.error("Cannot retrieve license information for attachment id " + attachmentContentId + " in release " + + releaseId + ".", e); + response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "500"); + } + + try { + JsonGenerator jsonGenerator = JSON_FACTORY.createGenerator(response.getWriter()); + jsonGenerator.writeStartObject(); + if (concludedLicenseIds.size() > 0) { + jsonGenerator.writeStringField(LICENSE_PREFIX, LanguageUtil.get(resourceBundle,"concluded.license.ids")); + jsonGenerator.writeArrayFieldStart(LICENSE_IDS); + concludedLicenseIds.forEach(licenseId -> wrapException(() -> { jsonGenerator.writeString(licenseId); })); + jsonGenerator.writeEndArray(); + } + jsonGenerator.writeStringField("otherLicense", LanguageUtil.get(resourceBundle,"other.license.id")); + jsonGenerator.writeArrayFieldStart("otherLicenseIds"); + otherLicenseNames.forEach(licenseId -> wrapException(() -> { jsonGenerator.writeString(licenseId); })); + jsonGenerator.writeEndArray(); + if (AttachmentType.INITIAL_SCAN_REPORT.equals(attachmentType)) { + jsonGenerator.writeStringField(LICENSE_PREFIX, LanguageUtil.get(resourceBundle, "possible.main.license.ids")); + jsonGenerator.writeStringField("totalFileCount", Long.toString(totalFileCount)); + jsonGenerator.writeStringField("fileName", attachmentName); + } + jsonGenerator.writeStringField(SW360Constants.STATUS, SW360Constants.SUCCESS); + jsonGenerator.writeEndObject(); + jsonGenerator.close(); + } catch (IOException | RuntimeException e) { + log.error("Cannot write JSON response for attachment id " + attachmentContentId + " in release " + releaseId + + ".", e); + response.setProperty(ResourceResponse.HTTP_STATUS_CODE, "500"); + } + } + private void serveClearingStatusList(ResourceRequest request, ResourceResponse response) { ProjectService.Iface client = thriftClients.makeProjectClient(); User user = UserCacheHolder.getUserFromRequest(request); diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/DisplayDocumentCreationInfoChanges.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/DisplayDocumentCreationInfoChanges.java new file mode 100644 index 0000000000..d1f2ba4a28 --- /dev/null +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/DisplayDocumentCreationInfoChanges.java @@ -0,0 +1,307 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.portal.tags; + +import com.google.common.base.Strings; +import org.apache.commons.lang.StringEscapeUtils; +import com.liferay.portal.kernel.language.LanguageUtil; +import com.liferay.portal.kernel.util.ResourceBundleUtil; + +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; + +import org.apache.thrift.meta_data.FieldMetaData; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.ResourceBundle; +import java.util.Set; + +import static org.eclipse.sw360.portal.tags.TagUtils.*; + +/** + * Display the fields that have changed in the SPDX Document Creation Info + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class DisplayDocumentCreationInfoChanges extends UserAwareTag { + private DocumentCreationInformation actual; + private DocumentCreationInformation additions; + private DocumentCreationInformation deletions; + private String tableClasses = ""; + private String idPrefix = ""; + + public void setActual(DocumentCreationInformation actual) { + this.actual = actual; + } + + public void setAdditions(DocumentCreationInformation additions) { + this.additions = additions; + } + + public void setDeletions(DocumentCreationInformation deletions) { + this.deletions = deletions; + } + + public void setTableClasses(String tableClasses) { + this.tableClasses = tableClasses; + } + + public void setIdPrefix(String idPrefix) { + this.idPrefix = idPrefix; + } + + public int doStartTag() throws JspException { + + JspWriter jspWriter = pageContext.getOut(); + + StringBuilder display = new StringBuilder(); + + if (additions == null || deletions == null) { + return SKIP_BODY; + } + + try { + for (DocumentCreationInformation._Fields field : DocumentCreationInformation._Fields.values()) { + switch (field) { + // ignored Fields + case ID: + case REVISION: + case TYPE: + case CREATED_BY: + case PERMISSIONS: + case DOCUMENT_STATE: + case EXTERNAL_DOCUMENT_REFS: + case CREATOR: + break; + default: + FieldMetaData fieldMetaData = DocumentCreationInformation.metaDataMap.get(field); + displaySimpleFieldOrSet(display, actual, additions, deletions, field, fieldMetaData, "", false); + } + } + + String renderString = display.toString(); + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), + getClass()); + + if (Strings.isNullOrEmpty(renderString)) { + renderString = "<div class=\"alert alert-danger\">" + + LanguageUtil.get(resourceBundle, "no.changes.in.basic.fields") + "</div>"; + } else { + renderString = String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + "<thead><tr><th colspan=\"4\">" + LanguageUtil.get(resourceBundle, "changes.for.basic.fields") + + " </th></tr>" + + String.format("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle, "field.name"), + LanguageUtil.get(resourceBundle, "current.value"), + LanguageUtil.get(resourceBundle, "former.value"), + LanguageUtil.get(resourceBundle, "suggested.value")) + + renderString + "</tbody></table>"; + } + String externalDocumentRefsRenderString = renderExternalDocumentRefs(); + String creatorRenderString = renderCreator(); + jspWriter.print(renderString + externalDocumentRefsRenderString.toString() + creatorRenderString.toString()); + } catch (Exception e) { + throw new JspException(e); + } + return SKIP_BODY; + } + + private boolean ensureSomethingTodoAndNoNull(DocumentCreationInformation._Fields field) { + if (!deletions.isSet(field) && !additions.isSet(field)) { + return false; + } + + if (field == DocumentCreationInformation._Fields.EXTERNAL_DOCUMENT_REFS){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } else if (field == DocumentCreationInformation._Fields.CREATOR){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } + return true; + } + + private String renderExternalDocumentRefs() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(DocumentCreationInformation._Fields.EXTERNAL_DOCUMENT_REFS)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(DocumentCreationInformation._Fields.EXTERNAL_DOCUMENT_REFS)){ + actual.externalDocumentRefs = new HashSet<>(); + } + + Set<ExternalDocumentReferences> additionsExternalDocumentRefs = additions.getExternalDocumentRefs(); + Set<ExternalDocumentReferences> deletionsExternalDocumentRefs = deletions.getExternalDocumentRefs(); + Set<ExternalDocumentReferences> currentExternalDocumentRefs = actual.getExternalDocumentRefs(); + int changeSize = 0; + if (additionsExternalDocumentRefs.size() > deletionsExternalDocumentRefs.size()) { + changeSize = additionsExternalDocumentRefs.size() + currentExternalDocumentRefs.size() - deletionsExternalDocumentRefs.size(); + } else { + changeSize = currentExternalDocumentRefs.size(); + } + + for (int i = 0; i < changeSize; i++) { + ExternalDocumentReferences externalDocumentRefDeletions = getExternalDocumentRefsByIndex(deletions, i); + ExternalDocumentReferences externalDocumentRefAdditions = getExternalDocumentRefsByIndex(additions, i); + ExternalDocumentReferences externalDocumentRef = getExternalDocumentRefsByIndex(actual, i); + String checkSumRendeString = null; + + for (ExternalDocumentReferences._Fields field : ExternalDocumentReferences._Fields.values()) { + FieldMetaData fieldMetaData = ExternalDocumentReferences.metaDataMap.get(field); + if (field == ExternalDocumentReferences._Fields.CHECKSUM) { + checkSumRendeString = renderCheckSum(externalDocumentRef, externalDocumentRefAdditions, externalDocumentRefDeletions); + } else { + displaySimpleFieldOrSet( + display, + externalDocumentRef, + externalDocumentRefAdditions, + externalDocumentRefDeletions, + field, fieldMetaData, "", false); + } + } + if (checkSumRendeString != null) { + display.append(checkSumRendeString); + } + } + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.external.document.references")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } + + private ExternalDocumentReferences getExternalDocumentRefsByIndex(DocumentCreationInformation document, int index) { + ExternalDocumentReferences externalDocumentRefs; + Iterator<ExternalDocumentReferences> externalDocumentRefsIterator = document.getExternalDocumentRefsIterator(); + while (externalDocumentRefsIterator.hasNext()) { + externalDocumentRefs = externalDocumentRefsIterator.next(); + if (externalDocumentRefs.getIndex() == index) { + externalDocumentRefs.setIndex(0); // Set 0 to not show Index when add or delete + return externalDocumentRefs; + } + } + return new ExternalDocumentReferences(); + } + + private String renderCreator() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(DocumentCreationInformation._Fields.CREATOR)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(DocumentCreationInformation._Fields.CREATOR)){ + actual.creator = new HashSet<>(); + } + + Set<Creator> additionsCreators = additions.getCreator(); + Set<Creator> deletionsCreators = deletions.getCreator(); + Set<Creator> currentCreators = actual.getCreator(); + int changeSize = 0; + if (additionsCreators.size() > deletionsCreators.size()) { + changeSize = additionsCreators.size() + currentCreators.size() - deletionsCreators.size(); + } else { + changeSize = currentCreators.size(); + } + + for (int i = 0; i < changeSize; i++) { + Creator creatorDeletions = getCreatorByIndex(deletions, i); + Creator creatorAdditions = getCreatorByIndex(additions, i); + Creator creator = getCreatorByIndex(actual, i); + + for (Creator._Fields field : Creator._Fields.values()) { + FieldMetaData fieldMetaData = Creator.metaDataMap.get(field); + displaySimpleFieldOrSet( + display, + creator, + creatorAdditions, + creatorDeletions, + field, fieldMetaData, "", false); + } + } + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.creator")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } + + private Creator getCreatorByIndex(DocumentCreationInformation document, int index) { + Creator creator; + Iterator<Creator> creatorIterator = document.getCreatorIterator(); + while (creatorIterator.hasNext()) { + creator = creatorIterator.next(); + if (creator.getIndex() == index) { + creator.setIndex(0); // Set 0 to not show Index when add or delete + return creator; + } + } + return new Creator(); + } + + private String renderCheckSum(ExternalDocumentReferences actualChecsum, ExternalDocumentReferences additionsChecsum, ExternalDocumentReferences deletionsChecsum) { + + if (!deletionsChecsum.isSet(ExternalDocumentReferences._Fields.CHECKSUM) + && !additionsChecsum.isSet(ExternalDocumentReferences._Fields.CHECKSUM)) { + return ""; + } + + if (!actualChecsum.isSet(ExternalDocumentReferences._Fields.CHECKSUM)) { + actualChecsum.checksum = new CheckSum(); + actualChecsum.checksum.algorithm = NOT_SET; + actualChecsum.checksum.checksumValue = NOT_SET; + } + + if (!deletionsChecsum.isSet(ExternalDocumentReferences._Fields.CHECKSUM)) { + deletionsChecsum.checksum = new CheckSum(); + deletionsChecsum.checksum.algorithm = NOT_SET; + deletionsChecsum.checksum.checksumValue = NOT_SET; + } + + if (!additionsChecsum.isSet(ExternalDocumentReferences._Fields.CHECKSUM)) { + additionsChecsum.checksum = new CheckSum(); + additionsChecsum.checksum.algorithm = NOT_SET; + additionsChecsum.checksum.checksumValue = NOT_SET; + } + + if (actualChecsum.checksum.algorithm.equals(additionsChecsum.checksum.algorithm) + && actualChecsum.checksum.checksumValue.equals(additionsChecsum.checksum.checksumValue)) { + return ""; + } + + String display = "<tr> <td>CheckSum:</td> <td> <ul> <li>algorithm: " + + StringEscapeUtils.escapeXml(actualChecsum.checksum.algorithm) + "</li> <li>checksumValue: " + + StringEscapeUtils.escapeXml(actualChecsum.checksum.checksumValue) + "</li> </ul> </td> <td> <li>algorithm: " + + StringEscapeUtils.escapeXml(deletionsChecsum.checksum.algorithm) + "</li> <li>checksumValue: " + + StringEscapeUtils.escapeXml(deletionsChecsum.checksum.checksumValue) + "</li> </ul> </td> <td> <li>algorithm: " + + StringEscapeUtils.escapeXml(additionsChecsum.checksum.algorithm) + " </li> <li>checksumValue: " + + StringEscapeUtils.escapeXml(additionsChecsum.checksum.checksumValue) + "</li> </ul> </td> </tr>"; + return display; + } + +} diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/DisplayPackageInfoChanges.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/DisplayPackageInfoChanges.java new file mode 100644 index 0000000000..118d7bf1fb --- /dev/null +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/DisplayPackageInfoChanges.java @@ -0,0 +1,365 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.portal.tags; + +import com.google.common.base.Strings; +import com.liferay.portal.kernel.language.LanguageUtil; +import com.liferay.portal.kernel.util.ResourceBundleUtil; + +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.Annotations; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.ExternalReference; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageVerificationCode; +import org.apache.thrift.meta_data.FieldMetaData; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.ResourceBundle; +import java.util.Set; + +import static org.eclipse.sw360.portal.tags.TagUtils.*; + +/** + * Display the fields that have changed in the SPDX Package Info + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class DisplayPackageInfoChanges extends UserAwareTag { + private PackageInformation actual; + private PackageInformation additions; + private PackageInformation deletions; + private String tableClasses = ""; + private String idPrefix = ""; + + public void setActual(PackageInformation actual) { + this.actual = actual; + } + + public void setAdditions(PackageInformation additions) { + this.additions = additions; + } + + public void setDeletions(PackageInformation deletions) { + this.deletions = deletions; + } + + public void setTableClasses(String tableClasses) { + this.tableClasses = tableClasses; + } + + public void setIdPrefix(String idPrefix) { + this.idPrefix = idPrefix; + } + + public int doStartTag() throws JspException { + + JspWriter jspWriter = pageContext.getOut(); + + StringBuilder display = new StringBuilder(); + + if (additions == null || deletions == null) { + return SKIP_BODY; + } + + try { + for (PackageInformation._Fields field : PackageInformation._Fields.values()) { + switch (field) { + // ignored Fields + case ID: + case REVISION: + case TYPE: + case CREATED_BY: + case PERMISSIONS: + case DOCUMENT_STATE: + case PACKAGE_VERIFICATION_CODE: + case ANNOTATIONS: + case CHECKSUMS: + case EXTERNAL_REFS: + break; + default: + FieldMetaData fieldMetaData = PackageInformation.metaDataMap.get(field); + displaySimpleFieldOrSet(display, actual, additions, deletions, field, fieldMetaData, "", false); + } + } + + String renderString = display.toString(); + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), + getClass()); + + if (Strings.isNullOrEmpty(renderString)) { + renderString = "<div class=\"alert alert-danger\">" + + LanguageUtil.get(resourceBundle, "no.changes.in.basic.fields") + "</div>"; + } else { + renderString = String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + "<thead><tr><th colspan=\"4\">" + LanguageUtil.get(resourceBundle, "changes.for.basic.fields") + + " </th></tr>" + + String.format("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle, "field.name"), + LanguageUtil.get(resourceBundle, "current.value"), + LanguageUtil.get(resourceBundle, "former.value"), + LanguageUtil.get(resourceBundle, "suggested.value")) + + renderString + "</tbody></table>"; + } + String packageVerificationCodeRenderString = renderPackageVerificationCode(); + String annotaionsRenderString = renderAnnotaions(); + String checkSumRenderString = renderCheckSum(); + String externalReferenceRenderString = renderExternalReference(); + jspWriter.print(renderString + packageVerificationCodeRenderString.toString() + checkSumRenderString.toString() + externalReferenceRenderString.toString() + annotaionsRenderString.toString()); + } catch (Exception e) { + throw new JspException(e); + } + return SKIP_BODY; + } + + private boolean ensureSomethingTodoAndNoNull(PackageInformation._Fields field) { + if (!deletions.isSet(field) && !additions.isSet(field)) { + return false; + } + + if (field == PackageInformation._Fields.CHECKSUMS){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } else if (field == PackageInformation._Fields.EXTERNAL_REFS){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } else if (field == PackageInformation._Fields.ANNOTATIONS){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } else if (field == PackageInformation._Fields.PACKAGE_VERIFICATION_CODE){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } + + return true; + } + + private String renderCheckSum() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(PackageInformation._Fields.CHECKSUMS)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(PackageInformation._Fields.CHECKSUMS)){ + actual.checksums = new HashSet<>(); + } + + Set<CheckSum> additionsCheckSums = additions.getChecksums(); + Set<CheckSum> deletionsCheckSums = deletions.getChecksums(); + Set<CheckSum> currentCheckSums = actual.getChecksums(); + int changeSize = 0; + if (additionsCheckSums.size() > deletionsCheckSums.size()) { + changeSize = additionsCheckSums.size() + currentCheckSums.size() - deletionsCheckSums.size(); + } else { + changeSize = currentCheckSums.size(); + } + + for (int i = 0; i < changeSize; i++) { + CheckSum checkSumDeletions = getChecksumsByIndex(deletions, i); + CheckSum checkSumAdditions = getChecksumsByIndex(additions, i); + CheckSum checkSum = getChecksumsByIndex(actual, i); + + for (CheckSum._Fields field : CheckSum._Fields.values()) { + FieldMetaData fieldMetaData = CheckSum.metaDataMap.get(field); + displaySimpleFieldOrSet( + display, + checkSum, + checkSumAdditions, + checkSumDeletions, + field, fieldMetaData, "", false); + } + } + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.checksum")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } + + private CheckSum getChecksumsByIndex(PackageInformation packageInfo, int index) { + CheckSum checksum; + Iterator<CheckSum> checksumIterator = packageInfo.getChecksumsIterator(); + while (checksumIterator.hasNext()) { + checksum = checksumIterator.next(); + if (checksum.getIndex() == index) { + checksum.setIndex(0); // Set 0 to not show Index when add or delete + return checksum; + } + } + return new CheckSum(); + } + + private String renderAnnotaions() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(PackageInformation._Fields.ANNOTATIONS)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(PackageInformation._Fields.ANNOTATIONS)){ + actual.annotations = new HashSet<>(); + } + + Set<Annotations> additionsAnnotations = additions.getAnnotations(); + Set<Annotations> deletionsAnnotations = deletions.getAnnotations(); + Set<Annotations> currentAnnotations = actual.getAnnotations(); + int changeSize = 0; + if (additionsAnnotations.size() > deletionsAnnotations.size()) { + changeSize = additionsAnnotations.size() + currentAnnotations.size() - deletionsAnnotations.size(); + } else { + changeSize = currentAnnotations.size(); + } + + for (int i = 0; i < changeSize; i++) { + Annotations annotationDeletions = getAnnotationsByIndex(deletions, i); + Annotations annotationAdditions = getAnnotationsByIndex(additions, i); + Annotations annotation = getAnnotationsByIndex(actual, i); + + for (Annotations._Fields field : Annotations._Fields.values()) { + FieldMetaData fieldMetaData = Annotations.metaDataMap.get(field); + displaySimpleFieldOrSet( + display, + annotation, + annotationAdditions, + annotationDeletions, + field, fieldMetaData, "", false); + } + } + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.annotaions.information")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } + + private Annotations getAnnotationsByIndex(PackageInformation packageInfo, int index) { + Annotations annotations; + Iterator<Annotations> annotationsIterator = packageInfo.getAnnotationsIterator(); + while (annotationsIterator.hasNext()) { + annotations = annotationsIterator.next(); + if (annotations.getIndex() == index) { + annotations.setIndex(0); // Set 0 to not show Index when add or delete + return annotations; + } + } + return new Annotations(); + } + + private String renderExternalReference() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(PackageInformation._Fields.EXTERNAL_REFS)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(PackageInformation._Fields.EXTERNAL_REFS)){ + actual.externalRefs = new HashSet<>(); + } + + Set<ExternalReference> additionsExternalReferences = additions.getExternalRefs(); + Set<ExternalReference> deletionsExternalReferences = deletions.getExternalRefs(); + Set<ExternalReference> currentExternalReferences = actual.getExternalRefs(); + int changeSize = 0; + if (additionsExternalReferences.size() > deletionsExternalReferences.size()) { + changeSize = additionsExternalReferences.size() + currentExternalReferences.size() - deletionsExternalReferences.size(); + } else { + changeSize = currentExternalReferences.size(); + } + + for (int i = 0; i < changeSize; i++) { + ExternalReference externalRefDeletions = getExternalReferenceByIndex(deletions, i); + ExternalReference externalRefAdditions = getExternalReferenceByIndex(additions, i); + ExternalReference externalRef = getExternalReferenceByIndex(actual, i); + + for (ExternalReference._Fields field : ExternalReference._Fields.values()) { + FieldMetaData fieldMetaData = ExternalReference.metaDataMap.get(field); + displaySimpleFieldOrSet( + display, + externalRef, + externalRefAdditions, + externalRefDeletions, + field, fieldMetaData, "", false); + } + } + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.external.references")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } + + private ExternalReference getExternalReferenceByIndex(PackageInformation packageInfo, int index) { + ExternalReference externalReference; + Iterator<ExternalReference> externalReferenceIterator = packageInfo.getExternalRefsIterator(); + while (externalReferenceIterator.hasNext()) { + externalReference = externalReferenceIterator.next(); + if (externalReference.getIndex() == index) { + externalReference.setIndex(0); // Set 0 to not show Index when add or delete + return externalReference; + } + } + return new ExternalReference(); + } + + private String renderPackageVerificationCode() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(PackageInformation._Fields.PACKAGE_VERIFICATION_CODE)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(PackageInformation._Fields.PACKAGE_VERIFICATION_CODE)){ + actual.packageVerificationCode = new PackageVerificationCode(); + } + + for (PackageVerificationCode._Fields field : PackageVerificationCode._Fields.values()) { + FieldMetaData fieldMetaData = PackageVerificationCode.metaDataMap.get(field); + displaySimpleFieldOrSet( + display, + actual.getPackageVerificationCode(), + additions.getPackageVerificationCode(), + deletions.getPackageVerificationCode(), + field, fieldMetaData, "", false); + } + + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.package.verification.code")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } +} \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/DisplaySPDXDocumentChanges.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/DisplaySPDXDocumentChanges.java new file mode 100644 index 0000000000..e25cdac7e3 --- /dev/null +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/DisplaySPDXDocumentChanges.java @@ -0,0 +1,481 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.portal.tags; + +import com.google.common.base.Strings; +import org.apache.commons.lang.StringEscapeUtils; +import com.liferay.portal.kernel.language.LanguageUtil; +import com.liferay.portal.kernel.util.ResourceBundleUtil; + +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.Annotations; +import org.eclipse.sw360.datahandler.thrift.spdx.otherlicensinginformationdetected.OtherLicensingInformationDetected; +import org.eclipse.sw360.datahandler.thrift.spdx.relationshipsbetweenspdxelements.RelationshipsBetweenSPDXElements; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetRange; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.*; +import org.apache.thrift.meta_data.FieldMetaData; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.JspWriter; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.ResourceBundle; +import java.util.Set; + +import static org.eclipse.sw360.portal.tags.TagUtils.*; + +/** + * Display the fields that have changed in the SPDX Document + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class DisplaySPDXDocumentChanges extends UserAwareTag { + private SPDXDocument actual; + private SPDXDocument additions; + private SPDXDocument deletions; + private String tableClasses = ""; + private String idPrefix = ""; + + public void setActual(SPDXDocument actual) { + this.actual = actual; + } + + public void setAdditions(SPDXDocument additions) { + this.additions = additions; + } + + public void setDeletions(SPDXDocument deletions) { + this.deletions = deletions; + } + + public void setTableClasses(String tableClasses) { + this.tableClasses = tableClasses; + } + + public void setIdPrefix(String idPrefix) { + this.idPrefix = idPrefix; + } + + public int doStartTag() throws JspException { + + JspWriter jspWriter = pageContext.getOut(); + + StringBuilder display = new StringBuilder(); + + if (additions == null || deletions == null) { + return SKIP_BODY; + } + + try { + for (SPDXDocument._Fields field : SPDXDocument._Fields.values()) { + switch (field) { + // ignored Fields + case ID: + case REVISION: + case TYPE: + case CREATED_BY: + case PERMISSIONS: + case DOCUMENT_STATE: + case OTHER_LICENSING_INFORMATION_DETECTEDS: + case SNIPPETS: + case ANNOTATIONS: + case RELATIONSHIPS: + break; + default: + FieldMetaData fieldMetaData = SPDXDocument.metaDataMap.get(field); + displaySimpleFieldOrSet(display, actual, additions, deletions, field, fieldMetaData, "", false); + } + } + + String renderString = display.toString(); + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), + getClass()); + + if (Strings.isNullOrEmpty(renderString)) { + renderString = "<div class=\"alert alert-danger\">" + + LanguageUtil.get(resourceBundle, "no.changes.in.basic.fields") + "</div>"; + } else { + renderString = String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + "<thead><tr><th colspan=\"4\">" + LanguageUtil.get(resourceBundle, "changes.for.basic.fields") + + " </th></tr>" + + String.format("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle, "field.name"), + LanguageUtil.get(resourceBundle, "current.value"), + LanguageUtil.get(resourceBundle, "former.value"), + LanguageUtil.get(resourceBundle, "suggested.value")) + + renderString + "</tbody></table>"; + } + String snippetRenderString = renderSnippetInformation(); + String relationshipSRenderString = renderRelationshipInformation(); + String annotaionsRenderString = renderAnnotaionsInformation(); + String otherLicensingRenderString = renderOtherLicensingInformationDetected(); + jspWriter.print(renderString + snippetRenderString.toString() + + relationshipSRenderString.toString() + + annotaionsRenderString.toString() + + otherLicensingRenderString.toString()); + } catch (Exception e) { + throw new JspException(e); + } + return SKIP_BODY; + } + + private boolean ensureSomethingTodoAndNoNull(SPDXDocument._Fields field) { + if (!deletions.isSet(field) && !additions.isSet(field)) { + return false; + } + + if (field == SPDXDocument._Fields.SNIPPETS){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } else if (field == SPDXDocument._Fields.RELATIONSHIPS){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } else if (field == SPDXDocument._Fields.ANNOTATIONS){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } else if (field == SPDXDocument._Fields.OTHER_LICENSING_INFORMATION_DETECTEDS){ + if (!deletions.isSet(field)) { + deletions.setFieldValue(field, new HashSet<>()); + } + if (!additions.isSet(field)) { + additions.setFieldValue(field, new HashSet<>()); + } + } + return true; + } + + private String renderSnippetInformation() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(SPDXDocument._Fields.SNIPPETS)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(SPDXDocument._Fields.SNIPPETS)){ + actual.snippets = new HashSet<>(); + } + + Set<SnippetInformation> additionsSnippetInformations = additions.getSnippets(); + Set<SnippetInformation> deletionsSnippetInformations = deletions.getSnippets(); + Set<SnippetInformation> currentSnippetInformations = actual.getSnippets(); + int changeSize = 0; + if (additionsSnippetInformations.size() > deletionsSnippetInformations.size()) { + changeSize = additionsSnippetInformations.size() + currentSnippetInformations.size() - deletionsSnippetInformations.size(); + } else { + changeSize = currentSnippetInformations.size(); + } + + for (int i = 0; i < changeSize; i++) { + SnippetInformation snippetDeletions = getSnippetInformationByIndex(deletions, i); + SnippetInformation snippetAdditions = getSnippetInformationByIndex(additions, i); + SnippetInformation snippet = getSnippetInformationByIndex(actual, i); + + String snippetRangeRendeString = null; + for (SnippetInformation._Fields field : SnippetInformation._Fields.values()) { + FieldMetaData fieldMetaData = SnippetInformation.metaDataMap.get(field); + if (field == SnippetInformation._Fields.SNIPPET_RANGES) { + snippetRangeRendeString = renderSnippetRange(snippet, snippetAdditions, snippetDeletions); + } else { + displaySimpleFieldOrSet( + display, + snippet, + snippetAdditions, + snippetDeletions, + field, fieldMetaData, "", false); + } + } + if (snippetRangeRendeString != null) { + display.append(snippetRangeRendeString); + } + } + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.snippets.information")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } + + private SnippetInformation getSnippetInformationByIndex(SPDXDocument spdx, int index) { + SnippetInformation snippet; + Iterator<SnippetInformation> snippetsIterator = spdx.getSnippetsIterator(); + while (snippetsIterator.hasNext()) { + snippet = snippetsIterator.next(); + if (snippet.getIndex() == index) { + snippet.setIndex(0); // Set 0 to not show Index when add or delete + return snippet; + } + } + return new SnippetInformation(); + } + + private String renderRelationshipInformation() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(SPDXDocument._Fields.RELATIONSHIPS)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(SPDXDocument._Fields.RELATIONSHIPS)){ + actual.relationships = new HashSet<>(); + } + + Set<RelationshipsBetweenSPDXElements> additionsRelationshipsBetweenSPDXElementss = additions.getRelationships(); + Set<RelationshipsBetweenSPDXElements> deletionsRelationshipsBetweenSPDXElementss = deletions.getRelationships(); + Set<RelationshipsBetweenSPDXElements> currentRelationshipsBetweenSPDXElementss = actual.getRelationships(); + int changeSize = 0; + if (additionsRelationshipsBetweenSPDXElementss.size() > deletionsRelationshipsBetweenSPDXElementss.size()) { + changeSize = additionsRelationshipsBetweenSPDXElementss.size() + currentRelationshipsBetweenSPDXElementss.size() + - deletionsRelationshipsBetweenSPDXElementss.size(); + } else { + changeSize = currentRelationshipsBetweenSPDXElementss.size(); + } + + for (int i = 0; i < changeSize; i++) { + + RelationshipsBetweenSPDXElements relationshipDeletions = getRelationshipByIndex(deletions, i); + RelationshipsBetweenSPDXElements relationshipAdditions = getRelationshipByIndex(additions, i); + RelationshipsBetweenSPDXElements relationship = getRelationshipByIndex(actual, i); + + for (RelationshipsBetweenSPDXElements._Fields field : RelationshipsBetweenSPDXElements._Fields.values()) { + FieldMetaData fieldMetaData = RelationshipsBetweenSPDXElements.metaDataMap.get(field); + displaySimpleFieldOrSet( + display, + relationship, + relationshipAdditions, + relationshipDeletions, + field, fieldMetaData, "", false); + } + } + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.relationship.information")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } + + private RelationshipsBetweenSPDXElements getRelationshipByIndex(SPDXDocument spdx, int index) { + RelationshipsBetweenSPDXElements relationship; + Iterator<RelationshipsBetweenSPDXElements> relationshipIterator = spdx.getRelationshipsIterator(); + while (relationshipIterator.hasNext()) { + relationship = relationshipIterator.next(); + if (relationship.getIndex() == index) { + return relationship; // Set 0 to not show Index when add or delete + } + } + return new RelationshipsBetweenSPDXElements(); + } + + private String renderAnnotaionsInformation() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(SPDXDocument._Fields.ANNOTATIONS)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(SPDXDocument._Fields.ANNOTATIONS)){ + actual.annotations = new HashSet<>(); + } + + Set<Annotations> additionsAnnotations = additions.getAnnotations(); + Set<Annotations> deletionsAnnotations = deletions.getAnnotations(); + Set<Annotations> currentAnnotations = actual.getAnnotations(); + int changeSize = 0; + if (additionsAnnotations.size() > deletionsAnnotations.size()) { + changeSize = additionsAnnotations.size() + currentAnnotations.size() - deletionsAnnotations.size(); + } else { + changeSize = currentAnnotations.size(); + } + + for (int i = 0; i < changeSize; i++) { + Annotations annotationDeletions = getAnnotationsByIndex(deletions, i); + Annotations annotationAdditions = getAnnotationsByIndex(additions, i); + Annotations annotation = getAnnotationsByIndex(actual, i); + + for (Annotations._Fields field : Annotations._Fields.values()) { + FieldMetaData fieldMetaData = Annotations.metaDataMap.get(field); + displaySimpleFieldOrSet( + display, + annotation, + annotationAdditions, + annotationDeletions, + field, fieldMetaData, "", false); + } + } + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.annotaions.information")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } + + private Annotations getAnnotationsByIndex(SPDXDocument spdx, int index) { + Annotations annotations; + Iterator<Annotations> annotationsIterator = spdx.getAnnotationsIterator(); + while (annotationsIterator.hasNext()) { + annotations = annotationsIterator.next(); + if (annotations.getIndex() == index) { + annotations.setIndex(0); // Set 0 to not show Index when add or delete + return annotations; + } + } + return new Annotations(); + } + + private String renderOtherLicensingInformationDetected() { + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); + ResourceBundle resourceBundle = ResourceBundleUtil.getBundle("content.Language", request.getLocale(), getClass()); + if (!ensureSomethingTodoAndNoNull(SPDXDocument._Fields.OTHER_LICENSING_INFORMATION_DETECTEDS)) { + return ""; + } + StringBuilder display = new StringBuilder(); + if (! actual.isSet(SPDXDocument._Fields.OTHER_LICENSING_INFORMATION_DETECTEDS)){ + actual.otherLicensingInformationDetecteds = new HashSet<>(); + } + + Set<OtherLicensingInformationDetected> additionsOtherLicensingInformationDetecteds = additions.getOtherLicensingInformationDetecteds(); + Set<OtherLicensingInformationDetected> deletionsOtherLicensingInformationDetecteds = deletions.getOtherLicensingInformationDetecteds(); + Set<OtherLicensingInformationDetected> currentOtherLicensingInformationDetecteds = actual.getOtherLicensingInformationDetecteds(); + int changeSize = 0; + if (additionsOtherLicensingInformationDetecteds.size() > deletionsOtherLicensingInformationDetecteds.size()) { + changeSize = additionsOtherLicensingInformationDetecteds.size() + + currentOtherLicensingInformationDetecteds.size() + - deletionsOtherLicensingInformationDetecteds.size(); + } else { + changeSize = currentOtherLicensingInformationDetecteds.size(); + } + + for (int i = 0; i < changeSize; i++) { + OtherLicensingInformationDetected otherLicensingDeletions = getOtherLicensingByIndex(deletions, i); + OtherLicensingInformationDetected otherLicensingAdditions = getOtherLicensingByIndex(additions, i); + OtherLicensingInformationDetected otherLicensing = getOtherLicensingByIndex(actual, i); + for (OtherLicensingInformationDetected._Fields field : OtherLicensingInformationDetected._Fields.values()) { + FieldMetaData fieldMetaData = OtherLicensingInformationDetected.metaDataMap.get(field); + displaySimpleFieldOrSet( + display, + otherLicensing, + otherLicensingAdditions, + otherLicensingDeletions, + field, fieldMetaData, "", false); + } + } + return "<h3>"+LanguageUtil.get(resourceBundle,"changes.in.other.licensing.information.detecteds")+ "</h3>" + + String.format("<table class=\"%s\" id=\"%schanges\" >", tableClasses, idPrefix) + + String.format("<thead><tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr></thead><tbody>", + LanguageUtil.get(resourceBundle,"field.name"), LanguageUtil.get(resourceBundle,"current.value"), + LanguageUtil.get(resourceBundle,"former.value"), LanguageUtil.get(resourceBundle,"suggested.value")) + + display.toString() + "</tbody></table>"; + } + + private OtherLicensingInformationDetected getOtherLicensingByIndex(SPDXDocument spdx, int index) { + OtherLicensingInformationDetected otherLicensing; + Iterator<OtherLicensingInformationDetected> otherLicensingIterator = spdx.getOtherLicensingInformationDetectedsIterator(); + while (otherLicensingIterator.hasNext()) { + otherLicensing = otherLicensingIterator.next(); + if (otherLicensing.getIndex() == index) { + otherLicensing.setIndex(0); // Set 0 to not show Index when add or delete + return otherLicensing; + } + } + return new OtherLicensingInformationDetected(); + } + + private String renderSnippetRange(SnippetInformation actual, SnippetInformation additions, SnippetInformation deletions) { + StringBuilder display = new StringBuilder(); + display.append("<tr><td>snippetRange:</td></tr>"); + if (! actual.isSet(SnippetInformation._Fields.SNIPPET_RANGES)){ + actual.snippetRanges = new HashSet<>(); + } + if (! additions.isSet(SnippetInformation._Fields.SNIPPET_RANGES)){ + additions.snippetRanges = new HashSet<>(); + } + if (! deletions.isSet(SnippetInformation._Fields.SNIPPET_RANGES)){ + deletions.snippetRanges = new HashSet<>(); + } + if (additions.snippetRanges.isEmpty() && deletions.snippetRanges.isEmpty()) { + return ""; + } + + Set<SnippetRange> additionsSnippetRanges = additions.getSnippetRanges(); + Set<SnippetRange> deletionsSnippetRanges = deletions.getSnippetRanges(); + Set<SnippetRange> currentSnippetRanges = actual.getSnippetRanges(); + int changeSize = 0; + if (additionsSnippetRanges.size() > deletionsSnippetRanges.size()) { + changeSize = additionsSnippetRanges.size() + currentSnippetRanges.size() - deletionsSnippetRanges.size(); + } else { + changeSize = currentSnippetRanges.size(); + } + + for (int i = 0; i < changeSize; i++) { + SnippetRange snippetRangeDeletions = getSnippetRangeByIndex(deletions, i); + SnippetRange snippetRangeAdditions = getSnippetRangeByIndex(additions, i); + SnippetRange snippetRange = getSnippetRangeByIndex(actual, i); + String renderActual = ""; + String renderDeletions = ""; + String renderAdditions = ""; + + for (SnippetRange._Fields field : SnippetRange._Fields.values()) { + if (snippetRange.getFieldValue(field) == null) { + snippetRange.setFieldValue(field, NOT_SET); + } + if (snippetRangeAdditions.getFieldValue(field) == null) { + snippetRangeAdditions.setFieldValue(field, NOT_SET); + } + if (snippetRangeDeletions.getFieldValue(field) == null) { + snippetRangeDeletions.setFieldValue(field, NOT_SET); + } + if (!snippetRange.equals(snippetRangeAdditions) && !SnippetRange._Fields.INDEX.equals(field)) { + renderActual = renderActual + "<li>" + field.getFieldName() + ": " + StringEscapeUtils.escapeXml(snippetRange.getFieldValue(field).toString()) + "</li>"; + renderDeletions = renderDeletions + "<li>" + field.getFieldName() + ": " + StringEscapeUtils.escapeXml(snippetRangeDeletions.getFieldValue(field).toString()) + "</li>"; + renderAdditions = renderAdditions + "<li>" + field.getFieldName() + ": " + StringEscapeUtils.escapeXml(snippetRangeAdditions.getFieldValue(field).toString()) + "</li>"; + } + } + String renderTotal = "<tr><td></td><td> <ul>" + renderActual + "</ul> </td> <td> <ul>" + + renderDeletions + "</ul> </td> <td> <ul>" + + renderAdditions + "</ul> </td> </tr>"; + if (renderActual != "") { + display.append(renderTotal); + } + } + return display.toString(); + } + + private SnippetRange getSnippetRangeByIndex(SnippetInformation snippet, int index) { + SnippetRange snippetRange; + Iterator<SnippetRange> snippetRangeIterator = snippet.getSnippetRangesIterator(); + while (snippetRangeIterator.hasNext()) { + snippetRange = snippetRangeIterator.next(); + if (snippetRange.getIndex() == index) { + snippet.setIndex(0); // Set 0 to not show Index when add or delete + return snippetRange; + } + } + return new SnippetRange(); + } + +} \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/OutTag.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/OutTag.java index 53a4205516..485ee10d14 100644 --- a/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/OutTag.java +++ b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/tags/OutTag.java @@ -30,6 +30,7 @@ public class OutTag extends OutSupport { private boolean jsQuoting = false; private boolean stripNewlines = true; + private boolean hashSet = false; private boolean bare = false; private Integer maxChar = -1; @@ -58,6 +59,11 @@ public int doStartTag() throws JspException { candidate = candidate.replaceAll("[\r\n]+", " "); } + if (hashSet) { + candidate = StringUtils.removeStart(candidate, "["); + candidate = StringUtils.chop(candidate); + } + this.value = candidate; if (!bare && abbreviated) { @@ -95,6 +101,10 @@ public void setStripNewlines(boolean stripNewlines) { this.stripNewlines = stripNewlines; } + public void setHashSet(boolean hashSet) { + this.hashSet = hashSet; + } + public void setBare(boolean bare) { this.bare = bare; } private String prepareTitleAttribute(String value) { diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/customTags.tld b/frontend/sw360-portlet/src/main/resources/META-INF/customTags.tld index be4350eb97..987639f889 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/customTags.tld +++ b/frontend/sw360-portlet/src/main/resources/META-INF/customTags.tld @@ -1206,6 +1206,12 @@ <type>java.lang.Boolean</type> <rtexprvalue>true</rtexprvalue> </attribute> + <attribute> + <name>hashSet</name> + <required>false</required> + <type>java.lang.Boolean</type> + <rtexprvalue>true</rtexprvalue> + </attribute> <attribute> <name>maxChar</name> <required>false</required> @@ -1381,4 +1387,128 @@ <function-class>org.eclipse.sw360.datahandler.common.SW360Utils</function-class> <function-signature>java.lang.String printName(org.eclipse.sw360.datahandler.thrift.users.User)</function-signature> </function> + + <function> + <name>printSPDXDocumentName</name> + <function-class>org.eclipse.sw360.datahandler.common.SW360Utils</function-class> + <function-signature>java.lang.String printName(org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument)</function-signature> + </function> + <function> + <name>printDocumentCreationInfoName</name> + <function-class>org.eclipse.sw360.datahandler.common.SW360Utils</function-class> + <function-signature>java.lang.String printName(org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation)</function-signature> + </function> + <function> + <name>printPackageInfoName</name> + <function-class>org.eclipse.sw360.datahandler.common.SW360Utils</function-class> + <function-signature>java.lang.String printName(org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation)</function-signature> + </function> + + <tag> + <name>DisplaySPDXDocumentChanges</name> + <tag-class>org.eclipse.sw360.portal.tags.DisplaySPDXDocumentChanges</tag-class> + <body-content>empty</body-content> + <attribute> + <name>actual</name> + <required>true</required> + <type>org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>additions</name> + <required>true</required> + <type>org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>deletions</name> + <required>true</required> + <type>org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>tableClasses</name> + <required>true</required> + <type>java.lang.String</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>idPrefix</name> + <required>true</required> + <type>java.lang.String</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + </tag> + + <tag> + <name>DisplayDocumentCreationInfoChanges</name> + <tag-class>org.eclipse.sw360.portal.tags.DisplayDocumentCreationInfoChanges</tag-class> + <body-content>empty</body-content> + <attribute> + <name>actual</name> + <required>true</required> + <type>org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>additions</name> + <required>true</required> + <type>org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>deletions</name> + <required>true</required> + <type>org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>tableClasses</name> + <required>true</required> + <type>java.lang.String</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>idPrefix</name> + <required>true</required> + <type>java.lang.String</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + </tag> + + <tag> + <name>DisplayPackageInfoChanges</name> + <tag-class>org.eclipse.sw360.portal.tags.DisplayPackageInfoChanges</tag-class> + <body-content>empty</body-content> + <attribute> + <name>actual</name> + <required>true</required> + <type>org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>additions</name> + <required>true</required> + <type>org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>deletions</name> + <required>true</required> + <type>org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>tableClasses</name> + <required>true</required> + <type>java.lang.String</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + <attribute> + <name>idPrefix</name> + <required>true</required> + <type>java.lang.String</type> + <rtexprvalue>true</rtexprvalue> + </attribute> + </tag> </taglib> diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/changelogs/elementView.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/changelogs/elementView.jsp index 284cdf4fbd..313eec77ea 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/changelogs/elementView.jsp +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/changelogs/elementView.jsp @@ -251,6 +251,8 @@ if(fieldValueOld === null || fieldValueOld === undefined || fieldValueNew === null || fieldValueNew === undefined ) { + removeIndexFields(fieldValueOld); + removeIndexFields(fieldValueNew); jsonStrOld = JSON.stringify(fieldValueOld, undefined, 5).replace(/\\n/g, '\n').replace(/\\r/g, '\r'); jsonStrNew = JSON.stringify(fieldValueNew, undefined, 5).replace(/\\n/g, '\n').replace(/\\r/g, '\r'); } @@ -310,10 +312,13 @@ for (let primaryValue of primaryField) { if(typeof primaryValue === 'object') { let matched = false; + let indexKey = "index"; for(let secondaryValue of secondaryField) { - if(secondaryValue[selector] === primaryValue[selector]) { + if (isEqualObject(secondaryValue, primaryValue, selector, indexKey)) { matched = true; if(differentiateObject) { + delete primaryValue[indexKey]; + delete secondaryValue[indexKey]; diffObject(primaryValue, secondaryValue, primarySpanHightlighter, secondarySpanHighlighter, indentlevel); primaryFieldTmp.push(primaryValue); secondaryFieldTmp.push(secondaryValue); @@ -322,7 +327,8 @@ } } if(!matched) { - let jsonString = JSON.stringify(primaryValue, undefined, 5*indentlevel); + removeIndexFields(primaryValue); + let jsonString = JSON.stringify(primaryValue, undefined, 10*indentlevel); jsonString = jsonString.substring(0, jsonString.length-1) + spaceForClosingBraces + jsonString.substring(jsonString.length-1); primaryFieldTmp.push($($.parseHTML(primarySpanHightlighter)).text(jsonString)[0].outerHTML); } @@ -336,6 +342,33 @@ } } + function isEqualObject(secondaryValue, primaryValue, selector, indexKey) { + if (primaryValue[selector] === secondaryValue[selector] && typeof primaryValue[indexKey] === 'undefined') { + return true; + } + if (primaryValue[indexKey] === secondaryValue[indexKey] && typeof primaryValue[selector] === 'undefined') { + return true; + } + return false; + } + + function removeIndexFields(object) { + if (Array.isArray(object)) { + for (let objectValue of object) { + removeIndexFields(objectValue); + } + } else if (typeof object === 'object') { + for (key in object) { + if (key === 'index') { + delete object[key]; + } + if (typeof object[key] === 'object') { + removeIndexFields(object[key]); + } + } + } + } + function copyFromSourceToDestinationArray(srcArr, destArr) { destArr.length = 0; for (obj of srcArr) { @@ -354,6 +387,11 @@ function highlightObject(fieldValuePrimary, fieldValueSecondary, primarySpanHightlighter, secondarySpanHighlighter, differentiateCommonObject, spaceForClosingBraces, indentlevel) { for(key in fieldValuePrimary) { + if (key === 'index') { + delete fieldValuePrimary[key]; + delete fieldValueSecondary[key]; + continue; + } if(fieldValueSecondary[key] === null || fieldValueSecondary[key] === undefined) { let highlighted = fieldValuePrimary[key]; if(typeof fieldValuePrimary[key] === 'object') { diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/edit.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/edit.jsp index 0aa0a72a30..cd1e136168 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/edit.jsp +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/edit.jsp @@ -152,6 +152,8 @@ </tbody> </table> <button type="button" class="btn btn-secondary mt-3" onclick="window.location.href='<%=addReleaseURL%>'"><liferay-ui:message key="add.releases" /></button> + <button type="button" class="btn btn-secondary mt-3" id="import-spdx-bom" data-component-name="${component.name}" data-action="import-spdx-bom"><liferay-ui:message key="import.spdx.bom" /></button> + <%@include file="/html/utils/includes/importBomForComponent.jspf" %> </div> <core_rt:if test="${not componentDivAddMode}" > <div id="tab-Attachments" class="tab-pane <core_rt:if test="${selectedTab == 'tab-Attachments'}">active show</core_rt:if>"> @@ -208,7 +210,6 @@ <jsp:include page="/html/utils/includes/searchUsers.jsp" /> <%@include file="/html/components/includes/vendors/searchVendor.jspf" %> - <script> require(['jquery', 'components/includes/vendors/searchVendor', 'modules/autocomplete', 'modules/dialog', 'modules/listgroup', 'modules/validation' ], function($, vendorsearch, autocomplete, dialog, listgroup, validation) { document.title = $("<span></span>").html("<sw360:out value='${component.name}'/> - " + document.title).text(); diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/editRelease.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/editRelease.jsp index d037398c4f..111565580e 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/editRelease.jsp +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/editRelease.jsp @@ -12,6 +12,7 @@ <%@ page import="org.eclipse.sw360.datahandler.thrift.attachments.Attachment" %> <%@ page import="org.eclipse.sw360.datahandler.thrift.components.ComponentType" %> <%@ page import="org.eclipse.sw360.datahandler.thrift.components.Release" %> +<%@ page import="org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument" %> <%@ page import="org.eclipse.sw360.portal.common.PortalConstants" %> <%@ page import="org.eclipse.sw360.datahandler.thrift.users.RequestedAction" %> <%@ page import="org.eclipse.sw360.datahandler.thrift.attachments.CheckStatus" %> @@ -45,7 +46,7 @@ <c:catch var="attributeNotFoundException"> <jsp:useBean id="component" class="org.eclipse.sw360.datahandler.thrift.components.Component" scope="request"/> <jsp:useBean id="release" class="org.eclipse.sw360.datahandler.thrift.components.Release" scope="request"/> - + <jsp:useBean id="spdxDocument" class="org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument" scope="request"/> <jsp:useBean id="usingProjects" type="java.util.Set<org.eclipse.sw360.datahandler.thrift.projects.Project>" scope="request"/> <jsp:useBean id="allUsingProjectsCount" type="java.lang.Integer" scope="request"/> @@ -76,6 +77,9 @@ <div class="col-3 sidebar"> <div id="detailTab" class="list-group" data-initial-tab="${selectedTab}" role="tablist"> <a class="list-group-item list-group-item-action <core_rt:if test="${selectedTab == 'tab-Summary'}">active</core_rt:if>" href="#tab-Summary" data-toggle="list" role="tab"><liferay-ui:message key="summary" /></a> + <core_rt:if test="${not addMode}" > + <a class="list-group-item list-group-item-action <core_rt:if test="${selectedTab == 'tab-SPDX'}">active</core_rt:if>" href="#tab-SPDX" data-toggle="list" role="tab"><liferay-ui:message key="spdx.document" /></a> + </core_rt:if> <a class="list-group-item list-group-item-action <core_rt:if test="${selectedTab == 'tab-linkedReleases'}">active</core_rt:if>" href="#tab-linkedReleases" data-toggle="list" role="tab"><liferay-ui:message key="linked.releases" /></a> <core_rt:if test="${not addMode}" > @@ -160,6 +164,9 @@ <div id="tab-Attachments" class="tab-pane <core_rt:if test="${selectedTab == 'tab-Attachments'}">active show</core_rt:if>"> <%@include file="/html/utils/includes/editAttachments.jspf" %> </div> + <div id="tab-SPDX" class="tab-pane <core_rt:if test="${selectedTab == 'tab-SPDX'}">active show</core_rt:if>" > + <%@include file="/html/components/includes/releases/spdx/edit.jspf" %> + </div> </core_rt:if> <core_rt:if test="${cotsMode}"> <div id="tab-CommercialDetails" class="tab-pane <core_rt:if test="${selectedTab == 'tab-CommercialDetails'}">active show</core_rt:if>"> @@ -230,7 +237,7 @@ </core_rt:if> <script> - require(['jquery', 'components/includes/vendors/searchVendor', 'modules/autocomplete', 'modules/dialog', 'modules/listgroup', 'modules/validation' ], function($, vendorsearch, autocomplete, dialog, listgroup, validation) { + require(['jquery', 'components/includes/vendors/searchVendor', 'modules/autocomplete', 'modules/dialog', 'modules/listgroup', 'modules/validation', 'components/includes/releases/validateLib', 'components/includes/releases/spdxjs'], function($, vendorsearch, autocomplete, dialog, listgroup, validation, validateLib, spdxjs) { document.title = $("<span></span>").html("<sw360:out value='${component.name}'/> - " + document.title).text(); listgroup.initialize('detailTab', $('#detailTab').data('initial-tab') || 'tab-Summary'); @@ -244,6 +251,46 @@ $('#formSubmit').click( function() { + if ("${addMode}" == "false") { + validateLib.setFormId('editSPDXForm'); + + validateLib.validate(); + + if (spdxjs.readDocumentCreator().length == 0) { + validateLib.addError('spdxCreator', ['required']); + + $('.creator-value').each(function () { + if ($(this).val().trim() == '') { + this.setCustomValidity('error'); + } + }); + + $('#spdxCreator')[0].setCustomValidity('error'); + + // It does not auto scroll to Creator if there is error with Creator + // So, code below used to scroll to Creator manually + let gotErrorBeforeCreator = false; + + for (let i = 1; i < 4; i++) { + if ($($('#editDocumentCreationInformation').find('tr')[i]).find('.invalid-feedback.d-block').length > 0) { + gotErrorBeforeCreator = true; + break; + } + } + + if (!gotErrorBeforeCreator) { + $('#creator-anonymous').get(0).scrollIntoView({behavior: "auto", block: "center", inline: "nearest"}) + } + } else { + $('.creator-value').each(function () { + this.setCustomValidity(''); + }); + + $('#spdxCreator')[0].setCustomValidity('') + } + + validateLib.showAllErrors(); + } $(document).find(".checkStatus select").attr("disabled", false); $(document).find(".checkedComment input").attr("disabled", false); <core_rt:choose> diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/clearingDetails.jspf b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/clearingDetails.jspf index eeafc2dcb7..f4b88d3e9e 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/clearingDetails.jspf +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/clearingDetails.jspf @@ -45,13 +45,15 @@ <core_rt:forEach items="${spdxAttachments}" var="spdxAttachment"> <tr id="${spdxAttachment.attachmentContentId}"> <td> - <sw360:out value="${spdxAttachment.filename}"/> + <div <core_rt:if test="${spdxAttachment.attachmentType eq 'INITIAL_SCAN_REPORT'}">class="actions d-inline" </core_rt:if> > + <sw360:out value="${spdxAttachment.filename}"/><core_rt:if test="${spdxAttachment.attachmentType eq 'INITIAL_SCAN_REPORT'}"><svg class="cursor lexicon-icon text-danger ml-2"><title><liferay-ui:message key='this.is.only.an.initial.scanner.isr.finding.of.the.contained.licenses.in.the.uploaded.source.file' /><br><liferay-ui:message key='the.final.license.list.may.differ.based.on.the.conclusions.made.by.the.clearing.expert.and.is.made.available.as.component.license.information.cli' /></title><use href='/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#warning' /></svg></core_rt:if> + </div> </td> <td data-attachment-id="${spdxAttachment.attachmentContentId}"> <button class="btn btn-secondary showSpdxContentBtn" data-attachment-name="${spdxAttachment.filename}" - data-release-id="${release.id}" data-attachment-id="${spdxAttachment.attachmentContentId}"><liferay-ui:message key="show.license.info" /></button> + data-release-id="${release.id}" data-attachment-id="${spdxAttachment.attachmentContentId}" data-attachment-type="${spdxAttachment.attachmentType}"><liferay-ui:message key="show.license.info" /></button> </td> - <td class="result"> + <td class="result actions"> </td> </tr> </core_rt:forEach> @@ -217,9 +219,10 @@ <%--for javascript library loading --%> <%@ include file="/html/utils/includes/requirejs.jspf" %> +<%@ include file="/html/utils/includes/pageSpinner.jspf" %> <script type="text/javascript"> - require(['jquery', 'modules/button', 'utils/includes/fossologyClearing'], function ($, button, fossology) { - + require(['jquery', 'modules/button', 'utils/includes/fossologyClearing', 'modules/dialog'], function ($, button, fossology, dialog) { + var releaseToLicenseDetailsMap = new Map(); fossology.initialize(); $('#ReleaseClearingOverview').on('click', '.action.fossology', function(event) { @@ -231,6 +234,30 @@ handleShowSpdxContentClick($(event.currentTarget)); }); + $("table#spdxAttachments").on("click", "svg.isr", function(event) { + let licenseName = $(event.currentTarget).data().licenseId, + attId = $(event.currentTarget).closest('tr').attr('id'), + attName = $(event.currentTarget).closest('tr').find('td:first div')[0].innerText; + displayLicenseToSrcMapping(attId, attName, licenseName); + }); + + function displayLicenseToSrcMapping(attId, attName, licenseName) { + let data, + list = $('<ul/>'); + if (releaseToLicenseDetailsMap.has(attId)) { + data = releaseToLicenseDetailsMap.get(attId); + } + + if (data && data[licenseName]) { + data[licenseName].forEach(function (file, index) { + list.append('<li>' + file + '</li>'); + }); + } else { + list.append('<li><liferay-ui:message key="source.file.information.not.found.in.isr"/></li>'); + } + dialog.info(attName, '<br>Source File List: <br>' + $(list)[0].outerHTML); + } + function handleShowSpdxContentClick($btn) { var tableData = $('#spdxAttachments').data(), rawUrl = tableData.loadSpdxLicenseInfoUrl, @@ -238,9 +265,11 @@ btnData = $btn.data(), releaseId = btnData.releaseId, attachmentId = btnData.attachmentId, + attachmentType = btnData.attachmentType, $row = $('#' + attachmentId), $btnCell = $row.find('td[data-attachment-id=' + attachmentId + ']'), - $resultCell = $btnCell.parent().find('.result'); + $resultCell = $btnCell.parent().find('.result') + isISR = attachmentType === 'INITIAL_SCAN_REPORT'; url.setParameter(tableData.releaseIdParameterName, releaseId); url.setParameter(tableData.attachmentIdParameterName, attachmentId); @@ -256,51 +285,80 @@ if (!result || result.length == 0 || Object.getOwnPropertyNames(result).length == 0) { replaceContentWithWarning($resultCell, 'No license information found in file.'); } else { - printResult($row, result); - addTakeOverBtn($btnCell, releaseId, attachmentId, result); + releaseToLicenseDetailsMap.set(attachmentId, result); + printResult($row, result, attachmentId, isISR); + addTakeOverBtn($btnCell, releaseId, attachmentId, result, attachmentType, isISR); } }).fail(function(error) { replaceContentWithError($resultCell, '<liferay-ui:message key="cannot.load.license.information" />: '+ error.statusText + ' (' + error.status + ').'); }).always(function() { - button.finish($btnCell.find('button')); + if (!isISR) { + button.finish($btnCell.find('button')); + } }); } - function printResult($row, data) { + function printResult($row, data, attachmentId, isISR) { var $list = $('<ul></ul>'), $otherList = $('<ul></ul>'), - $cell = $row.find('.result'); + $cell = $row.find('.result'), + fileCount = data.totalFileCount, + complexity = '<liferay-ui:message key="very.large" />'; + + if (fileCount <= 1000) { + complexity = '<liferay-ui:message key="small" />'; + } else if (fileCount > 1000 && fileCount <= 5000) { + complexity = '<liferay-ui:message key="medium" />'; + } else if (fileCount > 5000 && fileCount <= 10000) { + complexity = '<liferay-ui:message key="large" />'; + } $cell.html(''); - $cell.append($('<div>', { text: data.license})); - $cell.append($list); + if (isISR) { + $cell.append($('<div>', { html: '<liferay-ui:message key="total.number.of.files"/>: <b>' + data.totalFileCount + '</b>'})); + $cell.append($('<div>', { html: '<liferay-ui:message key="complexity"/>: <b>' + complexity + '</b> (<liferay-ui:message key="based.on.license.file.count" />)'})); + } if (data.licenseIds) { + $cell.append($('<div>', { html: '<b>' + data.license + '</b>'})); + $cell.append($list); data.licenseIds.forEach(function(id) { $list.append($('<li>', { text: id })); + if (isISR) { + let $infoIcon = "<span class='actions'><svg class='cursor lexicon-icon ml-2 isr' data-license-id=" + id + "> <title><liferay-ui:message key='view.file.list' /></title> <use href='/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#info-circle'/> </svg></span>"; + $($list).find('li:last').append($infoIcon); + } }); + } else { + $cell.append($('<div>', { text: 'Main / Concluded License Ids: N/A'})); } - $cell.append($('<div>', { text: data.otherLicense})); - $cell.append($otherList); if (data.otherLicenseIds) { + $cell.append($('<div>', { html: '<b>' + data.otherLicense + '</b>'})); + $cell.append($otherList); data.otherLicenseIds.forEach(function(id) { $otherList.append($('<li>', { text: id })); + if (isISR) { + let $infoIcon = "<span class='actions'><svg class='cursor lexicon-icon ml-2 isr' data-license-id=" + id + "> <title><liferay-ui:message key='view.file.list' /></title> <use href='/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#info-circle'/> </svg></span>"; + $($otherList).find('li:last').append($infoIcon); + } }); + } else { + $cell.append($('<div>', { text: 'Other License Ids: N/A'})); } } - function addTakeOverBtn($parent, releaseId, attachmentId, data) { - var $newBtn = $('<button>', { 'class': 'btn btn-primary spdxTakeOverBtn', 'data-release-id': releaseId, 'data-attachment-id': attachmentId, text: '<liferay-ui:message key="add.data.to.this.release" />' }); - - $newBtn.data('result', data); + function addTakeOverBtn($parent, releaseId, attachmentId, data, attachmentType, isISR) { $parent.html(''); - $parent.append($newBtn); - - $newBtn.on('click', function(event) { - handleTakeOverClick($(event.currentTarget)); - }); + if (!isISR) { + var $newBtn = $('<button>', { 'class': 'btn btn-primary spdxTakeOverBtn', 'data-release-id': releaseId, 'data-attachment-id': attachmentId, text: '<liferay-ui:message key="add.data.to.this.release" />' }); + $newBtn.data('result', data); + $parent.append($newBtn); + $newBtn.on('click', function(event) { + handleTakeOverClick($(event.currentTarget)); + }); + } } function handleTakeOverClick($btn) { @@ -317,6 +375,7 @@ dataObj = {}; url.setParameter(tableData.releaseIdParameterName, releaseId); + url.setParameter(tableData.attachmentIdParameterName, attachmentId); dataObj[ '<portlet:namespace/>' + tableData.spdxLicenseInfoParameterName ] = JSON.stringify(data); button.wait($btnCell.find('button')); diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/detailOverview.jspf b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/detailOverview.jspf index 9202738549..4aacc474fb 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/detailOverview.jspf +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/detailOverview.jspf @@ -32,6 +32,7 @@ <div class="col-3 sidebar"> <div id="detailTab" class="list-group" data-initial-tab="${selectedTab}" role="tablist"> <a class="list-group-item list-group-item-action <core_rt:if test="${selectedTab == 'tab-Summary'}">active</core_rt:if>" href="#tab-Summary" data-toggle="list" role="tab"><liferay-ui:message key="summary" /></a> + <a class="list-group-item list-group-item-action <core_rt:if test="${selectedTab == 'tab-SPDX'}">active</core_rt:if>" href="#tab-SPDX" data-toggle="list" role="tab"><liferay-ui:message key="spdx.document" /></a> <a class="list-group-item list-group-item-action <core_rt:if test="${selectedTab == 'tab-linkedReleases'}">active</core_rt:if>" href="#tab-linkedReleases" data-toggle="list" role="tab"><liferay-ui:message key="linked.releases" /></a> <a class="list-group-item list-group-item-action <core_rt:if test="${selectedTab == 'tab-ClearingDetails'}">active</core_rt:if>" href="#tab-ClearingDetails" data-toggle="list" role="tab"><liferay-ui:message key="clearing.details" /></a> <a class="list-group-item list-group-item-action <core_rt:if test="${selectedTab == 'tab-ECCDetails'}">active</core_rt:if>" href="#tab-ECCDetails" data-toggle="list" role="tab"> @@ -96,6 +97,22 @@ <sw360:DisplaySubscribeButton email="<%=themeDisplay.getUser().getEmailAddress()%>" object="${release}" id="SubscribeButton" /> </div> + + <div id="btnExportGroup" class="btn-group" role="group"> + <button id="btnExport" type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <liferay-ui:message key="export.sbom" /> + <clay:icon symbol="caret-bottom" /> + </button> + <div class="dropdown-menu" aria-labelledby="btnExport"> + <button class="dropdown-item export-sbom" type="button" data-type="SPDX" data-release-id="${release.id}">TAG</button> + <button class="dropdown-item export-sbom" type="button" data-type="RDF" data-release-id="${release.id}">RDF/XML</button> + <button class="dropdown-item export-sbom" type="button" data-type="JSON" data-release-id="${release.id}">JSON</button> + <button class="dropdown-item export-sbom" type="button" data-type="XLS" data-release-id="${release.id}">XLS</button> + <button class="dropdown-item export-sbom" type="button" data-type="XLSX" data-release-id="${release.id}">XLSX</button> + <button class="dropdown-item export-sbom" type="button" data-type="YAML" data-release-id="${release.id}">YAML</button> + </div> + </div> + <div class="list-group-companion" data-belong-to="tab-Attachments"> <core_rt:if test="${inReleaseDetailsContext}"> <div class="btn-group" role="group"> @@ -141,6 +158,13 @@ <%@include file="/html/components/includes/vendors/vendorDetail.jspf" %> <core_rt:set var="documentName"><sw360:ReleaseName release="${release}"/></core_rt:set> + <core_rt:set var="tableId" value="usingProjectsTableSummary"/> + <%@include file="/html/utils/includes/usingProjectsTable.jspf" %> + <%@include file="/html/utils/includes/usingComponentsTable.jspf"%> + </div> + <div id="tab-SPDX" class="tab-pane <core_rt:if test="${selectedTab == 'tab-SPDX'}">active show</core_rt:if>"> + <%@include file="/html/components/includes/releases/spdx/view.jsp" %> + <core_rt:set var="tableId" value="usingProjectsTableSPDX"/> <%@include file="/html/utils/includes/usingProjectsTable.jspf" %> <%@include file="/html/utils/includes/usingComponentsTable.jspf"%> </div> @@ -186,7 +210,7 @@ <script> document.title = $("<span></span>").html("<sw360:out value='${component.name}'/> - " + document.title).text(); - require(['jquery', 'components/includes/releases/linkProject', 'modules/button', 'modules/listgroup', 'utils/includes/clipboard'], function($, linkProject, button, listgroup, clipboard) { + require(['jquery', 'components/includes/releases/linkProject', 'modules/button', 'modules/listgroup', 'utils/includes/clipboard', 'modules/dialog'], function($, linkProject, button, listgroup, clipboard, dialog) { linkProject.initialize(); listgroup.initialize('detailTab', $('#detailTab').data('initial-tab') || 'tab-Summary'); @@ -246,6 +270,86 @@ clipboard.copyToClipboard(textToCopy, textSelector); }); + var portletURL + $('#btnExportGroup button.dropdown-item').on('click', function(event) { + $('#btnExport').prop('disabled', true) + var $dialog; + var releaseId = $(event.currentTarget).data().releaseId; + var type = $(event.currentTarget).data('type') + + function downloadFileExport (callback) { + $('#confirmDialog').hide() + $('#confirmDialog').modal('hide'); + var portletURLDownload = Liferay.PortletURL.createURL('<%= PortletURLFactoryUtil.create(request, portletDisplay.getId(), themeDisplay.getPlid(), PortletRequest.RESOURCE_PHASE) %>') + .setParameter('<%=PortalConstants.ACTION%>', 'download-export-spdx') + .setParameter('<%=PortalConstants.WHAT%>', type) + .setParameter('<%=PortalConstants.RELEASE_ID%>', releaseId); + window.location.href = portletURLDownload.toString(); + } + + $.ajax({ + url: exportSpreadsheet(type, releaseId), + cache: false, + dataType: "json", + }).done(function (data) { + $('#btnExport').prop('disabled', false) + $dialog = dialog.confirm( + null, + 'question-circle', + '<liferay-ui:message key="export.sbom" />', + '<p style="display: none;"><liferay-ui:message key="export.sbom.have.some.error" />:' + + '<div style="display: none;" class="alert alert-warning" id="verifySPDX"></div>', + '<liferay-ui:message key="export" />', + {}, + function(submit, callback) { + downloadFileExport(callback); + } + ); + $('#confirmDialog button.btn.btn-primary').prop('disabled', true) + var log = data.message + if (log != null) { + $('#verifySPDX').attr("style","overflow: auto; max-height: 20rem;") + $('#verifySPDX').append("<strong>" + log + "</strong>") + } + if (data.result == 'SUCCESS') { + $('#confirmDialog button.btn.btn-primary').prop('disabled', false) + cancelExport() + } else { + $('#confirmDialog > div > div > div.modal-body > p:nth-child(1)').attr("style","color: red;") + cancelExport() + } + }); + + function cancelExport() { + var portletURLCancel = Liferay.PortletURL.createURL('<%= PortletURLFactoryUtil.create(request, portletDisplay.getId(), themeDisplay.getPlid(), PortletRequest.RESOURCE_PHASE) %>') + .setParameter('<%=PortalConstants.ACTION%>', 'download-export-spdx') + .setParameter('<%=PortalConstants.WHAT%>', type) + .setParameter('<%=PortalConstants.RELEASE_ID%>', releaseId) + .setParameter('<%=PortalConstants.ACTION_CANCEL%>', 'action_cancel'); + $('#confirmDialog > div > div > div.modal-footer > button.btn.btn-light').on('click', function(event) { + $.ajax({ + url: portletURLCancel.toString(), + cache: false, + }) + }) + $('#confirmDialog > div > div > div.modal-header > button').on('click', function(event) { + $.ajax({ + url: portletURLCancel.toString(), + cache: false, + }) + }) + } + }); + + // Export Spreadsheet action + function exportSpreadsheet(type, releaseId) { + portletURL = Liferay.PortletURL.createURL('<%= PortletURLFactoryUtil.create(request, portletDisplay.getId(), themeDisplay.getPlid(), PortletRequest.RESOURCE_PHASE) %>') + .setParameter('<%=PortalConstants.ACTION%>', 'export-spdx') + .setParameter('<%=PortalConstants.WHAT%>', type) + .setParameter('<%=PortalConstants.RELEASE_ID%>', releaseId); + return portletURL.toString() + } + function doAjax(url, successCallback, errorCallback) { $.ajax({ type: 'POST', diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/edit.jspf b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/edit.jspf new file mode 100644 index 0000000000..9ac0ee25f8 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/edit.jspf @@ -0,0 +1,596 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + + ~ SPDX-License-Identifier: EPL-2.0 +--%> +<style> + table { + border-collapse: collapse !important; + border: none; + } + tr, + td { + border: none; + border-top-width: 0 !important; + } + .radio-label { + vertical-align: text-bottom !important; + } + .sub-label { + margin-right: 0.5rem; + margin-top: 0.5rem; + font-weight: 400; + font-size: 1rem; + } + .sub-title { + width: 10rem; + margin-top: 0.7rem; + margin-right: 1rem; + } + .sub-input { + width: auto; + } + .spdx-checkbox { + margin-top: 0.75rem; + width: 1rem; + height: 1rem; + } + .spdx-select { + width: auto; + flex: auto; + margin-right: 2rem; + } + .spdx-radio { + margin-top: 0.75rem; + margin-right: 0.5rem; + width: 1rem; + height: 1rem; + } + .spdx-date { + width: 12rem; + text-align: center; + } + .spdx-time { + width: 12rem; + text-align: center; + margin-left: 0.6rem; + } + .label-select { + flex: 1; + text-decoration: underline; + } + .spdx-delete-icon-main { + margin-top: 0.3rem; + margin-right: 1rem; + width: 1rem; + height: auto; + + cursor: pointer; + } + .spdx-delete-icon-sub { + margin-top: 0.3rem; + margin-right: 4rem; + width: 1rem; + height: auto; + + cursor: pointer; + } + .spdx-add-button-main { + margin-left: 11rem; + margin-bottom: 2rem; + width: 10rem; + } + .spdx-add-button-sub { + width: 10rem; + } + thead { + cursor: pointer; + } + .spdx-table .form-group { + margin-right: 1rem; + } + .spdx-table textarea { + margin-left: 0 !important; + } + .spdx-table .form-control { + margin-left: 0 !important; + } + #spdxLiteMode { + margin-left: -1px !important; + } + input[type=date] { + margin-right: 1rem; + } +</style> +<script> + let spdxDocumentObj = jQuery.parseJSON(JSON.stringify(${ spdxDocumentJson })); + let documentCreationInformationObj = jQuery.parseJSON(JSON.stringify(${ documentCreationInfoJson })); + let packagesInformationObj = jQuery.parseJSON(JSON.stringify(${ packageInfoJson })); +</script> +<%@include file="/html/utils/includes/requirejs.jspf" %> +<core_rt:set var="setRelationshipType" value='<%=PortalConstants.SET_RELATIONSHIP_TYPE%>'/> +<div id="editSPDXForm" name="editSPDXForm" class="form needs-validation" novalidate> + <div class="form-group btn-group"> + <button id="spdxFullMode" class="btn btn-info">SPDX Full</button> + <button id="spdxLiteMode" class="btn btn-secondary">SPDX Lite</button> + </div> + <%@include file="/html/components/includes/releases/spdx/includes/editDocumentCreationInformation.jsp" %> + <%@include file="/html/components/includes/releases/spdx/includes/editPackageInformation.jsp" %> + <%@include file="/html/components/includes/releases/spdx/includes/editSnippetInformation.jsp" %> + <%@include file="/html/components/includes/releases/spdx/includes/editOtherLicensingInformationDetected.jsp" %> + <%@include file="/html/components/includes/releases/spdx/includes/editRelationshipsBetweenSPDXElements.jsp"%> + <%@include file="/html/components/includes/releases/spdx/includes/editAnnotations.jsp" %> + <input style="display: none;" type="text" id="spdxDocumentData" name="<portlet:namespace/><%=SPDXDocument._Fields.TYPE%>" value="" /> + <input style="display: none;" type="text" id="documentCreationInfoData" name="<portlet:namespace/><%=SPDXDocument._Fields.SPDX_DOCUMENT_CREATION_INFO_ID%>" value="" /> + <input style="display: none;" type="text" id="packageInfoData" name="<portlet:namespace/><%=SPDXDocument._Fields.SPDX_PACKAGE_INFO_IDS%>" value="" /> +</div> +<script> + $('#spdxFullMode').on('click', function (e) { + e.preventDefault(); + $(this).addClass('btn-info'); + $(this).removeClass('btn-secondary'); + $('#spdxLiteMode').addClass('btn-secondary'); + $('#spdxLiteMode').removeClass('btn-info'); + $('.spdx-full').css('display', ''); + }); + $('#spdxLiteMode').on('click', function (e) { + e.preventDefault(); + $(this).addClass('btn-info'); + $(this).removeClass('btn-secondary'); + $('#spdxFullMode').addClass('btn-secondary'); + $('#spdxFullMode').removeClass('btn-info'); + $('.spdx-full').css('display', 'none'); + }); + $('thead').on('click', function () { + if ($(this).next().css('display') == 'none') { + $(this).next().css('display', ''); + } else { + $(this).next().css('display', 'none'); + } + }); + $('.spdx-table button').on('click', function (e) { + e.preventDefault(); + }); + $('.spdx-add-button-main').on('click', function (e) { + e.preventDefault(); + }) + $('.spdx-add-button-sub').on('click', function (e) { + e.preventDefault(); + }) + let middleName = '<%=themeDisplay.getUser().getMiddleName()%>'; + if (middleName == '') { + middleName = ' '; + } else { + middleName = ' ' + middleName + ' '; + } + let userDisplay = "<%=themeDisplay.getUser().getFirstName()%>" + middleName + "<%=themeDisplay.getUser().getLastName()%>" + " (<%=themeDisplay.getUser().getEmailAddress()%>)"; + require(['jquery', 'components/includes/releases/spdxjs', 'components/includes/releases/validateLib', 'utils/array'], function ($, spdxjs, validateLib) { + // Sort data based on index + spdxDocumentObj.snippets.sort(dynamicSort('index', 'int')); + spdxDocumentObj.relationships.sort(dynamicSort('index', 'int')); + spdxDocumentObj.annotations.sort(dynamicSort('index', 'int')); + spdxDocumentObj.otherLicensingInformationDetecteds.sort(dynamicSort('index', 'int')); + for (let i = 0; i < spdxDocumentObj.snippets.length; i++) { + spdxDocumentObj.snippets[i].snippetRanges.sort(dynamicSort('index', 'int')); + } + documentCreationInformationObj.externalDocumentRefs.sort(dynamicSort('index', 'int')); + documentCreationInformationObj.creator.sort(dynamicSort('index', 'int')); + + packagesInformationObj.sort(dynamicSort('index', 'int')); + for (let i = 0; i < packagesInformationObj.length; i++) { + packagesInformationObj[i].checksums.sort(dynamicSort('index', 'int')); + packagesInformationObj[i].externalRefs.sort(dynamicSort('index', 'int')); + packagesInformationObj[i].annotations.sort(dynamicSort('index', 'int')); + } + // 6.6 External Document Reference - Add + $('[name=add-externalDocRef]').on('click', function (e) { + e.preventDefault(); + let index = 0; + if (documentCreationInformationObj.externalDocumentRefs.length > 0) { + index = parseInt(documentCreationInformationObj.externalDocumentRefs[documentCreationInformationObj.externalDocumentRefs.length - 1].index) + 1; + } + let newObj = { 'externalDocumentId': '', + 'checksum': { 'algorithm': '', 'checksumValue': '' }, + 'spdxDocument': '', + 'index': index }; + documentCreationInformationObj.externalDocumentRefs.push(newObj); + spdxjs.addMain($(this)); + $('#externalDocumentRefs').change(); + }); + // 6.6 External Document Reference - Delete + $('[name=delete-externalDocRef]').on('click', function (e) { + let selectedIndex = $('#externalDocumentRefs')[0].selectedIndex; + documentCreationInformationObj.externalDocumentRefs.splice(selectedIndex, 1); + spdxjs.deleteMain($(this)); + }); + // 6.6 External Document Reference - Change + $('#externalDocumentRefs').on('change', function (e) { + let selectedIndex = $('#externalDocumentRefs')[0].selectedIndex; + spdxjs.fillExternalDocRef(selectedIndex); + }); + // 6.8 SPDX Document creator - Add + $('[name=add-spdx-creator]').on('click', function (e) { + spdxjs.addSub($(this)); + $('[name=delete-spdx-creator]').last().bind('click', function() { + spdxjs.deleteSub($(this)); + }); + }); + // 6.8 SPDX Document creator - Delete + $('[name=delete-spdx-creator]').on('click', function (e) { + spdxjs.deleteSub($(this)); + }); + // 7.10 Checksum - Add + $('#addNewAlgorithm').on('click', function() { + spdxjs.addSub($(this)); + let selectedPackage = $('#selectPackage')[0].selectedIndex; + $('.checksum-delete').last().bind('click', function() { + spdxjs.deleteSub($(this)); + spdxjs.storePackageInfo(selectedPackage); + }); + $('.checksum-algorithm, .checksum-value').bind('change keyup', function() { + if ($(this).is(":focus")) { + spdxjs.storePackageInfo(selectedPackage); + } + }); + }); + // 7 Package - Add + let newPackageInfo = false; + $('[name=add-package]').on('click', function(e) { + e.preventDefault(); + newPackageInfo = false; + validateLib.setFormId('sectionPackageInformation'); + validateLib.validate(); + if (!validateLib.allValid()) { + validateLib.showAllErrors(); + } else { + newPackageInfo = true; + validateLib.hideAllErrors(); + let index = 0; + if (packagesInformationObj.length > 0) { + index = parseInt(packagesInformationObj[packagesInformationObj.length - 1].index) + 1; + } + let newObj = {'name': '', + 'SPDXID': '', + 'versionInfo': '', + 'packageFileName': '', + 'supplier': '', + 'originator': '', + 'downloadLocation': '', + 'filesAnalyzed': '', + 'packageVerificationCode': '', + 'checksums': [], + 'homepage': '', + 'sourceInfo': '', + 'licenseConcluded': '', + 'licenseInfoFromFiles': [], + 'licenseDeclared': '', + 'licenseComments': '', + 'copyrightText': '', + 'summary': '', + 'description': '', + 'packageComment': '', + 'externalRefs': [], + 'attributionText': '', + 'index': index}; + packagesInformationObj.push(newObj); + spdxjs.addMain($(this)); + $('#selectPackage').change(); + } + }); + // 7 Package - Delete + $('[name=delete-package]').on('click', function(e) { + let selectedIndex = $('#selectPackage')[0].selectedIndex; + packagesInformationObj.splice(selectedIndex, 1); + spdxjs.deleteMain($(this)); + }); + // 7 Package - Change + $('#selectPackage').on('change', function(e) { + let selectedIndex = $('#selectPackage')[0].selectedIndex; + validateLib.setFormId('sectionPackageInformation'); + if (newPackageInfo) { + validateLib.hideAllErrors(); + spdxjs.fillPackage(selectedIndex); + $(this).data('prev', selectedIndex); + newPackageInfo = false; + return; + } + validateLib.validate(); + if (!validateLib.allValid()) { + validateLib.showAllErrors(); + $(this).val($(this).data('prev') + 1); + } else { + validateLib.hideAllErrors(); + spdxjs.fillPackage(selectedIndex); + $(this).data('prev', selectedIndex); + } + }); + // 7.21 External Reference - Add + $('[name=add-externalRef]').on('click', function(e) { + e.preventDefault(); + let index = 0; + let selectedPackage = $('#selectPackage')[0].selectedIndex; + if (packagesInformationObj[selectedPackage].externalRefs.length > 0) { + index = parseInt(packagesInformationObj[selectedPackage].externalRefs[packagesInformationObj[selectedPackage].externalRefs.length - 1].index) + 1; + } + let newObj = { 'referenceCategory': 'SECURITY', + 'referenceLocator': '', + 'referenceType': 'cpe22Type', + 'comment': '', + 'index': index }; + packagesInformationObj[selectedPackage].externalRefs.push(newObj); + spdxjs.addMain($(this)); + $('#externalReferences').change(); + }); + // 7.21 External Reference - Delete + $('[name=delete-externalRef]').on('click', function(e) { + let selectedIndex = $('#externalReferences')[0].selectedIndex; + let selectedPackage = $('#selectPackage')[0].selectedIndex; + packagesInformationObj[selectedPackage].externalRefs.splice(selectedIndex, 1); + spdxjs.deleteMain($(this)); + }); + // 7.21 External Reference - Change + $('#externalReferences').on('change', function(e) { + let selectedIndex = $('#externalReferences')[0].selectedIndex; + let selectedPackage = $('#selectPackage')[0].selectedIndex; + spdxjs.fillExternalRef(packagesInformationObj[selectedPackage], selectedIndex); + }); + // 7.21 External Reference Category + const referenceCategories = { 'SECURITY': ['cpe22Type', 'cpe23Type'], + 'PACKAGE-MANAGER': ['maven-central', 'npm', 'nuget', 'bower', 'purl'], + 'PERSISTENT-ID': [], + 'OTHER': [] }; + $('#referenceCategory').on('change', function() { + let category = $('#referenceCategory').val(); + let types = referenceCategories[category]; + if (types.length > 0) { + $("#referenceType-1").css('display', 'block'); + $("#referenceType-1").val(types[0]); + $("#referenceType-2").css('display', 'none'); + $("#referenceType-1").empty(); + for (let i = 0; i < types.length; i++) { + let option = '<option>' + types[i] + '</option>'; + $("#referenceType-1").append(option); + } + } else { + $("#referenceType-1").css('display', 'none'); + $("#referenceType-2").css('display', 'block'); + $("#referenceType-2").val(''); + } + if ($('#referenceCategory').is(":focus")) { + let index = $('#externalReferences')[0].selectedIndex; + let selectedPackage = $('#selectPackage')[0].selectedIndex; + spdxjs.storeExternalRef(packagesInformationObj[selectedPackage], index); + } + }); + // 9 Snippet - Add + let newSnippet = false; + $('[name=add-snippet]').on('click', function(e) { + e.preventDefault(); + let index = 0; + if (spdxDocumentObj.snippets.length > 0) { + index = parseInt(spdxDocumentObj.snippets[spdxDocumentObj.snippets.length - 1].index) + 1; + } + let newObj = {'SPDXID': '', + 'snippetFromFile': '', + 'snippetRanges': [], + 'licenseConcluded': '', + 'licenseInfoInSnippets': [], + 'licenseComments': '', + 'copyrightText': '', + 'comment': '', + 'name': '', + 'snippetAttributionText': '', + 'index': index}; + spdxDocumentObj.snippets.push(newObj); + spdxjs.addMain($(this)); + $('#selectSnippet').change(); + }); + // 9 Snippet - Delete + $('[name=delete-snippet]').on('click', function(e) { + let selectedIndex = $('#selectSnippet')[0].selectedIndex; + spdxDocumentObj.snippets.splice(selectedIndex, 1); + spdxjs.deleteMain($(this)); + }); + // 9 Snippet - Change + $('#selectSnippet').on('change', function(e) { + let selectedIndex = $('#selectSnippet')[0].selectedIndex; + spdxjs.fillSnippet(selectedIndex); + }); + // 9.3 & 9.4 Snippet Range - Add + $('#addNewRange').on('click', function() { + spdxjs.addSub($(this)); + $('[name=delete-snippetRange]').bind('click', function() { + spdxjs.deleteSub($(this)); + spdxjs.storeSnippet(); + }); + $('.range-type, .start-pointer, .end-pointer, .reference').bind('change keyup', function() { + if ($(this).is(":focus")) { + spdxjs.storeSnippet(); + } + }); + }); + // 10 Other Licensing - Add + let newOtherLicensing = false; + $('[name=add-otherLicensing]').on('click', function(e) { + e.preventDefault(); + newOtherLicensing = false; + validateLib.setFormId('sectionOtherLicensing'); + validateLib.validate(); + if (!validateLib.allValid()) { + validateLib.showAllErrors(); + } else { + newOtherLicensing = true; + validateLib.hideAllErrors(); + let index = 0; + if (spdxDocumentObj.otherLicensingInformationDetecteds.length > 0) { + index = parseInt(spdxDocumentObj.otherLicensingInformationDetecteds[spdxDocumentObj.otherLicensingInformationDetecteds.length - 1].index) + 1; + } + let newObj = { 'licenseId': '', + 'extractedText': '', + 'licenseName': '', + 'licenseCrossRefs': [], + 'index': index }; + spdxDocumentObj.otherLicensingInformationDetecteds.push(newObj); + spdxjs.addMain($(this)); + $('#selectOtherLicensing').change(); + } + }); + // 10 Other Licensing - Delete + $('[name=delete-otherLicensing]').on('click', function(e) { + newOtherLicensing = true; + let selectedIndex = $('#selectOtherLicensing')[0].selectedIndex; + spdxDocumentObj.otherLicensingInformationDetecteds.splice(selectedIndex, 1); + spdxjs.deleteMain($(this)); + }); + // 10 Other Licensing - Change + $('#selectOtherLicensing').on('change', function(e) { + let selectedIndex = $('#selectOtherLicensing')[0].selectedIndex; + validateLib.setFormId('sectionOtherLicensing'); + if (newOtherLicensing) { + validateLib.hideAllErrors(); + spdxjs.fillOtherLicensing(selectedIndex); + $(this).data('prev', selectedIndex); + newOtherLicensing = false; + return; + } + validateLib.validate(); + if (!validateLib.allValid()) { + validateLib.showAllErrors(); + $(this).val($(this).data('prev') + 1); + } else { + validateLib.hideAllErrors(); + spdxjs.fillOtherLicensing(selectedIndex); + $(this).data('prev', selectedIndex); + } + }); + // 11 Relationship - Change source + $('#selectRelationshipSource').on('change', function() { + spdxjs.initRelationships(); + }); + // 11 Relationship - Add + $('[name=add-relationship]').on('click', function(e) { + e.preventDefault(); + let index = 0; + let source = spdxjs.getRelationshipsSource(); + if (source.length > 0) { + index = parseInt(source[source.length - 1].index) + 1; + } + let newObj = { 'spdxElementId': '', + 'relationshipType': '', + 'relatedSpdxElement': '', + 'relationshipComment': '', + 'index': index }; + source.push(newObj); + spdxjs.addMain($(this)); + $('#selectRelationship').change(); + }); + // 11 Relationship - Delete + $('[name=delete-relationship]').on('click', function(e) { + let selectedIndex = $('#selectRelationship')[0].selectedIndex; + let source = spdxjs.getRelationshipsSource(); + source.splice(selectedIndex, 1); + spdxjs.deleteMain($(this)); + }); + // 11 Relationship - Change + $('#selectRelationship').on('change', function(e) { + let selectedIndex = $('#selectRelationship')[0].selectedIndex; + let source = spdxjs.getRelationshipsSource(); + spdxjs.fillRelationship(source, selectedIndex); + }); + // 12 Annotation - Change source + $('#selectAnnotationSource').on('change', function() { + spdxjs.initAnnotations(); + }); + // 12 Annotation - Add + $('[name=add-annotation]').on('click', function(e) { + e.preventDefault(); + let source = spdxjs.getAnnotationsSource(); + let index = 0; + if (source.length > 0) { + index = parseInt(source[source.length - 1].index) + 1; + } + let newObj = { 'annotator': '', + 'annotationDate': '', + 'annotationType': '', + 'annotationComment': '', + 'spdxIdRef': '', + 'index': index }; + source.push(newObj); + spdxjs.addMain($(this)); + $('#selectAnnotation').change(); + }); + // 12 Annotation - Delete + $('[name=delete-annotation]').on('click', function(e) { + let source = spdxjs.getAnnotationsSource(); + let selectedIndex = $('#selectAnnotation')[0].selectedIndex; + source.splice(selectedIndex, 1); + spdxjs.deleteMain($(this)); + }); + // 12 Annotation - Change + $('#selectAnnotation').on('change', function(e) { + let source = spdxjs.getAnnotationsSource(); + let selectedIndex = $('#selectAnnotation')[0].selectedIndex; + spdxjs.fillAnnotation(source, selectedIndex); + }); + $(function () { + $('.spdx-radio').on('change', function () { + spdxjs.updateRadioButton($(this)); + }); + $('.section').find('input, select, textarea').not('spdx-select').on('change keyup', function () { + + if ($(this).hasClass('spdx-select')) { + return; + } + if (!$(this).is(":focus")) { + return; + } + let index = $('#externalDocumentRefs')[0].selectedIndex; + spdxjs.storeExternalDocRef(index); + + let selectedPackage = $('#selectPackage')[0].selectedIndex; + index = $('#externalReferences')[0].selectedIndex; + spdxjs.storeExternalRef(packagesInformationObj[selectedPackage], index); + + index = $('#selectPackage')[0].selectedIndex; + spdxjs.storePackageInfo(index); + + index = $('#selectSnippet')[0].selectedIndex; + spdxjs.storeSnippet(index); + + index = $('#selectOtherLicensing')[0].selectedIndex; + spdxjs.storeOtherLicensing(index); + + index = $('#selectRelationship')[0].selectedIndex; + spdxjs.storeRelationship(index); + + index = $('#selectAnnotation')[0].selectedIndex; + spdxjs.storeAnnotation(index); + }); + function initialize() { + spdxjs.initDocumentCreation(userDisplay); + spdxjs.initPackageInfo(); + spdxjs.initSnippetInfo(); + spdxjs.initOtherLicensing(); + spdxjs.initRelationships(); + spdxjs.initAnnotations(); + } + initialize(); + }); + $('#formSubmit').click(function() { + spdxjs.storeDocumentCreation(); + let packageIndex = 0; + packagesInformationObj.forEach( p => { + p['index'] = packageIndex; + packageIndex++; + }) + $('#spdxDocumentData').val(JSON.stringify(spdxDocumentObj)); + $('#documentCreationInfoData').val(JSON.stringify(documentCreationInformationObj)); + $('#packageInfoData').val(JSON.stringify(packagesInformationObj)); + }); + }); +</script> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editAnnotations.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editAnnotations.jsp new file mode 100644 index 0000000000..2f924961ab --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editAnnotations.jsp @@ -0,0 +1,102 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + + ~ SPDX-License-Identifier: EPL-2.0 +--%> +<table class="table spdx-table spdx-full" id="editAnnotations"> + <thead> + <tr> + <th colspan="3">12. Annotation Information</th> + </tr> + </thead> + <tbody class="section section-annotation"> + <tr> + <td> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem; padding-left: 1rem;"> + <label for="selectAnnotationSource" style="text-decoration: underline;" class="sub-title">Select Source</label> + <select id="selectAnnotationSource" type="text" class="form-control spdx-select always-enable" style="margin-right: 4rem;"> + <option>SPDX Document</option> + <option>Package</option> + </select> + </div> + <div style="display: flex; flex-direction: column; padding-left: 1rem;"> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label for="selectAnnotation" style="text-decoration: underline;" class="sub-title">Select Annotation</label> + <select id="selectAnnotation" type="text" class="form-control spdx-select"></select> + <svg class="disabled lexicon-icon spdx-delete-icon-main" name="delete-annotation" data-row-id="" viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <use href="/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#trash"/> + </svg> + </div> + <button class="spdx-add-button-main" name="add-annotation">Add new Annotation</button> + </div> + </td> + </tr> + <tr> + <td style="display: flex"> + <div class="form-group" style="flex: 3"> + <label class="mandatory" for="annotator">12.1 Annotator</label> + <div style="display: flex"> + <select id="annotatorType" style="flex: 2; margin-right: 1rem;" type="text" class="form-control" placeholder="Enter type"> + <option value="Organization" selected="">Organization</option> + <option value="Person">Person</option> + <option value="Tool">Tool</option> + </select> + <input style="flex: 6; margin-right: 1rem;" id="annotatorValue" type="text" class="form-control" + placeholder="Enter annotator" > + </div> + </div> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="annotationCreatedDate">12.2 Annotation date </label> + <div style="display: flex"> + <div> + <input id="annotationCreatedDate" style="width: 12rem; text-align: center;" type="date" + class="form-control needs-validation" rule="required" placeholder="creation.date.yyyy.mm.dd" > + <div id="annotationCreatedDate-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="invalid.format" /> + </div> + </div> + </div> + <div> + <input id="annotationCreatedTime" style="width: 12rem; text-align: center; margin-left: 10px;" + type="time" step="1" class="form-control needs-validation" rule="required" placeholder="creation.time.hh.mm.ss" > + <div id="annotationCreatedTime-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="invalid.format" /> + </div> + </div> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td style="display: flex"> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="annotationType">12.3 Annotation type</label> + <input id="annotationType" class="form-control" type="text" placeholder="Enter annotation type" > + </div> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="spdxIdRef">12.4 SPDX identifier reference</label> + <input id="spdxIdRef" class="form-control" type="text" + placeholder="Enter SPDX identifier reference" > + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory" for="annotationComment">12.5 Annotation comment</label> + <textarea class="form-control" id="annotationComment" rows="5" + placeholder="Enter annotation comment"></textarea> + </div> + </td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editDocumentCreationInformation.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editDocumentCreationInformation.jsp new file mode 100644 index 0000000000..16178ce4d0 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editDocumentCreationInformation.jsp @@ -0,0 +1,260 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + + ~ SPDX-License-Identifier: EPL-2.0 +--%> + +<table class="table spdx-table" id="editDocumentCreationInformation"> + <thead> + <tr> + <th colspan="3">6. Document Creation Information</th> + </tr> + </thead> + <tbody> + <tr> + <td style="display: flex"> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="spdxVersion">6.1 SPDX version</label> + <div style="display: flex"> + <label class="sub-label">SPDX-</label> + <input id="spdxVersion" class="form-control needs-validation" rule="required" + type="text" placeholder="Enter SPDX version" + value="<sw360:out value="${spdxDocumentCreationInfo.spdxVersion}" />"> + </div> + <div id="spdxVersion-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="dataLicense">6.2 Data license</label> + <input id="dataLicense" class="form-control needs-validation" rule="required" type="text" + placeholder="Enter data license" + value="<sw360:out value="${spdxDocumentCreationInfo.dataLicense}" />"> + <div id="dataLicense-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="spdxIdentifier">6.3 SPDX identifier</label> + <div style="display: flex"> + <label class="sub-label">SPDXRef-</label> + <input id="spdxIdentifier" class="form-control needs-validation" rule="required" type="text" + placeholder="Enter SPDX identifier" value="<sw360:out value="${spdxDocumentCreationInfo.SPDXID}" />"> + </div> + <div id="spdxIdentifier-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory" for="documentName">6.4 Document name</label> + <input id="documentName" type="text" + class="form-control needs-validation" rule="required" + placeholder="Enter document name" value="<sw360:out value="${spdxDocumentCreationInfo.name}" />"> + </div> + <div id="documentName-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory" for="documentNamespace">6.5 SPDX document namespace</label> + <input id="documentNamespace" class="form-control needs-validation" rule="required" type="text" + placeholder="Enter SPDX document namespace" + value="<sw360:out value="${spdxDocumentCreationInfo.documentNamespace}" />"> + </div> + <div id="documentNamespace-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </td> + </tr> + <tr> + <td class="spdx-full"> + <div class="form-group section section-external-doc-ref"> + <label for="externalDocumentRefs">6.6 External document references</label> + <div style="display: flex; flex-direction: column; padding-left: 1rem;"> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label for="externalDocumentRefs" style="text-decoration: underline;" class="sub-title">Select Reference</label> + <select id="externalDocumentRefs" type="text" class="form-control spdx-select"></select> + <svg class="disabled lexicon-icon spdx-delete-icon-main" name="delete-externalDocRef" data-row-id="" viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <use href="/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#trash"/> + </svg> + </div> + <button class="spdx-add-button-main" name="add-externalDocRef">Add new Reference</button> + </div> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label class="sub-title" for="externalDocumentId">External document ID</label> + <input id="externalDocumentId" style="width: auto; flex: auto;" type="text" class="form-control" + placeholder="Enter external document ID"> + </div> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label class="sub-title" for="externalDocument">External document</label> + <input id="externalDocument" style="width: auto; flex: auto;" type="text" class="form-control" + placeholder="Enter external document"> + </div> + <div style="display: flex;"> + <label class="sub-title">Checksum</label> + <div style="display: flex; flex-direction: column; flex: 7"> + <div style="display: flex; margin-bottom: 0.75rem;"> + <input style="flex: 2; margin-right: 1rem;" type="text" class="form-control" + id="checksumAlgorithm" placeholder="Enter algorithm"> + <input style="flex: 6;" type="text" class="form-control" id="checksumValue" + placeholder="Enter value"> + </div> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td> + <div class="form-group"> + <label for="licenseListVersion">6.7 License list version</label> + <input id="licenseListVersion" class="form-control" type="text" + placeholder="Enter license list version" value="<sw360:out value="${spdxDocumentCreationInfo.licenseListVersion}" />"> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory" for="creator">6.8 Creators</label> + <div style="display: flex; flex-direction: column;"> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label class="sub-title" for="creator-anonymous">Anonymous</label> + <input id="creator-anonymous" class="spdx-checkbox" type="checkbox" onclick="setAnonymous()" > + </div> + <div style="display: flex;"> + <label class="sub-title">List</label> + <div style="display: flex; flex-direction: column; flex: 7"> + <div style="display: none; margin-bottom: 0.75rem;" name="creatorRow"> + <select style="flex: 2; margin-right: 1rem;" type="text" + class="form-control creator-type" placeholder="Enter type" + onchange="changeCreatorType(this)"> + <option value="Organization" selected>Organization</option> + <option value="Person">Person</option> + <option value="Tool">Tool</option> + </select> + <input style="flex: 6; margin-right: 2rem;" type="text" + class="form-control creator-value" placeholder="Enter creator"> + <svg class="disabled lexicon-icon spdx-delete-icon-sub" + name="delete-spdx-creator" data-row-id="" viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <path class="lexicon-icon-outline lx-trash-body-border" d="M64.4,440.7c0,39.3,31.9,71.3,71.3,71.3h240.6c39.3,0,71.3-31.9,71.3-71.3v-312H64.4V440.7z M128.2,192.6h255.5v231.7c0,13.1-10.7,23.8-23.8,23.8H152c-13.1,0-23.8-10.7-23.8-23.8V192.6z"></path> + <polygon class="lexicon-icon-outline lx-trash-lid" points="351.8,32.9 351.8,0 160.2,0 160.2,32.9 64.4,32.9 64.4,96.1 447.6,96.1 447.6,32.9 "></polygon> + <rect class="lexicon-icon-outline lx-trash-line-2" x="287.9" y="223.6" width="63.9" height="191.6"></rect> + <rect class="lexicon-icon-outline lx-trash-line-1" x="160.2" y="223.6" width="63.9" height="191.6"></rect> + </svg> + </div> + <button class="spdx-add-button-sub spdx-add-button-sub-creator" name="add-spdx-creator">Add new creator</button> + </div> + </div> + <input id="spdxCreator" class="form-control" style="display: none" rule="required" type="text"> + <div id="spdxCreator-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td style="display: flex; flex-direction: column;"> + <div class="form-group"> + <label class="mandatory" for="createdDate">6.9 Created</label> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <div> + <input id="createdDate" type="date" class="form-control spdx-date needs-validation" + rule="required" placeholder="created.date.yyyy.mm.dd"> + <div id="createdDate-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="invalid.format" /> + </div> + </div> + </div> + <div> + <input id="createdTime" type="time" step="1" class="form-control spdx-time needs-validation" + rule="required" placeholder="created.time.hh.mm.ss"> + <div id="createdTime-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="invalid.format" /> + </div> + </div> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td colspan="3"> + <div class="form-group"> + <label for="creatorComment">6.10 Creator comment</label> + <textarea class="form-control" id="creatorComment" rows="5" + placeholder="Enter creator comment"></textarea> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td colspan="3"> + <div class="form-group"> + <label for="documentComment">6.11 Document comment</label> + <textarea class="form-control" id="documentComment" rows="5" + placeholder="Enter document comment"></textarea> + </div> + </td> + </tr> + </tbody> +</table> + +<script> + function setAnonymous() { + let selectboxes = $('#creator-anonymous').parent().next().find('select'); + if ($('#creator-anonymous').is(':checked')) { + selectboxes.each(function (index) { + if ($(this).val() == 'Organization' || $(this).val() == 'Person') { + $(this).attr('disabled', 'true'); + $(this).next().attr('disabled', 'true'); + $(this).next().next().css('cursor', 'not-allowed'); + } + }); + } else { + selectboxes.each(function (index) { + if ($(this).val() == 'Organization' || $(this).val() == 'Person') { + $(this).removeAttr('disabled'); + $(this).next().removeAttr('disabled'); + $(this).next().next().css('cursor', 'pointer'); + } + }); + } + } + function changeCreatorType(selectbox) { + if ($('#creator-anonymous').is(':checked') && + ($(selectbox).val() == 'Organization' || $(selectbox).val() == 'Person')) { + $(selectbox).attr('disabled', 'true'); + $(selectbox).next().attr('disabled', 'true'); + $(selectbox).next().next().css('cursor', 'not-allowed'); + } + } +</script> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editOtherLicensingInformationDetected.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editOtherLicensingInformationDetected.jsp new file mode 100644 index 0000000000..002f055e5f --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editOtherLicensingInformationDetected.jsp @@ -0,0 +1,108 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + + ~ SPDX-License-Identifier: EPL-2.0 +--%> + +<table class="table spdx-table" id="editOtherLicensingInformationDetected"> + <thead> + <tr> + <th colspan="3">10. Other Licensing Information Detected</th> + </tr> + </thead> + <tbody id="sectionOtherLicensing" class="section section-other-licensing"> + <tr> + <td> + <div style="display: flex; flex-direction: column; padding-left: 1rem;"> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label for="selectOtherLicensing" style="text-decoration: underline;" class="sub-title">Select Other Licensing</label> + <select id="selectOtherLicensing" type="text" class="form-control spdx-select"></select> + <svg class="disabled lexicon-icon spdx-delete-icon-main" name="delete-otherLicensing" data-row-id="" viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <use href="/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#trash"/> + </svg> + </div> + <button class="spdx-add-button-main" name="add-otherLicensing">Add new Licensing</button> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory" for="licenseId">10.1 License identifier</label> + <div style="display: flex"> + <label class="sub-label">LicenseRef-</label> + <input id="licenseId" class="form-control needs-validation" rule="required" + type="text" placeholder="Enter license identifier"> + </div> + <div id="licenseId-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory" for="extractedText">10.2 Extracted text</label> + <textarea class="form-control needs-validation" rule="required" id="extractedText" rows="5" + name="_sw360_portlet_components_EXTRACTED_TEXT" placeholder="Enter extracted text"></textarea> + </div> + <div id="extractedText-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </td> + </tr> + <tr> + <td style="display: flex; flex-direction: column;"> + <div class="form-group"> + <label class="mandatory">10.3 License name</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" id="licenseNameExist" type="radio" name="_sw360_portlet_components_LICENSE_NAME" value="EXIST"> + <input style="flex: 6; margin-right: 1rem;" id="licenseName" + class="form-control needs-validation" type="text" + placeholder="Enter license name" rule="required"> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="licenseNameNoAssertion" type="radio" name="_sw360_portlet_components_LICENSE_NAME" value="NOASSERTION"> + <label class="form-check-label radio-label" for="licenseNameNoAssertion">NOASSERTION</label> + </div> + </div> + <div id="licenseName-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td> + <div class="form-group"> + <label for="licenseCrossRefs">10.4 License cross reference</label> + <textarea class="form-control" id="licenseCrossRefs" rows="5" + placeholder="Enter license cross reference"></textarea> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label for="licenseComment">10.5 License comment</label> + <textarea class="form-control" id="licenseCommentOnOtherLicensing" rows="5" + placeholder="Enter license comment"></textarea> + </div> + </td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editPackageInformation.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editPackageInformation.jsp new file mode 100644 index 0000000000..944de99fbb --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editPackageInformation.jsp @@ -0,0 +1,498 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + + ~ SPDX-License-Identifier: EPL-2.0 +--%> + +<core_rt:if test="${not spdxPackageInfo.isEmpty()}"> + <core_rt:set var="package" value="${spdxPackageInfo.iterator().next()}" /> +</core_rt:if> +<core_rt:if test="${not spdxPackageInfo.isEmpty()}"> + <core_rt:set var="spdxPackages" value="${spdxPackageInfo}" /> +</core_rt:if> +<table class="table spdx-table" id="editPackageInformation"> + <thead> + <tr> + <th colspan="3">7. Package Information</th> + </tr> + </thead> + <tbody id="sectionPackageInformation" class="section section-package"> + <tr> + <td> + <div style="display: flex; flex-direction: column; padding-left: 1rem;"> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label for="selectPackage" style="text-decoration: underline;" class="sub-title">Select Package</label> + <select id="selectPackage" type="text" class="form-control spdx-select"></select> + <svg class="disabled lexicon-icon spdx-delete-icon-main" name="delete-package" data-row-id="" viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <use href="/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#trash"/> + </svg> + </div> + <button class="spdx-add-button-main" name="add-package">Add new Package</button> + </div> + </td> + </tr> + + <tr> + <td style="display: flex"> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="packageName">7.1 Package name</label> + <div style="display: flex"> + <input id="packageName" class="form-control needs-validation" rule="required" type="text" + placeholder="Enter package name" name="_sw360_portlet_components_PACKAGE_NAME" + value="<sw360:out value="${package.name}" />"> + </div> + <div id="packageName-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="packageSPDXId">7.2 Package SPDX identifier</label> + <div style="display: flex"> + <label class="sub-label">SPDXRef-</label> + <input id="packageSPDXId" class="form-control needs-validation" rule="required" type="text" + placeholder="Enter package SPDX identifier" name="_sw360_portlet_components_PACKAGE_SPDX_ID" + value="<sw360:out value="${package.SPDXID}" />"> + </div> + <div id="packageSPDXId-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td style="display: flex"> + <div class="form-group" style="flex: 1"> + <label for="versionInfo">7.3 Package version</label> + <div style="display: flex"> + <input id="versionInfo" class="form-control" type="text" placeholder="Enter package version" + name="_sw360_portlet_components_VERSION_INFO" value="<sw360:out value="${package.versionInfo}" />"> + </div> + </div> + <div class="form-group" style="flex: 1"> + <label for="packageFileName">7.4 Package file name</label> + <div style="display: flex"> + <input id="packageFileName" class="form-control" type="text" + placeholder="Enter package file name" name="_sw360_portlet_components_PACKAGE_FILE_NAME" + value="<sw360:out value="${package.packageFileName}" />"> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td style="display: flex; flex-direction: column;"> + <div class="form-group"> + <label>7.5 Package supplier</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" type="radio" name="_sw360_portlet_components_SUPPLIER" + value="EXIST"> + <select id="supplierType" style="flex: 2; margin-right: 1rem;" class="form-control"> + <option>Organization</option> + <option>Person</option> + </select> + <input style="flex: 6; margin-right: 1rem;" id="supplierValue" + class="form-control" type="text" + name="_sw360_portlet_components_SUPPLIER_VALUE" placeholder="Enter package supplier"> + </div> + <div style="flex: 2"> + <input class="spdx-radio" id="supplierNoAssertion" type="radio" + name="_sw360_portlet_components_SUPPLIER" value="NOASSERTION"> + <label class="form-check-label radio-label" for="supplierNoAssertion">NOASSERTION</label> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td style="display: flex; flex-direction: column;"> + <div class="form-group"> + <label>7.6 Package originator</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" type="radio" name="_sw360_portlet_components_ORIGINATOR" + value="EXIST"> + <select id="originatorType" style="flex: 2; margin-right: 1rem;" class="form-control"> + <option>Organization</option> + <option>Person</option> + </select> + <input style="flex: 6; margin-right: 1rem;" class="form-control" id="originatorValue" + type="text" name="_sw360_portlet_components_ORIGINATOR_VALUE" + placeholder="Enter package originator"> + </div> + <div style="flex: 2"> + <input class="spdx-radio" id="originatorNoAssertion" type="radio" + name="_sw360_portlet_components_ORIGINATOR" value="NOASSERTION"> + <label class="form-check-label radio-label" for="originatorNoAssertion">NOASSERTION</label> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td style="display: flex; flex-direction: column;"> + <div class="form-group"> + <label class="mandatory">7.7 Package download location</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" type="radio" id="downloadLocationExist" + name="_sw360_portlet_components_DOWNLOAD_LOCATION" value="EXIST"> + <input style="flex: 6; margin-right: 1rem;" class="form-control needs-validation" + id="downloadLocationValue" rule="required" type="text" + name="_sw360_portlet_components_DOWNLOAD_LOCATION_VALUE" + placeholder="Enter package download location"> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="downloadLocationNone" type="radio" + name="_sw360_portlet_components_DOWNLOAD_LOCATION" value="NONE"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="downloadLocationNone">NONE</label> + <input class="spdx-radio" id="downloadLocationNoAssertion" type="radio" + name="_sw360_portlet_components_DOWNLOAD_LOCATION" value="NOASSERTION"> + <label class="form-check-label radio-label" + for="downloadLocationNoAssertion">NOASSERTION</label> + </div> + </div> + <div id="downloadLocationValue-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label>7.8 Files analyzed</label> + <div style="display: flex; flex-direction: row;"> + <div> + <input class="spdx-radio" id="FilesAnalyzedTrue" type="radio" + name="_sw360_portlet_components_FILES_ANALYZED" checked value="true"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="FilesAnalyzedTrue">TRUE</label> + <input class="spdx-radio" id="FilesAnalyzedFalse" type="radio" + name="_sw360_portlet_components_FILES_ANALYZED" value="false"> + <label class="form-check-label radio-label" for="FilesAnalyzedFalse">FALSE</label> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td> + <div class="form-group"> + <label for="verificationCodeValue" class="mandatory">7.9 Package verification code</label> + <div> + <input style="margin-bottom: 0.75rem;" class="form-control" id="verificationCodeValue" + name="_sw360_portlet_components_VERIFICATION_CODE_VALUE" + placeholder="Enter verification code value"></input> + <textarea class="form-control" id="excludedFiles" rows="5" + name="_sw360_portlet_components_EXCLUDED_FILES" + placeholder="Enter excluded files"><sw360:out value="${package.packageVerificationCode.excludedFiles.toString()}" /></textarea> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td> + <div class="form-group"> + <label>7.10 Package checksum</label> + <div style="display: flex;"> + <label class="sub-title">Checksum</label> + <div style="display: flex; flex-direction: column; flex: 7"> + <div style="display: none; margin-bottom: 0.75rem;" name="checksumRow"> + <input style="flex: 2; margin-right: 1rem;" type="text" class="form-control checksum-algorithm" + placeholder="Enter algorithm"> + <input style="flex: 6; margin-right: 2rem;" type="text" class="form-control checksum-value" + placeholder="Enter value"> + <svg class="disabled lexicon-icon spdx-delete-icon-sub checksum-delete" data-row-id="" + viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <path class="lexicon-icon-outline lx-trash-body-border" d="M64.4,440.7c0,39.3,31.9,71.3,71.3,71.3h240.6c39.3,0,71.3-31.9,71.3-71.3v-312H64.4V440.7z M128.2,192.6h255.5v231.7c0,13.1-10.7,23.8-23.8,23.8H152c-13.1,0-23.8-10.7-23.8-23.8V192.6z"></path> + <polygon class="lexicon-icon-outline lx-trash-lid" points="351.8,32.9 351.8,0 160.2,0 160.2,32.9 64.4,32.9 64.4,96.1 447.6,96.1 447.6,32.9 "></polygon> + <rect class="lexicon-icon-outline lx-trash-line-2" x="287.9" y="223.6" width="63.9" height="191.6"></rect> + <rect class="lexicon-icon-outline lx-trash-line-1" x="160.2" y="223.6" width="63.9" height="191.6"></rect> + </svg> + </div> + <button id="addNewAlgorithm" class="spdx-add-button-sub spdx-add-button-sub-checksum" >Add new algorithm</button> + </div> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label>7.11 Package home page</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" id="packageHomepageExist" type="radio" + name="_sw360_portlet_components_PACKAGE_HOMEPAGE" value="EXIST"> + <input style="flex: 6; margin-right: 1rem;" id="packageHomepageValue" + class="form-control" type="text" + name="_sw360_portlet_components_PACKAGE_HOMEPAGE_VALUE" + placeholder="Enter package home page"> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="packageHomepageNone" type="radio" + name="_sw360_portlet_components_PACKAGE_HOMEPAGE" value="NONE"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="packageHomepageNone">NONE</label> + <input class="spdx-radio" id="packageHomepageNoAssertion" type="radio" + name="_sw360_portlet_components_PACKAGE_HOMEPAGE" value="NOASSERTION"> + <label class="form-check-label radio-label" + for="packageHomepageNoAssertion">NOASSERTION</label> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td> + <div class="form-group"> + <label for="sourceInfo">7.12 Source information</label> + <textarea class="form-control" id="sourceInfo" rows="5" name="_sw360_portlet_components_SOURCE_INFO" + placeholder="Enter source information">${package.sourceInfo}</textarea> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory">7.13 Concluded license</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" id="licenseConcludedExist" type="radio" + name="_sw360_portlet_components_LICENSE_CONCLUDED" value="EXIST"> + <input style="flex: 6; margin-right: 1rem;" class="form-control needs-validation" + rule="required" id="licenseConcludedValue" type="text" + name="_sw360_portlet_components_LICENSE_CONCLUDED_VALUE" + placeholder="Enter concluded license"> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="licenseConcludedNone" type="radio" + name="_sw360_portlet_components_LICENSE_CONCLUDED" value="NONE"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="licenseConcludedNone">NONE</label> + <input class="spdx-radio" id="licenseConcludedNoAssertion" type="radio" + name="_sw360_portlet_components_LICENSE_CONCLUDED" value="NOASSERTION"> + <label class="form-check-label radio-label" + for="licenseConcludedNoAssertion">NOASSERTION</label> + </div> + </div> + <div id="licenseConcludedValue-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td> + <div class="form-group"> + <label class="mandatory">7.14 All licenses information from files</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" id="licenseInfoFromFilesExist" type="radio" + name="_sw360_portlet_components_LICENSE_INFO_FROM_FILES" value="EXIST"> + <textarea style="flex: 6; margin-right: 1rem;" id="licenseInfoFromFilesValue" rows="5" + class="form-control" type="text" + name="_sw360_portlet_components_LICENSE_INFO_FROM_FILES_VALUE" + placeholder="Enter all licenses information from files"><sw360:out value="${package.licenseInfoFromFiles.toString()}" hashSet="true"/></textarea> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="licenseInfoFromFilesNone" type="radio" + name="_sw360_portlet_components_LICENSE_INFO_FROM_FILES" value="NONE"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="licenseInfoFromFilesNone">NONE</label> + <input class="spdx-radio" id="licenseInfoFromFilesNoAssertion" type="radio" + name="_sw360_portlet_components_LICENSE_INFO_FROM_FILES" value="NOASSERTION"> + <label class="form-check-label radio-label" + for="licenseInfoFromFilesNoAssertion">NOASSERTION</label> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory">7.15 Declared license</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" id="licenseDeclaredExist" type="radio" + name="_sw360_portlet_components_DECLARED_LICENSE" value="EXIST"> + <input style="flex: 6; margin-right: 1rem;" id="licenseDeclaredValue" + class="form-control needs-validation" type="text" rule="required" + name="_sw360_portlet_components_DECLARED_LICENSE_VALUE" + placeholder="Enter declared license"> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="licenseDeclaredNone" type="radio" + name="_sw360_portlet_components_DECLARED_LICENSE" value="NONE"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="licenseDeclaredNone">NONE</label> + <input class="spdx-radio" id="licenseDeclaredNoAssertion" type="radio" + name="_sw360_portlet_components_DECLARED_LICENSE" value="NOASSERTION"> + <label class="form-check-label radio-label" + for="licenseDeclaredNoAssertion">NOASSERTION</label> + </div> + </div> + <div id="licenseDeclaredValue-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td colspan="3"> + <div class="form-group"> + <label for="licenseComments">7.16 Comments on license</label> + <textarea class="form-control" id="licenseComments" rows="5" + name="_sw360_portlet_components_LICENSE_COMMENTS" + placeholder="Enter comments on license">${package.licenseComments}</textarea> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory">7.17 Copyright text</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" id="copyrightTextExist" type="radio" + name="_sw360_portlet_components_COPYRIGHT_TEXT" value="EXIST"> + <textarea style="flex: 6; margin-right: 1rem;" id="copyrightTextValue" rows="5" + class="form-control needs-validation" rule="required" type="text" + name="_sw360_portlet_components_COPYRIGHT_TEXT_VALUE" + placeholder="Enter copyright text">${package.copyrightText}</textarea> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="copyrightTextNone" type="radio" + name="_sw360_portlet_components_COPYRIGHT_TEXT" value="NONE"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="copyrightTextNone">NONE</label> + <input class="spdx-radio" id="copyrightTextNoAssertion" type="radio" + name="_sw360_portlet_components_COPYRIGHT_TEXT" value="NOASSERTION"> + <label class="form-check-label radio-label" + for="copyrightTextNoAssertion">NOASSERTION</label> + </div> + </div> + <div id="copyrightTextValue-error-messages"> + <div class="invalid-feedback" rule="required"> + <liferay-ui:message key="this.field.must.be.not.empty" /> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td> + <div class="form-group"> + <label for="summary">7.18 Package summary description</label> + <textarea class="form-control" id="summary" rows="5" + name="_sw360_portlet_components_PACKAGE_SUMMARY" + placeholder="Enter package summary description">${package.summary}</textarea> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td> + <div class="form-group"> + <label for="description">7.19 Package detailed description</label> + <textarea class="form-control" id="description" rows="5" + name="_sw360_portlet_components_PACKAGE_DESCRIPTION" + placeholder="Enter package detailed description">${package.description}</textarea> + </div> + </td> + </tr> + <tr> + <td colspan="3"> + <div class="form-group"> + <label for="spdxPackageComment">7.20 Package comment</label> + <textarea class="form-control" id="spdxPackageComment" rows="5" + name="_sw360_portlet_components_PACKAGE_COMMENT" + placeholder="Enter package comment">${package.packageComment}</textarea> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td> + <div class="form-group section section-external-ref"> + <label>7.21 External references</label> + <div style="display: flex; flex-direction: column; padding-left: 1rem;"> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label style="text-decoration: underline;" class="sub-title">Select Reference</label> + <select type="text" class="form-control spdx-select" id="externalReferences"></select> + <svg class="disabled lexicon-icon spdx-delete-icon-main" name="delete-externalRef" data-row-id="" viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <use href="/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#trash" /> + </svg> + </div> + <button class="spdx-add-button-main" name="add-externalRef">Add new Reference</button> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label class="sub-title">Category</label> + <select style="width: auto; flex: auto;" id="referenceCategory" type="text" + class="form-control" placeholder="Enter category" + name="_sw360_portlet_components_REFERENCE_CATEGORY"> + <option value="SECURITY">SECURITY</option> + <option value="PACKAGE-MANAGER">PACKAGE-MANAGER</option> + <option value="PERSISTENT-ID">PERSISTENT-ID</option> + <option value="OTHER">OTHER</option> + </select> + </div> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label class="sub-title">Type</label> + <select style="width: auto; flex: auto;" id="referenceType-1" type="text" + class="form-control" placeholder="Enter type" + name="_sw360_portlet_components_REFERENCE_TYPE-1"> + <option>cpe22Type</option> + <option>cpe23Type</option> + </select> + <input style="width: auto; flex: auto; display: none;" id="referenceType-2" type="text" + class="form-control" placeholder="Enter type" + name="_sw360_portlet_components_REFERENCE_TYPE-2"> + </select> + </div> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label class="sub-title">Locator</label> + <input style="width: auto; flex: auto;" type="text" class="form-control" + id="externalReferencesLocator" placeholder="Enter locator" + name="_sw360_portlet_components_REFERENCE_LOCATOR"> + </div> + <div style="display: flex; flex-direction: row;"> + <label class="sub-title">7.22 Comment</label> + <textarea style="width: auto; flex: auto;" type="text" rows="5" class="form-control" + id="externalReferencesComment" placeholder="Enter comment" + name="_sw360_portlet_components_REFERENCE_COMMENT"></textarea> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td colspan="3"> + <div class="form-group"> + <label for="spdxPackageComment">7.23 Package attribution text</label> + <textarea class="form-control" id="spdxPackageAttributionText" rows="5" + name="_sw360_portlet_components_PACKAGE_COMMENT" + placeholder="Enter package attribution text"><sw360:out value="${package.attributionText.toString()}" stripNewlines="false" hashSet="true"/></textarea> + </div> + </td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editRelationshipsBetweenSPDXElements.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editRelationshipsBetweenSPDXElements.jsp new file mode 100644 index 0000000000..41127302f9 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editRelationshipsBetweenSPDXElements.jsp @@ -0,0 +1,71 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + + ~ SPDX-License-Identifier: EPL-2.0 +--%> + +<table class="table spdx-table spdx-full" id="editRelationshipsBetweenSPDXElements"> + <thead> + <tr> + <th colspan="3">11. Relationship between SPDX Elements Information</th> + </tr> + </thead> + <tbody class="section section-relationship"> + <tr> + <td> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem; padding-left: 1rem;"> + <label for="selectRelationshipSource" style="text-decoration: underline;" class="sub-title">Select Source</label> + <select id="selectRelationshipSource" type="text" class="form-control spdx-select always-enable" style="margin-right: 4rem;"> + <option>SPDX Document</option> + <option>Package</option> + </select> + </div> + <div style="display: flex; flex-direction: column; padding-left: 1rem;"> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label for="selectRelationship" style="text-decoration: underline;" class="sub-title">Select Relationship</label> + <select id="selectRelationship" type="text" class="form-control spdx-select"></select> + <svg class="disabled lexicon-icon spdx-delete-icon-main" name="delete-relationship" data-row-id="" viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <use href="/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#trash"/> + </svg> + </div> + <button class="spdx-add-button-main" name="add-relationship">Add new Relationship</button> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label for="spdxElement">11.1 Relationship</label> + <div style="display: flex"> + <input style="margin-right: 1rem;" id="spdxElement" class="form-control" + name="_sw360_portlet_components_LICENSE_ID" type="text" placeholder="Enter SPDX element"> + <select class="form-control" id="relationshipType" style="margin-right: 1rem;" > + <option value="" selected=""></option> + <core_rt:forEach items="${setRelationshipType}" var="entry"> + <option value="${entry}" class="textlabel stackedLabel">${entry}</option> + </core_rt:forEach> + </select> + <input id="relatedSPDXElement" class="form-control" name="_sw360_portlet_components_LICENSE_ID" + type="text" placeholder="Enter related SPDX element"> + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label for="relationshipComment">11.2 Relationship comment</label> + <textarea class="form-control" id="relationshipComment" rows="5" + name="_sw360_portlet_components_RELATIONSHIP_COMMENT" + placeholder="Enter relationship comment"></textarea> + </div> + </td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editSnippetInformation.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editSnippetInformation.jsp new file mode 100644 index 0000000000..54e5938039 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/includes/editSnippetInformation.jsp @@ -0,0 +1,200 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + + ~ SPDX-License-Identifier: EPL-2.0 +--%> + +<table class="table spdx-table spdx-full" id="editSnippetInformation"> + <thead> + <tr> + <th>9. Snippet Information</th> + </tr> + </thead> + <tbody id ="sectionSnippet" class="section section-snippet"> + <tr> + <td> + <div style="display: flex; flex-direction: column; padding-left: 1rem;"> + <div style="display: flex; flex-direction: row; margin-bottom: 0.75rem;"> + <label for="selectSnippet" style="text-decoration: underline;" class="sub-title">Select Snippet</label> + <select id="selectSnippet" type="text" class="form-control spdx-select"></select> + <svg class="disabled lexicon-icon spdx-delete-icon-main" name="delete-snippet" data-row-id="" viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <use href="/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#trash"/> + </svg> + </div> + <button class="spdx-add-button-main" name="add-snippet">Add new Snippet</button> + </div> + </td> + </tr> + <tr> + <td style="display: flex"> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="snippetSpdxIdentifier">9.1 Snippet SPDX identifier</label> + <div style="display: flex"> + <label class="sub-label">SPDXRef-</label> + <input id="snippetSpdxIdentifier" class="form-control" + name="_sw360_portlet_components_SPDXSPDX_IDENTIFIER" + type="text" placeholder="Enter snippet SPDX identifier" > + </div> + </div> + <div class="form-group" style="flex: 1"> + <label class="mandatory" for="snippetFromFile">9.2 Snippet from file SPDX identifier</label> + <div style="display: flex"> + <select id="snippetFromFile" class="form-control" style="flex: 1;"> + <option>SPDXRef</option> + <option>DocumentRef</option> + </select> + <div style="margin: 0.5rem;">-</div> + <input style="flex: 3;" id="snippetFromFileValue" class="form-control" + name="_sw360_portlet_components_SPDXSPDX_VERSION" type="text" + placeholder="Enter snippet from file SPDX identifier" > + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory">9.3 & 9.4 Snippet ranges</label> + <div style="display: flex; flex-direction: column; padding-left: 1rem;"> + <div style="display: none; margin-bottom: 0.75rem;" name="snippetRange"> + <select style="flex: 1; margin-right: 1rem;" type="text" class="form-control range-type" placeholder="Enter type"> + <option value="BYTE" selected>BYTE</option> + <option value="LINE">LINE</option> + </select> + <input style="flex: 2; margin-right: 1rem;" type="text" class="form-control start-pointer" placeholder="Enter start pointer"> + <input style="flex: 2; margin-right: 1rem;" type="text" class="form-control end-pointer" placeholder="Enter end pointer"> + <input style="flex: 4; margin-right: 2rem;" type="text" class="form-control reference" placeholder="Enter reference"> + <svg class="lexicon-icon spdx-delete-icon-sub hidden" name="delete-snippetRange" data-row-id="" viewBox="0 0 512 512"> + <title><liferay-ui:message key="delete" /></title> + <path class="lexicon-icon-outline lx-trash-body-border" d="M64.4,440.7c0,39.3,31.9,71.3,71.3,71.3h240.6c39.3,0,71.3-31.9,71.3-71.3v-312H64.4V440.7z M128.2,192.6h255.5v231.7c0,13.1-10.7,23.8-23.8,23.8H152c-13.1,0-23.8-10.7-23.8-23.8V192.6z"></path> + <polygon class="lexicon-icon-outline lx-trash-lid" points="351.8,32.9 351.8,0 160.2,0 160.2,32.9 64.4,32.9 64.4,96.1 447.6,96.1 447.6,32.9 "></polygon> + <rect class="lexicon-icon-outline lx-trash-line-2" x="287.9" y="223.6" width="63.9" height="191.6"></rect> + <rect class="lexicon-icon-outline lx-trash-line-1" x="160.2" y="223.6" width="63.9" height="191.6"></rect> + </svg> + </div> + <button id="addNewRange" class="spdx-add-button-sub">Add new Range</button> + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory">9.5 Snippet concluded license</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" id="spdxConcludedLicenseExist" type="radio" + name="_sw360_portlet_components_CONCLUDED_LICENSE" value="EXIST"> + <input style="flex: 6; margin-right: 1rem;" id="spdxConcludedLicenseValue" + class="form-control" type="text" + name="_sw360_portlet_components_CONCLUDED_LICENSE_VALUE" + placeholder="Enter snippet concluded license"> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="spdxConcludedLicenseNone" type="radio" + name="_sw360_portlet_components_CONCLUDED_LICENSE" value="NONE"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="spdxConcludedLicenseNone">NONE</label> + <input class="spdx-radio" id="spdxConcludedLicenseNoAssertion" type="radio" + name="_sw360_portlet_components_CONCLUDED_LICENSE" value="NOASSERTION"> + <label class="form-check-label radio-label" + for="spdxConcludedLicenseNoAssertion">NOASSERTION</label> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label>9.6 License information in snippet</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" id="licenseInfoInFile" type="radio" + name="_sw360_portlet_components_LICENSE_INFO_IN_FILE" value="EXIST"> + <textarea style="flex: 6; margin-right: 1rem;" id="licenseInfoInFileValue" rows="5" + class="form-control" type="text" + name="_sw360_portlet_components_LICENSE_INFO_IN_FILE_SOURCE" + placeholder="Enter license information in snippet"></textarea> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="licenseInfoInFileNone" type="radio" + name="_sw360_portlet_components_LICENSE_INFO_IN_FILE" value="NONE"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="licenseInfoInFileNone">NONE</label> + <input class="spdx-radio" id="licenseInfoInFileNoAssertion" type="radio" + name="_sw360_portlet_components_LICENSE_INFO_IN_FILE" value="NOASSERTION"> + <label class="form-check-label radio-label" + for="licenseInfoInFileNoAssertion">NOASSERTION</label> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td colspan="3"> + <div class="form-group"> + <label for="snippetLicenseComments">9.7 Snippet comments on license</label> + <textarea class="form-control" id="snippetLicenseComments" rows="5" + placeholder="Enter snippet comments on license"></textarea> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label class="mandatory">9.8 Snippet copyright text</label> + <div style="display: flex; flex-direction: row;"> + <div style="display: inline-flex; flex: 3; margin-right: 1rem;"> + <input class="spdx-radio" id="snippetCopyrightText" type="radio" name="_sw360_portlet_components_SNIPPET_COPYRIGHT_TEXT" value="EXIST"> + <textarea style="flex: 6; margin-right: 1rem;" id="copyrightTextValueSnippet" rows="5" + class="form-control" type="text" + placeholder="Enter snippet copyright text"></textarea> + </div> + <div style="flex: 2;"> + <input class="spdx-radio" id="snippetCopyrightTextNone" type="radio" name="_sw360_portlet_components_SNIPPET_COPYRIGHT_TEXT" value="NONE"> + <label style="margin-right: 2rem;" class="form-check-label radio-label" + for="snippetCopyrightTextNone">NONE</label> + <input class="spdx-radio" id="snippetCopyrightTextNoAssertion" type="radio" name="_sw360_portlet_components_SNIPPET_COPYRIGHT_TEXT" value="NOASSERTION"> + <label class="form-check-label radio-label" + for="snippetCopyrightTextNoAssertion">NOASSERTION</label> + </div> + </div> + </div> + </td> + </tr> + <tr> + <td colspan="3"> + <div class="form-group"> + <label for="snippetComment">9.9 Snippet comment</label> + <textarea class="form-control" id="snippetComment" rows="5" + placeholder="Enter snippet comment"></textarea> + </div> + </td> + </tr> + <tr> + <td> + <div class="form-group"> + <label for="snippetName">9.10 Snippet name</label> + <input id="snippetName" type="text" + class="form-control" placeholder="Enter snippet name" > + </div> + </td> + </tr> + <tr> + <td colspan="3"> + <div class="form-group"> + <label for="snippetAttributionText">9.11 Snippet attribution text</label> + <textarea class="form-control" id="snippetAttributionText" rows="5" + placeholder="Enter snippet attribution text"></textarea> + </div> + </td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/view.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/view.jsp new file mode 100644 index 0000000000..3354179759 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/spdx/view.jsp @@ -0,0 +1,1178 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 +--%> + +<div class="form-group btn-group"> + <button id="spdxFullMode" class="btn btn-info">SPDX Full</button> + <button id="spdxLiteMode" class="btn btn-secondary">SPDX Lite</button> +</div> + +<table class="table label-value-table spdx-table" id="DocumentCreationInformation"> + <thead class="spdx-thead"> + <tr> + <th>6. Document Creation Information</th> + </tr> + </thead> + <tbody> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.1 SPDX version</div> + <div class="spdx-col-2"> + <sw360:out value="${spdxDocumentCreationInfo.spdxVersion}" /> + </div> + </td> + </tr> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.2 Data license</div> + <div class="spdx-col-2"> + <sw360:out value="${spdxDocumentCreationInfo.dataLicense}" /> + </div> + </td> + </tr> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.3 SPDX identifier</div> + <div class="spdx-col-2"> + <sw360:out value="${spdxDocumentCreationInfo.SPDXID}" /> + </div> + </td> + </tr> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.4 Document name</div> + <div class="spdx-col-2"> + <sw360:out value="${spdxDocumentCreationInfo.name}" /> + </div> + </td> + </tr> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.5 SPDX document namespace</div> + <div class="spdx-col-2"> + <sw360:out value="${spdxDocumentCreationInfo.documentNamespace}" /> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.6 External document references</div> + <div class="spdx-col-2 section" data-size="3"> + <div class="spdx-flex-row"> + <div class="spdx-col-1 spdx-label-index">Index</div> + <select class="spdx-col-3" id="externalDocumentRefs" onchange="displayIndex(this)"> + </select> + </div> + <core_rt:forEach items="${spdxDocumentCreationInfo.externalDocumentRefs}" + var="externalDocumentRefeData" varStatus="loop"> + <div class="spdx-flex-row" data-index="${externalDocumentRefeData.index}"> + <div class="spdx-col-1 spdx-key">External document ID</div> + <div class="spdx-col-3"> + <sw360:out value="${externalDocumentRefeData.externalDocumentId}" /> + </div> + </div> + <div class="spdx-flex-row" data-index="${externalDocumentRefeData.index}"> + <div class="spdx-col-1 spdx-key">External document</div> + <div class="spdx-col-3"> + <sw360:out value="${externalDocumentRefeData.spdxDocument}" /> + </div> + </div> + <div class="spdx-flex-row" data-index="${externalDocumentRefeData.index}"> + <div class="spdx-col-2 spdx-key">Checksum</div> + <div class="spdx-col-3"> + <sw360:out + value="${externalDocumentRefeData.checksum.algorithm}" /> + : + <sw360:out value="${externalDocumentRefeData.checksum.checksumValue}" /> + </div> + </div> + </core_rt:forEach> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.7 License list version</div> + <div class="spdx-col-2"> + <sw360:out value="${spdxDocumentCreationInfo.licenseListVersion}" /> + </div> + </td> + </tr> + <core_rt:set var="creators" value="${spdxDocumentCreationInfo.creator}" /> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.8 Creators</div> + <div class="spdx-col-2" id="creators"> + <core_rt:forEach items="${creators}" var="creatorData" varStatus="loop"> + <div class="spdx-flex-row creator" data-index="${creatorData.index}"> + <div class="spdx-col-1 spdx-key"> + <sw360:out value="${creatorData.type}" /> + </div> + <div class="spdx-col-3"> + <sw360:out value="${creatorData.value}" /> + </div> + </div> + </core_rt:forEach> + </div> + </td> + </tr> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.9 Created</div> + <div class="spdx-col-2" id="createdDateTime"> + <sw360:out value="${spdxDocumentCreationInfo.created}" /> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.10 Creator comment</div> + <div class="spdx-col-2" id="creatorComment"> + <sw360:out value="${spdxDocumentCreationInfo.creatorComment}" /> + </div> + </td> + </tr> + <tr class="spdx-full"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">6.11 Document comment</div> + <div class="spdx-col-2" id="documentComment"> + <sw360:out value="${spdxDocumentCreationInfo.documentComment}" /> + </div> + </td> + </tr> + </tbody> +</table> +<!-- <core_rt:if test="${not empty spdxPackageInfo}"> + <core_rt:set var="package" value="${spdxPackageInfo.iterator().next()}" /> +</core_rt:if> --> +<table class="table label-value-table spdx-table" id="PackageInformation"> + <thead class="spdx-thead"> + <tr> + <th>7. Package Information</th> + </tr> + </thead> + <tbody class="section" data-size="23"> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1 spdx-label-index">Index</div> + <select id="packageInfoSelect" class="spdx-col-2" onchange="changePackageIndex(this)"></select> + </td> + </tr> + + <core_rt:forEach items="${spdxPackageInfo}" var="package" varStatus="loop"> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.1 Package name</div> + <div class="spdx-col-2"> + <sw360:out value="${package.name}" /> + </div> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.2 Package SPDX identifier</div> + <div class="spdx-col-2"> + <sw360:out value="${package.SPDXID}" /> + </div> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.3 Package version</div> + <div class="spdx-col-2"> + <sw360:out value="${package.versionInfo}" /> + </div> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.4 Package file name</div> + <div class="spdx-col-2"> + <sw360:out value="${package.packageFileName}" /> + </div> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.5 Package supplier</div> + <div class="spdx-col-2"> + <sw360:out value="${package.supplier}" /> + </div> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.6 Package originator</div> + <div class="spdx-col-2"> + <sw360:out value="${package.originator}" /> + </div> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.7 Package download location</div> + <div class="spdx-col-2"> + <sw360:out value="${package.downloadLocation}" /> + </div> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.8 Files analyzed</div> + <div class="spdx-col-2 spdx-uppercase"> + <sw360:out value="${package.filesAnalyzed}" /> + </div> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.9 Package verification code</div> + <div class="spdx-col-2 spdx-flex-col"> + <div class="spdx-flex-row"> + <div class="spdx-col-1 spdx-key">Value</div> + <div class="spdx-col-3"> + <sw360:out value="${package.packageVerificationCode.value}" /> + </div> + </div> + <div class="spdx-flex-row"> + <div class="spdx-col-1 spdx-key">Excluded files</div> + <p class="spdx-col-3 " id="excludedFiles"> + <core_rt:forEach items="${package.packageVerificationCode.excludedFiles}" + var="excludedFileData" varStatus="loop"> + <sw360:out value="${excludedFileData}" /> <br> + </core_rt:forEach> + </p> + </div> + </div> + </div> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.10 Package checksum</div> + <div class="spdx-col-2" id="checksums"> + <core_rt:forEach items="${package.checksums}" var="checksumData" varStatus="loop"> + <div class="spdx-flex-row checksum" data-index="${checksumData.index}"> + <div class="spdx-col-1 spdx-key"> + <sw360:out value="${checksumData.algorithm}" /> + </div> + <div class="spdx-col-3"> + <sw360:out value="${checksumData.checksumValue}" /> + </div> + </div> + </core_rt:forEach> + </div> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.11 Package home page</div> + <div class="spdx-col-2"> + <sw360:out value="${package.homepage}" /> + </div> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.12 Source information</div> + <div class="spdx-col-2 " id="sourceInfo"> + <sw360:out value="${package.sourceInfo}" /> + </div> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.13 Concluded license</div> + <div class="spdx-col-2"> + <sw360:out value="${package.licenseConcluded}" /> + </div> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.14 All licenses information from files</div> + <p class="spdx-col-2 " id="licenseInfoFromFile"> + <core_rt:forEach items="${package.licenseInfoFromFiles}" var="licenseInfoFromFileData" + varStatus="loop"> + <sw360:out value="${licenseInfoFromFileData} " /> + </core_rt:forEach> + </p> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.15 Declared license</div> + <div class="spdx-col-2"> + <sw360:out value="${package.licenseDeclared}" /> + </div> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.16 Comments on license</div> + <p class="spdx-col-2 " id="licenseComments"> + <sw360:out value="${package.licenseComments}" stripNewlines="false" /> + </p> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.17 Copyright text</div> + <p class="spdx-col-2 " id="copyrightText"> + <sw360:out value="${package.copyrightText}" stripNewlines="false" /> + </p> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.18 Package summary description</div> + <p class="spdx-col-2 " id="summary" > + <sw360:out value="${package.summary}" stripNewlines="false" /> + </p> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.19 Package detailed description</div> + <p class="spdx-col-2 " id="description"> + <sw360:out value="${package.description}" stripNewlines="false" /> + </p> + </td> + </tr> + <tr data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.20 Package comment</div> + <p class="spdx-col-2 " id="packageComment"> + <sw360:out value="${package.packageComment}" stripNewlines="false" /> + </p> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.21 External references </div> + <core_rt:if test="${package.externalRefs.size() gt 0}"> + <div class="spdx-col-2 section" data-size="4"> + <div class="spdx-flex-row"> + <div class="spdx-col-1 spdx-label-index">Index</div> + <select id="externalReferenceSelect${package.index}" class="spdx-col-3" onchange="displayIndex(this)"> + </select> + </div> + <core_rt:forEach items="${package.externalRefs}" var="externalRefsData" varStatus="loop"> + <div class="spdx-flex-row" data-index="${externalRefsData.index}"> + <div class="spdx-col-1 spdx-key">Category</div> + <div class="spdx-col-3 spdx-uppercase"> + <sw360:out value="${externalRefsData.referenceCategory}" /> + </div> + </div> + <div class="spdx-flex-row" data-index="${externalRefsData.index}"> + <div class="spdx-col-1 spdx-key">Type</div> + <div class="spdx-col-3"> + <sw360:out value="${externalRefsData.referenceType}" /> + </div> + </div> + <div class="spdx-flex-row" data-index="${externalRefsData.index}"> + <div class="spdx-col-1 spdx-key">Locator</div> + <div class="spdx-col-3"> + <sw360:out value="${externalRefsData.referenceLocator}" /> + </div> + </div> + <div class="spdx-flex-row" data-index="${externalRefsData.index}"> + <div class="spdx-col-1 spdx-key">7.22 Comment</div> + <p class="spdx-col-3" id="externalRefComment-${externalRefsData.index}"> + <sw360:out value="${externalRefsData.comment}" /> + </p> + </div> + </core_rt:forEach> + </core_rt:if> + </div> + </td> + </tr> + <tr class="spdx-full" data-index="${package.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">7.23 Package attribution text</div> + <p class="spdx-col-2 " id="attributionText"> + <core_rt:forEach items="${package.attributionText}" var="attributionTextData" varStatus="loop"> + <sw360:out value="${attributionTextData}"/><br> + </core_rt:forEach> + </p> + </td> + </tr> + </core_rt:forEach> + </tbody> +</table> + +<core_rt:set var="snippets" value="${spdxDocument.snippets}" /> +<table class="table label-value-table spdx-table spdx-full" id="SnippetInformation"> + <thead class="spdx-thead"> + <tr> + <th>9. Snippet Information</th> + </tr> + </thead> + <tbody class="section" data-size="10"> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1 spdx-label-index">Index</div> + <select id="snippetInfoSelect" class="spdx-col-2" onchange="displayIndex(this)"></select> + </td> + </tr> + <core_rt:forEach items="${snippets}" var="snippetsData" varStatus="loop"> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.1 Snippet SPDX identifier</div> + <div class="spdx-col-2"> + <sw360:out value="${snippetsData.SPDXID}" /> + </div> + </td> + </tr> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.2 Snippet from file SPDX identifier</div> + <div class="spdx-col-2"> + <sw360:out value="${snippetsData.snippetFromFile}" /> + </div> + </td> + </tr> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.3 & 9.4 Snippet ranges</div> + <div class="spdx-col-2 spdx-flex-col" id="snippetRanges-${snippetsData.index}"> + <core_rt:forEach items="${snippetsData.snippetRanges}" var="snippetRangeData" varStatus="loop"> + <div class="spdx-flex-row snippetRange-${snippetsData.index}" data-index="${snippetRangeData.index}"> + <div class="spdx-col-1 spdx-key"> + <sw360:out value="${snippetRangeData.rangeType}" /> + </div> + <div class="spdx-col-1 spdx-flex-row"> + <div class="spdx-col-1"> + <sw360:out value="${snippetRangeData.startPointer}" /> + </div> + <div class="spdx-col-1">~</div> + <div class="spdx-col-1"> + <sw360:out value="${snippetRangeData.endPointer}" /> + </div> + </div> + <div class="spdx-col-3"> + <sw360:out value="${snippetRangeData.reference}" /> + </div> + </div> + </core_rt:forEach> + </div> + </td> + </tr> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.5 Snippet concluded license</div> + <div class="spdx-col-2"> + <sw360:out value="${snippetsData.licenseConcluded}" /> + </div> + </td> + </tr> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.6 License information in snippet</div> + <p class="spdx-col-2 "> + <core_rt:forEach items="${snippetsData.licenseInfoInSnippets}" var="licenseInfoInSnippetData" + varStatus="loop"> + <sw360:out value="${licenseInfoInSnippetData} " /> <br> + </core_rt:forEach> + </p> + </td> + </tr> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.7 Snippet comments on license</div> + <p class="spdx-col-2 " id="snippetLicenseComments-${snippetsData.index}"> + <sw360:out value="${snippetsData.licenseComments}" stripNewlines="false" /> + </p> + </td> + </tr> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.8 Snippet copyright text</div> + <p class="spdx-col-2 " id="snippetCopyrightText-${snippetsData.index}"> + <sw360:out value="${snippetsData.copyrightText}" stripNewlines="false" /> + </p> + </td> + </tr> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.9 Snippet comment</div> + <p class="spdx-col-2 " id="snippetComment-${snippetsData.index}"> + <sw360:out value="${snippetsData.comment}" stripNewlines="false" /> + </p> + </td> + </tr> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.10 Snippet name</div> + <p class="spdx-col-2 "> + <sw360:out value="${snippetsData.name}" /> + </p> + </td> + </tr> + <tr data-index="${snippetsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">9.11 Snippet attribution text</div> + <p class="spdx-col-2 " id="snippetAttributionText-${snippetsData.index}"> + <sw360:out value="${snippetsData.snippetAttributionText}" stripNewlines="false" /> + </p> + </td> + </tr> + </core_rt:forEach> + </tbody> +</table> + +<core_rt:set var="otherLicensing" value="${spdxDocument.otherLicensingInformationDetecteds}" /> +<table class="table label-value-table spdx-table" id="OtherLicensingInformationDetected"> + <thead class="spdx-thead"> + <tr> + <th>10. Other Licensing Information Detected</th> + </tr> + </thead> + <tbody class="section" data-size="5"> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1 spdx-label-index">Index</div> + <select id="otherLicensingSelect" class="spdx-col-2" onchange="displayIndex(this)"></select> + </td> + </tr> + <core_rt:forEach items="${otherLicensing}" var="otherLicensingData" varStatus="loop"> + <tr data-index="${otherLicensingData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">10.1 License identifier</div> + <div class="spdx-col-2"> + <sw360:out value="${otherLicensingData.licenseId}" /> + </div> + </td> + </tr> + <tr data-index="${otherLicensingData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">10.2 Extracted text</div> + <p class="spdx-col-2 " id="extractedText-${otherLicensingData.index}"> + <sw360:out value="${otherLicensingData.extractedText}" stripNewlines="false" /> + </p> + </td> + </tr> + <tr data-index="${otherLicensingData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">10.3 License name</div> + <div class="spdx-col-2"> + <sw360:out value="${otherLicensingData.licenseName}" /> + </div> + </td> + </tr> + <tr class="spdx-full" data-index="${otherLicensingData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">10.4 License cross reference</div> + <p class="spdx-col-2 " id="licenseCrossRefs-${otherLicensingData.index}"> + <core_rt:forEach items="${otherLicensingData.licenseCrossRefs}" var="licenseCrossRefsData" varStatus="loop"> + <sw360:out value="${licenseCrossRefsData}"/><br> + </core_rt:forEach> + </p> + </td> + </tr> + <tr data-index="${otherLicensingData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">10.5 License comment</div> + <p class="spdx-col-2 " id="otherLicenseComment-${otherLicensingData.index}"> + <sw360:out value="${otherLicensingData.licenseComment}" stripNewlines="false" /> + </p> + </td> + </tr> + </core_rt:forEach> + </tbody> +</table> + +<core_rt:set var="relationships" value="${spdxDocument.relationships}" /> +<core_rt:forEach items="${spdxPackageInfo}" var="spdxPackage" varStatus="loop"> + <core_rt:if test="${spdxPackage.index eq 0}"> + <core_rt:set var="packageRelationships" value="${spdxPackage.relationships}" /> + </core_rt:if> +</core_rt:forEach> +<table class="table label-value-table spdx-table spdx-full" id="RelationshipsbetweenSPDXElements"> + <thead class="spdx-thead"> + <tr> + <th>11. Relationship between SPDX Elements Information</th> + </tr> + </thead> + <tbody class="section" data-size="3"> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1 spdx-label-index">Source</div> + <select id="relationshipSourceSelect" class="spdx-col-2" onchange="changeRelationshipSource(this)"> + <option>SPDX Document</option> + <option>Package</option> + </select> + </td> + </tr> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1 spdx-label-index">Index</div> + <select id="relationshipSelect" class="spdx-col-2" onchange="displayRelationshipIndex(this)"></select> + </td> + </tr> + <core_rt:forEach items="${relationships}" var="relationshipsData" varStatus="loop"> + <tr class="relationship-document" data-index="${relationshipsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">11.1 Relationship</div> + <div class="spdx-col-2 spdx-flex-col"> + <div class="spdx-flex-row"> + <div class="spdx-col-1"> + <sw360:out value="${relationshipsData.spdxElementId}" /> + </div> + <div class="spdx-col-1 spdx-flex-row"> + <sw360:out + value="${relationshipsData.relationshipType.replace('relationshipType_', '')}" /> + </div> + <div class="spdx-col-3"> + <sw360:out value="${relationshipsData.relatedSpdxElement}" /> + </div> + </div> + </div> + </td> + </tr> + <tr class="relationship-document" data-index="${relationshipsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">11.2 Relationship comment</div> + <p class="spdx-col-2 " id="relationshipComment-${relationshipsData.index}"> + <sw360:out value="${relationshipsData.relationshipComment}" stripNewlines="false" /> + </p> + </td> + </tr> + </core_rt:forEach> + + + <core_rt:forEach items="${packageRelationships}" var="relationshipsData" varStatus="loop"> + <tr class="relationship-package" data-index="${relationshipsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">11.1 Relationship</div> + <div class="spdx-col-2 spdx-flex-col"> + <div class="spdx-flex-row"> + <div class="spdx-col-1"> + <sw360:out value="${relationshipsData.spdxElementId}" /> + </div> + <div class="spdx-col-1 spdx-flex-row"> + <sw360:out + value="${relationshipsData.relationshipType.replace('relationshipType_', '')}" /> + </div> + <div class="spdx-col-3"> + <sw360:out value="${relationshipsData.relatedSpdxElement}" /> + </div> + </div> + </div> + </td> + </tr> + <tr class="relationship-package" data-index="${relationshipsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">11.2 Relationship comment</div> + <p class="spdx-col-2 " id="relationshipComment-${relationshipsData.index}"> + <sw360:out value="${relationshipsData.relationshipComment}" stripNewlines="false" /> + </p> + </td> + </tr> + </core_rt:forEach> + </tbody> +</table> + +<core_rt:set var="documentAnnotations" value="${spdxDocument.annotations}" /> +<core_rt:forEach items="${spdxPackageInfo}" var="spdxPackage" varStatus="loop"> + <core_rt:if test="${spdxPackage.index eq 0}"> + <core_rt:set var="packageAnnotations" value="${spdxPackage.annotations}" /> + </core_rt:if> +</core_rt:forEach> +<table class="table label-value-table spdx-table spdx-full" id="Annotations"> + <thead class="spdx-thead"> + <tr> + <th>12. Annotation Information</th> + </tr> + </thead> + <tbody class="section" data-size="5"> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1 spdx-label-index">Source</div> + <select id="annotationSourceSelect" class="spdx-col-2" onchange="changeAnnotationSource(this)"> + <option>SPDX Document</option> + <option>Package</option> + </select> + </td> + </tr> + <tr> + <td class="spdx-flex-row"> + <div class="spdx-col-1 spdx-label-index">Index</div> + <select id="annotationSelect" class="spdx-col-2" onchange="displayAnnotationIndex(this)"></select> + </td> + </tr> + <core_rt:forEach items="${documentAnnotations}" var="annotationsData" varStatus="loop"> + <tr class="annotation-document" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.1 Annotator</div> + <p class="spdx-col-2 "> + <sw360:out value="${annotationsData.annotator}" /> + </p> + </div> + </td> + </tr> + <tr class="annotation-document" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.2 Annotation date</div> + <p class="spdx-col-2 " id="annotation-document-date-${loop.count}"> + <sw360:out value="${annotationsData.annotationDate}" /> + </p> + </td> + </tr> + <tr class="annotation-document" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.3 Annotation type</div> + <div class="spdx-col-2"> + <div class="spdx-flex-row"> + <div class="spdx-col-3"> + <sw360:out value="${annotationsData.annotationType}" /> + </div> + </div> + </div> + </td> + </tr> + <tr class="annotation-document" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.4 SPDX identifier reference</div> + <div class="spdx-col-2"> + <sw360:out value="${annotationsData.spdxIdRef}" /> + </div> + </td> + </tr> + <tr class="annotation-document" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.5 Annotation comment</div> + <p class="spdx-col-2 " id="documentAnnotationComment-${annotationsData.index}"> + <sw360:out value="${annotationsData.annotationComment}" stripNewlines="false" /> + </p> + </td> + </tr> + </core_rt:forEach> + <core_rt:forEach items="${packageAnnotations}" var="annotationsData" varStatus="loop"> + <tr class="annotation-package" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.1 Annotator</div> + <p class="spdx-col-2 "> + <sw360:out value="${annotationsData.annotator}" /> + </p> + </div> + </td> + </tr> + <tr class="annotation-package" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.2 Annotation date</div> + <p class="spdx-col-2 " id="annotation-package-date-${loop.count}"> + <sw360:out value="${annotationsData.annotationDate}" /> + </p> + </td> + </tr> + <tr class="annotation-package" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.3 Annotation type</div> + <div class="spdx-col-2"> + <div class="spdx-flex-row"> + <div class="spdx-col-3"> + <sw360:out value="${annotationsData.annotationType}" /> + </div> + </div> + </div> + </td> + </tr> + <tr class="annotation-package" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.4 SPDX identifier reference</div> + <div class="spdx-col-2"> + <sw360:out value="${annotationsData.spdxIdRef}" /> + </div> + </td> + </tr> + <tr class="annotation-package" data-index="${annotationsData.index}"> + <td class="spdx-flex-row"> + <div class="spdx-col-1">12.5 Annotation comment</div> + <p class="spdx-col-2 " id="packageAnnotationComment-${annotationsData.index}"> + <sw360:out value="${annotationsData.annotationComment}" stripNewlines="false" /> + </p> + </td> + </tr> + </core_rt:forEach> + </tbody> +</table> + +<style> + .spdx-col-1 { + flex: 1; + } + + .spdx-col-2 { + flex: 2; + } + + .spdx-col-3 { + flex: 3; + max-width: 60%; + } + + .spdx-flex-row { + display: flex; + flex-direction: row; + overflow: auto; + ; + } + + .spdx-flex-col { + display: flex; + flex-direction: column; + } + + /*. {*/ + /* margin-bottom: 0;*/ + /*}*/ + + .spdx-key { + font-weight: bold; + } + + .spdx-label-index { + text-decoration: underline; + } + + .spdx-table td:first-child { + width: 100% !important; + } + + .spdx-thead { + cursor: pointer; + } + + .spdx-uppercase { + text-transform: uppercase; + } +</style> + +<script type="text/javascript"> + function sortElements(parent, elements) { + for (let i = 0; i < elements.length; i++) { + $(parent).find('[data-index=' + i + ']').appendTo($(parent)); + } + } + + function readArray(tag) { + let arr = $(tag).text(); + arr = arr.replaceAll('\t', '').split('\n').map(str => str.trim()).filter(function(str) { + return str != ''; + }).sort(); + return arr; + } + + function fillArray(tag, value) { + $(tag).text(''); + + for (let i = 0; i< value.length; i++) { + let temp = value[i].replaceAll('&','&').replaceAll('<', '<').replaceAll('>', '>'); + $(tag).append(temp); + $(tag).append('<br>'); + } + } + + function formatArrayParagraph(tag) { + fillArray(tag, readArray(tag)); + } + + function dynamicSort(property, type) { + var sortOrder = 1; + + if(property[0] === "-") { + sortOrder = -1; + + property = property.substr(1); + } + + return function (a,b) { + var result; + + switch (type) { + case 'int': + result = (parseInt(a[property]) < parseInt(b[property])) ? -1 : (parseInt(a[property]) > (b[property])) ? 1 : 0; + break; + case 'string': + default: + result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0; + } + + return result * sortOrder; + } + } + + $(function () { + let spdxDocumentObj = jQuery.parseJSON(JSON.stringify(${ spdxDocumentJson })); + let documentCreationInformationObj = jQuery.parseJSON(JSON.stringify(${ documentCreationInfoJson })); + let packagesInformationObj = jQuery.parseJSON(JSON.stringify(${ packageInfoJson })); + packagesInformationObj.sort(dynamicSort('index', 'int')); + let packageInformationObj = packagesInformationObj[0]; + formatArrayParagraph('#excludedFiles'); + formatArrayParagraph('#licenseInfoFromFile'); + formatArrayParagraph('#attributionText'); + formatArrayParagraph('#creatorComment'); + + fillArray('#creatorComment', documentCreationInformationObj.creatorComment.split('\n')); + fillArray('#documentComment', documentCreationInformationObj.documentComment.split('\n')); + + fillArray('#sourceInfo', packageInformationObj.sourceInfo.split('\n')); + fillArray('#licenseComments', packageInformationObj.licenseComments.split('\n')); + fillArray('#copyrightText', packageInformationObj.copyrightText.split('\n')); + fillArray('#summary', packageInformationObj.summary.split('\n')); + fillArray('#description', packageInformationObj.description.split('\n')); + fillArray('#packageComment', packageInformationObj.packageComment.split('\n')); + + for (let i = 0; i < packageInformationObj.externalRefs.length; i++) { + for (let j = 0; j < packageInformationObj.externalRefs.length; j++) { + if (packageInformationObj.externalRefs[j].index == i) { + fillArray('#externalRefComment-' + i, packageInformationObj.externalRefs[j].comment.split('\n')); + } + } + } + + for (let i = 0; i < spdxDocumentObj.snippets.length; i++) { + for (let j = 0; j < spdxDocumentObj.snippets.length; j++) { + if (spdxDocumentObj.snippets[j].index == i) { + fillArray('#snippetLicenseComments-' + i, spdxDocumentObj.snippets[j].licenseComments.split('\n')); + fillArray('#snippetCopyrightText-' + i, spdxDocumentObj.snippets[j].copyrightText.split('\n')); + fillArray('#snippetComment-' + i, spdxDocumentObj.snippets[j].comment.split('\n')); + fillArray('#snippetAttributionText-' + i, spdxDocumentObj.snippets[j].snippetAttributionText.split('\n')); + } + } + } + + for (let i = 0; i < spdxDocumentObj.otherLicensingInformationDetecteds.length; i++) { + for (let j = 0; j < spdxDocumentObj.otherLicensingInformationDetecteds.length; j++) { + if (spdxDocumentObj.otherLicensingInformationDetecteds[j].index == i) { + fillArray('#extractedText-' + i, spdxDocumentObj.otherLicensingInformationDetecteds[j].extractedText.split('\n')); + fillArray('#otherLicenseComment-' + i, spdxDocumentObj.otherLicensingInformationDetecteds[j].licenseComment.split('\n')); + } + } + } + + for (let i = 0; i < spdxDocumentObj.relationships.length; i++) { + for (let j = 0; j < spdxDocumentObj.relationships.length; j++) { + if (spdxDocumentObj.relationships[j].index == i) { + fillArray('#relationshipComment-' + i, spdxDocumentObj.relationships[j].relationshipComment.split('\n')); + } + } + } + + for (let i = 0; i < spdxDocumentObj.annotations.length; i++) { + for (let j = 0; j < spdxDocumentObj.annotations.length; j++) { + if (spdxDocumentObj.annotations[j].index == i) { + fillArray('#documentAnnotationComment-' + i, spdxDocumentObj.annotations[j].annotationComment.split('\n')); + } + } + } + + for (let i = 0; i < packageInformationObj.annotations.length; i++) { + for (let j = 0; j < packageInformationObj.annotations.length; j++) { + if (packageInformationObj.annotations[j].index == i) { + fillArray('#packageAnnotationComment-' + i, packageInformationObj.annotations[j].annotationComment.split('\n')); + } + } + } + + for (let i = 0; i < $('#otherLicensingSelect').find('option').last().text(); i++) { + formatArrayParagraph('#licenseCrossRefs-' + i); + } + + $('#spdxFullMode').on('click', function (e) { + e.preventDefault(); + + $(this).addClass('btn-info'); + $(this).removeClass('btn-secondary'); + + $('#spdxLiteMode').addClass('btn-secondary'); + $('#spdxLiteMode').removeClass('btn-info'); + + $('.spdx-full').css('display', ''); + }); + + $('#spdxLiteMode').on('click', function (e) { + e.preventDefault(); + + $(this).addClass('btn-info'); + $(this).removeClass('btn-secondary'); + + $('#spdxFullMode').addClass('btn-secondary'); + $('#spdxFullMode').removeClass('btn-info'); + + $('.spdx-full').css('display', 'none'); + }); + + // Expand/collapse section when click on the header + $('thead').on('click', function () { + if ($(this).next().css('display') == 'none') { + $(this).next().css('display', ''); + } else { + $(this).next().css('display', 'none'); + } + }); + + $('.spdx-table select').each(function () { + if ($(this).children().length == 0) { + $(this).attr('disabled', 'true'); + } + }); + + sortElements('#creators', $('.creator').toArray()); + sortElements('#checksums', $('.checksum').toArray()); + let snippetIndex = $('#snippetInfoSelect').val() - 1; + sortElements('#snippetRanges-' + snippetIndex, $('.snippetRange-' + snippetIndex).toArray()); + + $('.spdx-table select').change(); + }); + + function generateSelecterOption(selectId, length) { + $('#' + selectId).find('option').remove(); + for (var i = 1; i <= length; i++) { + var option = document.createElement("option"); + option.text = i; + $('#' + selectId).append(option); + } + if (length == 0) { + $('#' + selectId).attr('disabled', 'disabled'); + } else { + $('#' + selectId).removeAttr('disabled', 'disabled'); + } + } + generateSelecterOption('snippetInfoSelect', "${snippets.size()}"); + generateSelecterOption('otherLicensingSelect', "${otherLicensing.size()}"); + generateSelecterOption('relationshipSelect', "${relationships.size()}"); + generateSelecterOption('annotationSelect', "${documentAnnotations.size()}"); + generateSelecterOption('externalDocumentRefs', "${spdxDocumentCreationInfo.externalDocumentRefs.size()}"); + generateSelecterOption('packageInfoSelect', "${spdxPackageInfo.size()}"); + + var packageIndex = $('#packageInfoSelect')[0].selectedIndex; + var packagesInformationObj = jQuery.parseJSON(JSON.stringify(${ packageInfoJson })); + packagesInformationObj.sort(dynamicSort('index', 'int')); + generateSelecterOption('externalReferenceSelect'+packagesInformationObj[packageIndex].externalRefs.length, packagesInformationObj[packageIndex].externalRefs.length); + + function displayIndex(el) { + var index = $(el).val(); + var section = $(el).closest('.section'); + var size = section.data()['size']; + + section.children().css('display', 'none'); + section.children().eq(0).css('display', ''); + + section.find('[data-index=' + (index - 1).toString() + ']').css('display', ''); + + if ($(el).attr('id') == 'snippetInfoSelect') { + sortElements('#snippetRanges-' + (index - 1), $('.snippetRange-' + (index - 1)).toArray()); + } + } + + function changePackageIndex(el) { + if ($(el).attr('id') == 'packageInfoSelect') { + var index = $(el).val(); + var section = $(el).closest('.section'); + var size = section.data()['size']; + + section.children().css('display', 'none'); + section.children().eq(0).css('display', ''); + + section.find('[data-index=' + (index - 1).toString() + ']').css('display', ''); + + var packagesInformationObj = jQuery.parseJSON(JSON.stringify(${ packageInfoJson })); + packagesInformationObj.sort(dynamicSort('index', 'int')); + let packageIndex = $('#packageInfoSelect')[0].selectedIndex; + generateSelecterOption('externalReferenceSelect'+(index-1), packagesInformationObj[packageIndex].externalRefs.length); + $('#externalReferenceSelect'+(index-1)).change(); + } + } + + function displayAnnotationIndex(el) { + var index = $(el).val(); + var section = $(el).closest('.section'); + var size = section.data()['size']; + + section.children().css('display', 'none'); + section.children().eq(0).css('display', ''); + section.children().eq(1).css('display', ''); + + if ($('#annotationSourceSelect').val() == 'SPDX Document') { + $('.annotation-document[data-index=' + (index - 1).toString() + ']').css('display', 'table-row'); + } else { + $('.annotation-package[data-index=' + (index - 1).toString() + ']').css('display', 'table-row'); + } + } + + function changeAnnotationSource(el) { + if ($('#annotationSourceSelect').val() == 'Package') { + generateSelecterOption('annotationSelect', '${packageAnnotations.size()}'); + } else { + generateSelecterOption('annotationSelect', '${documentAnnotations.size()}'); + } + $('#annotationSelect').change(); + } + + <core_rt:forEach items="${documentAnnotations}" var="documentAnnotationData" varStatus="loop"> + displayDateTime('annotation-document-date-${loop.count}', '${documentAnnotationData.annotationDate}'); + </core_rt:forEach> + + displayDateTime('createdDateTime', "${spdxDocumentCreationInfo.created}"); + + <core_rt:forEach items="${packageAnnotations}" var="packageAnnotationData" varStatus="loop"> + displayDateTime('annotation-package-date-${loop.count}', '${packageAnnotationData.annotationDate}'); + </core_rt:forEach> + + function displayDateTime(id, value) { + if (value == '') { + return; + } + + let timeStamp = Date.parse(value); + + let date = new Date(timeStamp); + + let localTimeStamp = timeStamp - date.getTimezoneOffset(); + + let localDate = new Date(localTimeStamp); + + let dateTime = localDate.getFullYear() + + '-' + (localDate.getMonth() + 1).toString().padStart(2, '0') + + '-' + localDate.getDate().toString().padStart(2, '0') + + ' ' + date.getHours().toString().padStart(2, '0') + + ':' + date.getMinutes().toString().padStart(2, '0') + + ':' + date.getSeconds().toString().padStart(2, '0'); + + document.getElementById(id).innerHTML = dateTime; + } + + function changeRelationshipSource(el) { + if ($('#relationshipSourceSelect').val() == 'Package') { + generateSelecterOption('relationshipSelect', '${packageRelationships.size()}'); + } else { + generateSelecterOption('relationshipSelect', '${relationships.size()}'); + } + $('#relationshipSelect').change(); + } + + function displayRelationshipIndex(el) { + var index = $(el).val(); + var section = $(el).closest('.section'); + var size = section.data()['size']; + + section.children().css('display', 'none'); + section.children().eq(0).css('display', ''); + section.children().eq(1).css('display', ''); + + if ($('#relationshipSourceSelect').val() == 'SPDX Document') { + $('.relationship-document[data-index=' + (index - 1).toString() + ']').css('display', 'table-row'); + } else { + $('.relationship-package[data-index=' + (index - 1).toString() + ']').css('display', 'table-row'); + } + } +</script> diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/view.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/view.jsp index dd63106bad..a62d131f37 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/view.jsp +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/view.jsp @@ -167,23 +167,23 @@ <div class="col"> <div class="row portlet-toolbar"> <div class="col-auto"> - <div class="btn-toolbar" role="toolbar"> + <div class="btn-toolbar" role="toolbar"> <div class="btn-group" role="group"> - <button type="button" class="btn btn-primary" onclick="window.location.href='<%=addComponentURL%>'"><liferay-ui:message key="add.component" /></button> - <button type="button" class="btn btn-secondary" data-action="import-spdx-bom"><liferay-ui:message key="import.spdx.bom" /></button> - </div> - <div id="btnExportGroup" class="btn-group" role="group"> - <button id="btnExport" type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - <liferay-ui:message key="export.spreadsheet" /> - <clay:icon symbol="caret-bottom" /> - </button> - <div class="dropdown-menu" aria-labelledby="btnExport"> - <a class="dropdown-item" href="#" data-type="componentOnly"><liferay-ui:message key="components.only" /></a> - <a class="dropdown-item" href="#" data-type="componentWithReleases"><liferay-ui:message key="components.with.releases" /></a> - </div> - </div> - </div> - </div> + <button type="button" class="btn btn-primary" onclick="window.location.href='<%=addComponentURL%>'"><liferay-ui:message key="add.component" /></button> + <button type="button" class="btn btn-secondary" id="import-spdx-bom" data-action="import-spdx-bom"><liferay-ui:message key="import.spdx.bom" /></button> + </div> + <div id="btnExportGroup" class="btn-group" role="group"> + <button id="btnExport" type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <liferay-ui:message key="export.spreadsheet" /> + <clay:icon symbol="caret-bottom" /> + </button> + <div class="dropdown-menu" aria-labelledby="btnExport"> + <a class="dropdown-item" href="#" data-type="componentOnly"><liferay-ui:message key="components.only" /></a> + <a class="dropdown-item" href="#" data-type="componentWithReleases"><liferay-ui:message key="components.with.releases" /></a> + </div> + </div> + </div> + </div> <div class="col portlet-title text-truncate" title="<liferay-ui:message key="components" /> (${totalRows})"> <liferay-ui:message key="components" />(<span id="componentCounter">${totalRows}</span>) </div> @@ -204,7 +204,7 @@ <%--for javascript library loading --%> <%@ include file="/html/utils/includes/requirejs.jspf" %> -<%@ include file="/html/utils/includes/importBom.jspf" %> +<%@ include file="/html/utils/includes/importBomForComponent.jspf" %> <script> var renderCallback = function () { }; diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/documentcreationinfo/delete.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/documentcreationinfo/delete.jsp new file mode 100644 index 0000000000..bf440c1188 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/documentcreationinfo/delete.jsp @@ -0,0 +1,93 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + --%> +<%@include file="/html/init.jsp"%> +<%-- the following is needed by liferay to display error messages--%> +<%@include file="/html/utils/includes/errorKeyToMessage.jspf"%> + +<portlet:defineObjects /> +<liferay-theme:defineObjects /> + +<%@ page import="com.liferay.portal.kernel.portlet.PortletURLFactoryUtil" %> +<%@ page import="org.eclipse.sw360.datahandler.thrift.moderation.DocumentType" %> +<%@ page import="javax.portlet.PortletRequest" %> +<%@ page import="org.eclipse.sw360.portal.common.PortalConstants" %> + +<jsp:useBean id="moderationRequest" class="org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest" scope="request"/> +<jsp:useBean id="actual_DocumentCreationInfo" class="org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation" scope="request" /> + +<core_rt:set var="spdxDocumentCreationInfo" value="${actual_DocumentCreationInfo}" scope="request"/> +<div class="container" id="moderation-request-merge" data-document-type="<%=DocumentType.SPDX_DOCUMENT_CREATION_INFO%>"> + + <core_rt:set var="moderationTitle" value="Delete ${sw360:printDocumentCreationInfoName(spdxDocumentCreationInfo)}" scope="request" /> + <%@include file="/html/moderation/includes/moderationHeader.jspf"%> + + <div class="row"> + <div class="col"> + <div id="moderation-wizard" class="accordion"> + <div class="card"> + <div id="moderation-header-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-header" aria-expanded="true" aria-controls="moderation-header"> + <liferay-ui:message key="moderation.request.information" /> + </button> + </h2> + </div> + <div id="moderation-header" class="collapse show" aria-labelledby="moderation-header-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/moderation/includes/moderationInfo.jspf"%> + </div> + </div> + </div> + <core_rt:if test="${sw360:isOpenModerationRequest(moderationRequest)}"> + <div class="card"> + <div id="moderation-changes-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-changes" aria-expanded="false" aria-controls="moderation-changes"> + <liferay-ui:message key="proposed.changes" /> + </button> + </h2> + </div> + <div id="moderation-changes" class="collapse p-3" aria-labelledby="moderation-changes-heading" data-parent="#moderation-wizard"> + <div class="alert alert-danger mb-0"> + The SPDX Document <sw360:out value='${sw360:printDocumentCreationInfoName(spdxDocumentCreationInfo)}'/> is requested to be deleted. + </div> + </div> + </div> + </core_rt:if> + <div class="card"> + <div id="current-document-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#current-document" aria-expanded="false" aria-controls="current-document"> + <liferay-ui:message key="current.document.creation.information" /> + </button> + </h2> + </div> + <div id="current-document" class="collapse" aria-labelledby="current-document-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/utils/includes/requirejs.jspf" %> + <%@include file="/html/components/includes/releases/spdx/view.jsp"%> + </div> + </div> + </div> + </div> + </div> + </div> +</div> + +<script> + $('#spdxFullMode').hide(); + $('#spdxLiteMode').hide(); + $('#PackageInformation').hide(); + $('#SnippetInformation').hide(); + $('#OtherLicensingInformationDetected').hide(); + $('#RelationshipsbetweenSPDXElements').hide(); + $('#Annotations').hide(); +</script> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/documentcreationinfo/merge.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/documentcreationinfo/merge.jsp new file mode 100644 index 0000000000..853fa6df03 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/documentcreationinfo/merge.jsp @@ -0,0 +1,102 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + --%> +<%@include file="/html/init.jsp"%> +<%-- the following is needed by liferay to display error messages--%> +<%@include file="/html/utils/includes/errorKeyToMessage.jspf"%> + +<portlet:defineObjects /> +<liferay-theme:defineObjects /> + +<%@ page import="com.liferay.portal.kernel.portlet.PortletURLFactoryUtil" %> +<%@ page import="org.eclipse.sw360.datahandler.thrift.moderation.DocumentType" %> +<%@ page import="javax.portlet.PortletRequest" %> +<%@ page import="org.eclipse.sw360.portal.common.PortalConstants" %> + +<jsp:useBean id="moderationRequest" class="org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest" scope="request"/> +<jsp:useBean id="actual_DocumentCreationInfo" class="org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation" scope="request" /> +<jsp:useBean id="defaultLicenseInfoHeaderText" class="java.lang.String" scope="request" /> + +<core_rt:set var="spdxDocumentCreationInfo" value="${actual_DocumentCreationInfo}" scope="request"/> + +<div class="container" id="moderation-request-merge" data-document-type="<%=DocumentType.SPDX_DOCUMENT_CREATION_INFO%>"> + + <core_rt:set var="moderationTitle" value="Change ${sw360:printDocumentCreationInfoName(spdxDocumentCreationInfo)}" scope="request" /> + <%@include file="/html/moderation/includes/moderationHeader.jspf"%> + + <div class="row"> + <div class="col"> + <div id="moderation-wizard" class="accordion"> + <div class="card"> + <div id="moderation-header-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-header" aria-expanded="true" aria-controls="moderation-header"> + <liferay-ui:message key="moderation.request.information" /> + </button> + </h2> + </div> + <div id="moderation-header" class="collapse show" aria-labelledby="moderation-header-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/moderation/includes/moderationInfo.jspf"%> + </div> + </div> + </div> + <core_rt:if test="${sw360:isOpenModerationRequest(moderationRequest)}"> + <div class="card"> + <div id="moderation-changes-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-changes" aria-expanded="false" aria-controls="moderation-changes"> + <liferay-ui:message key="proposed.changes" /> + </button> + </h2> + </div> + <div id="moderation-changes" class="collapse" aria-labelledby="moderation-changes-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <h4 class="mt-2"><liferay-ui:message key="basic.fields" /></h4> + <sw360:DisplayDocumentCreationInfoChanges + actual="${actual_DocumentCreationInfo}" + additions="${moderationRequest.documentCreationInfoAdditions}" + deletions="${moderationRequest.documentCreationInfoDeletions}" + idPrefix="basicFields" + tableClasses="table table-bordered" + /> + </div> + </div> + </div> + </core_rt:if> + <div class="card"> + <div id="current-document-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#current-document" aria-expanded="false" aria-controls="current-document"> + <liferay-ui:message key="current.document.creation.information" /> + </button> + </h2> + </div> + <div id="current-document" class="collapse" aria-labelledby="current-document-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/utils/includes/requirejs.jspf" %> + <%@include file="/html/components/includes/releases/spdx/view.jsp"%> + </div> + </div> + </div> + </div> + </div> + </div> +</div> + +<script> + $('#spdxFullMode').hide(); + $('#spdxLiteMode').hide(); + $('#PackageInformation').hide(); + $('#SnippetInformation').hide(); + $('#OtherLicensingInformationDetected').hide(); + $('#RelationshipsbetweenSPDXElements').hide(); + $('#Annotations').hide(); +</script> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/packageinfo/delete.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/packageinfo/delete.jsp new file mode 100644 index 0000000000..9e7e5a6aec --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/packageinfo/delete.jsp @@ -0,0 +1,96 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + --%> +<%@include file="/html/init.jsp"%> +<%-- the following is needed by liferay to display error messages--%> +<%@include file="/html/utils/includes/errorKeyToMessage.jspf"%> + +<portlet:defineObjects /> +<liferay-theme:defineObjects /> + +<%@ page import="com.liferay.portal.kernel.portlet.PortletURLFactoryUtil" %> +<%@ page import="org.eclipse.sw360.datahandler.thrift.moderation.DocumentType" %> +<%@ page import="javax.portlet.PortletRequest" %> +<%@ page import="org.eclipse.sw360.portal.common.PortalConstants" %> + +<jsp:useBean id="moderationRequest" class="org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest" scope="request"/> +<jsp:useBean id="actual_PackageInfo" class="org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation" scope="request" /> + +<core_rt:set var="packageInfo" value="${actual_PackageInfo}" scope="request"/> +<div class="container" id="moderation-request-merge" data-document-type="<%=DocumentType.SPDX_PACKAGE_INFO%>"> + + <core_rt:set var="moderationTitle" value="Delete ${sw360:printPackageInfoName(packageInfo)}" scope="request" /> + <%@include file="/html/moderation/includes/moderationHeader.jspf"%> + + <div class="row"> + <div class="col"> + <div id="moderation-wizard" class="accordion"> + <div class="card"> + <div id="moderation-header-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-header" aria-expanded="true" aria-controls="moderation-header"> + <liferay-ui:message key="moderation.request.information" /> + </button> + </h2> + </div> + <div id="moderation-header" class="collapse show" aria-labelledby="moderation-header-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/moderation/includes/moderationInfo.jspf"%> + </div> + </div> + </div> + <core_rt:if test="${sw360:isOpenModerationRequest(moderationRequest)}"> + <div class="card"> + <div id="moderation-changes-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-changes" aria-expanded="false" aria-controls="moderation-changes"> + <liferay-ui:message key="proposed.changes" /> + </button> + </h2> + </div> + <div id="moderation-changes" class="collapse p-3" aria-labelledby="moderation-changes-heading" data-parent="#moderation-wizard"> + <div class="alert alert-danger mb-0"> + The SPDX Document <sw360:out value='${sw360:printPackageInfoName(packageInfo)}'/> is requested to be deleted. + </div> + </div> + </div> + </core_rt:if> + <div class="card"> + <div id="current-document-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#current-document" aria-expanded="false" aria-controls="current-document"> + <liferay-ui:message key="current.package.information" /> + </button> + </h2> + </div> + <div id="current-document" class="collapse" aria-labelledby="current-document-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <core_rt:set var="package" value="${actual_PackageInfo}" /> + <core_rt:set var="packageAnnotations" value="${actual_PackageInfo.annotations}" /> + <%@include file="/html/utils/includes/requirejs.jspf" %> + <%@include file="/html/components/includes/releases/spdx/view.jsp"%> + </div> + </div> + </div> + </div> + </div> + </div> +</div> + +<script> + $('#spdxFullMode').hide(); + $('#spdxLiteMode').hide(); + $('#DocumentCreationInformation').hide(); + $('#SnippetInformation').hide(); + $('#OtherLicensingInformationDetected').hide(); + $('#RelationshipsbetweenSPDXElements').hide(); + $('#annotationSourceSelect').val('Package'); + changeAnnotationSource($('#annotationSourceSelect')); +</script> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/packageinfo/merge.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/packageinfo/merge.jsp new file mode 100644 index 0000000000..dcdd8457e6 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/packageinfo/merge.jsp @@ -0,0 +1,105 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + --%> +<%@include file="/html/init.jsp"%> +<%-- the following is needed by liferay to display error messages--%> +<%@include file="/html/utils/includes/errorKeyToMessage.jspf"%> + +<portlet:defineObjects /> +<liferay-theme:defineObjects /> + +<%@ page import="com.liferay.portal.kernel.portlet.PortletURLFactoryUtil" %> +<%@ page import="org.eclipse.sw360.datahandler.thrift.moderation.DocumentType" %> +<%@ page import="javax.portlet.PortletRequest" %> +<%@ page import="org.eclipse.sw360.portal.common.PortalConstants" %> + +<jsp:useBean id="moderationRequest" class="org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest" scope="request"/> +<jsp:useBean id="actual_PackageInfo" class="org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation" scope="request" /> +<jsp:useBean id="defaultLicenseInfoHeaderText" class="java.lang.String" scope="request" /> + +<core_rt:set var="packageInfo" value="${actual_PackageInfo}" scope="request"/> + +<div class="container" id="moderation-request-merge" data-document-type="<%=DocumentType.SPDX_PACKAGE_INFO%>"> + + <core_rt:set var="moderationTitle" value="Change ${sw360:printPackageInfoName(packageInfo)}" scope="request" /> + <%@include file="/html/moderation/includes/moderationHeader.jspf"%> + + <div class="row"> + <div class="col"> + <div id="moderation-wizard" class="accordion"> + <div class="card"> + <div id="moderation-header-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-header" aria-expanded="true" aria-controls="moderation-header"> + <liferay-ui:message key="moderation.request.information" /> + </button> + </h2> + </div> + <div id="moderation-header" class="collapse show" aria-labelledby="moderation-header-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/moderation/includes/moderationInfo.jspf"%> + </div> + </div> + </div> + <core_rt:if test="${sw360:isOpenModerationRequest(moderationRequest)}"> + <div class="card"> + <div id="moderation-changes-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-changes" aria-expanded="false" aria-controls="moderation-changes"> + <liferay-ui:message key="proposed.changes" /> + </button> + </h2> + </div> + <div id="moderation-changes" class="collapse" aria-labelledby="moderation-changes-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <h4 class="mt-2"><liferay-ui:message key="basic.fields" /></h4> + <sw360:DisplayPackageInfoChanges + actual="${actual_PackageInfo}" + additions="${moderationRequest.packageInfoAdditions}" + deletions="${moderationRequest.packageInfoDeletions}" + idPrefix="basicFields" + tableClasses="table table-bordered" + /> + </div> + </div> + </div> + </core_rt:if> + <div class="card"> + <div id="current-document-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#current-document" aria-expanded="false" aria-controls="current-document"> + <liferay-ui:message key="current.package.information" /> + </button> + </h2> + </div> + <div id="current-document" class="collapse" aria-labelledby="current-document-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <core_rt:set var="package" value="${actual_PackageInfo}" /> + <core_rt:set var="packageAnnotations" value="${actual_PackageInfo.annotations}" /> + <%@include file="/html/utils/includes/requirejs.jspf" %> + <%@include file="/html/components/includes/releases/spdx/view.jsp"%> + </div> + </div> + </div> + </div> + </div> + </div> +</div> + +<script> + $('#spdxFullMode').hide(); + $('#spdxLiteMode').hide(); + $('#DocumentCreationInformation').hide(); + $('#SnippetInformation').hide(); + $('#OtherLicensingInformationDetected').hide(); + $('#RelationshipsbetweenSPDXElements').hide(); + $('#annotationSourceSelect').val('Package'); + changeAnnotationSource($('#annotationSourceSelect')); +</script> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/spdxdocument/delete.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/spdxdocument/delete.jsp new file mode 100644 index 0000000000..ecd13d4e33 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/spdxdocument/delete.jsp @@ -0,0 +1,92 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + --%> +<%@include file="/html/init.jsp"%> +<%-- the following is needed by liferay to display error messages--%> +<%@include file="/html/utils/includes/errorKeyToMessage.jspf"%> + +<portlet:defineObjects /> +<liferay-theme:defineObjects /> + +<%@ page import="com.liferay.portal.kernel.portlet.PortletURLFactoryUtil" %> +<%@ page import="org.eclipse.sw360.datahandler.thrift.moderation.DocumentType" %> +<%@ page import="javax.portlet.PortletRequest" %> +<%@ page import="org.eclipse.sw360.portal.common.PortalConstants" %> + +<jsp:useBean id="moderationRequest" class="org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest" scope="request"/> +<jsp:useBean id="actual_SPDXDocument" class="org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument" scope="request" /> + +<core_rt:set var="spdxDocument" value="${actual_SPDXDocument}" scope="request"/> +<div class="container" id="moderation-request-merge" data-document-type="<%=DocumentType.SPDXDOCUMENT%>"> + + <core_rt:set var="moderationTitle" value="Delete ${sw360:printSPDXDocumentName(spdxDocument)}" scope="request" /> + <%@include file="/html/moderation/includes/moderationHeader.jspf"%> + + <div class="row"> + <div class="col"> + <div id="moderation-wizard" class="accordion"> + <div class="card"> + <div id="moderation-header-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-header" aria-expanded="true" aria-controls="moderation-header"> + <liferay-ui:message key="moderation.request.information" /> + </button> + </h2> + </div> + <div id="moderation-header" class="collapse show" aria-labelledby="moderation-header-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/moderation/includes/moderationInfo.jspf"%> + </div> + </div> + </div> + <core_rt:if test="${sw360:isOpenModerationRequest(moderationRequest)}"> + <div class="card"> + <div id="moderation-changes-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-changes" aria-expanded="false" aria-controls="moderation-changes"> + <liferay-ui:message key="proposed.changes" /> + </button> + </h2> + </div> + <div id="moderation-changes" class="collapse p-3" aria-labelledby="moderation-changes-heading" data-parent="#moderation-wizard"> + <div class="alert alert-danger mb-0"> + The SPDX Document <sw360:out value='${sw360:printSPDXDocumentName(spdxDocument)}'/> is requested to be deleted. + </div> + </div> + </div> + </core_rt:if> + <div class="card"> + <div id="current-document-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#current-document" aria-expanded="false" aria-controls="current-document"> + <liferay-ui:message key="current.spdxdocument" /> + </button> + </h2> + </div> + <div id="current-document" class="collapse" aria-labelledby="current-document-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/utils/includes/requirejs.jspf" %> + <%@include file="/html/components/includes/releases/spdx/view.jsp"%> + </div> + </div> + </div> + </div> + </div> + </div> +</div> + +<script> + $('#spdxFullMode').hide(); + $('#spdxLiteMode').hide(); + $('#DocumentCreationInformation').hide(); + $('#PackageInformation').hide(); + $('#annotationSourceSelect').val('SPDX Document'); + changeAnnotationSource($('#annotationSourceSelect')); +</script> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/spdxdocument/merge.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/spdxdocument/merge.jsp new file mode 100644 index 0000000000..da999d944e --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/moderation/spdx/spdxdocument/merge.jsp @@ -0,0 +1,101 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + --%> +<%@include file="/html/init.jsp"%> +<%-- the following is needed by liferay to display error messages--%> +<%@include file="/html/utils/includes/errorKeyToMessage.jspf"%> + +<portlet:defineObjects /> +<liferay-theme:defineObjects /> + +<%@ page import="com.liferay.portal.kernel.portlet.PortletURLFactoryUtil" %> +<%@ page import="org.eclipse.sw360.datahandler.thrift.moderation.DocumentType" %> +<%@ page import="javax.portlet.PortletRequest" %> +<%@ page import="org.eclipse.sw360.portal.common.PortalConstants" %> + +<jsp:useBean id="moderationRequest" class="org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest" scope="request"/> +<jsp:useBean id="actual_SPDXDocument" class="org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument" scope="request" /> +<jsp:useBean id="defaultLicenseInfoHeaderText" class="java.lang.String" scope="request" /> + +<core_rt:set var="spdxDocument" value="${actual_SPDXDocument}" scope="request"/> + +<div class="container" id="moderation-request-merge" data-document-type="<%=DocumentType.SPDXDOCUMENT%>"> + + <core_rt:set var="moderationTitle" value="Change ${sw360:printSPDXDocumentName(spdxDocument)}" scope="request" /> + <%@include file="/html/moderation/includes/moderationHeader.jspf"%> + + <div class="row"> + <div class="col"> + <div id="moderation-wizard" class="accordion"> + <div class="card"> + <div id="moderation-header-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-header" aria-expanded="true" aria-controls="moderation-header"> + <liferay-ui:message key="moderation.request.information" /> + </button> + </h2> + </div> + <div id="moderation-header" class="collapse show" aria-labelledby="moderation-header-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/moderation/includes/moderationInfo.jspf"%> + </div> + </div> + </div> + <core_rt:if test="${sw360:isOpenModerationRequest(moderationRequest)}"> + <div class="card"> + <div id="moderation-changes-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#moderation-changes" aria-expanded="false" aria-controls="moderation-changes"> + <liferay-ui:message key="proposed.changes" /> + </button> + </h2> + </div> + <div id="moderation-changes" class="collapse" aria-labelledby="moderation-changes-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <h4 class="mt-2"><liferay-ui:message key="basic.fields" /></h4> + <sw360:DisplaySPDXDocumentChanges + actual="${actual_SPDXDocument}" + additions="${moderationRequest.SPDXDocumentAdditions}" + deletions="${moderationRequest.SPDXDocumentDeletions}" + idPrefix="basicFields" + tableClasses="table table-bordered" + /> + </div> + </div> + </div> + </core_rt:if> + <div class="card"> + <div id="current-document-heading" class="card-header"> + <h2 class="mb-0"> + <button class="btn btn-secondary btn-block" type="button" data-toggle="collapse" data-target="#current-document" aria-expanded="false" aria-controls="current-document"> + <liferay-ui:message key="current.spdxdocument" /> + </button> + </h2> + </div> + <div id="current-document" class="collapse" aria-labelledby="current-document-heading" data-parent="#moderation-wizard"> + <div class="card-body"> + <%@include file="/html/utils/includes/requirejs.jspf" %> + <%@include file="/html/components/includes/releases/spdx/view.jsp"%> + </div> + </div> + </div> + </div> + </div> + </div> +</div> + +<script> + $('#spdxFullMode').hide(); + $('#spdxLiteMode').hide(); + $('#DocumentCreationInformation').hide(); + $('#PackageInformation').hide(); + $('#annotationSourceSelect').val('SPDX Document'); + changeAnnotationSource($('#annotationSourceSelect')); +</script> \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/includes/projects/clearingStatus.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/includes/projects/clearingStatus.jsp index 6172174755..70aeef47c5 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/includes/projects/clearingStatus.jsp +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/includes/projects/clearingStatus.jsp @@ -40,6 +40,10 @@ <portlet:param name="<%=PortalConstants.ACTION%>" value="<%=PortalConstants.LICENSE_TO_SOURCE_FILE%>"/> </portlet:resourceURL> +<portlet:resourceURL var="loadSpdxLicenseInfoUrl"> + <portlet:param name="<%=PortalConstants.ACTION%>" value='<%=PortalConstants.LOAD_SPDX_LICENSE_INFO%>'/> +</portlet:resourceURL> + <portlet:resourceURL var="addLicenseToReleaseUrl"> <portlet:param name="<%=PortalConstants.ACTION%>" value="<%=PortalConstants.ADD_LICENSE_TO_RELEASE%>"/> <portlet:param name="<%=PortalConstants.PROJECT_ID%>" value="${docid}"/> @@ -147,6 +151,7 @@ <%--for javascript library loading --%> <%@ include file="/html/utils/includes/requirejs.jspf" %> <%@ include file="/html/utils/includes/licenseToSrcMapping.jspf" %> +<%@ include file="/html/utils/includes/scannerFindings.jspf" %> <script> AUI().use('liferay-portlet-url', function () { var PortletURL = Liferay.PortletURL; @@ -315,7 +320,7 @@ AUI().use('liferay-portlet-url', function () { return row; }), columns: [ - {title: "<liferay-ui:message key="name" />", data : "name", "defaultContent": "", render: {display: detailUrl}}, + {title: "<liferay-ui:message key="name" />", data : "name", "defaultContent": "", render: {display: detailUrl} }, {title: "<liferay-ui:message key="type" />", data : "type", "defaultContent": ""}, {title: "<liferay-ui:message key="project.path" />", data : "projectOrigin", "defaultContent": "", render: $.fn.dataTable.render.text() }, {title: "<liferay-ui:message key="release.path" />", data : "releaseOrigin", "defaultContent": "", render: $.fn.dataTable.render.text() }, @@ -390,8 +395,12 @@ AUI().use('liferay-portlet-url', function () { else { url = makeProjectViewUrl(row.id); } - let viewUrl = $("<a></a>").attr("href",url).css("word-break","break-word").text(name); - return viewUrl[0].outerHTML; + let viewUrl = $("<a></a>").attr("href",url).css("word-break","break-word").text(name), + $infoIcon = ''; + if (row.clearingState === 'Scan available') { + $infoIcon = "<span class='actions'><svg class='cursor lexicon-icon m-2 isr' data-doc-id="+ row.id +"> <title><liferay-ui:message key='view.scanner.findings.license'/></title> <use href='/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#info-circle'/></svg></span>"; + } + return viewUrl[0].outerHTML + $infoIcon; } else { return "<liferay-ui:message key="inaccessible.release" />"; } @@ -445,7 +454,7 @@ AUI().use('liferay-portlet-url', function () { function renderState(data, type, row) { if(row.isRelease==="true") { - return renderClearingStateBox(row.clearingState); + return renderClearingStateBox(row.clearingState, row.id); } return renderProjectStateBox(row.projectState,row.clearingState) } @@ -466,7 +475,7 @@ AUI().use('liferay-portlet-url', function () { return $state[0].outerHTML; } - function renderClearingStateBox(stateVal) { + function renderClearingStateBox(stateVal, docId) { var $state = $('<div>', { 'class': 'content-center' }); @@ -583,7 +592,7 @@ AUI().use('liferay-portlet-url', function () { }); $(this).find(".releaseClearingState:eq(0)").each(function(){ - $(this).html(renderClearingStateBox($(this).data("releaseclearingstate"))); + $(this).html(renderClearingStateBox($(this).data("releaseclearingstate"), $(this).data("releaseid"))); }); $(this).find("td.actions").each(function() { @@ -628,7 +637,7 @@ AUI().use('liferay-portlet-url', function () { }); $('#LinkedProjectsInfo').find(".releaseClearingState").each(function(){ - $(this).html(renderClearingStateBox($(this).data("releaseclearingstate"))); + $(this).html(renderClearingStateBox($(this).data("releaseclearingstate"), $(this).data("releaseid"))); }); $('#LinkedProjectsInfo tr').find("td.actions").each(function() { renderLicenses($(this)); diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/includes/projects/linkedObligations.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/includes/projects/linkedObligations.jsp index 7449887f31..878a0e7812 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/includes/projects/linkedObligations.jsp +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/projects/includes/projects/linkedObligations.jsp @@ -38,7 +38,6 @@ <core_rt:set var="linkedObligations" value="${obligationData.linkedObligationStatus}" /> <core_rt:if test="${isObligationPresent}"> <jsp:useBean id="projectObligationsInfoByRelease" type="java.util.List<org.eclipse.sw360.datahandler.thrift.licenseinfo.LicenseInfoParsingResult>" scope="request" /> - <jsp:useBean id="obligationFromReadmeOSS" type="java.lang.Integer" scope="request"/> <jsp:useBean id="approvedObligationsCount" type="java.lang.Integer" scope="request"/> <jsp:useBean id="excludedReleases" type="java.util.Set<org.eclipse.sw360.datahandler.thrift.components.Release>" scope="request" /> </core_rt:if> @@ -115,7 +114,7 @@ AUI().use('liferay-portlet-url', function () { <core_rt:when test="${approvedObligationsCount == 0}"> badgeClass="badge badge-danger" </core_rt:when> - <core_rt:when test="${approvedObligationsCount == obligationFromReadmeOSS}"> + <core_rt:when test="${approvedObligationsCount == linkedObligations.size()}"> badgeClass="badge badge-success" </core_rt:when> <core_rt:otherwise> diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/ajax/linkedProjectsRows.jspf b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/ajax/linkedProjectsRows.jspf index ad5da28a23..98cc8c443f 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/ajax/linkedProjectsRows.jspf +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/ajax/linkedProjectsRows.jspf @@ -75,6 +75,11 @@ <a href="<sw360:DisplayReleaseLink releaseId="${releaseLink.id}" bare="true" scopeGroupId="${concludedScopeGroupId}" />"> <sw360:out value="${releaseLink.name} ${releaseLink.version}" maxChar="60"/> </a> + <core_rt:if test="${releaseLink.clearingState eq 'SCAN_AVAILABLE'}"> + <span class="actions"> + <svg class="cursor lexicon-icon m-2 isr" data-doc-id="${releaseLink.id}"><title><liferay-ui:message key='view.scanner.findings.license' /></title><use href="/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#info-circle"/></svg> + </span> + </core_rt:if> </td> <td> <sw360:DisplayEnum value="${releaseLink.componentType}"/> @@ -92,7 +97,7 @@ <sw360:DisplayLicenseCollection licenseIds="${releaseLink.otherLicenseIds}" main="false" releaseId="${releaseLink.id}" scopeGroupId="${pageContext.getAttribute('scopeGroupId')}" icon="info-circle"/> </core_rt:if> </td> - <td data-releaseclearingstate='<sw360:DisplayEnum value="${releaseLink.clearingState}" bare="true"/>' class="releaseClearingState"> + <td data-releaseid="${releaseLink.id}" data-releaseclearingstate='<sw360:DisplayEnum value="${releaseLink.clearingState}" bare="true"/>' class="releaseClearingState"> </td> <td> ${requestScope["relMainLineState"][releaseLink.id]} diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/ajax/linkedReleasesClearingStatusRows.jsp b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/ajax/linkedReleasesClearingStatusRows.jsp index 30685d5e66..c679540333 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/ajax/linkedReleasesClearingStatusRows.jsp +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/ajax/linkedReleasesClearingStatusRows.jsp @@ -33,6 +33,11 @@ <a href="<sw360:DisplayReleaseLink releaseId="${releaseLink.id}" bare="true" scopeGroupId="${concludedScopeGroupId}" />"> <sw360:out value="${releaseLink.name} ${releaseLink.version}" maxChar="60" /> </a> + <core_rt:if test="${releaseLink.clearingState eq 'SCAN_AVAILABLE'}"> + <span class="actions" > + <svg class="cursor lexicon-icon m-2 isr" data-doc-id="${releaseLink.id}"><title><liferay-ui:message key='view.scanner.findings.license' /></title><use href="/o/org.eclipse.sw360.liferay-theme/images/clay/icons.svg#info-circle"/></svg> + </span> + </core_rt:if> </td> <td> <sw360:DisplayEnum value="${releaseLink.componentType}" /> @@ -49,7 +54,7 @@ <sw360:DisplayLicenseCollection licenseIds="${releaseLink.otherLicenseIds}" main="false" releaseId="${releaseLink.id}" scopeGroupId="${pageContext.getAttribute('scopeGroupId')}" icon="info-circle"/> </core_rt:if> </td> - <td data-releaseclearingstate='<sw360:DisplayEnum value="${releaseLink.clearingState}" bare="true"/>' class="releaseClearingState"></td> + <td data-releaseid="${releaseLink.id}" data-releaseclearingstate='<sw360:DisplayEnum value="${releaseLink.clearingState}" bare="true"/>' class="releaseClearingState"></td> <td> ${requestScope["relMainLineState"][releaseLink.id]} </td> diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/importBom.jspf b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/importBom.jspf index 6c89e08532..e3fa1df329 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/importBom.jspf +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/importBom.jspf @@ -39,7 +39,7 @@ </portlet:resourceURL> <div class="dialogs"> - <div id="spdxBomUpload" data-title="Upload SPDX BOM" class="modal fade" tabindex="-1" role="dialog" + <div id="spdxBomUpload" data-title="Upload SBOM" class="modal fade" tabindex="-1" role="dialog" data-portlet-namespace="<portlet:namespace/>" data-new-attachment-url="<%=newAttachmentAjaxURL%>" data-upload-attachment-part-url="<%=uploadPartAjaxURL%>" diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/importBomForComponent.jspf b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/importBomForComponent.jspf new file mode 100644 index 0000000000..1156d19c49 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/importBomForComponent.jspf @@ -0,0 +1,315 @@ +<%-- + ~ Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + ~ Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + + ~ SPDX-License-Identifier: EPL-2.0 +--%> + +<portlet:resourceURL var="newAttachmentAjaxURL"> + <portlet:param name="<%=PortalConstants.ACTION%>" value='<%=PortalConstants.ATTACHMENT_RESERVE_ID%>'/> + <portlet:param name="<%=PortalConstants.DOCUMENT_TYPE%>" value="${documentType}"/> +</portlet:resourceURL> + +<portlet:resourceURL var="uploadPartAjaxURL"> + <portlet:param name="<%=PortalConstants.ACTION%>" value='<%=PortalConstants.ATTACHMENT_UPLOAD%>'/> + <portlet:param name="<%=PortalConstants.DOCUMENT_TYPE%>" value="${documentType}"/> +</portlet:resourceURL> + +<portlet:resourceURL var="prepareImportBomAjaxURL"> + <portlet:param name="<%=PortalConstants.ACTION%>" value='<%=PortalConstants.PREPARE_IMPORT_BOM%>'/> + <portlet:param name="<%=PortalConstants.DOCUMENT_TYPE%>" value="${documentType}"/> +</portlet:resourceURL> + +<portlet:resourceURL var="importBomAjaxURL"> + <portlet:param name="<%=PortalConstants.ACTION%>" value='<%=PortalConstants.IMPORT_BOM%>'/> + <portlet:param name="<%=PortalConstants.DOCUMENT_TYPE%>" value="${documentType}"/> +</portlet:resourceURL> + +<portlet:resourceURL var="importBomAsNewAjaxURL"> + <portlet:param name="<%=PortalConstants.ACTION%>" value='<%=PortalConstants.IMPORT_BOM_AS_NEW%>'/> + <portlet:param name="<%=PortalConstants.DOCUMENT_TYPE%>" value="${documentType}"/> +</portlet:resourceURL> + +<div class="dialogs"> + <div id="spdxBomUpload" data-title="Upload SBOM" class="modal fade" tabindex="-1" role="dialog" + data-portlet-namespace="<portlet:namespace/>" + data-new-attachment-url="<%=newAttachmentAjaxURL%>" + data-upload-attachment-part-url="<%=uploadPartAjaxURL%>" + data-prepare-import-bom-url="<%=prepareImportBomAjaxURL%>" + data-import-bom-url="<%=importBomAjaxURL%>" + data-import-bom-as-new-url="<%=importBomAsNewAjaxURL%>" > + <div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable" role="document"> + <div class="modal-content"> + <div class="modal-body container" id="spdxBomUploadUpload"> + <h2>Upload BOM document as ${documentType}</h2> + <p>This currently only supports SPDX RDF/XML or JSON files with a uniq described top level node.</p> + <div class="lfr-dynamic-uploader"> + <div class="lfr-upload-container"> + <div id="spdx-fileupload-drop" class="upload-target"> + <span>Drop a File Here</span> + <br/> + Or + <br/> + <button id="spdx-fileupload-browse" type="button" class="btn btn-secondary">Browse</button> + </div> + </div> + </div> + </div> + <div id="spdxBomUploadStatus"></div> + <div id="spdxBomUploadAction" class="modal-footer"> + <button id="importSBOM" type="button" class="btn btn-primary" data-dismiss="modal" style="display: none">Import</button> + <button id="importSBOMAsNew" type="button" class="btn btn-secondary" data-dismiss="modal" style="display: none">Import As New</button> + <button id="cancelImportSBOM" type="button" class="btn btn-light" data-dismiss="modal">Cancel</button> + </div> + </div> + </div> + </div> +</div> + +<script> + require(['jquery', 'resumable', 'modules/dialog', 'modules/validation'], function($, Resumable, dialog, validation) { + var dialogDivId = '#spdxBomUpload'; + + var dialogDiv = $(dialogDivId); + var contentDiv = dialogDiv.find("#spdxBomUploadUpload"); + var statusDiv = dialogDiv.find("#spdxBomUploadStatus"); + var dialogData = dialogDiv.data(); + var portletNamespace = dialogData.portletNamespace; + + var urls = {}; + urls.newAttachment = dialogData.newAttachmentUrl; + urls.uploadAttachmentPart = dialogData.uploadAttachmentPartUrl; + urls.prepareImportBom = dialogData.prepareImportBomUrl; + urls.importBom = dialogData.importBomUrl; + urls.importBomAsNew = dialogData.importBomAsNewUrl; + + var attachmentContentId = ''; + var rdfFilePath = ''; + + const neededKeys = ['isComponentDuplicate','isReleaseDuplicate','name','version']; + + function getAttachmentIdPromise(file) { + var data = {}; + data[portletNamespace + "fileName"] = file.fileName || file.name; + + return $.ajax({ + url: urls.newAttachment, + cache: false, + dataType: 'text', + data: data + }); + } + + function prepareImportBom(attachmentContentId) { + var data = {}; + + data[portletNamespace + "<%=PortalConstants.ATTACHMENT_CONTENT_ID%>"] = attachmentContentId; + + return $.ajax({ + url: urls.prepareImportBom, + cache: false, + dataType: "json", + data: data + }); + } + + function importBomFromAttachment(attachmentContentId, rdfFilePath) { + var data = {}; + + data[portletNamespace + "<%=PortalConstants.ATTACHMENT_CONTENT_ID%>"] = attachmentContentId; + data[portletNamespace + "<%=PortalConstants.RDF_FILE_PATH%>"] = rdfFilePath; + + return $.ajax({ + url: urls.importBom, + cache: false, + dataType: "json", + data: data + }); + } + + function importBomAsNewFromAttachment(attachmentContentId, newReleaseVersion, rdfFilePath) { + var data = {}; + + data[portletNamespace + "<%=PortalConstants.ATTACHMENT_CONTENT_ID%>"] = attachmentContentId; + data[portletNamespace + "<%=PortalConstants.NEW_RELEASE_VERSION%>"] = newReleaseVersion; + data[portletNamespace + "<%=PortalConstants.RDF_FILE_PATH%>"] = rdfFilePath; + + return $.ajax({ + url: urls.importBomAsNew, + cache: false, + dataType: "json", + data: data + }); + } + + var r = new Resumable({ + target: urls.uploadAttachmentPart, + parameterNamespace: portletNamespace, + simultaneousUploads: 1, + generateUniqueIdentifier: getAttachmentIdPromise, + chunkRetryInterval: 2000, + maxChunkRetries: 3 + }); + + r.assignBrowse($('#spdx-fileupload-browse')[0]); + r.assignDrop($('#spdx-fileupload-drop')[0]); + + r.on('fileAdded', function (file) { + console.log("fileAdded..."); + contentDiv.hide(); + $('#cancelImportSBOM').hide(); + statusDiv.show(); + r.upload(); + statusDiv.html("<h2>Uploading ...</h2>"); + }); + r.on('fileProgress', function (file) { + console.log("fileProgress..."); + }); + r.on('fileSuccess', function (file) { + console.log("fileSuccess..."); + statusDiv.html("<h2>Importing ...</h2>"); + attachmentContentId = file.uniqueIdentifier; + prepareImportBom(attachmentContentId).then(function (data) { + rdfFilePath = data.message; + + $('#cancelImportSBOM').show(); + + console.log("prepareImportsuccess..."); + + if (rdfFilePath == 'error-convert') { + console.log("File import is Invalid"); + $('#importSBOM').hide(); + $('#importSBOMAsNew').hide(); + $('#cancelImportSBOM').show(); + statusDiv.html("<h2 style='color:red; text-align: center'>-- File import is Invalid --</h2>"); + contentDiv.show(); + } else { + if (neededKeys.every(key => Object.keys(data).includes(key))) { + $('#importSBOM').css('display', 'block'); + + componentName = data.name; + + componentVersion = data.version; + + actualComponentName = $('#import-spdx-bom').data().componentName; + + if (actualComponentName) { + if (actualComponentName === componentName) { + if (!data.isReleaseDuplicate) { + statusDiv.html("<h3>The new Release will be created, do you want to import?</h3>"); + statusDiv.append("<div>New Release: <b>"+componentName+" "+componentVersion+"</b></div>"); + } else { + statusDiv.html("<h3>The Release existed, do you want:</h3>"); + statusDiv.append("<div>Update Release: <b>"+componentName+" "+componentVersion+"</b> (please choose Import button)</div>"); + statusDiv.append("<div>Create new Release: <b>"+componentName+" ("+componentVersion+"-sbom-"+attachmentContentId.substring(0,6)+")</b> (please choose Import As New button)</div>"); + $('#importSBOMAsNew').show(); + } + } else { + $('#importSBOM').hide(); + statusDiv.html("<h3>The provided SPDX file is not a release of \""+actualComponentName+"\" component.</h3>"); + contentDiv.show(); + } + } else { + if (!data.isComponentDuplicate) { + statusDiv.html("<h3>The new Component and new Release will be created, do you want to import?</h3>"); + statusDiv.append("<div>New Component: <b>"+componentName+"</b></div>"); + statusDiv.append("<div>New Release: <b>"+componentName+" "+componentVersion+"</b></div>"); + } else if (!data.isReleaseDuplicate) { + statusDiv.html("<h3>The new Release will be created, do you want to import?</h3>"); + statusDiv.append("<div>New Release: <b>"+componentName+" "+componentVersion+"</b></div>"); + } else { + statusDiv.html("<h3>The Component and Release existed, do you want:</h3>"); + statusDiv.append("<div>Update Release: <b>"+componentName+" "+componentVersion+"</b> (please choose Import button)</div>"); + statusDiv.append("<div>Create new Component and Release: <b>"+componentName+" ("+componentVersion+"-sbom-"+attachmentContentId.substring(0,6)+")</b> (please choose Import As New button)</div>"); + $('#importSBOMAsNew').show(); + } + } + } else { + let errorKey = "Missing:"; + neededKeys.forEach(key => { + if (!Object.keys(data).includes(key)) { + errorKey += " " + key; + } + }); + $('#importSBOM').hide(); + $('#importSBOMAsNew').hide(); + $('#cancelImportSBOM').show(); + statusDiv.html("<h2>Failed :(</h2>"); + statusDiv.append("<div>" + errorKey + "</div>"); + contentDiv.show(); + } + } + }).catch(function (err) { + $('#importSBOM').hide(); + $('#importSBOMAsNew').hide(); + $('#cancelImportSBOM').show(); + statusDiv.html("<h2>Failed :(</h2>"); + statusDiv.append("<div>" + JSON.stringify(err) + "</div>"); + contentDiv.show(); + }); + }); + r.on('fileError', function (file) { + console.log("fileError..."); + statusDiv.html("<h2>Failed</h2>"); + statusDiv.append("<div>with fileError</div>"); + }); + + $('#import-spdx-bom').on('click', function() { + //function open(selector, data, submitCallback, beforeShowFn, afterShowFn) { + $dialog = dialog.open(dialogDivId, + {}, // data + function(submit, callback) { + // submitCallback + }, + function() { + // beforeShowFn + + statusDiv.html(""); + contentDiv.show(); + + $('#importSBOM').hide(); + $('#importSBOMAsNew').hide(); + + $('#importSBOM').on('click', function() { + if (attachmentContentId === "") return; + importBomFromAttachment(attachmentContentId, rdfFilePath).then(function (data) { + statusDiv.html("<h2>Imported</h2>"); + if ('redirectUrl' in data) { + statusDiv.append("<div>Created as <a href=\"" + data.redirectUrl + "\">" + data.message + "</a>, redirecting ...</div>"); + window.location.href = data.redirectUrl; + } else { + statusDiv.append("<div>" + JSON.stringify(data) + "</div>"); + contentDiv.show(); + } + }); + }); + + $('#importSBOMAsNew').on('click', function() { + if (attachmentContentId === "") return; + importBomAsNewFromAttachment(attachmentContentId, componentVersion+"-sbom-"+attachmentContentId.substring(0,6), rdfFilePath).then(function (data) { + statusDiv.html("<h2>Imported</h2>"); + if ('redirectUrl' in data) { + statusDiv.append("<div>Created as <a href=\"" + data.redirectUrl + "\">" + data.message + "</a>, redirecting ...</div>"); + window.location.href = data.redirectUrl; + } else { + statusDiv.append("<div>" + JSON.stringify(data) + "</div>"); + contentDiv.show(); + } + }); + }); + + $('#cancelImportSBOM').on('click', function() { + r.cancel(); + }); + }, + function() { + // afterShowFn + } + ); + }); + }); +</script> diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/scannerFindings.jspf b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/scannerFindings.jspf new file mode 100644 index 0000000000..bed2c7132a --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/scannerFindings.jspf @@ -0,0 +1,88 @@ +<%-- + ~ Copyright Siemens AG, 2022. Part of the SW360 Portal Project. + ~ With contributions by Siemens Healthcare Diagnostics Inc, 2022. + ~ + ~ This program and the accompanying materials are made + ~ available under the terms of the Eclipse Public License 2.0 + ~ which is available at https://www.eclipse.org/legal/epl-2.0/ + ~ + ~ SPDX-License-Identifier: EPL-2.0 + --%> +<script type="text/javascript"> +AUI().use('liferay-portlet-url', function () { + var PortletURL = Liferay.PortletURL; + var releaseIdToScannerFindingsMap = new Map(); + require(['jquery', 'modules/dialog'], function($, dialog) { + + $("table").on("click", "svg.isr", function(event) { + let docName = $(this).closest('tr').children('td:first').find('a').text().trim(), + docId = $(event.currentTarget).attr('data-doc-id'); + getScannerFindingsForRelease(docId, docName); + }); + + function getScannerFindingsForRelease(releaseId, relName) { + if (releaseIdToScannerFindingsMap.has(releaseId)) { + displayScannerFindingsForRelease(releaseId, relName, releaseIdToScannerFindingsMap.get(releaseId)); + return; + } + jQuery.ajax({ + type: 'GET', + url: '<%=loadSpdxLicenseInfoUrl%>', + cache: false, + data: { + "<portlet:namespace/><%=PortalConstants.RELEASE_ID%>": releaseId + }, + success: function (response) { + response = JSON.parse(response); + if (response.status == 'success') { + releaseIdToScannerFindingsMap.set(releaseId, response); + let scannerFindings = response.data; + displayScannerFindingsForRelease(releaseId, relName, response); + } + else { + dialog.warn('<liferay-ui:message key="failed.to.load.scanner.findings.with.error" />: <b>' + response.message + '!</b>'); + } + }, + error: function (e) { + dialog.warn('<liferay-ui:message key="error.fetching.license.info.from.ISR.file" />! <br>' + e.statusText + ' (' + e.status + ').'); + } + }); + } + + function displayScannerFindingsForRelease(releaseId, relName, data) { + let list = $('<ul/>'), + fileCount = data.totalFileCount, + complexity = '<liferay-ui:message key="very.large" />'; + + if (fileCount <= 1000) { + complexity = '<liferay-ui:message key="small" />'; + } else if (fileCount > 1000 && fileCount <= 5000) { + complexity = '<liferay-ui:message key="medium" />'; + } else if (fileCount > 5000 && fileCount <= 10000) { + complexity = '<liferay-ui:message key="large" />'; + } + + if (!data.licenseIds && !data.otherLicenseIds) { + list.append('<li><liferay-ui:message key="license.information.not.found.in.isr"/></li>'); + } + if (data.licenseIds) { + data.licenseIds.forEach(function(id) { + list.append($('<li>', { text: id })); + }); + } + + if (data.otherLicenseIds) { + data.otherLicenseIds.forEach(function(id) { + list.append($('<li>', { text: id })); + }); + } + dialog.info(relName,'<span class="alert alert-warning"> <liferay-ui:message key="this.is.only.an.initial.scanner.isr.finding.of.the.contained.licenses.in.the.uploaded.source.file" /><br>' + + '<liferay-ui:message key="the.final.license.list.may.differ.based.on.the.conclusions.made.by.the.clearing.expert.and.is.made.available.as.component.license.information.cli" /><br>' + + '<liferay-ui:message key="to.view.the.files.corresponding.to.each.licenses.go.to.clearing.details.tab.of.respective.release" /></span><br>' + + '<liferay-ui:message key="file.name"/>: <b>' + data.fileName + '</b><br><liferay-ui:message key="total.number.of.files"/>: <b>' + data.totalFileCount + + '</b><br><liferay-ui:message key="complexity"/>: <b>' + complexity + '</b> (<liferay-ui:message key="based.on.license.file.count" />)' + $(list)[0].outerHTML); + } + + }); +}); +</script> diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/usingProjectsTable.jspf b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/usingProjectsTable.jspf index 055175035a..9cc0052d5a 100644 --- a/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/usingProjectsTable.jspf +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/usingProjectsTable.jspf @@ -15,7 +15,7 @@ (${usingProjects.size()} visible / ${allUsingProjectsCount - usingProjects.size()} restricted) projects. </div> - <table id="usingProjectsTable" class="table table-bordered mt-1"> + <table id="${tableId}" class="table table-bordered mt-1"> <colgroup> <col style="width: 50%;" /> <col style="width: 25%;" /> @@ -44,7 +44,7 @@ ]); </core_rt:forEach> - datatables.create('#usingProjectsTable', { + datatables.create('#${tableId}', { data: result, lengthChange: false, language: { diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/js/components/includes/releases/spdxjs.js b/frontend/sw360-portlet/src/main/resources/META-INF/resources/js/components/includes/releases/spdxjs.js new file mode 100644 index 0000000000..7d37c002b4 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/js/components/includes/releases/spdxjs.js @@ -0,0 +1,1157 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +define('components/includes/releases/spdxjs', ['jquery',"components/includes/releases/validateLib"] , function($,validateLib) { + function enableSection(section, state) { + if (!state) { + section.find('button').attr('disabled', 'disabled'); + + section.find('select').attr('disabled', 'disabled'); + + section.find('input').attr('disabled', 'disabled'); + + section.find('textarea').attr('disabled', 'disabled'); + + section.find('.spdx-delete-icon-main').css('cursor', 'not-allowed'); + + section.find('.spdx-delete-icon-sub').css('cursor', 'not-allowed'); + } else { + section.find('button').removeAttr('disabled'); + + section.find('select').removeAttr('disabled'); + + section.find('input').removeAttr('disabled'); + + section.find('textarea').removeAttr('disabled'); + + section.find('select').removeAttr('disabled'); + + section.find('.spdx-delete-icon-main').css('cursor', 'pointer'); + + section.find('.spdx-delete-icon-sub').css('cursor', 'pointer'); + } + + section.find('.always-enable').removeAttr('disabled'); + + section.find('.spdx-radio').each(function () { + $(this).parent().parent().find('input[type=text]').attr('disabled', 'true'); + + $(this).parent().parent().find('select').attr('disabled', 'true'); + + $(this).parent().parent().find('textarea').attr('disabled', 'true'); + }); + + section.find('.spdx-add-button-main').removeAttr('disabled'); + } + + function clearSection(section) { + section.find('input[type=text]').val(''); + + section.find('textarea').val(''); + + section.find('select').not('.spdx-select').prop("selectedIndex", 0).change(); + + section.find('input[type=radio]').prop('checked', false); + } + + function deleteMain(deleteBtn) { + if ($(deleteBtn).css('cursor') == 'not-allowed') { + return; + } + + let selectbox = $(deleteBtn).prev('select'); + + selectbox.find('option:selected').remove(); + + if (selectbox.find('option').length == 0) { + selectbox.attr('disabled', 'true'); + + $(deleteBtn).css('cursor', 'not-allowed') + } + + let newItem = selectbox.find('option:selected').val(); + + if (typeof (newItem) == 'undefined') { + section = selectbox.closest('.section'); + + enableSection(section, false); + + clearSection(section); + } else { + selectbox.change(); + } + } + + function deleteSub(deleteBtn) { + if ($(deleteBtn).css('cursor') == 'not-allowed') { + return; + } + + let section = $(deleteBtn).parent().parent(); + + if (section.find('.spdx-delete-icon-sub').length == 1) { + $(deleteBtn).parent().css('display', 'none'); + + $(deleteBtn).addClass('hidden'); + } else { + $(deleteBtn).parent().remove(); + } + } + + function addMain(addBtn) { + let selectbox = $(addBtn).prev().find('select'); + + let newIndex = parseInt(selectbox.find('option').last().val()) + 1; + + if (isNaN(newIndex)) { + newIndex = 1; + } + + selectbox.append('<option>' + newIndex + '</option>'); + + section = selectbox.closest('.section'); + + enableSection(section, true); + + clearSection(section); + + selectbox.val(newIndex); + } + + function addSub(addBtn) { + if ($(addBtn).prev().css('display') == 'none') { + $(addBtn).prev().css('display', 'flex'); + + $(addBtn).prev().find('[name=delete-snippetRange]').removeClass('hidden'); + + $(addBtn).prev().find('[name=checksum-delete]').removeClass('hidden'); + + clearSection($(addBtn).prev()); + + $(addBtn).prev().find('*').removeAttr('disabled'); + + $(addBtn).prev().find('.spdx-delete-icon-sub').css('cursor', 'pointer'); + + if ($(addBtn).hasClass('spdx-add-button-sub-creator')) { + if ($('#creator-anonymous').is(':checked')) { + $(addBtn).prev().find('.creator-type').val('Tool'); + } else { + $(addBtn).prev().find('.creator-type').val('Organization'); + } + } + } else { + let newItem = $(addBtn).prev().clone(); + + clearSection(newItem) + + newItem.find('*').removeAttr('disabled'); + + newItem.find('.spdx-delete-icon-sub').css('cursor', 'pointer'); + + if ($(addBtn).hasClass('spdx-add-button-sub-creator')) { + if ($('#creator-anonymous').is(':checked')) { + newItem.find('.creator-type').val('Tool'); + } else { + newItem.find('.creator-type').val('Organization'); + } + } + + $(addBtn).before(newItem); + } + } + + function updateRadioButton(button) { + if ($(button).attr('id') == 'FilesAnalyzedFalse' && $(button).is(':checked')) { + $('#verificationCodeValue').attr('disabled', 'true'); + + $('#excludedFiles').attr('disabled', 'true'); + + $('#licenseInfoFromFilesExist').attr('disabled', 'true'); + + $('#licenseInfoFromFilesValue').attr('disabled', 'true'); + + $('#licenseInfoFromFilesNone').attr('disabled', 'true'); + + $('#licenseInfoFromFilesNoAssertion').attr('disabled', 'true'); + + return; + } + + if ($(button).attr('id') == 'FilesAnalyzedTrue' && $(button).is(':checked')) { + $('#verificationCodeValue').removeAttr('disabled'); + + $('#excludedFiles').removeAttr('disabled'); + + $('#licenseInfoFromFilesExist').removeAttr('disabled'); + + $('#licenseInfoFromFilesNone').removeAttr('disabled'); + + $('#licenseInfoFromFilesNoAssertion').removeAttr('disabled'); + + if (!$('#licenseInfoFromFilesNone').is(':checked') && !$('#licenseInfoFromFilesNoAssertion').is(':checked')) { + $('#licenseInfoFromFilesExist').click(); + } + + if ($('#licenseInfoFromFilesExist').is(':checked')) { + $('#licenseInfoFromFilesValue').removeAttr('disabled'); + } + + return; + } + + if (button.val() == 'NONE' || button.val() == 'NOASSERTION') { + button.parent().parent().find('input[type=text]').attr('disabled', 'true'); + + button.parent().parent().find('select').attr('disabled', 'true'); + + button.parent().parent().find('textarea').attr('disabled', 'true'); + } else { + button.parent().parent().find('input[type=text]').removeAttr('disabled'); + + button.parent().parent().find('select').removeAttr('disabled'); + + button.parent().parent().find('textarea').removeAttr('disabled'); + } + } + + function readDocumentCreator() { + let creators = []; + + let index = 0; + + $('[name=creatorRow]').each(function () { + if ($(this).css('display') == 'none') { + return; + } + + if ($(this).find('.creator-type').first().attr('disabled')) { + return; + } + + let creatorType = $(this).find('.creator-type').first().val().trim(); + + let creatorValue = $(this).find('.creator-value').first().val().trim(); + + if (creatorValue != '') { + creators.push({ 'type': creatorType, 'value': creatorValue, 'index': index }); + index += 1; + } + }); + + return creators; + } + + function fillDateTime(datePicker, timePicker, value) { + let timeStamp = Date.parse(value); + + let date = new Date(timeStamp); + + let localTimeStamp = timeStamp - date.getTimezoneOffset(); + + let localDate = new Date(localTimeStamp); + + $(datePicker).val(localDate.getFullYear() + + '-' + (localDate.getMonth() + 1).toString().padStart(2, '0') + + '-' + localDate.getDate().toString().padStart(2, '0')); + + $(timePicker).val(date.getHours().toString().padStart(2, '0') + + ':' + date.getMinutes().toString().padStart(2, '0') + + ':' + date.getSeconds().toString().padStart(2, '0')); + } + + function fillMultiOptionsField(inputTag, value, type = 'text') { + if (type == 'array' && value.length == 1) { + if (value[0].toUpperCase() == 'NONE' || value[0].toUpperCase() == 'NOASSERTION') { + $(inputTag).val(''); + + $(inputTag).parent().parent().find('input[value=' + value[0].toUpperCase() + ']').click(); + + return; + } + } + + if (type != 'array' && (value.toUpperCase() == 'NONE' || value.toUpperCase() == 'NOASSERTION')) { + $(inputTag)[0].selectedIndex = 0; + + $(inputTag).parent().find('input').val(''); + + $(inputTag).parent().find('textarea').val(''); + + $(inputTag).parent().parent().find('input[value=' + value.toUpperCase() + ']').click(); + } else { + switch (type) { + case 'array': + fillArray(inputTag, value); + break; + case 'annotator': + fillAnnotator(inputTag, value); + break; + case 'text': + default: + $(inputTag).val(value); + $(inputTag).prev().click(); + } + + $(inputTag).parent().parent().find('input[value=EXIST]').click(); + } + } + + function fillArray(textarea, value) { + if (Array.isArray(value)) { + $(textarea).val(value.sort().join('\n')); + } else { + $(textarea).val(''); + } + } + + function fillAnnotator(typeTag, value) { + if (value.startsWith('Organization: ')) { + $(typeTag).val('Organization'); + + $(typeTag).next().val(value.substr(14)); + } else if (value.startsWith('Person: ')) { + $(typeTag).val('Person'); + + $(typeTag).next().val(value.substr(8)); + } else if (value.startsWith('Tool: ')) { + $(typeTag).val('Tool'); + + $(typeTag).next().val(value.substr(6)); + } else { + $(typeTag).val('Organization'); + + $(typeTag).next().val(''); + } + } + + function readMultiOptionField(inputTag, type = 'text') { + if ($(inputTag).attr('disabled')) { + if (type == 'array') { + return [$(inputTag).parent().parent().find('[type=radio]:checked').val()]; + } else { + return $(inputTag).parent().parent().find('[type=radio]:checked').val(); + } + } else { + switch (type) { + case 'array': + return readArray(inputTag); + case 'annotator': + return readAnnotator(inputTag); + case 'text': + default: + let result = $(inputTag).val().trim(); + + if (result.toUpperCase() == 'NONE' || result.toUpperCase() == 'NOASSERTION') { + return result.toUpperCase(); + } else { + return result; + } + } + } + } + + function readArray(textarea) { + let result = $(textarea).val().split('\n'); + + for (let i = 0; i < result.length; i++) { + result[i] = result[i].trim(); + } + + result.filter(function(e) { return e !== '' }).sort(); + + if (result.length == 1 && (result[0].toUpperCase() == 'NONE' || result[0].toUpperCase() == 'NOASSERTION')) { + return [result[0].toUpperCase()]; + } + + return result.filter(function(v) { return v !=='' } ); + } + + function readAnnotator(typeTag) { + let val = $(typeTag).parent().parent().find('.spdx-radio:checked').val(); + + if (val != 'EXIST') { + $(typeTag).parent().parent().find('[type=radio]:checked').val(); + } + + if ($(typeTag).next().val().trim() != '') { + val = $(typeTag).val() + ': ' + $(typeTag).next().val().trim(); + } else { + val = ''; + } + + return val; + } + + function readDateTime(datePicker, timePicker) { + if ($(datePicker).val() == '' || $(timePicker).val() == '') { + return ''; + } + + let localDate = new Date($(datePicker).val() + ' ' + $(timePicker).val()); + + return localDate.toISOString().slice(0, -5) + 'Z'; + } + + function fillSelectbox(selectbox, num) { + $(selectbox).find('option').remove(); + + for (let i = 0; i < num; i++) { + $(selectbox).append('<option>' + (i + 1).toString() + '</option>'); + } + + if (num > 0) { + $(selectbox).val(1); + } + } + + // --------------------------------- Document Creation --------------------------------- + + function initDocumentCreation(userDisplay) { + if (documentCreationInformationObj['spdxVersion'].startsWith('SPDX-')) { + $('#spdxVersion').val(documentCreationInformationObj['spdxVersion'].substr(5).trim()); + } else { + $('#spdxVersion').val('2.2'); + } + + if (documentCreationInformationObj['dataLicense'] == '') { + $('#dataLicense').val('CC0-1.0'); + } + + if (documentCreationInformationObj['SPDXID'].startsWith('SPDXRef-')) { + $('#spdxIdentifier').val(documentCreationInformationObj['SPDXID'].substr(8).trim()); + } else { + $('#spdxIdentifier').val('DOCUMENT'); + } + + if (documentCreationInformationObj.externalDocumentRefs.length == 0) { + enableSection($('.section-external-doc-ref'), false); + } else { + fillSelectbox('#externalDocumentRefs', documentCreationInformationObj.externalDocumentRefs.length); + + fillExternalDocRef(0); + } + + if (documentCreationInformationObj.creator.length == 0) { + $('.spdx-add-button-sub-creator').first().click(); + $('.creator-type').last().val('Person'); + $('.creator-value').last().val(userDisplay); + } else { + for (let i = 0; i < documentCreationInformationObj.creator.length; i++) { + addSub($('.spdx-add-button-sub-creator').first()); + $('.creator-type').last().val(documentCreationInformationObj.creator[i].type); + $('.creator-value').last().val(documentCreationInformationObj.creator[i].value); + } + } + + $('[name=delete-spdx-creator]').bind('click', function() { + deleteSub($(this)); + }); + + if (documentCreationInformationObj.created == '') { + fillDateTime('#createdDate', '#createdTime', (new Date().toISOString())); + } else { + fillDateTime('#createdDate', '#createdTime', documentCreationInformationObj.created); + } + + $('#creatorComment').val(documentCreationInformationObj['creatorComment'].trim()); + $('#documentComment').val(documentCreationInformationObj['documentComment'].trim()); + } + + function storeDocumentCreation() { + if ($('#spdxVersion').val().trim() == '') { + documentCreationInformationObj['spdxVersion'] = 'SPDX-2.2'; + } else { + documentCreationInformationObj['spdxVersion'] = 'SPDX-' + $('#spdxVersion').val().trim(); + } + + if ($('#dataLicense').val().trim() == '') { + documentCreationInformationObj['dataLicense'] = 'CC0-1.0'; + } else { + documentCreationInformationObj['dataLicense'] = $('#dataLicense').val().trim(); + } + + if ($('#spdxIdentifier').val().trim() == '') { + documentCreationInformationObj['SPDXID'] = 'SPDXRef-DOCUMENT'; + } else { + documentCreationInformationObj['SPDXID'] = 'SPDXRef-' + $('#spdxIdentifier').val().trim(); + } + + documentCreationInformationObj['name'] = $('#documentName').val().trim(); + + documentCreationInformationObj['documentNamespace'] = $('#documentNamespace').val().trim(); + + documentCreationInformationObj['licenseListVersion'] = $('#licenseListVersion').val().trim(); + + documentCreationInformationObj.creator = readDocumentCreator(); + + documentCreationInformationObj['created'] = readDateTime('#createdDate', '#createdTime'); + + documentCreationInformationObj['creatorComment'] = $('#creatorComment').val().trim(); + + documentCreationInformationObj['documentComment'] = $('#documentComment').val().trim(); + + if (documentCreationInformationObj['created'] == '') { + documentCreationInformationObj['created'] = (new Date()).toISOString(); + } + } + + // --------------------------------- External Document Reference --------------------------------- + + function fillExternalDocRef(index) { + index = $('#externalDocumentRefs')[0].selectedIndex; + + let obj = documentCreationInformationObj.externalDocumentRefs[index]; + + $('#externalDocumentId').val(obj['externalDocumentId']); + + $('#externalDocument').val(obj['spdxDocument']); + + $('#checksumAlgorithm').val(obj['checksum']['algorithm']); + + $('#checksumValue').val(obj['checksum']['checksumValue']); + } + + function storeExternalDocRef(index) { + if (index < 0 || index > documentCreationInformationObj.externalDocumentRefs.length - 1) { + return; + } + + let obj = documentCreationInformationObj.externalDocumentRefs[index]; + + obj['externalDocumentId'] = $('#externalDocumentId').val().trim(); + + obj['spdxDocument'] = $('#externalDocument').val().trim(); + + let algorithm = $('#checksumAlgorithm').val().trim(); + + let checksumValue = $('#checksumValue').val().trim(); + + if (algorithm == '' || checksumValue == '') { + obj['checksum']['algorithm'] = ''; + obj['checksum']['checksumValue'] = ''; + } else { + obj['checksum']['algorithm'] = algorithm; + obj['checksum']['checksumValue'] = checksumValue; + } + } + + // --------------------------------- Package Information --------------------------------- + + function initPackageInfo() { + if (packagesInformationObj.length == 0) { + enableSection($('.section-package'), false); + } else { + fillSelectbox('#selectPackage', packagesInformationObj.length); + + fillPackage(0); + } + } + + function fillPackage(index) { + const packageInformationObj = packagesInformationObj[index] + + $('#packageName').val(packageInformationObj['name']); + + if (packageInformationObj.SPDXID.startsWith('SPDXRef-')) { + $('#packageSPDXId').val(packageInformationObj.SPDXID.substr(8)); + } else { + $('#packageSPDXId').val('Package-' + packageInformationObj['name']); + } + + $('#versionInfo').val(packageInformationObj['versionInfo']); + $('#packageFileName').val(packageInformationObj['packageFileName']); + $('#sourceInfo').val(packageInformationObj['sourceInfo']); + $('#licenseComments').val(packageInformationObj['licenseComments']); + $('#summary').val(packageInformationObj['summary']); + $('#description').val(packageInformationObj['description']); + $('#spdxPackageComment').val(packageInformationObj['packageComment']); + + + + fillMultiOptionsField('#supplierType', packageInformationObj.supplier, 'annotator'); + + fillMultiOptionsField('#originatorType', packageInformationObj.originator, 'annotator'); + + fillMultiOptionsField('#downloadLocationValue', packageInformationObj.downloadLocation); + + if (packageInformationObj.filesAnalyzed) { + $('#FilesAnalyzedTrue').click(); + + $('#verificationCodeValue').val(packageInformationObj.packageVerificationCode.value); + + fillArray('#excludedFiles', packageInformationObj.packageVerificationCode.excludedFiles); + } else { + $('#FilesAnalyzedFalse').click(); + + $('#verificationCodeValue').val(''); + + $('#excludedFiles').val(''); + } + + if ($('[name=checksum-delete].hidden').length == 0) { + const checksumsNum = $('[name=checksumRow]').length; + + for (let i = 0; i < checksumsNum; i++) { + if (i == 0) { + $($('[name=checksumRow]')[i]).css('display', 'none'); + + $($('[name=checksumRow]')[i]).find('[name=checksum-delete]').addClass('hidden'); + + clearSection($($('[name=checksumRow]')[i])); + } else { + $('[name=checksumRow]').last().remove(); + } + } + } + + for (let i = 0; i < packageInformationObj.checksums.length; i++) { + addSub($('.spdx-add-button-sub-checksum').first()); + + $('.checksum-delete').last().bind('click', function() { + deleteSub($(this)); + }); + + let algorithm = packageInformationObj.checksums[i].algorithm; + + let checksumValue = packageInformationObj.checksums[i].checksumValue; + + $('.checksum-algorithm').last().val(algorithm); + + $('.checksum-value').last().val(checksumValue); + } + + $('.checksum-algorithm, .checksum-value').bind('change keyup', function() { + let selectedPackage = $('#selectPackage')[0].selectedIndex; + if ($(this).is(":focus")) { + //storePackageInfo(packageInformationObj.index); + storePackageInfo(selectedPackage); + } + }); + + $('.checksum-delete').bind('click', function() { + let selectedPackage = $('#selectPackage')[0].selectedIndex; + deleteSub($(this)); + storePackageInfo(selectedPackage); + //storePackageInfo(packageInformationObj.index); + }); + + fillMultiOptionsField('#packageHomepageValue', packageInformationObj.homepage); + + fillMultiOptionsField('#licenseConcludedValue', packageInformationObj.licenseConcluded); + + fillMultiOptionsField('#licenseInfoFromFilesValue', packageInformationObj.licenseInfoFromFiles, 'array'); + + fillMultiOptionsField('#licenseDeclaredValue', packageInformationObj.licenseDeclared); + + fillMultiOptionsField('#copyrightTextValue', packageInformationObj.copyrightText); + + if (packageInformationObj.externalRefs.length == 0) { + enableSection($('.section-external-ref'), false); + $('#externalReferences').empty(); + } else { + fillSelectbox('#externalReferences', packageInformationObj.externalRefs.length); + + fillExternalRef(packageInformationObj, 0); + } + + fillArray('#spdxPackageAttributionText', packageInformationObj.attributionText); + + } + + + function storePackageInfo(packageIndex) { + let packageInformationObj = packagesInformationObj[packageIndex]; + packageInformationObj['name'] = $('#packageName').val().trim(); + + if ($('#packageSPDXId').val().trim() == '') { + packageInformationObj['SPDXID'] = 'SPDXRef-Package-' + packageInformationObj['name']; + } else { + packageInformationObj['SPDXID'] = 'SPDXRef-' + $('#packageSPDXId').val().trim(); + } + + packageInformationObj['versionInfo'] = $('#versionInfo').val().trim(); + + packageInformationObj['packageFileName'] = $('#packageFileName').val().trim(); + + packageInformationObj['supplier'] = readMultiOptionField('#supplierType', 'annotator'); + + packageInformationObj['originator'] = readMultiOptionField('#originatorType', 'annotator'); + + packageInformationObj['downloadLocation'] = readMultiOptionField('#downloadLocationValue'); + + packageInformationObj['filesAnalyzed'] = $('[name=_sw360_portlet_components_FILES_ANALYZED]:checked').val(); + + if (packageInformationObj['filesAnalyzed'] == 'true') { + packageInformationObj['packageVerificationCode']['value'] = $('#verificationCodeValue').val().trim(); + + packageInformationObj['packageVerificationCode']['excludedFiles'] = readArray('#excludedFiles'); + } else { + packageInformationObj['packageVerificationCode']['value'] = ''; + + packageInformationObj['packageVerificationCode']['excludedFiles'] = ''; + } + + packageInformationObj['checksums'] = []; + + let index = 0; + + $('[name=checksumRow]').each(function() { + let algorithm = $(this).find('.checksum-algorithm').first().val().trim(); + + let checksumValue = $(this).find('.checksum-value').first().val().trim(); + + if (algorithm !='' && checksumValue != '') { + packageInformationObj['checksums'].push({ 'algorithm': algorithm, 'checksumValue': checksumValue, 'index': index }); + index += 1; + } + }); + + packageInformationObj['homepage'] = readMultiOptionField('#packageHomepageValue'); + + packageInformationObj['sourceInfo'] = $('#sourceInfo').val().trim(); + + packageInformationObj['licenseConcluded'] = readMultiOptionField('#licenseConcludedValue'); + + if (packageInformationObj['filesAnalyzed'] == 'true') { + packageInformationObj['licenseInfoFromFiles'] = readMultiOptionField('#licenseInfoFromFilesValue', 'array'); + } else { + packageInformationObj['licenseInfoFromFiles'] = []; + } + + packageInformationObj['licenseDeclared'] = readMultiOptionField('#licenseDeclaredValue'); + + packageInformationObj['licenseComments'] = $('#licenseComments').val().trim(); + + packageInformationObj['copyrightText'] = readMultiOptionField('#copyrightTextValue'); + + packageInformationObj['summary'] = $('#summary').val().trim(); + + packageInformationObj['description'] = $('#description').val().trim(); + + packageInformationObj['packageComment'] = $('#spdxPackageComment').val().trim(); + + packageInformationObj['attributionText'] = readMultiOptionField('#spdxPackageAttributionText', 'array'); + } + + // --------------------------------- External Reference --------------------------------- + + function fillExternalRef(packageInformationObj, index) { + let obj = packageInformationObj.externalRefs[index]; + $('#externalReferences').removeAttr('disabled'); + + $('#referenceCategory').val(obj['referenceCategory']); + + $('#referenceCategory').change(); + $('#referenceCategory').removeAttr('disabled'); + + if (obj['referenceCategory'] == 'SECURITY' || obj['referenceCategory'] == 'PACKAGE-MANAGER') { + $('#referenceType-1').val(obj['referenceType']); + $('#referenceType-1').removeAttr('disabled'); + + } else { + $('#referenceType-2').val(obj['referenceType']); + $('#referenceType-2').removeAttr('disabled'); + + } + + $('#externalReferencesLocator').val(obj['referenceLocator']); + $('#externalReferencesLocator').removeAttr('disabled'); + + + $('#externalReferencesComment').val(obj['comment']); + $('#externalReferencesComment').removeAttr('disabled'); + } + + function storeExternalRef(packageInformationObj, index) { + if (index < 0 || index > packageInformationObj.externalRefs.length - 1) { + return; + } + + let obj = packageInformationObj.externalRefs[index]; + + obj['referenceCategory'] = $('#referenceCategory').val().trim(); + + if (obj['referenceCategory'] == 'SECURITY' || obj['referenceCategory'] == 'PACKAGE-MANAGER') { + obj['referenceType'] = $('#referenceType-1').val().trim(); + } else { + obj['referenceType'] = $('#referenceType-2').val().trim(); + } + + obj['referenceLocator'] = $('#externalReferencesLocator').val().trim(); + + obj['comment'] = $('#externalReferencesComment').val().trim(); + } + + // --------------------------------- Snippet Information --------------------------------- + + function initSnippetInfo() { + if (spdxDocumentObj.snippets.length == 0) { + enableSection($('.section-snippet'), false); + } else { + fillSelectbox('#selectSnippet', spdxDocumentObj.snippets.length); + + fillSnippet(0); + } + } + + function fillSnippet(index) { + const obj = spdxDocumentObj.snippets[index]; + + if (obj['SPDXID'].startsWith('SPDXRef-')) { + $('#snippetSpdxIdentifier').val(obj['SPDXID'].substr(8)); + } else { + $('#snippetSpdxIdentifier').val('Snippet-' + obj['name']); + } + + if (obj['snippetFromFile'].startsWith('SPDXRef-')) { + $('#snippetFromFile').val('SPDXRef'); + + $('#snippetFromFileValue').val(obj['snippetFromFile'].substr(8)); + } else if (obj['snippetFromFile'].startsWith('DocumentRef-')) { + $('#snippetFromFile').val('DocumentRef'); + + $('#snippetFromFileValue').val(obj['snippetFromFile'].substr(12)); + } else { + $('#snippetFromFile').val('SPDXRef'); + + $('#snippetFromFileValue').val(''); + } + + if ($('[name=delete-snippetRange].hidden').length == 0) { + const rangesNum = $('[name=snippetRange]').length; + + for (let i = 0; i < rangesNum; i++) { + if (i == 0) { + $($('[name=snippetRange]')[i]).css('display', 'none'); + + $($('[name=snippetRange]')[i]).find('[name=delete-snippetRange]').addClass('hidden'); + + clearSection($($('[name=snippetRange]')[i])); + } else { + $('[name=snippetRange]').last().remove(); + } + } + } + + for (let i = 0; i < obj.snippetRanges.length; i++) { + addSub('#addNewRange'); + + $('.range-type').last().val(obj.snippetRanges[i].rangeType); + + $('.start-pointer').last().val(obj.snippetRanges[i].startPointer); + + $('.end-pointer').last().val(obj.snippetRanges[i].endPointer); + + $('.reference').last().val(obj.snippetRanges[i].reference); + } + + $('.range-type, .start-pointer, .end-pointer, .reference').bind('change keyup', function() { + if ($(this).is(":focus")) { + storeSnippet(); + } + }); + + $('[name=delete-snippetRange]').bind('click', function() { + deleteSub($(this)); + + storeSnippet(); + }); + + fillMultiOptionsField('#spdxConcludedLicenseValue', obj.licenseConcluded); + + fillMultiOptionsField('#licenseInfoInFileValue', obj.licenseInfoInSnippets, 'array'); + + $('#snippetLicenseComments').val(obj.licenseComments); + + fillMultiOptionsField('#copyrightTextValueSnippet', obj.copyrightText); + + $('#snippetComment').val(obj.comment); + + $('#snippetName').val(obj.name); + + $('#snippetAttributionText').val(obj.snippetAttributionText); + } + + function storeSnippet(index) { + if (typeof(index) == 'undefined') { + index = $('#selectSnippet')[0].selectedIndex; + } + + if (index < 0 || index > spdxDocumentObj.snippets - 1) { + return; + } + + let obj = spdxDocumentObj.snippets[index]; + + if ($('#snippetSpdxIdentifier').val().trim() != '') { + obj['SPDXID'] = 'SPDXRef-' + $('#snippetSpdxIdentifier').val().trim(); + } else { + obj['SPDXID'] = 'SPDXRef-Snippet-' + $('#snippetName').val().trim(); + } + + if ($('#snippetFromFileValue').val().trim() != '') { + obj['snippetFromFile'] = $('#snippetFromFile').val() + '-' + $('#snippetFromFileValue').val().trim(); + } else { + obj['snippetFromFile'] = ''; + } + + obj['snippetRanges'] = []; + + if ($('[name=snippetRange]').first().css('display') != 'none') { + obj['snippetRanges'] = []; + + let index = 0; + + $('[name=snippetRange]').each(function() { + let range = {'rangeType': '', 'startPointer': '', 'endPointer': '', 'reference': ''}; + + range['rangeType'] = $(this).find('.range-type').first().val().trim(); + + range['startPointer'] = $(this).find('.start-pointer').first().val().trim(); + + range['endPointer'] = $(this).find('.end-pointer').first().val().trim(); + + range['reference'] = $(this).find('.reference').first().val().trim(); + + range['index'] = index; + + index += 1; + + if (range['startPointer'] != '' && range['endPointer'] != '' && range['reference'] != '') { + obj['snippetRanges'].push(range); + } + }) + } + + obj['licenseConcluded'] = readMultiOptionField('#spdxConcludedLicenseValue'); + + obj['licenseInfoInSnippets'] = readMultiOptionField('#licenseInfoInFileValue', 'array'); + + obj['licenseComments'] = $('#snippetLicenseComments').val().trim(); + + obj['copyrightText'] = readMultiOptionField('#copyrightTextValueSnippet'); + + obj['comment'] = $('#snippetComment').val().trim(); + + obj['name'] = $('#snippetName').val().trim(); + + obj['snippetAttributionText'] = $('#snippetAttributionText').val().trim(); + } + + // --------------------------------- Other Licensing --------------------------------- + + function initOtherLicensing() { + if (spdxDocumentObj.otherLicensingInformationDetecteds.length == 0) { + enableSection($('.section-other-licensing'), false); + } else { + fillSelectbox('#selectOtherLicensing', spdxDocumentObj.otherLicensingInformationDetecteds.length); + + fillOtherLicensing(0); + } + } + + function fillOtherLicensing(index) { + let obj = spdxDocumentObj.otherLicensingInformationDetecteds[index]; + + if (obj.licenseId.startsWith('LicenseRef-')) { + $('#licenseId').val(obj.licenseId.substr(11)); + } else { + $('#licenseId').val(obj.licenseName); + } + + $('#extractedText').val(obj.extractedText); + + fillMultiOptionsField('#licenseName', obj.licenseName); + + fillArray('#licenseCrossRefs', obj.licenseCrossRefs); + + $('#licenseCommentOnOtherLicensing').val(obj.licenseComment); + } + + function storeOtherLicensing(index) { + if (index < 0 || index > spdxDocumentObj.otherLicensingInformationDetecteds - 1) { + return; + } + + let obj = spdxDocumentObj.otherLicensingInformationDetecteds[index]; + + if ($('#licenseId').val().trim() != '') { + obj['licenseId'] = 'LicenseRef-' + $('#licenseId').val().trim(); + } else { + obj['licenseId'] = 'LicenseRef-' + readMultiOptionField('#licenseName'); + } + + obj['extractedText'] = $('#extractedText').val().trim(); + + obj['licenseName'] = readMultiOptionField('#licenseName'); + + obj['licenseCrossRefs'] = readArray('#licenseCrossRefs'); + + obj['licenseComment'] = $('#licenseCommentOnOtherLicensing').val().trim(); + } + + // --------------------------------- Relationship --------------------------------- + function getRelationshipsSource() { + if ($('#selectRelationshipSource').val() == 'Package') { + return packagesInformationObj[0].relationships; + } + + return spdxDocumentObj.relationships; + } + + + function initRelationships() { + let source = getRelationshipsSource(); + if (source.length == 0) { + enableSection($('.section-relationship'), false); + } else { + fillSelectbox('#selectRelationship', source.length); + + fillRelationship(source, 0); + } + } + + function fillRelationship(sourceRelationship, index) { + let obj = sourceRelationship[index]; + + $('#spdxElement').val(obj.spdxElementId); + + $('#relationshipType').val(obj.relationshipType.toUpperCase()); + + $('#relatedSPDXElement').val(obj.relatedSpdxElement); + + $('#relationshipComment').val(obj.relationshipComment); + } + + function storeRelationship(index) { + let source = getRelationshipsSource() + if (index < 0 || index > source - 1) { + return; + } + + let obj = source[index]; + + obj['spdxElementId'] = $('#spdxElement').val().trim(); + + obj['relationshipType'] = $('#relationshipType').val().toUpperCase().trim(); + + obj['relatedSpdxElement'] = $('#relatedSPDXElement').val().trim(); + + obj['relationshipComment'] = $('#relationshipComment').val().trim(); + } + + // --------------------------------- Annotation --------------------------------- + + function getAnnotationsSource() { + if ($('#selectAnnotationSource').val() == 'Package') { + return packagesInformationObj[0].annotations; + } + + return spdxDocumentObj.annotations; + } + + function initAnnotations() { + let source = getAnnotationsSource(); + + if (source.length == 0) { + enableSection($('.section-annotation'), false); + + $('#selectAnnotation').find('option').remove(); + } else { + enableSection($('.section-annotation'), true); + + fillSelectbox('#selectAnnotation', source.length); + + fillAnnotation(source, 0); + } + } + + function fillAnnotation(source, index) { + let obj = source[index]; + + fillAnnotator('#annotatorType', obj['annotator']); + + fillDateTime('#annotationCreatedDate', '#annotationCreatedTime', obj['annotationDate']); + + $('#annotationType').val(obj['annotationType']); + + $('#spdxIdRef').val(obj['spdxIdRef']); + + $('#annotationComment').val(obj['annotationComment']); + } + + function storeAnnotation(index) { + let source = getAnnotationsSource(); + + if (index < 0 || index > source.length - 1) { + return; + } + + let obj = source[index]; + + if ($('#annotatorValue').val().trim() != '') { + obj['annotator'] = $('#annotatorType').val() + ': ' + $('#annotatorValue').val().trim(); + } else { + obj['annotator'] = ''; + } + + obj['annotationDate'] = readDateTime('#annotationCreatedDate', '#annotationCreatedTime'); + + obj['annotationType'] = $('#annotationType').val().trim(); + + obj['spdxIdRef'] = $('#spdxIdRef').val().trim(); + + obj['annotationComment'] = $('#annotationComment').val().trim(); + } + + + return { + addMain: addMain, + addSub: addSub, + deleteMain: deleteMain, + deleteSub: deleteSub, + + updateRadioButton: updateRadioButton, + + initDocumentCreation: initDocumentCreation, + storeDocumentCreation: storeDocumentCreation, + + readDocumentCreator: readDocumentCreator, + + fillExternalDocRef: fillExternalDocRef, + storeExternalDocRef: storeExternalDocRef, + + initPackageInfo: initPackageInfo, + fillPackage: fillPackage, + storePackageInfo: storePackageInfo, + + fillExternalRef: fillExternalRef, + storeExternalRef: storeExternalRef, + + initSnippetInfo: initSnippetInfo, + fillSnippet: fillSnippet, + storeSnippet: storeSnippet, + + initOtherLicensing: initOtherLicensing, + fillOtherLicensing: fillOtherLicensing, + storeOtherLicensing: storeOtherLicensing, + + getRelationshipsSource: getRelationshipsSource, + initRelationships: initRelationships, + fillRelationship: fillRelationship, + storeRelationship: storeRelationship, + + getAnnotationsSource: getAnnotationsSource, + initAnnotations: initAnnotations, + fillAnnotation: fillAnnotation, + storeAnnotation: storeAnnotation + }; +}); diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/js/components/includes/releases/validateLib.js b/frontend/sw360-portlet/src/main/resources/META-INF/resources/js/components/includes/releases/validateLib.js new file mode 100644 index 0000000000..8e6a955951 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/js/components/includes/releases/validateLib.js @@ -0,0 +1,254 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +define('components/includes/releases/validateLib', ['jquery'], function ($) { + var errors = []; + var totalErrors = 0; + var formId = ''; + + const numRegex = /^[-+]?\d*$/; + const urlRegex = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/; + const downloadUrlRegex = /(?:git|ssh|https?|ftp|git@[-\w.]+):(\/\/)?(.*?)(\.git)?(\/?|\#[-\d\w._]+?)$/; + + function regex(val, params) { + if (!required(val)) { + return true; + } + var regexText = new RegExp(params); + if (val.match(regexText) != null) { + return true; + } else { + return false; + } + } + + function required(val) { + if ((val == null) || (val == '')) { + return false; + } else { + return true; + } + } + + function integer(val, params) { + if (isNaN(val)) { + return false; + } + + var x = parseFloat(val); + + return (x | 0) === x; + } + + function max(val, params) { + if (val.match(numRegex) == null) { + return true; + } + + if (isNaN(parseInt(val)) || isNaN(parseInt(params))) { + return true; + } + + return parseInt(val) <= parseInt(params); + } + + function min(val, params) { + if (val.match(numRegex) == null) { + return true; + } + + if (isNaN(parseInt(val)) || isNaN(parseInt(params))) { + return true; + } + + return parseInt(val) >= parseInt(params); + } + + function isUrl(val, params) { + if (!required(val)) { + return true; + } + if (val.match(urlRegex)) { + return true; + } else { + return false; + } + } + + function isDownloadUrl(val, params) { + if (!required(val)) { + return true; + } + if (val.match(downloadUrlRegex)) { + return true; + } else { + return false; + } + } + + function getElementVal(element) { + if ($(element).is('input')) { // Textbox, checkbox + var type = $(element).attr('type'); + + switch (type) { + case 'text': + return $(element).val(); + break; + + case 'checkbox': + return $(element).is(':checked'); + break; + + case 'radio': + var name = $(element).attr('name'); + var all_radio_button = document.getElementsByName(name); + for (radio_button in all_radio_button) { + if ($(radio_button).is(':checked')) { + return 'checked'; + } + } + return null; + break; + case 'date': + return $(element).val(); + break; + case 'time': + return $(element).val(); + break; + default: + return undefined; + break; + } + } + + if ($(element).is('select')) { // Selectbox + return $(element).val(); + } + + if ($(element).is('textarea')) { // Textarea + return $(element).val(); + } + + return undefined; + } + + function setFormId(val) { + formId = val; + } + + function validate() { + errors = []; + totalErrors = 0; + + const elements = $('#' + formId).find('.needs-validation'); + + for (var i = 0; i < elements.length; i++) { + const element = elements[i]; + if (typeof $(element).attr('disabled') === 'undefined') { + const elementId = String($(element).attr('id')); + const elementErrors = validateElement(element); + + errors.push({ + id: elementId, + rules: elementErrors + }); + + totalErrors += elementErrors.length; + } + } + } + + function allValid() { + return totalErrors == 0; + } + + function validateElement(element) { + var errors = []; + // var elementRule = $(element).attr('rule'); + if (typeof $(element).attr('rule') === 'undefined' || $(element).attr('rule').length == 0) { + return errors; + } + + var val = getElementVal($(element)); + + var valdationRules = $(element).attr('rule').split('|'); + + valdationRules.forEach(rule => { + var ruleName = rule.split(':')[0]; + var params = rule.split(':')[1]; + + if (!eval(ruleName)(val, params)) { + errors.push(ruleName); + } + }); + return errors; + } + + function showAllErrors() { + hideAllErrors(); + + if (allValid()) { + return; + } + + $('#' + formId).addClass('was-validated'); + + errors.forEach(error => { + showError(error.id, error.rules); + }) + } + + function showError(elementId, rules) { + try { + if (rules.length == 0) { + $('#' + elementId)[0].setCustomValidity(''); + return; + } + + $('#' + elementId)[0].setCustomValidity('error'); + } catch { + console.log('Cannot show error'); + } + + rules.forEach(rule => { + $('#' + elementId + '-error-messages').find("[rule='" + rule + "']").addClass('d-block'); + }) + } + + function hideAllErrors() { + $('#' + formId).removeClass('was-validated'); + const elements = $('#' + formId).find('.needs-validation'); + + for (var i = 0; i < elements.length; i++) { + $(elements[i])[0].setCustomValidity(''); + $('.invalid-feedback').css('display', 'none'); + $('.invalid-feedback').removeClass('d-block'); + } + } + + function addError(elementId, elementErrors) { + errors.push({ + id: elementId, + rules: elementErrors + }); + + totalErrors += elementErrors.length; + } + + return { + setFormId: setFormId, + validate: validate, + allValid: allValid, + addError: addError, + showAllErrors: showAllErrors, + hideAllErrors: hideAllErrors + } +}); \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/META-INF/resources/js/utils/array.js b/frontend/sw360-portlet/src/main/resources/META-INF/resources/js/utils/array.js new file mode 100644 index 0000000000..ea4dab22a2 --- /dev/null +++ b/frontend/sw360-portlet/src/main/resources/META-INF/resources/js/utils/array.js @@ -0,0 +1,34 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +function dynamicSort(property, type) { + var sortOrder = 1; + + if(property[0] === "-") { + sortOrder = -1; + + property = property.substr(1); + } + + return function (a,b) { + var result; + + switch (type) { + case 'int': + result = (parseInt(a[property]) < parseInt(b[property])) ? -1 : (parseInt(a[property]) > (b[property])) ? 1 : 0; + break; + case 'string': + default: + result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0; + } + + return result * sortOrder; + } +} \ No newline at end of file diff --git a/frontend/sw360-portlet/src/main/resources/content/Language.properties b/frontend/sw360-portlet/src/main/resources/content/Language.properties index cdcdc67980..3e8edee6e9 100644 --- a/frontend/sw360-portlet/src/main/resources/content/Language.properties +++ b/frontend/sw360-portlet/src/main/resources/content/Language.properties @@ -105,6 +105,7 @@ authorization.header.authorization.token.api.token=Authorization Header (Authori auto.refresh.in=Auto-refresh in auto-refresh.in.5=Auto-refresh in 5 ba-bl.slash.group=BA-BL/Group +based.on.license.file.count=based on license file count basic.fields=Basic fields basic.info=Basic Info bazaar=Bazaar @@ -241,6 +242,7 @@ commercial.details=Commercial Details commercial.details.administration=Commercial Details Administration committer=Committer common.weakness.enumeration=Common weakness enumeration +complexity=Complexity component=Component component.clearing.report=Component Clearing Report component.could.not.be.added=Component could not be added. @@ -539,6 +541,7 @@ error=Error error.adding.comment.to.clearing.request=Error adding comment to clearing request error.creating.clearing.request=Error creating clearing request! error.fetching.component.from.backend=Error fetching component from backend. +error.fetching.license.info.from.ISR.file=Error fetching license info from ISR file error.fetching.license.to.source.file.mapping=Error fetching license to source file mapping error.fetching.project.from.backend=Error fetching project from backend. error.fetching.release.from.backend=Error fetching release from backend. @@ -580,6 +583,7 @@ failed.to.delete.the.obligation=Failed to delete the obligation! failed.to.edit.secondary.departments.and.roles.for.user=Failed to edit Secondary Departments and Roles for user failed.to.fetch.clearing.request.from.db=Failed to fetch clearing request from database! failed.to.load=Failed to load! +failed.to.load.scanner.findings.with.error=Failed to load Scanner findings with error failed.to.load.source.file.with.error=Failed to load source file with error failed.to.reopen.clearing.request=Failed to reopen clearing request! failed.to.update.clearing.request=Failed to update clearing request! @@ -664,7 +668,7 @@ import=Import import.export=Import & Export import.failed=Import failed. import.projects=Import Projects -import.spdx.bom=Import SPDX BOM +import.spdx.bom=Import SBOM import.spdx.information=Import SPDX Information import.spdx.licenses=Import SPDX licenses incorrect=Incorrect @@ -692,10 +696,12 @@ is.a.subproject=Is a subproject is.checked=Is checked is.used.by.a.total.of.allUsingProjectsCount.usingProjects.size.visible.allUsingProjectsCount.usingProjects.size.restricted.projects=is used by a total of ${allUsingProjectsCount} (${usingProjects.size()} visible / ${allUsingProjectsCount - usingProjects.size()} restricted) projects. is.used.by.the.following.components=is used by the following components +isr.attachment.not.found.in.the.release=ISR (Initial Scan Reports) attachment not found in release keep.password.empty.to.reuse.old.password=Keep password empty to reuse old password key.user=Key user keyword.search=Keyword Search languages=Languages +large=Large last.7.days=Last 7 days last.30.days=Last 30 days last.export.happened.with.old.storage.format.so.restored.data.might.not.be.correct.for.attachments.of.releases.in.subprojects=Last export happened with old storage format, so restored data might not be correct for attachments of releases in subprojects. @@ -729,6 +735,7 @@ license.details=License Details license.fullname=License Fullname license.info=License Info license.info.header=License Info Header +license.information.not.found.in.isr=License information not found in ISR (Initial Scan Report) license.names=License names licenses=Licenses license.clearing=License Clearing @@ -809,6 +816,7 @@ more.info=More Info more.than.1000.documents.affected.the.merge.operation.might.time.out.in.consequence.only.some.of=More than 1000 documents affected. The merge operation might time out. In consequence only some of multiple.approved.cli.are.found.in.the.release=Multiple approved CLI are found in release multiple.attachments.with.same.name.or.content.cannot.be.present.in.attachment.list=Multiple attachments with same name or content cannot be present in attachment list. +multiple.isr.are.found.in.the.release=Multiple ISR (Initial Scan Reports) are found in release my.components=My Components my.projects=My Projects my.subscriptions=My Subscriptions @@ -978,6 +986,7 @@ please.select.the.project.visibility=Please select the project visibility! please.select.the.component.visibility=Please select the component visibility! please.select.the.server=Please select the server! prease.enter.a.valid.url=Please enter a valid URL! +possible.main.license.ids=Possible Main License Ids postpone.request=Postpone Request pre.evaluation.date.yyyy.mm.dd=Pre-evaluation date YYYY-MM-DD preferred.clearing.date=Preferred Clearing Date @@ -1201,6 +1210,7 @@ show.x.entries=Show _MENU_ entries side.by.side=Side by side sign.in=Sign In size=Size +small=Small software.platforms=Software Platforms some.of.the.stored.selection.could.not.be.restored.please.check.carefully=Some of the stored selection could not be restored. Please check carefully! some.projects.failed.to.import=Some projects failed to import @@ -1216,6 +1226,7 @@ source.code.self.made=Source Code Self-Made source.code.tool.made=Source Code Tool-Made source.component=Source component source.file.information.not.found.in.cli=Source file information not found in CLI +source.file.information.not.found.in.isr=Source file information not found in ISR source.release=Source release: source.vendor=Source vendor spdx.attachments=SPDX Attachments @@ -1271,6 +1282,7 @@ the.license.type.x.cannot.be.deleted.since.it.is.being.used.in.some.licenses.ple the.documents.might.be.changed.accordingly.in.this.case.just.restart.the.operation.as.long.as.the.source.release=the documents might be changed accordingly. In this case just restart the operation as long as the source release the.documents.might.be.changed.accordingly.in.this.case.just.restart.the.operation.as.long.as.the.source.vendor=the documents might be changed accordingly. In this case just restart the operation as long as the source vendor the.fields.assessor.department.and.assessement.date.will.be.taken.as.well="The fields <b>Assessor Department</b> and <b>Assessement Date</b> will be taken as well." +the.final.license.list.may.differ.based.on.the.conclusions.made.by.the.clearing.expert.and.is.made.available.as.component.license.information.cli=The final license lists may differ based on the conclusions made by the clearing expert and is made available as Component License Information (CLI). the.following.documents.will.be.affected.by.this.merge.and.are.changed.accordingly=The following documents will be affected by this merge and are changed accordingly: the.following.duplicate.identifiers.were.found=The following duplicate identifiers were found. the.following.projects.will.be.imported=The following projects will be imported: @@ -1292,6 +1304,7 @@ this.error.can.lead.to.inconsistencies.in.the.database.please.inform.the.adminis this.error.can.lead.to.inconsistencies.in.the.database.please.inform.the.administrator.with.the.following.information=This error can lead to inconsistencies in the database. Please inform the administrator with the following information: this.function.is.meant.to.be.followed.by.a.new.license.import=This function is meant to be followed by a new license import. this.is.auto.generated.comment=*** <b>This is auto-generated comment</b> *** <br> +this.is.only.an.initial.scanner.isr.finding.of.the.contained.licenses.in.the.uploaded.source.file=This is only an Initial Scanner (ISR) finding of the contained licenses in the uploaded source file. this.license.is.unchecked=This license is <b>UNCHECKED</b>. this.obligation.is.not.associated.with.linked.release=This obligation is not associated with linked release this.project.contains=This project contains @@ -1301,7 +1314,9 @@ this.release.x.contains.1=This release <span data-name="name"></span> contains title=Title to=To to.be.replaced=To be replaced +to.view.the.files.corresponding.to.each.licenses.go.to.clearing.details.tab.of.respective.release=To view the files corresponding to each licenses, go to "Clearing Details" tab of respective Release. today=Today +total.number.of.files=Total number of files enable.svm=Enable SVM obligation=Obligation obligation.details=Obligation Details @@ -1384,12 +1399,14 @@ VerificationState-CHECKED=It is verified. VerificationState-INCORRECT=It was decided that the verification should be rejected. VerificationState-NOT_CHECKED=No one has yet looked at this and verified it. version=Version +very.large=Very Large view.clearing.request=View Clearing Request view.clearing.request.failure.message=We are not able to find the clearing request [ID: <b data-name="crId"></b>] in db for the project <b data-name="projectName"></b><br>Try again later. view.by.releases=View by Releases view.change.logs=View Change Logs view.file.list=View file list view.obligations=View Obligations +view.scanner.findings.license=View scanner findings (License) visibility=Visibility Visibility=Private: Only visible by creator (and admin which applies to all visibility levels) 
Me and Moderators: Visible by creator and moderators 
Group and Moderators: All users of the same group and the moderators 
Everyone: Every user who is logged into the system Visibility-BUISNESSUNIT_AND_MODERATORS=All users of the same group and the moderators. @@ -1496,6 +1513,30 @@ object=Object import.obligation.element=Import Obligation Element input=Input an.obligation.with.the.same.name.already.exists=An Obligation with the same name already exists. +## spdx multi language +this.field.must.be.not.empty=This field must be not empty! +invalid.format=Invalid format! +spdx.document=SPDX Document +enter.data.license=Enter Data License +enter.spdx.document.name=Enter SPDX Document Name +enter.spdx.document.namespace=Enter SPDX Document Namespace +changes.in.external.document.references=Changes in External Document References +changes.in.creator=Changes in Creator +changes.in.checksum=Changes in Checksum +changes.in.annotaions.information=Changes in Annotations Information +changes.in.external.references=Changes in External References +changes.in.package.verification.code=Changes in Package Verification Code +changes.in.snippets.information=Changes in Snippets Information +changes.in.relationship.information=Changes in Relationship Information +changes.in.other.licensing.information.detecteds=Changes in Other Licensing Information Detecteds +current.package.information=Current Package Information +current.spdxdocument=Current SPDX Document +current.document.creation.information=Current Document Creation Information +export.sbom=Export SBOM +export.sbom.have.some.warning=Export SBOM have some warning +export.sbom.have.some.error=Export SBOM have some error +no.warning.error.export.sbom=No warning/error export sbom + ## Refer to http://cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/ and add your datatables language datatables.lang=https://cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/English.json diff --git a/frontend/sw360-portlet/src/main/resources/content/Language_ja.properties b/frontend/sw360-portlet/src/main/resources/content/Language_ja.properties index 5788a72cdc..94920263db 100644 --- a/frontend/sw360-portlet/src/main/resources/content/Language_ja.properties +++ b/frontend/sw360-portlet/src/main/resources/content/Language_ja.properties @@ -105,6 +105,7 @@ authorization.header.authorization.token.api.token=認証ヘッダ, (認証 API- auto.refresh.in=自動更新 auto-refresh.in.5=5分毎に自動更新 ba-bl.slash.group=BA-BL/Group +based.on.license.file.count=based on license file count basic.fields=基本分野 basic.info=基本情報 bazaar=Bazaar @@ -241,6 +242,7 @@ commercial.details=商用物の詳細情報 commercial.details.administration=商用物の詳細情報管理 committer=コミッター common.weakness.enumeration=CWE(common.weakness.enumeration) +complexity=Complexity component=コンポーネント component.clearing.report=コンポーネントクリアリングレポート component.could.not.be.added=コンポーネントを追加できませんでした。 @@ -539,6 +541,7 @@ error=エラー error.adding.comment.to.clearing.request=クリアリングリクエスト コメント追加エラー error.creating.clearing.request=クリアリングリクエストコメント追加エラー! error.fetching.component.from.backend=バックエンドからのコンポーネントの取得する際にエラーが発生 +error.fetching.license.info.from.ISR.file=Error fetching license info from ISR file error.fetching.license.to.source.file.mapping=Error fetching license to source file mapping error.fetching.project.from.backend=バックエンドからプロジェクトを取得する際にエラーが発生 error.fetching.release.from.backend=バックエンドからリリースを取得する際にエラーが発生 @@ -580,6 +583,7 @@ failed.to.delete.the.obligation=オブリゲーション削除失敗! failed.to.edit.secondary.departments.and.roles.for.user=第2所属部門とロールの編集失敗! failed.to.fetch.clearing.request.from.db=データベースからクリアリングリクエスト取得失敗! failed.to.load=ロード失敗! +failed.to.load.scanner.findings.with.error=Failed to load Scanner findings with error failed.to.load.source.file.with.error=エラーによりソースファイルのロードに失敗しました. failed.to.reopen.clearing.request=クリアリングリクエストを再び開くのに失敗しました! failed.to.update.clearing.request=クリアリングリクエストを更新するのに失敗しました! @@ -664,7 +668,7 @@ import=インポート import.export=インポート& エクスポート import.failed=インポートに失敗しました。 import.projects=プロジェクトのインポート -import.spdx.bom=SPDX BOMのインポート +import.spdx.bom=SBOMのインポート import.spdx.information=SPDX情報のインポート import.spdx.licenses=SPDXライセンスのインポート incorrect=間違い @@ -692,10 +696,12 @@ is.a.subproject=サブプロジェクトは is.checked=チェック済 is.used.by.a.total.of.allUsingProjectsCount.usingProjects.size.visible.allUsingProjectsCount.usingProjects.size.restricted.projects=は、${allUsingProjectsCount} (${usingProjects.size()}見えるプロジェクト / ${allUsingProjectsCount - usingProjects.size()}制限されたプロジェクトの合計${allUsingProjectsCount})で利用されています。 is.used.by.the.following.components=は以下のコンポーネントで使用されます。 +isr.attachment.not.found.in.the.release=ISR (Initial Scan Reports) attachment not found in release keep.password.empty.to.reuse.old.password=古いパスワードを再利用するためにはパスワードを空にしてください key.user=キーユーザ keyword.search=キーワード検索 languages=言語 +large=Large last.7.days=過去7日間 last.30.days=過去30日間 last.export.happened.with.old.storage.format.so.restored.data.might.not.be.correct.for.attachments.of.releases.in.subprojects=最後のエクスポートは古いストレージ形式で行われたため、サブプロジェクト内のリリースの添付ファイルでは復元されたデータが正しくない可能性があります。 @@ -729,6 +735,7 @@ license.details=ライセンスの詳細 license.fullname=ライセンスのフルネーム license.info=ライセンス情報 license.info.header=ライセンス情報ヘッダ +license.information.not.found.in.isr=License information not found in ISR (Initial Scan Report) license.names=ライセンス名 licenses=ライセンス license.clearing=ライセンスクリアリング @@ -809,6 +816,7 @@ more.info=詳細情報 more.than.1000.documents.affected.the.merge.operation.might.time.out.in.consequence.only.some.of=1000以上のドキュメントが影響を受けています。結果として、一部のマージ操作がタイムアウトする可能性があります。 multiple.approved.cli.are.found.in.the.release=承認された複数のCLIがリリースに見つかりました. multiple.attachments.with.same.name.or.content.cannot.be.present.in.attachment.list=同じ名前または内容の複数の添付ファイルが添付ファイル リストに存在することはできません。 +multiple.isr.are.found.in.the.release=Multiple ISR (Initial Scan Reports) are found in release my.components=マイコンポーネント my.projects=マイプロジェクト my.subscriptions=マイサブスクリプション @@ -978,6 +986,7 @@ please.select.the.project.visibility=プロジェクトの可視性を選択し please.select.the.component.visibility=コンポーネントの可視性を選択してください please.select.the.server=サーバーを選択してください prease.enter.a.valid.url=適切なURLを入力してください +possible.main.license.ids=Possible Main License Ids postpone.request=リクエスト延期 pre.evaluation.date.yyyy.mm.dd=事前評価日 YYYY-MM-DD preferred.clearing.date=理想的なクリアリング日時 @@ -1201,6 +1210,7 @@ show.x.entries=MENU_エントリを表示する side.by.side=横に並んで sign.in=サインイン size=サイズ +small=Small software.platforms=ソフトウェアプラットフォーム some.of.the.stored.selection.could.not.be.restored.please.check.carefully=保存されている選択範囲の一部が復元できませんでした。よく確認してください。 some.projects.failed.to.import=一部のプロジェクトでインポートに失敗しました @@ -1216,6 +1226,7 @@ source.code.self.made=独自・自作のソースコード source.code.tool.made=ツール生成のソースコード source.component=ソースコンポーネント source.file.information.not.found.in.cli=ソースファイル情報がCLIの中に見つからない +source.file.information.not.found.in.isr=Source file information not found in ISR source.release=ソースリリース。 source.vendor=ソースベンダ spdx.attachments=SPDXアタッチメント @@ -1271,6 +1282,7 @@ the.license.type.x.cannot.be.deleted.since.it.is.being.used.in.some.licenses.ple the.documents.might.be.changed.accordingly.in.this.case.just.restart.the.operation.as.long.as.the.source.release=を使用すると、ドキュメントが変更される可能性があります。この場合は、ソースリリースの the.documents.might.be.changed.accordingly.in.this.case.just.restart.the.operation.as.long.as.the.source.vendor=を使用すると、それに応じてドキュメントが変更される可能性があります。この場合は、ソースベンダーの the.fields.assessor.department.and.assessement.date.will.be.taken.as.well=<b>Assessor Department</b> と <b>Assessement Date</b> のフィールドも同様に取り込まれます。 +the.final.license.list.may.differ.based.on.the.conclusions.made.by.the.clearing.expert.and.is.made.available.as.component.license.information.cli=The final license lists may differ based on the conclusions made by the clearing expert and is made available as Component License Information (CLI). the.following.documents.will.be.affected.by.this.merge.and.are.changed.accordingly=以下の文書はこのマージの影響を受け、それに応じて変更されます。 the.following.duplicate.identifiers.were.found=以下の重複した識別子が見つかりました。 the.following.projects.will.be.imported=以下のプロジェクトがインポートされます。 @@ -1292,6 +1304,7 @@ this.error.can.lead.to.inconsistencies.in.the.database.please.inform.the.adminis this.error.can.lead.to.inconsistencies.in.the.database.please.inform.the.administrator.with.the.following.information=このエラーにより、データベースの不整合が発生する可能性があります。管理者に以下の情報をお知らせください。 this.function.is.meant.to.be.followed.by.a.new.license.import=この機能は、新しいライセンスのインポートに続くことを意図しています。 this.is.auto.generated.comment=*** <b>This is auto-generated comment</b> *** <br> +this.is.only.an.initial.scanner.isr.finding.of.the.contained.licenses.in.the.uploaded.source.file=This is only an Initial Scanner (ISR) finding of the contained licenses in the uploaded source file. this.license.is.unchecked=このライセンスは<b>UNCHECKED</b>です。 this.obligation.is.not.associated.with.linked.release=このオブリゲーションは、リンクされたリリースと無関係 this.project.contains=このプロジェクトには以下のものが含まれています。 @@ -1301,7 +1314,9 @@ this.release.x.contains.1=今回のリリース <span data-name="name"></span> title=タイトル to=To to.be.replaced=置き換わる +to.view.the.files.corresponding.to.each.licenses.go.to.clearing.details.tab.of.respective.release=To view the files corresponding to each licenses, go to "Clearing Details" tab of respective Release. today=本日 +total.number.of.files=Total number of files enable.svm= SVM(Security Vulnerability Monitor)を有効 obligation=オブリゲーション obligation.details=オブリゲーション詳細 @@ -1384,12 +1399,14 @@ VerificationState-CHECKED=確認済みです。 VerificationState-INCORRECT=検証を拒否することが決定されました。 VerificationState-NOT_CHECKED=まだ誰もこれを見て検証していない。 version=バージョン +very.large=Very Large view.clearing.request=クリアリングリクエストビュー view.clearing.request.failure.message=クリアリングリクエストを発見できない [ID: <b data-name="crId"></b>] プロジェクトのデータベース <b data-name="projectName"></b><br>再試行してください. view.by.releases=リリースごとに表示 view.change.logs=変更ログビュー view.file.list=ファイルリストビュー view.obligations=オブリゲーションを表示 +view.scanner.findings.license=View scanner findings (License) visibility=可視範囲 Visibility=Private: 作成者(およびすべての表示レベルに適用される管理者)のみが表示できます。
Me and Moderators: 作成者とモデレーターが表示できます。
Group and Moderators: 同じグループメンバーとモデレータが見れる 
Everyone: このシステムにログインしている人全員が見れる Visibility-BUISNESSUNIT_AND_MODERATORS=同じグループのすべてのユーザーとモデレーター。 diff --git a/frontend/sw360-portlet/src/main/resources/content/Language_vi.properties b/frontend/sw360-portlet/src/main/resources/content/Language_vi.properties index 59fc89e74b..d972c71ab4 100644 --- a/frontend/sw360-portlet/src/main/resources/content/Language_vi.properties +++ b/frontend/sw360-portlet/src/main/resources/content/Language_vi.properties @@ -106,6 +106,7 @@ authorization.header.authorization.token.api.token=Phần đầu mã truy cập. auto.refresh.in=Tự động làm mới trong auto-refresh.in.5=Tự động làm mới trong 5 ba-bl.slash.group=BA-BL/Group +based.on.license.file.count=based on license file count basic.fields=Các trường cơ bản bazaar=Chợ basic.info=Basic Info @@ -242,6 +243,7 @@ commercial.details=Chi tiết thương mại commercial.details.administration=Quản trị chi tiết thương mại committer=Cộng đồng common.weakness.enumeration=Bảng liệt kê điểm yếu chung +complexity=Complexity component=Thành phần component.clearing.report=Báo cáo dọn dẹp thành phần component.could.not.be.added=Thành phần không thể được thêm vào. @@ -543,6 +545,7 @@ error=lỗi error.adding.comment.to.clearing.request=Lỗi thêm bình luận để xóa yêu cầu error.creating.clearing.request=Error creating clearing request! error.fetching.component.from.backend=Lỗi tìm nạp thành phần từ Backend. +error.fetching.license.info.from.ISR.file=Error fetching license info from ISR file error.fetching.license.to.source.file.mapping=Error fetching license to source file mapping error.fetching.project.from.backend=Lỗi tìm nạp dự án từ Backend. error.fetching.release.from.backend=Lỗi tìm nạp bản phát hành từ Backend. @@ -584,6 +587,7 @@ failed.to.delete.the.obligation=Không thể xóa nghĩa vụ! failed.to.edit.secondary.departments.and.roles.for.user=Failed to edit Secondary Departments and Roles for user failed.to.fetch.clearing.request.from.db=Failed to fetch clearing request from database! failed.to.load=Failed to load! +failed.to.load.scanner.findings.with.error=Failed to load Scanner findings with error failed.to.load.source.file.with.error=Failed to load source file with error failed.to.reopen.clearing.request=Failed to reopen clearing request! failed.to.update.clearing.request=Failed to update clearing request! @@ -668,7 +672,7 @@ import=Nhập import.export=Nhập và xuất import.failed=Nhập thất bại. import.projects=Nhập các dự án -import.spdx.bom=Nhập SPDX BOM +import.spdx.bom=Nhập SBOM import.spdx.information=Nhập thông tin SPDX import.spdx.licenses=Nhập giấy phép SPDX incorrect=Sai @@ -696,10 +700,12 @@ is.a.subproject=Là một tiểu dự án is.checked=Đã được kiểm tra is.used.by.a.total.of.allUsingProjectsCount.usingProjects.size.visible.allUsingProjectsCount.usingProjects.size.restricted.projects=được sử dụng bởi tổng số các dự án ${allUsingProjectsCount} (${usingProjects.size()} nhìn thấy / ${allUsingProjectsCount - usedProjects.size()}). is.used.by.the.following.components=được sử dụng bởi các thành phần sau +isr.attachment.not.found.in.the.release=ISR (Initial Scan Reports) attachment not found in release keep.password.empty.to.reuse.old.password=Keep password empty to reuse old password key.user=Người dùng chính keyword.search=Tìm kiếm từ khóa languages=Ngôn ngữ +large=Large last.7.days=Last 7 days last.30.days=Last 30 days last.export.happened.with.old.storage.format.so.restored.data.might.not.be.correct.for.attachments.of.releases.in.subprojects=Lần xuất cuối cùng xảy ra với định dạng lưu trữ cũ, do đó dữ liệu được khôi phục có thể không chính xác đối với tệp đính kèm của bản phát hành trong các dự án con. @@ -733,6 +739,7 @@ license.details=Chi tiết giấy phép license.fullname=Tên đầy đủ của giấy phép license.info=Thông tin giấy phép license.info.header=Tiêu đề thông tin giấy phép +license.information.not.found.in.isr=License information not found in ISR (Initial Scan Report) license.names=Tên giấy phép licenses=Giấy phép license.clearing=License Clearing @@ -813,6 +820,7 @@ more.info=Thêm thông tin more.than.1000.documents.affected.the.merge.operation.might.time.out.in.consequence.only.some.of=Hơn 1000 tài liệu bị ảnh hưởng. Hoạt động hợp nhất có thể hết thời gian. Do đó, chỉ có một số multiple.approved.cli.are.found.in.the.release=Multiple approved CLI are found in release multiple.attachments.with.same.name.or.content.cannot.be.present.in.attachment.list=Nhiều tệp đính kèm có cùng tên hoặc nội dung không thể có trong danh sách tệp đính kèm. +multiple.isr.are.found.in.the.release=Multiple ISR (Initial Scan Reports) are found in release my.components=Thành phần của tôi my.projects=Dự án của tôi my.subscriptions=Đăng ký của tôi @@ -977,6 +985,7 @@ please.select.the.project.visibility=Vui lòng chọn tầm nhìn của dự án please.select.the.component.visibility=Please select the component visibility! please.select.the.server=Vui lòng chọn máy chủ! prease.enter.a.valid.url=Please enter a valid URL! +possible.main.license.ids=Possible Main License Ids postpone.request=Yêu cầu trì hoãn pre.evaluation.date.yyyy.mm.dd=Ngày đánh giá trước YYYY-MM-DD preferred.clearing.date=Preferred Clearing Date @@ -1198,6 +1207,7 @@ show.x.entries=Hiện thị _MENU_ mục side.by.side=Bên cạnh nhau sign.in=Đăng nhập size=Kích thước +small=Small software.platforms=Nền tảng phần mềm some.of.the.stored.selection.could.not.be.restored.please.check.carefully=Một số lựa chọn được lưu trữ không thể được khôi phục. Vui lòng kiểm tra cẩn thận! some.projects.failed.to.import=Một số dự án không thể nhập @@ -1213,6 +1223,7 @@ source.code.self.made=Mã nguồn tự làm source.code.tool.made=Mã nguồn được tạo ra source.component=Thành phần nguồn source.file.information.not.found.in.cli=Source file information not found in CLI +source.file.information.not.found.in.isr=Source file information not found in ISR source.release=Phát hành nguồn: source.vendor=Nhà cung cấp nguồn spdx.attachments=Tệp đính kèm SPDX @@ -1268,6 +1279,7 @@ the.license.type.x.cannot.be.deleted.since.it.is.being.used.in.some.licenses.ple the.documents.might.be.changed.accordingly.in.this.case.just.restart.the.operation.as.long.as.the.source.release=các tài liệu có thể được thay đổi cho phù hợp. Trong trường hợp này, chỉ cần khởi động lại hoạt động miễn là phát hành nguồn the.documents.might.be.changed.accordingly.in.this.case.just.restart.the.operation.as.long.as.the.source.vendor=các tài liệu có thể được thay đổi cho phù hợp. Trong trường hợp này, chỉ cần khởi động lại hoạt động miễn là nhà cung cấp nguồn the.fields.assessor.department.and.assessement.date.will.be.taken.as.well="Những trường <b>Người giám định</b> và <b>Ngày đánh giá</b> cũng sẽ được thực hiện." +the.final.license.list.may.differ.based.on.the.conclusions.made.by.the.clearing.expert.and.is.made.available.as.component.license.information.cli=The final license lists may differ based on the conclusions made by the clearing expert and is made available as Component License Information (CLI). the.following.documents.will.be.affected.by.this.merge.and.are.changed.accordingly=Các tài liệu sau đây sẽ bị ảnh hưởng bởi sự hợp nhất này và được thay đổi tương ứng: the.following.duplicate.identifiers.were.found=Các định danh trùng lặp sau đây đã được tìm thấy. the.following.projects.will.be.imported=Các dự án sau đây sẽ được nhập: @@ -1289,6 +1301,7 @@ this.error.can.lead.to.inconsistencies.in.the.database.please.inform.the.adminis this.error.can.lead.to.inconsistencies.in.the.database.please.inform.the.administrator.with.the.following.information=Lỗi này có thể dẫn đến sự không nhất quán trong cơ sở dữ liệu. Vui lòng thông báo cho quản trị viên với các thông tin sau: this.function.is.meant.to.be.followed.by.a.new.license.import=Chức năng này có nghĩa là được theo sau bởi nhập một giấy phép mới. this.is.auto.generated.comment=*** <b>This is auto-generated comment</b> *** <br> +this.is.only.an.initial.scanner.isr.finding.of.the.contained.licenses.in.the.uploaded.source.file=This is only an Initial Scanner (ISR) finding of the contained licenses in the uploaded source file. this.license.is.unchecked=Giấy phép này là <b>CHƯA KIỂM TRA</b>. this.obligation.is.not.associated.with.linked.release=Nghĩa vụ này không liên quan đến phát hành liên kết this.project.contains=Dự án này chứa @@ -1298,7 +1311,9 @@ this.release.x.contains.1=Bản phát hành <span data-name="name"></span> chứ title=Tiêu đề to=To to.be.replaced=Sẽ được thay thế +to.view.the.files.corresponding.to.each.licenses.go.to.clearing.details.tab.of.respective.release=To view the files corresponding to each licenses, go to "Clearing Details" tab of respective Release. today=Today +total.number.of.files=Total number of files #oblig=Việc cần làm obligation=Obligation #oblig.details=Chi tiết việc cần làm @@ -1388,12 +1403,14 @@ VerificationState-CHECKED=Nó được xác minh. VerificationState-INCORRECT=Nó đã được quyết định rằng việc xác minh nên bị từ chối. VerificationState-NOT_CHECKED=Không ai đã xem xét điều này và xác minh nó. version=Phiên bản +very.large=Very Large view.clearing.request=View Clearing Request view.clearing.request.failure.message=We are not able to find the clearing request [ID: <b data-name="crId"></b>] in db for the project <b data-name="projectName"></b><br>Try again later. view.by.releases=Xem bởi các bản phát hành view.change.logs=View Change Logs view.file.list=View file list view.obligations=Xem nghĩa vụ +view.scanner.findings.license=View scanner findings (License) visibility=Tầm nhìn Visibility=Riêng tư: Chỉ hiển thị bởi người tạo (và quản trị viên áp dụng cho tất cả các mức độ hiển thị) 
Tôi và Người kiểm duyệt: Hiển thị bởi người tạo và người kiểm duyệt 
Nhóm và Người kiểm duyệt: Tất cả người dùng của cùng một nhóm và người kiểm duyệt 
Mọi người: Mọi người dùng đăng nhập vào hệ thống Visibility-BUISNESSUNIT_AND_MODERATORS=Tất cả người dùng của cùng một nhóm và người điều hành. @@ -1501,6 +1518,11 @@ object=Đối tượng import.obligation.element=Nhập nghĩa vụ thành phần input=Nhập an.obligation.with.the.same.name.already.exists=Nghĩa vụ có cùng tên đã tồn tại. +export.sbom=Xuất SBOM +export.sbom.have.some.warning=Xuất SBOM có một số cảnh báo +export.sbom.have.some.error=Xuất SBOM có một số lỗi +no.warning.error.export.sbom=Không có lỗi/cảnh báo khi xuất + ## Refer to http://cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/ and add your datatables language datatables.lang=https://cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/Vietnamese.json diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java index a8893a1e65..c1ba455889 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/DatabaseSettings.java @@ -43,6 +43,7 @@ public class DatabaseSettings { public static final String COUCH_DB_CONFIG; public static final String COUCH_DB_USERS; public static final String COUCH_DB_VM; + public static final String COUCH_DB_SPDX; public static final int LUCENE_SEARCH_LIMIT; public static final boolean LUCENE_LEADING_WILDCARD; @@ -63,6 +64,7 @@ public class DatabaseSettings { COUCH_DB_CONFIG = props.getProperty("couchdb.config", "sw360config"); COUCH_DB_USERS = props.getProperty("couchdb.usersdb", "sw360users"); COUCH_DB_VM = props.getProperty("couchdb.vulnerability_management", "sw360vm"); + COUCH_DB_SPDX = props.getProperty("couchdb.sw360spdx", "sw360spdx"); LUCENE_SEARCH_LIMIT = Integer.parseInt(props.getProperty("lucenesearch.limit", "25")); LUCENE_LEADING_WILDCARD = Boolean.parseBoolean(props.getProperty("lucenesearch.leading.wildcard", "false")); diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/Moderator.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/Moderator.java index 8372a812ae..6d9c9bd321 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/Moderator.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/Moderator.java @@ -17,7 +17,6 @@ import org.eclipse.sw360.datahandler.thrift.ThriftClients; import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentContent; -import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.moderation.ModerationRequest; import org.eclipse.sw360.datahandler.thrift.moderation.ModerationService; import org.apache.logging.log4j.LogManager; @@ -84,10 +83,6 @@ protected T updateBasicField(U field, FieldMetaData fieldMetaData, T document, T case TType.STRING: case TType.ENUM: - if (document instanceof Release && ((field == Release._Fields.NAME || field == Release._Fields.VERSION) - && "Dummy_Value".equals(documentAdditions.getFieldValue(field)))) { - break; - } document.setFieldValue(field, documentAdditions.getFieldValue(field)); break; case TType.I32: diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java index 79f9e0539b..4916649f68 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Constants.java @@ -72,6 +72,9 @@ public class SW360Constants { public static final String TYPE_VULNERABILITYDTO = "vulDTO"; public static final String TYPE_OBLIGATIONELEMENT = "obligationElement"; public static final String TYPE_OBLIGATIONNODE = "obligationNode"; + public static final String TYPE_SPDX_DOCUMENT = "SPDXDocument"; + public static final String TYPE_SPDX_DOCUMENT_CREATION_INFO = "documentCreationInformation"; + public static final String TYPE_SPDX_PACKAGE_INFO = "packageInformation"; /** * Hashmap containing the name field for each type. diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Utils.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Utils.java index 6b066e3687..6c238e8209 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Utils.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/SW360Utils.java @@ -38,6 +38,9 @@ import org.eclipse.sw360.datahandler.thrift.users.UserService; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.Vulnerability; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -303,6 +306,27 @@ public static String printName(User user) { return user.getEmail(); } + public static String printName(SPDXDocument spdx) { + if (spdx == null || isNullOrEmpty(spdx.getId())) { + return "New SPDX Document"; + } + return spdx.getId(); + } + + public static String printName(DocumentCreationInformation documentCreationInfo) { + if (documentCreationInfo == null || isNullOrEmpty(documentCreationInfo.getName())) { + return "New SPDX Document Creation Info"; + } + return documentCreationInfo.getName(); + } + + public static String printName(PackageInformation packageInfo) { + if (packageInfo == null || isNullOrEmpty(packageInfo.getName())) { + return "New SPDX Package Info"; + } + return packageInfo.getName(); + } + public static String printFullname(User user) { if (user == null || isNullOrEmpty(user.getEmail())) { return "New User"; diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/ThriftEnumUtils.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/ThriftEnumUtils.java index 3ede2b1497..6c71fa2151 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/ThriftEnumUtils.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/common/ThriftEnumUtils.java @@ -112,6 +112,7 @@ private ThriftEnumUtils() { .put(AttachmentType.README_OSS, "ReadMe OSS") .put(AttachmentType.OTHER, "Other") .put(AttachmentType.SECURITY_ASSESSMENT, "Security Assessment") + .put(AttachmentType.SBOM, "SBOM") .put(AttachmentType.INITIAL_SCAN_REPORT, "Initial Scan Report") .build(); @@ -137,6 +138,7 @@ private ThriftEnumUtils() { .put(AttachmentType.README_OSS, "RDM") .put(AttachmentType.OTHER, "OTH") .put(AttachmentType.SECURITY_ASSESSMENT, "SECA") + .put(AttachmentType.SBOM, "SBOM") .put(AttachmentType.INITIAL_SCAN_REPORT, "ISR") .build(); diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseMixInForChangeLog.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseMixInForChangeLog.java index 4f6ccd7a31..fdc8191ccd 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseMixInForChangeLog.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseMixInForChangeLog.java @@ -21,6 +21,16 @@ import org.eclipse.sw360.datahandler.thrift.projects.ProjectProjectRelationship; import org.eclipse.sw360.datahandler.thrift.projects.ObligationStatusInfo; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.Annotations; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.CheckSum; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.Creator; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.ExternalDocumentReferences; +import org.eclipse.sw360.datahandler.thrift.spdx.otherlicensinginformationdetected.OtherLicensingInformationDetected; +import org.eclipse.sw360.datahandler.thrift.spdx.relationshipsbetweenspdxelements.RelationshipsBetweenSPDXElements; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetRange; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.ExternalReference; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageVerificationCode; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -235,4 +245,125 @@ public static abstract class ProjectReleaseRelationshipMixin extends ProjectRele }) public static abstract class ProjectProjectRelationshipMixin extends ProjectProjectRelationship { } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setAlgorithm", + "setChecksumValue", + "setIndex" + }) + public static abstract class CheckSumMixin extends CheckSum { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setAnnotator", + "setAnnotationDate", + "setAnnotationType", + "setSpdxIdRef", + "setAnnotationComment", + "setIndex" + }) + public static abstract class AnnotationsMixin extends Annotations { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setExternalDocumentId", + "setChecksum", + "setSpdxDocument", + "setIndex" + }) + public static abstract class ExternalDocumentReferencesMixin extends ExternalDocumentReferences { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setType", + "setValue", + "setIndex" + }) + public static abstract class CreatorMixin extends Creator { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setLicenseId", + "setExtractedText", + "setLicenseName", + "setLicenseCrossRefs", + "licenseCrossRefsSize", + "licenseCrossRefsIterator", + "setLicenseComment", + "setIndex" + }) + public static abstract class OtherLicensingInformationDetectedMixin extends OtherLicensingInformationDetected { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setExcludedFiles", + "excludedFilesSize", + "excludedFilesIterator", + "setIndex", + "setValue" + }) + public static abstract class PackageVerificationCodeMixin extends PackageVerificationCode { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setReferenceCategory", + "setReferenceLocator", + "setReferenceType", + "setComment", + "setIndex" + }) + public static abstract class ExternalReferenceMixin extends ExternalReference { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setSpdxElementId", + "setRelationshipType", + "setRelatedSpdxElement", + "setRelationshipComment", + "setIndex" + }) + public static abstract class RelationshipsBetweenSPDXElementsMixin extends RelationshipsBetweenSPDXElements { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setSPDXID", + "setSnippetFromFile", + "setSnippetRanges", + "snippetRangesSize", + "snippetRangesIterator", + "setLicenseConcluded", + "setLicenseInfoInSnippets", + "licenseInfoInSnippetsSize", + "licenseInfoInSnippetsIterator", + "setLicenseComments", + "setCopyrightText", + "setComment", + "setName", + "setSnippetAttributionText", + "setIndex", + "spdxid" + }) + public static abstract class SnippetInformationMixin extends SnippetInformation { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setRangeType", + "setStartPointer", + "setEndPointer", + "setReference", + "setIndex" + }) + public static abstract class SnippetRangeMixin extends SnippetRange { + } + } diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseMixInForSPDXDocument.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseMixInForSPDXDocument.java new file mode 100644 index 0000000000..8c60f2d488 --- /dev/null +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/couchdb/DatabaseMixInForSPDXDocument.java @@ -0,0 +1,226 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.couchdb; + +import org.eclipse.sw360.datahandler.thrift.ProjectReleaseRelationship; +import org.eclipse.sw360.datahandler.thrift.attachments.Attachment; +import org.eclipse.sw360.datahandler.thrift.changelogs.ChangeLogs; +import org.eclipse.sw360.datahandler.thrift.changelogs.ChangedFields; +import org.eclipse.sw360.datahandler.thrift.changelogs.ReferenceDocData; +import org.eclipse.sw360.datahandler.thrift.components.COTSDetails; +import org.eclipse.sw360.datahandler.thrift.components.ClearingInformation; +import org.eclipse.sw360.datahandler.thrift.components.EccInformation; +import org.eclipse.sw360.datahandler.thrift.components.Repository; +import org.eclipse.sw360.datahandler.thrift.projects.ObligationStatusInfo; +import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; +import org.eclipse.sw360.datahandler.thrift.spdx.annotations.Annotations; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.CheckSum; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.Creator; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.ExternalDocumentReferences; +import org.eclipse.sw360.datahandler.thrift.spdx.otherlicensinginformationdetected.OtherLicensingInformationDetected; +import org.eclipse.sw360.datahandler.thrift.spdx.relationshipsbetweenspdxelements.RelationshipsBetweenSPDXElements; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation.SnippetRange; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.ExternalReference; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageVerificationCode; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; + + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +/* + * @author nam1.nguyenphuong@toshiba.co.jp + */ +public class DatabaseMixInForSPDXDocument { + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setAnnotator", + "setAnnotationDate", + "setAnnotationType", + "setSpdxIdRef", + "setAnnotationComment", + "setIndex", + "index" + }) + public static abstract class AnnotationsMixin extends Annotations { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setIndex", + "index", + "setAlgorithm", + "setChecksumValue" + }) + public static abstract class CheckSumMixin extends CheckSum { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setReferenceType", + "setIndex", + "setReferenceCategory", + "index", + "setComment", + "setReferenceLocator" + }) + public static abstract class ExternalReferenceMixin extends ExternalReference { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setExternalDocumentId", + "setChecksum", + "setSpdxDocument", + "setIndex", + "index" + }) + public static abstract class ExternalDocumentReferencesMixin extends ExternalDocumentReferences { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setSPDXID", + "setSnippetFromFile", + "setSnippetRanges", + "snippetRangesSize", + "snippetRangesIterator", + "setLicenseConcluded", + "setLicenseInfoInSnippets", + "licenseInfoInSnippetsSize", + "licenseInfoInSnippetsIterator", + "setLicenseComments", + "setCopyrightText", + "setComment", + "setName", + "setSnippetAttributionText", + "setIndex", + "spdxid", + "index" + }) + public static abstract class SnippetInformationMixin extends SnippetInformation { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setRangeType", + "setStartPointer", + "setEndPointer", + "setReference", + "setIndex", + "index" + }) + public static abstract class SnippetRangeMixin extends SnippetRange { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setSpdxElementId", + "setRelationshipType", + "setRelatedSpdxElement", + "setRelationshipComment", + "setIndex", + "index" + }) + public static abstract class RelationshipsBetweenSPDXElementsMixin extends RelationshipsBetweenSPDXElements { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setLicenseId", + "setExtractedText", + "setLicenseName", + "setLicenseCrossRefs", + "licenseCrossRefsSize", + "licenseCrossRefsIterator", + "setLicenseComment", + "setIndex", + "index" + }) + public static abstract class OtherLicensingInformationDetectedMixin extends OtherLicensingInformationDetected { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setExcludedFiles", + "excludedFilesSize", + "excludedFilesIterator", + "setIndex", + "setValue" + }) + public static abstract class PackageVerificationCodeMixin extends PackageVerificationCode { + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties({ + "setAnnotations", + "type", + "setPermissions", + "permissions", + "setAttributionText", + "setId", + "id", + "attributionTextSize", + "setLicenseDeclared", + "setSupplier", + "setLicenseComments", + "checksumsIterator", + "setFilesAnalyzed", + "setPackageFileName", + "setPackageComment", + "setVersionInfo", + "revision", + "setExternalRefs", + "setName", + "permissionsSize", + "setDescription", + "setSourceInfo", + "setHomepage", + "setRevision", + "setLicenseInfoFromFiles", + "checksumsSize", + "setChecksums", + "annotationsSize", + "setSummary", + "setOriginator", + "externalRefsSize", + "setPackageVerificationCode", + "setLicenseConcluded", + "spdxDocumentId", + "setType", + "documentState", + "setSpdxDocumentId", + "setDownloadLocation", + "licenseInfoFromFilesSize", + "setCopyrightText", + "setValue", + "excludedFilesSize", + "setExcludedFiles", + "createdBy", + "setCreatedBy", + "setDocumentState", + "setSPDXID", + "annotationsIterator", + "externalRefsIterator", + "attributionTextIterator", + "index", + "setIndex", + "relationshipsSize", + "spdxid", + "setRelationships", + "relationshipsIterator", + "licenseInfoFromFilesIterator" + }) + public static abstract class PackageInformationMixin extends PackageInformation { + } +} diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/ComponentPermissions.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/ComponentPermissions.java index bce510de7b..927ddf8d96 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/ComponentPermissions.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/ComponentPermissions.java @@ -147,9 +147,6 @@ protected Set<String> getUserEquivalentOwnerGroup(){ departments.addAll(user.getSecondaryDepartmentsAndRoles().keySet()); } departments.add(user.getDepartment()); - if(!PermissionUtils.IS_COMPONENT_VISIBILITY_RESTRICTION_ENABLED) { - return departments; - } Set<String> finalDepartments = new HashSet<String>(); String departmentIfUserInBU = getDepartmentIfUserInBU(document, departments); finalDepartments.add(departmentIfUserInBU); diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/PermissionUtils.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/PermissionUtils.java index 1e921bec1b..1d952ce153 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/PermissionUtils.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/PermissionUtils.java @@ -22,6 +22,9 @@ import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.Vulnerability; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; /** * Created by bodet on 16/02/15. @@ -153,6 +156,12 @@ public static <T> DocumentPermissions<T> makePermission(T document, User user) { return (DocumentPermissions<T>) new UserPermissions((User) document, user); } else if (document instanceof Vulnerability) { return (DocumentPermissions<T>) new VulnerabilityPermissions((Vulnerability) document, user); + } else if (document instanceof SPDXDocument) { + return (DocumentPermissions<T>) new SpdxDocumentPermissions((SPDXDocument) document, user); + } else if (document instanceof DocumentCreationInformation) { + return (DocumentPermissions<T>) new SpdxDocumentCreationInfoPermissions((DocumentCreationInformation) document, user); + } else if (document instanceof PackageInformation) { + return (DocumentPermissions<T>) new SpdxPackageInfoPermissions((PackageInformation) document, user); } else { throw new IllegalArgumentException("Invalid input type!"); } diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/SpdxDocumentCreationInfoPermissions.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/SpdxDocumentCreationInfoPermissions.java new file mode 100644 index 0000000000..87becbd209 --- /dev/null +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/SpdxDocumentCreationInfoPermissions.java @@ -0,0 +1,56 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.permissions; + +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; +import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; +import org.eclipse.sw360.datahandler.thrift.users.User; + +import java.util.Map; +import java.util.Set; +import static org.eclipse.sw360.datahandler.common.CommonUtils.toSingletonSet; + +/** + * Created on 11/08/2021. + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentCreationInfoPermissions extends DocumentPermissions<DocumentCreationInformation> { + + private final Set<String> moderators; + private final Set<String> createdBy; + + protected SpdxDocumentCreationInfoPermissions(DocumentCreationInformation document, User user) { + super(document, user); + this.createdBy = toSingletonSet(document.createdBy); + moderators = toSingletonSet(document.createdBy); + } + + @Override + public void fillPermissions(DocumentCreationInformation spdx, Map<RequestedAction, Boolean> permissions) { + spdx.permissions = permissions; + } + + @Override + public boolean isActionAllowed(RequestedAction action) { + return getStandardPermissions(action); + } + + @Override + protected Set<String> getContributors() { + return moderators; + } + + @Override + protected Set<String> getModerators() { + return moderators; + } +} diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/SpdxDocumentPermissions.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/SpdxDocumentPermissions.java new file mode 100644 index 0000000000..08bf5452e6 --- /dev/null +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/SpdxDocumentPermissions.java @@ -0,0 +1,56 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.permissions; + +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.*; +import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; +import org.eclipse.sw360.datahandler.thrift.users.User; + +import java.util.Map; +import java.util.Set; +import static org.eclipse.sw360.datahandler.common.CommonUtils.toSingletonSet; + +/** + * Created on 10/08/2021. + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxDocumentPermissions extends DocumentPermissions<SPDXDocument> { + + private final Set<String> moderators; + private final Set<String> createdBy; + + protected SpdxDocumentPermissions(SPDXDocument document, User user) { + super(document, user); + this.createdBy = toSingletonSet(document.createdBy); + moderators = toSingletonSet(document.createdBy); + } + + @Override + public void fillPermissions(SPDXDocument spdx, Map<RequestedAction, Boolean> permissions) { + spdx.permissions = permissions; + } + + @Override + public boolean isActionAllowed(RequestedAction action) { + return getStandardPermissions(action); + } + + @Override + protected Set<String> getContributors() { + return moderators; + } + + @Override + protected Set<String> getModerators() { + return moderators; + } +} diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/SpdxPackageInfoPermissions.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/SpdxPackageInfoPermissions.java new file mode 100644 index 0000000000..e1151bee64 --- /dev/null +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/SpdxPackageInfoPermissions.java @@ -0,0 +1,56 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.sw360.datahandler.permissions; + +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; +import org.eclipse.sw360.datahandler.thrift.users.RequestedAction; +import org.eclipse.sw360.datahandler.thrift.users.User; + +import java.util.Map; +import java.util.Set; +import static org.eclipse.sw360.datahandler.common.CommonUtils.toSingletonSet; + +/** + * Created on 11/08/2021. + * + * @author hieu1.phamvan@toshiba.co.jp + */ +public class SpdxPackageInfoPermissions extends DocumentPermissions<PackageInformation> { + + private final Set<String> moderators; + private final Set<String> createdBy; + + protected SpdxPackageInfoPermissions(PackageInformation packageInfo, User user) { + super(packageInfo, user); + this.createdBy = toSingletonSet(packageInfo.createdBy); + moderators = toSingletonSet(packageInfo.createdBy); + } + + @Override + public void fillPermissions(PackageInformation packageInfo, Map<RequestedAction, Boolean> permissions) { + packageInfo.permissions = permissions; + } + + @Override + public boolean isActionAllowed(RequestedAction action) { + return getStandardPermissions(action); + } + + @Override + protected Set<String> getContributors() { + return moderators; + } + + @Override + protected Set<String> getModerators() { + return moderators; + } +} diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftClients.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftClients.java index 06f60ac338..9d38259274 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftClients.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftClients.java @@ -18,7 +18,6 @@ import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.thrift.TConfiguration; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.THttpClient; @@ -41,6 +40,10 @@ import org.eclipse.sw360.datahandler.thrift.users.UserService; import org.eclipse.sw360.datahandler.thrift.vendors.VendorService; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.VulnerabilityService; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocumentService; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformationService; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformationService; +import org.eclipse.sw360.datahandler.thrift.spdx.fileinformation.FileInformationService; import java.net.MalformedURLException; import java.net.URL; @@ -62,8 +65,6 @@ public class ThriftClients { public static final String BACKEND_PROXY_URL; public static final int THRIFT_CONNECTION_TIMEOUT; public static final int THRIFT_READ_TIMEOUT; - public static final int THRIFT_MAX_MESSAGE_SIZE; - public static final int THRIFT_MAX_FRAME_SIZE; //! Service addresses private static final String ATTACHMENT_SERVICE_URL = "/attachments/thrift"; @@ -83,6 +84,11 @@ public class ThriftClients { private static final String WSIMPORT_SERVICE_URL = "/wsimport/thrift"; private static final String CHANGELOGS_SERVICE_URL = "/changelogs/thrift"; private static final String HEALTH_SERVICE_URL = "/health/thrift"; + private static final String SPDX_SERVICE_URL = "/spdxdocument/thrift"; + private static final String SPDX_DOCUMENT_INFO_SERVICE_URL = "/spdxdocumentcreationinfo/thrift"; + private static final String SPDX_PACKAGE_INFO_SERVICE_URL = "/spdxpackageinfo/thrift"; + private static final String SPDX_FILE_INFO_SERVICE_URL = "/fileinformation/thrift"; + // A service which has to be scheduled by the scheduler should be registered here! // names of services that can be scheduled by the schedule service, i.e. that have an "update" method @@ -99,9 +105,6 @@ public class ThriftClients { THRIFT_CONNECTION_TIMEOUT = Integer.valueOf(props.getProperty("backend.timeout.connection", "5000")); THRIFT_READ_TIMEOUT = Integer.valueOf(props.getProperty("backend.timeout.read", "600000")); - THRIFT_MAX_MESSAGE_SIZE = Integer.valueOf(props.getProperty("backend.thrift.max.message.size", String.valueOf(TConfiguration.DEFAULT_MAX_MESSAGE_SIZE))); - THRIFT_MAX_FRAME_SIZE = Integer.valueOf(props.getProperty("backend.thrift.max.frame.size", String.valueOf(TConfiguration.DEFAULT_MAX_FRAME_SIZE))); - log.info("The following configuration will be used for connections to the backend:\n" + "\tURL : " + BACKEND_URL + "\n" + "\tProxy : " + BACKEND_PROXY_URL + "\n" + @@ -117,18 +120,15 @@ public ThriftClients() { private static TProtocol makeProtocol(String url, String service) { THttpClient thriftClient = null; final String destinationAddress = url + service; - final TConfiguration thriftConfigure = TConfiguration.custom().setMaxMessageSize(THRIFT_MAX_MESSAGE_SIZE) - .setMaxFrameSize(THRIFT_MAX_FRAME_SIZE).build(); - try { if (BACKEND_PROXY_URL != null) { URL proxyUrl = new URL(BACKEND_PROXY_URL); HttpHost proxy = new HttpHost(proxyUrl.getHost(), proxyUrl.getPort(), proxyUrl.getProtocol()); DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); CloseableHttpClient httpClient = HttpClients.custom().setRoutePlanner(routePlanner).build(); - thriftClient = new THttpClient(thriftConfigure, destinationAddress, httpClient); + thriftClient = new THttpClient(destinationAddress, httpClient); } else { - thriftClient = new THttpClient(thriftConfigure, destinationAddress); + thriftClient = new THttpClient(destinationAddress); } thriftClient.setConnectTimeout(THRIFT_CONNECTION_TIMEOUT); thriftClient.setReadTimeout(THRIFT_READ_TIMEOUT); @@ -207,4 +207,21 @@ public ChangeLogsService.Iface makeChangeLogsClient() { public HealthService.Iface makeHealthClient() { return new HealthService.Client(makeProtocol(BACKEND_URL, HEALTH_SERVICE_URL)); } + + public SPDXDocumentService.Iface makeSPDXClient() { + return new SPDXDocumentService.Client(makeProtocol(BACKEND_URL, SPDX_SERVICE_URL)); + } + + public DocumentCreationInformationService.Iface makeSPDXDocumentInfoClient() { + return new DocumentCreationInformationService.Client(makeProtocol(BACKEND_URL, SPDX_DOCUMENT_INFO_SERVICE_URL)); + } + + public PackageInformationService.Iface makeSPDXPackageInfoClient() { + return new PackageInformationService.Client(makeProtocol(BACKEND_URL, SPDX_PACKAGE_INFO_SERVICE_URL)); + } + + public FileInformationService.Iface makeSPDXFileInfoClient() { + return new FileInformationService.Client(makeProtocol(BACKEND_URL, SPDX_FILE_INFO_SERVICE_URL)); + } + } diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java index 91acb2d450..95f1186758 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftUtils.java @@ -30,6 +30,10 @@ import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.*; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.*; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.*; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.*; +import org.eclipse.sw360.datahandler.thrift.spdx.fileinformation.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -67,6 +71,10 @@ public class ThriftUtils { .add(ExternalToolProcess.class, ExternalToolProcessStep.class) // external tools like Fossology service .add(Vulnerability.class, ReleaseVulnerabilityRelation.class, ProjectVulnerabilityRating.class) .add(ChangeLogs.class) // Changelog Service + .add(SPDXDocument.class ) // SPDX Document service + .add(DocumentCreationInformation.class ) // Document Creation Information service + .add(PackageInformation.class ) // Package Information service + .add(FileInformation.class) // File Information Service .build(); public static final List<Class<?>> THRIFT_NESTED_CLASSES = ImmutableList.<Class<?>>builder() diff --git a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftValidate.java b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftValidate.java index 570a8868a4..7667e769cd 100644 --- a/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftValidate.java +++ b/libraries/lib-datahandler/src/main/java/org/eclipse/sw360/datahandler/thrift/ThriftValidate.java @@ -17,9 +17,12 @@ import org.eclipse.sw360.datahandler.thrift.licenses.*; import org.eclipse.sw360.datahandler.thrift.projects.Project; import org.eclipse.sw360.datahandler.thrift.projects.ProjectClearingState; +import org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation.DocumentCreationInformation; import org.eclipse.sw360.datahandler.thrift.projects.ObligationList; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument.SPDXDocument; +import org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo.PackageInformation; import java.util.Collection; import java.util.Collections; @@ -255,4 +258,31 @@ public static void prepareProjectObligation(ObligationList obligation) throws SW assertNotEmpty(obligation.getLinkedObligationStatus().keySet(), "linked obligtions cannot be empty"); obligation.setType(TYPE_PROJECT_OBLIGATION); } + + public static void prepareSPDXDocument(SPDXDocument spdx) throws SW360Exception { + // Check required fields + + // Check type + spdx.setType(TYPE_SPDX_DOCUMENT); + // Unset temporary fields + spdx.unsetPermissions(); + } + + public static void prepareSpdxDocumentCreationInfo(DocumentCreationInformation documentCreationInfo) throws SW360Exception { + // Check required fields + + // Check type + documentCreationInfo.setType(TYPE_SPDX_DOCUMENT_CREATION_INFO); + // Unset temporary fields + documentCreationInfo.unsetPermissions(); +} + + public static void prepareSpdxPackageInfo(PackageInformation packageInfo) throws SW360Exception { + // Check required fields + + // Check type + packageInfo.setType(TYPE_SPDX_PACKAGE_INFO); + // Unset temporary fields + packageInfo.unsetPermissions(); + } } diff --git a/libraries/lib-datahandler/src/main/resources/sw360.properties b/libraries/lib-datahandler/src/main/resources/sw360.properties index fc4cda098a..a9c31f396c 100644 --- a/libraries/lib-datahandler/src/main/resources/sw360.properties +++ b/libraries/lib-datahandler/src/main/resources/sw360.properties @@ -43,9 +43,5 @@ ## first byte of the response is available. #backend.timeout.read = 600000 -## These two properties used to set max message size and frame size of thrift transport -#backend.thrift.max.message.size=104857600 -#backend.thrift.max.frame.size=16384000 - ## This property is used to enable the component visibility restriction feature. #component.visibility.restriction.enabled=true \ No newline at end of file diff --git a/libraries/lib-datahandler/src/main/thrift/attachments.thrift b/libraries/lib-datahandler/src/main/thrift/attachments.thrift index e42d9b7a97..5e4cdfad9a 100644 --- a/libraries/lib-datahandler/src/main/thrift/attachments.thrift +++ b/libraries/lib-datahandler/src/main/thrift/attachments.thrift @@ -39,8 +39,9 @@ enum AttachmentType { SCREENSHOT = 15, OTHER = 16, README_OSS = 17, - SECURITY_ASSESSMENT = 18, - INITIAL_SCAN_REPORT = 19 + SBOM = 18, + SECURITY_ASSESSMENT = 19, + INITIAL_SCAN_REPORT = 20 } enum CheckStatus { diff --git a/libraries/lib-datahandler/src/main/thrift/changelogs.thrift b/libraries/lib-datahandler/src/main/thrift/changelogs.thrift index 6f2defeae3..b4367010c4 100644 --- a/libraries/lib-datahandler/src/main/thrift/changelogs.thrift +++ b/libraries/lib-datahandler/src/main/thrift/changelogs.thrift @@ -30,7 +30,13 @@ enum Operation { MERGE_RELEASE = 10, OBLIGATION_UPDATE = 11, OBLIGATION_ADD = 12, - SPLIT_COMPONENT = 13 + SPLIT_COMPONENT = 13, + SPDXDOCUMENT_CREATE = 14, + SPDXDOCUMENT_DELETE = 15, + SPDX_DOCUMENT_CREATION_INFO_CREATE = 16, + SPDX_DOCUMENT_CREATION_INFO_DELETE = 17, + SPDX_PACKAGE_INFO_CREATE = 18, + SPDX_PACKAGE_INFO_DELETE = 19 } struct ChangeLogs { diff --git a/libraries/lib-datahandler/src/main/thrift/components.thrift b/libraries/lib-datahandler/src/main/thrift/components.thrift index 519f83768c..8dcc3058e2 100644 --- a/libraries/lib-datahandler/src/main/thrift/components.thrift +++ b/libraries/lib-datahandler/src/main/thrift/components.thrift @@ -27,6 +27,7 @@ typedef sw360.MainlineState MainlineState typedef sw360.ProjectReleaseRelationship ProjectReleaseRelationship typedef sw360.SW360Exception SW360Exception typedef sw360.PaginationData PaginationData +typedef sw360.ImportBomRequestPreparation ImportBomRequestPreparation typedef attachments.Attachment Attachment typedef attachments.FilledAttachment FilledAttachment typedef users.User User @@ -272,6 +273,8 @@ struct Release { 90: optional DocumentState documentState, 200: optional map<RequestedAction, bool> permissions, + + 400: optional string spdxId, } enum ComponentType { @@ -824,10 +827,15 @@ service ComponentService { */ string getCyclicLinkedReleasePath(1: Release release, 2: User user); + // /** + // * parse a bom file and write the information to SW360 + // **/ + ImportBomRequestPreparation prepareImportBom(1: User user, 2:string attachmentContentId); + /** * parse a bom file and write the information to SW360 **/ - RequestSummary importBomFromAttachmentContent(1: User user, 2:string attachmentContentId); + RequestSummary importBomFromAttachmentContent(1: User user, 2:string attachmentContentId, 3:string newReleaseVersion, 4:string releaseId, 5:string rdfFilePath); /** * split data like releases and attachments from source component to target component. @@ -838,4 +846,9 @@ service ComponentService { * Gets all releases with complete details */ list<Release> getAllReleasesForUser(1: User user); + + /** + * parse a bom file and write the information to SW360 + **/ + RequestSummary exportSPDX(1: User user, 2:string releaseId, 3:string outputFormat); } diff --git a/libraries/lib-datahandler/src/main/thrift/moderation.thrift b/libraries/lib-datahandler/src/main/thrift/moderation.thrift index 1931911ca6..1ff2843294 100644 --- a/libraries/lib-datahandler/src/main/thrift/moderation.thrift +++ b/libraries/lib-datahandler/src/main/thrift/moderation.thrift @@ -14,6 +14,9 @@ include "components.thrift" include "projects.thrift" include "users.thrift" include "licenses.thrift" +include "spdx/spdxdocument.thrift" +include "spdx/documentcreationinformation.thrift" +include "spdx/packageinformation.thrift" namespace java org.eclipse.sw360.datahandler.thrift.moderation namespace php sw360.thrift.moderation @@ -31,6 +34,9 @@ typedef licenses.License License typedef licenses.Obligation Obligation typedef components.ComponentType ComponentType typedef projects.ClearingRequest ClearingRequest +typedef spdxdocument.SPDXDocument SPDXDocument +typedef documentcreationinformation.DocumentCreationInformation DocumentCreationInformation +typedef packageinformation.PackageInformation PackageInformation enum DocumentType { COMPONENT = 1, @@ -38,6 +44,9 @@ enum DocumentType { PROJECT = 3, LICENSE = 4, USER = 5, + SPDXDOCUMENT = 6, + SPDX_DOCUMENT_CREATION_INFO = 7, + SPDX_PACKAGE_INFO = 8, } struct ModerationRequest { @@ -74,6 +83,13 @@ struct ModerationRequest { 32: optional Project projectDeletions, 33: optional License licenseDeletions, + 50: optional SPDXDocument SPDXDocumentAdditions, + 51: optional SPDXDocument SPDXDocumentDeletions, + 52: optional DocumentCreationInformation documentCreationInfoAdditions, + 53: optional DocumentCreationInformation documentCreationInfoDeletions, + 54: optional PackageInformation packageInfoAdditions, + 55: optional PackageInformation packageInfoDeletions, + } service ModerationService { @@ -115,6 +131,27 @@ service ModerationService { **/ RequestStatus createLicenseRequest(1: License license, 2: User user); + /** + * write moderation request for SPDXDocument to database, + * differences are written as additions and deletions to moderation request, + * set requestingUser of moderation request to user + **/ + RequestStatus createSPDXDocumentRequest(1: SPDXDocument spdx, 2: User user); + + /** + * write moderation request for spdx document creation info to database, + * differences are written as additions and deletions to moderation request, + * set requestingUser of moderation request to user + **/ + RequestStatus createSpdxDocumentCreationInfoRequest(1: DocumentCreationInformation documentCreationInfo, 2: User user); + + /** + * write moderation request for spdx document creation info to database, + * differences are written as additions and deletions to moderation request, + * set requestingUser of moderation request to user + **/ + RequestStatus createSpdxPackageInfoRequest(1: PackageInformation packageInfo, 2: User user); + /** * write moderation request for activating a user account to database **/ @@ -138,6 +175,24 @@ service ModerationService { **/ oneway void createProjectDeleteRequest(1: Project project, 2: User user); + /** + * write moderation request for deleting project to database, + * set requestingUser of moderation request to user + **/ + oneway void createSPDXDocumentDeleteRequest(1: SPDXDocument spdx, 2: User user); + + /** + * write moderation request for deleting spdx document creation info to database, + * set requestingUser of moderation request to user + **/ + oneway void createSpdxDocumentCreationInfoDeleteRequest(1: DocumentCreationInformation documentCreationInfo, 2: User user); + + /** + * write moderation request for deleting spdx package info to database, + * set requestingUser of moderation request to user + **/ + oneway void createSpdxPackageInfoDeleteRequest(1: PackageInformation packageInfo, 2: User user); + /** * get list of moderation requests for document with documentId currently present in database **/ diff --git a/libraries/lib-datahandler/src/main/thrift/spdx/annotations.thrift b/libraries/lib-datahandler/src/main/thrift/spdx/annotations.thrift new file mode 100644 index 0000000000..d52061db6e --- /dev/null +++ b/libraries/lib-datahandler/src/main/thrift/spdx/annotations.thrift @@ -0,0 +1,21 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +namespace java org.eclipse.sw360.datahandler.thrift.spdx.annotations +namespace php sw360.thrift.spdx.annotations + +struct Annotations { + 1: optional string annotator, // 12.1 + 2: optional string annotationDate, // 12.2 + 3: optional string annotationType, // 12.3 + 4: optional string spdxIdRef, // 12.4 + 5: optional string annotationComment, // 12.5 + 6: optional i32 index, +} \ No newline at end of file diff --git a/libraries/lib-datahandler/src/main/thrift/spdx/documentcreationinformation.thrift b/libraries/lib-datahandler/src/main/thrift/spdx/documentcreationinformation.thrift new file mode 100644 index 0000000000..af5d2d4858 --- /dev/null +++ b/libraries/lib-datahandler/src/main/thrift/spdx/documentcreationinformation.thrift @@ -0,0 +1,75 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +include "sw360.thrift" +include "users.thrift" + +namespace java org.eclipse.sw360.datahandler.thrift.spdx.documentcreationinformation +namespace php sw360.thrift.spdx.documentcreationinformation + +typedef sw360.RequestStatus RequestStatus +typedef sw360.RequestSummary RequestSummary +typedef sw360.AddDocumentRequestSummary AddDocumentRequestSummary +typedef sw360.DocumentState DocumentState +typedef sw360.SW360Exception SW360Exception +typedef sw360.PaginationData PaginationData +typedef users.User User +typedef users.RequestedAction RequestedAction + +struct DocumentCreationInformation { + 1: optional string id, + 2: optional string revision, + 3: optional string type = "documentCreationInformation", + 4: optional string spdxDocumentId, // Id of the parent SPDX Document + 5: optional string spdxVersion, // 6.1 + 6: optional string dataLicense, // 6.2 + 7: optional string SPDXID, // 6.3 + 8: optional string name, // 6.4 + 9: optional string documentNamespace, // 6.5 + 10: optional set<ExternalDocumentReferences> externalDocumentRefs, // 6.6 + 11: optional string licenseListVersion, // 6.7 + 12: optional set<Creator> creator, // 6.8 + 13: optional string created, // 6.9 + 14: optional string creatorComment, // 6.10 + 15: optional string documentComment, // 6.11 + // Information for ModerationRequests + 20: optional DocumentState documentState, + 21: optional map<RequestedAction, bool> permissions, + 22: optional string createdBy, +} + +struct ExternalDocumentReferences { + 1: optional string externalDocumentId, + 2: optional CheckSum checksum, + 3: optional string spdxDocument, + 4: optional i32 index, +} + +struct CheckSum { + 1: optional string algorithm, + 2: optional string checksumValue, + 3: optional i32 index, +} + +struct Creator { + 1: optional string type, + 2: optional string value, + 3: optional i32 index, +} + +service DocumentCreationInformationService { + list<DocumentCreationInformation> getDocumentCreationInformationSummary(1: User user); + DocumentCreationInformation getDocumentCreationInformationById(1: string id, 2: User user); + DocumentCreationInformation getDocumentCreationInfoForEdit(1: string id, 2: User user); + AddDocumentRequestSummary addDocumentCreationInformation(1: DocumentCreationInformation documentCreationInformation, 2: User user); + RequestStatus updateDocumentCreationInformation(1: DocumentCreationInformation documentCreationInformation, 2: User user); + RequestStatus updateDocumentCreationInfomationFromModerationRequest(1: DocumentCreationInformation documentCreationInfoAdditions, 2: DocumentCreationInformation documentCreationInfoDeletions, 3: User user); + RequestStatus deleteDocumentCreationInformation(1: string id, 2: User user); +} diff --git a/libraries/lib-datahandler/src/main/thrift/spdx/fileinformation.thrift b/libraries/lib-datahandler/src/main/thrift/spdx/fileinformation.thrift new file mode 100644 index 0000000000..be2f77ab32 --- /dev/null +++ b/libraries/lib-datahandler/src/main/thrift/spdx/fileinformation.thrift @@ -0,0 +1,65 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +include "sw360.thrift" +include "users.thrift" +include "documentcreationinformation.thrift" +include "snippetinformation.thrift" +include "annotations.thrift" +include "otherlicensinginformationdetected.thrift" + + +namespace java org.eclipse.sw360.datahandler.thrift.spdx.fileinformation +namespace php sw360.thrift.spdx.fileinformation + +typedef sw360.RequestStatus RequestStatus +typedef sw360.RequestSummary RequestSummary +typedef sw360.AddDocumentRequestSummary AddDocumentRequestSummary +typedef sw360.DocumentState DocumentState +typedef sw360.SW360Exception SW360Exception +typedef sw360.PaginationData PaginationData +typedef users.User User +typedef users.RequestedAction RequestedAction +typedef documentcreationinformation.CheckSum CheckSum +typedef snippetinformation.SnippetInformation SnippetInformation +typedef annotations.Annotations Annotation +typedef otherlicensinginformationdetected.OtherLicensingInformationDetected OtherLicensingInformationDetected + +struct FileInformation { + 1: optional string id, + 2: optional string revision, + 3: optional string type = "spdxFileInformation", + 4: optional string fileName, + 5: optional string SPDXID, + 6: optional set<string> fileTypes, + 7: optional set<CheckSum> checksums, + 8: optional string licenseConcluded, + 9: optional set<string> licenseInfoInFiles, + 10: optional string licenseComments, + 11: optional string copyrightText, + 12: optional string fileComment, + 13: optional string noticeText, + 14: optional set<string> fileContributors, + 15: optional set<string> fileAttributionText, + 16: optional set<SnippetInformation> snippetInformation, + 17: optional set<OtherLicensingInformationDetected> hasExtractedLicensingInfos, + 18: optional set<Annotation> annotations, +} + +service FileInformationService { + list<FileInformation> getFileInformationsShort(1: set<string> ids, 2: User user); + list<FileInformation> getFileInformationSummary(1: User user); + FileInformation getFileInformationById(1: string id, 2: User user); + RequestStatus addFileInformation(1: FileInformation fileInformation, 2: User user); + AddDocumentRequestSummary addFileInformations(1: set<FileInformation> fileInformations, 2: User user); + RequestStatus updateFileInformation(1: FileInformation fileInformation, 2: User user); + RequestSummary updateFileInformations(1: set<FileInformation> fileInformations, 2: User user); + RequestStatus deleteFileInformation(1: string id, 2: User user); +} \ No newline at end of file diff --git a/libraries/lib-datahandler/src/main/thrift/spdx/otherlicensinginformationdetected.thrift b/libraries/lib-datahandler/src/main/thrift/spdx/otherlicensinginformationdetected.thrift new file mode 100644 index 0000000000..9c3ef13cc5 --- /dev/null +++ b/libraries/lib-datahandler/src/main/thrift/spdx/otherlicensinginformationdetected.thrift @@ -0,0 +1,21 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +namespace java org.eclipse.sw360.datahandler.thrift.spdx.otherlicensinginformationdetected +namespace php sw360.thrift.spdx.otherlicensinginformationdetected + +struct OtherLicensingInformationDetected { + 1: optional string licenseId, // 10.1 + 2: optional string extractedText, // 10.2 + 3: optional string licenseName, // 10.3 + 4: optional set<string> licenseCrossRefs, // 10.4 + 5: optional string licenseComment, // 10.5 + 6: optional i32 index, +} \ No newline at end of file diff --git a/libraries/lib-datahandler/src/main/thrift/spdx/packageinformation.thrift b/libraries/lib-datahandler/src/main/thrift/spdx/packageinformation.thrift new file mode 100644 index 0000000000..2c7f75f468 --- /dev/null +++ b/libraries/lib-datahandler/src/main/thrift/spdx/packageinformation.thrift @@ -0,0 +1,92 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +include "sw360.thrift" +include "users.thrift" +include "documentcreationinformation.thrift" +include "annotations.thrift" +include "relationshipsbetweenspdxelements.thrift" + +namespace java org.eclipse.sw360.datahandler.thrift.spdx.spdxpackageinfo +namespace php sw360.thrift.spdx.spdxpackageinfo + +typedef sw360.RequestStatus RequestStatus +typedef sw360.RequestSummary RequestSummary +typedef sw360.AddDocumentRequestSummary AddDocumentRequestSummary +typedef sw360.DocumentState DocumentState +typedef sw360.SW360Exception SW360Exception +typedef sw360.PaginationData PaginationData +typedef users.User User +typedef users.RequestedAction RequestedAction +typedef documentcreationinformation.CheckSum CheckSum +typedef annotations.Annotations Annotation +typedef relationshipsbetweenspdxelements.RelationshipsBetweenSPDXElements RelationshipsBetweenSPDXElements + +struct PackageInformation { + 1: optional string id, + 2: optional string revision, + 3: optional string type = "packageInformation", + 4: optional string spdxDocumentId, // Id of the parent SPDX Document + 5: optional string name, // 7.1 + 6: optional string SPDXID, // 7.2 + 7: optional string versionInfo, // 7.3 + 8: optional string packageFileName, // 7.4 + 9: optional string supplier, // 7.5 + 10: optional string originator, // 7.6 + 11: optional string downloadLocation, // 7.7 + 12: optional bool filesAnalyzed, // 7.8 + 13: optional PackageVerificationCode packageVerificationCode, // 7.9 + 14: optional set<CheckSum> checksums, // 7.10 + 15: optional string homepage, // 7.11 + 16: optional string sourceInfo, // 7.12 + 17: optional string licenseConcluded, // 7.13 + 18: optional set<string> licenseInfoFromFiles, // 7.14 + 19: optional string licenseDeclared, // 7.15 + 20: optional string licenseComments, // 7.16 + 21: optional string copyrightText, // 7.17 + 22: optional string summary, // 7.18 + 23: optional string description, // 7.19 + 24: optional string packageComment, // 7.20 + 25: optional set<ExternalReference> externalRefs, //7.21 + 26: optional set<string> attributionText, // 7.22 + 27: optional set<Annotation> annotations, // 7.23 + // Information for ModerationRequests + 30: optional DocumentState documentState, + 31: optional map<RequestedAction, bool> permissions, + 32: optional string createdBy, + 33: optional i32 index, + 34: optional set<RelationshipsBetweenSPDXElements> relationships, // 11. Relationships + +} + +struct PackageVerificationCode { + 1: optional set<string> excludedFiles, + 2: optional string value, +} + +struct ExternalReference { + 1: optional string referenceCategory, + 2: optional string referenceLocator, + 3: optional string referenceType, + 4: optional string comment, + 5: optional i32 index, +} + +service PackageInformationService { + list<PackageInformation> getPackageInformationSummary(1: User user); + PackageInformation getPackageInformationById(1: string id, 2: User user); + PackageInformation getPackageInformationForEdit(1: string id, 2: User user); + AddDocumentRequestSummary addPackageInformation(1: PackageInformation packageInformation, 2: User user); + AddDocumentRequestSummary addPackageInformations(1: set<PackageInformation> packageInformations, 2: User user); + RequestStatus updatePackageInformation(1: PackageInformation packageInformation, 2: User user); + RequestSummary updatePackageInformations(1: set<PackageInformation> packageInformations, 2: User user); + RequestStatus updatePackageInfomationFromModerationRequest(1: PackageInformation packageInfoAdditions, 2: PackageInformation packageInfoDeletions, 3: User user); + RequestStatus deletePackageInformation(1: string id, 2: User user); +} \ No newline at end of file diff --git a/libraries/lib-datahandler/src/main/thrift/spdx/relationshipsbetweenspdxelements.thrift b/libraries/lib-datahandler/src/main/thrift/spdx/relationshipsbetweenspdxelements.thrift new file mode 100644 index 0000000000..d3b2356bbd --- /dev/null +++ b/libraries/lib-datahandler/src/main/thrift/spdx/relationshipsbetweenspdxelements.thrift @@ -0,0 +1,20 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +namespace java org.eclipse.sw360.datahandler.thrift.spdx.relationshipsbetweenspdxelements +namespace php sw360.thrift.spdx.relationshipsbetweenspdxelements + +struct RelationshipsBetweenSPDXElements { + 1: optional string spdxElementId, // 11.1 + 2: optional string relationshipType, // 11.1 + 3: optional string relatedSpdxElement, // 11.1 + 4: optional string relationshipComment, // 11.2 + 5: optional i32 index, +} \ No newline at end of file diff --git a/libraries/lib-datahandler/src/main/thrift/spdx/snippetinformation.thrift b/libraries/lib-datahandler/src/main/thrift/spdx/snippetinformation.thrift new file mode 100644 index 0000000000..22b336b567 --- /dev/null +++ b/libraries/lib-datahandler/src/main/thrift/spdx/snippetinformation.thrift @@ -0,0 +1,34 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +namespace java org.eclipse.sw360.datahandler.thrift.spdx.snippetinformation +namespace php sw360.thrift.spdx.snippetinformation + +struct SnippetInformation { + 1: optional string SPDXID, // 9.1 + 2: optional string snippetFromFile , // 9.2 + 3: optional set<SnippetRange> snippetRanges, // 9.3, 9.4 + 4: optional string licenseConcluded, // 9.5 + 5: optional set<string> licenseInfoInSnippets, // 9.6 + 6: optional string licenseComments, // 9.7 + 7: optional string copyrightText, // 9.8 + 8: optional string comment, // 9.9 + 9: optional string name, // 9.10 + 10: optional string snippetAttributionText, // 9.11 + 11: optional i32 index, +} + +struct SnippetRange { + 1: optional string rangeType, + 2: optional string startPointer, + 3: optional string endPointer, + 4: optional string reference, + 5: optional i32 index, +} \ No newline at end of file diff --git a/libraries/lib-datahandler/src/main/thrift/spdx/spdxdocument.thrift b/libraries/lib-datahandler/src/main/thrift/spdx/spdxdocument.thrift new file mode 100644 index 0000000000..cb9cda7adc --- /dev/null +++ b/libraries/lib-datahandler/src/main/thrift/spdx/spdxdocument.thrift @@ -0,0 +1,60 @@ +/* + * Copyright TOSHIBA CORPORATION, 2021. Part of the SW360 Portal Project. + * Copyright Toshiba Software Development (Vietnam) Co., Ltd., 2021. Part of the SW360 Portal Project. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +include "sw360.thrift" +include "users.thrift" +include "snippetinformation.thrift" +include "relationshipsbetweenspdxelements.thrift" +include "annotations.thrift" +include "otherlicensinginformationdetected.thrift" + +namespace java org.eclipse.sw360.datahandler.thrift.spdx.spdxdocument +namespace php sw360.thrift.spdx.spdxdocument + +typedef sw360.RequestStatus RequestStatus +typedef sw360.RequestSummary RequestSummary +typedef sw360.AddDocumentRequestSummary AddDocumentRequestSummary +typedef sw360.DocumentState DocumentState +typedef sw360.SW360Exception SW360Exception +typedef sw360.PaginationData PaginationData +typedef users.User User +typedef users.RequestedAction RequestedAction +typedef snippetinformation.SnippetInformation SnippetInformation +typedef relationshipsbetweenspdxelements.RelationshipsBetweenSPDXElements RelationshipsBetweenSPDXElements +typedef annotations.Annotations Annotations +typedef otherlicensinginformationdetected.OtherLicensingInformationDetected OtherLicensingInformationDetected + +struct SPDXDocument { + 1: optional string id, + 2: optional string revision, + 3: optional string type = "SPDXDocument", + 4: optional string releaseId, // Id of the parent release + 5: optional string spdxDocumentCreationInfoId, // Id of Document Creation Info + 6: optional set<string> spdxPackageInfoIds, // Ids of Package Info + 7: optional set<string> spdxFileInfoIds, // Ids of File Info + 8: optional set<SnippetInformation> snippets, // 9. Snippet Information + 9: optional set<RelationshipsBetweenSPDXElements> relationships, // 11. Relationships + 10: optional set<Annotations> annotations, // 12. Annotations + 11: optional set<OtherLicensingInformationDetected> otherLicensingInformationDetecteds, // 10. Other Licensing Information Detected + // Information for ModerationRequests + 20: optional DocumentState documentState, + 21: optional map<RequestedAction, bool> permissions, + 22: optional string createdBy, +} + +service SPDXDocumentService { + list<SPDXDocument> getSPDXDocumentSummary(1: User user); + SPDXDocument getSPDXDocumentById(1: string id, 2: User user); + SPDXDocument getSPDXDocumentForEdit(1: string id, 2: User user); + AddDocumentRequestSummary addSPDXDocument(1: SPDXDocument spdx, 2: User user); + RequestStatus updateSPDXDocument(1: SPDXDocument spdx, 2: User user); + RequestStatus updateSPDXDocumentFromModerationRequest(1: SPDXDocument spdxAdditions, 2: SPDXDocument spdxDeletions, 3: User user); + RequestStatus deleteSPDXDocument(1: string id, 2: User user); +} \ No newline at end of file diff --git a/libraries/lib-datahandler/src/main/thrift/sw360.thrift b/libraries/lib-datahandler/src/main/thrift/sw360.thrift index ad62816f72..7eaaf9f3a6 100644 --- a/libraries/lib-datahandler/src/main/thrift/sw360.thrift +++ b/libraries/lib-datahandler/src/main/thrift/sw360.thrift @@ -186,6 +186,15 @@ struct AddDocumentRequestSummary { 3: optional string message; } +struct ImportBomRequestPreparation { + 1: required RequestStatus requestStatus; + 2: optional bool isComponentDuplicate; + 3: optional bool isReleaseDuplicate; + 4: optional string name; + 5: optional string version; + 6: optional string message; +} + struct CustomProperties { 1: optional string id, 2: optional string revision, diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/changelog/ChangeLogController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/changelog/ChangeLogController.java index 3ce14ce4f8..168a7a5054 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/changelog/ChangeLogController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/changelog/ChangeLogController.java @@ -77,6 +77,7 @@ public ResponseEntity getChangeLogForDocument(Pageable pageable, @PathVariable(" ResourceClassNotFoundException { User sw360User = restControllerHelper.getSw360UserFromAuthentication(); List<ChangeLogs> changelogs = sw360ChangeLogService.getChangeLogsByDocumentId(docId, sw360User); + changelogs.stream().forEach(cl -> cl.setChangeTimestamp(cl.getChangeTimestamp().split(" ")[0])); PaginationResult<ChangeLogs> paginationResult = restControllerHelper.createPaginationResult(request, pageable, changelogs, SW360Constants.TYPE_CHANGELOG); ObjectMapper mapper = new ObjectMapper();