diff --git a/backend/src-common/pom.xml b/backend/src-common/pom.xml index fe223ceaf3..676da8cca9 100644 --- a/backend/src-common/pom.xml +++ b/backend/src-common/pom.xml @@ -42,6 +42,34 @@ spdx-tools compile + + org.spdx + java-spdx-library + + + + org.apache.jena + jena-arq + + + + org.apache.jena + jena-core + + + + org.apache.jena + jena-osgi + + + + org.apache.jena + jena-base + + + org.spdx + tools-java + org.apache.logging.log4j 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 e71ef9797b..1c959af483 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 @@ -62,9 +62,12 @@ import org.eclipse.sw360.spdx.SpdxBOMImporterSink; import org.jetbrains.annotations.NotNull; import org.spdx.rdfparser.InvalidSPDXAnalysisException; - +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.MalformedURLException; import java.util.*; import java.util.concurrent.TimeUnit; @@ -513,12 +516,26 @@ public AddDocumentRequestSummary addRelease(Release release, User user) throws S } private boolean isDuplicate(Component component, boolean caseInsenstive){ - Set duplicates = componentRepository.getComponentIdsByName(component.getName(), caseInsenstive); - return duplicates.size()>0; + return isDuplicate(component.getName(), caseInsenstive); } private boolean isDuplicate(Release release){ - List 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 duplicates = componentRepository.getComponentIdsByName(componentName, caseInsenstive); + return duplicates.size()>0; + } + + private boolean isDuplicate(String releaseName, String releaseVersion) { + if (isNullEmptyOrWhitespace(releaseName)) { + return false; + } + List duplicates = releaseRepository.searchByNameAndVersion(releaseName, releaseVersion); return duplicates.size()>0; } @@ -2363,7 +2380,7 @@ 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); try { @@ -2371,13 +2388,65 @@ public RequestSummary importBomFromAttachmentContent(User user, String attachmen 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); + + String fileType = getFileType(attachmentContent.getFilename()); + final String ext = "." + fileType; + final File sourceFile = DatabaseHandlerUtil.saveAsTempFile(user, inputStream, attachmentContentId, ext); + + ImportBomRequestPreparation importBomRequestPreparation = spdxBOMImporter.prepareImportSpdxBOMAsRelease(sourceFile); + 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(sourceFile.getAbsolutePath()); + } + + return importBomRequestPreparation; } - } catch (InvalidSPDXAnalysisException | IOException e) { + } catch (IOException e) { throw new SW360Exception(e.getMessage()); } } + 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); + } + } + + public RequestSummary importBomFromAttachmentContent(User user, String attachmentContentId, String rdfFilePath) throws SW360Exception { + final AttachmentContent attachmentContent = attachmentConnector.getAttachmentContent(attachmentContentId); + final SpdxBOMImporterSink spdxBOMImporterSink = new SpdxBOMImporterSink(user, null, this); + final SpdxBOMImporter spdxBOMImporter = new SpdxBOMImporter(spdxBOMImporterSink); + File file = new File(rdfFilePath); + return spdxBOMImporter.importSpdxBOMAsRelease(file, attachmentContent); + } + + 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 void removeLeadingTrailingWhitespace(Release release) { DatabaseHandlerUtil.trimStringFields(release, listOfStringFieldsInReleaseToTrim); @@ -2521,4 +2590,5 @@ public void sendExportSpreadsheetSuccessMail(String url, String recepient) throw MailConstants.TEXT_SPREADSHEET_EXPORT_SUCCESS, SW360Constants.NOTIFICATION_CLASS_COMPONENT, "", false, "component", url); } + } 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 37a24af415..3b464e6363 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; @@ -690,7 +692,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); } @@ -979,5 +981,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/ProjectDatabaseHandler.java b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java index e9229a557f..bb5681ae41 100644 --- a/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java +++ b/backend/src-common/src/main/java/org/eclipse/sw360/datahandler/db/ProjectDatabaseHandler.java @@ -46,6 +46,7 @@ import org.eclipse.sw360.spdx.SpdxBOMImporterSink; import org.spdx.rdfparser.InvalidSPDXAnalysisException; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -1419,13 +1420,29 @@ public RequestSummary importBomFromAttachmentContent(User user, String attachmen try (final InputStream inputStream = attachmentStreamConnector.unsafeGetAttachmentStream(attachmentContent)) { final SpdxBOMImporterSink spdxBOMImporterSink = new SpdxBOMImporterSink(user, this, componentDatabaseHandler); final SpdxBOMImporter spdxBOMImporter = new SpdxBOMImporter(spdxBOMImporterSink); - return spdxBOMImporter.importSpdxBOMAsProject(inputStream, attachmentContent); + String fileType = getFileType(attachmentContent.getFilename()); + final String ext = "." + fileType; + final File sourceFile = DatabaseHandlerUtil.saveAsTempFile(user, inputStream, attachmentContentId, ext); + return spdxBOMImporter.importSpdxBOMAsProject(sourceFile, attachmentContent); } - } catch (InvalidSPDXAnalysisException | IOException e) { + } 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 void removeLeadingTrailingWhitespace(Project project) { DatabaseHandlerUtil.trimStringFields(project, listOfStringFieldsInProjToTrim); 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..a9966a4244 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,17 +16,24 @@ 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.spdx.rdfparser.InvalidSPDXAnalysisException; -import org.spdx.rdfparser.SPDXDocumentFactory; -import org.spdx.rdfparser.model.*; - +import org.spdx.library.model.enumerations.RelationshipType; +import org.spdx.library.model.pointer.ByteOffsetPointer; +import org.spdx.library.model.pointer.LineCharPointer; +import org.spdx.library.model.pointer.SinglePointer; +import org.spdx.library.model.pointer.StartEndPointer; +import org.spdx.library.model.*; +import org.spdx.library.InvalidSPDXAnalysisException; +import org.spdx.tools.InvalidFileNameException; +import org.spdx.tools.SpdxToolsHelper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.io.InputStream; +import java.io.File; +import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -36,116 +45,206 @@ public SpdxBOMImporter(SpdxBOMImporterSink sink) { this.sink = sink; } - public RequestSummary importSpdxBOMAsRelease(InputStream inputStream, AttachmentContent attachmentContent) - throws InvalidSPDXAnalysisException, SW360Exception { - return importSpdxBOM(inputStream, attachmentContent, SW360Constants.TYPE_RELEASE); + public ImportBomRequestPreparation prepareImportSpdxBOMAsRelease(File targetFile) { + final ImportBomRequestPreparation requestPreparation = new ImportBomRequestPreparation(); + final SpdxDocument spdxDocument = openAsSpdx(targetFile); + if (spdxDocument == null) { + requestPreparation.setRequestStatus(RequestStatus.FAILURE); + requestPreparation.setMessage("error-read-file"); + return requestPreparation; + } + try { + final List describedPackages = spdxDocument.getDocumentDescribes().stream().collect(Collectors.toList()); + final List packages = describedPackages.stream() + .filter(SpdxPackage.class::isInstance) + .collect(Collectors.toList()); + if (packages.isEmpty()) { + requestPreparation.setMessage("The provided BOM did not contain any top level packages."); + requestPreparation.setRequestStatus(RequestStatus.FAILURE); + return requestPreparation; + } else if (packages.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 SpdxElement spdxElement = packages.get(0); + if (spdxElement instanceof SpdxPackage) { + final SpdxPackage spdxPackage = (SpdxPackage) spdxElement; + requestPreparation.setName(getValue(spdxPackage.getName())); + requestPreparation.setVersion(getValue(spdxPackage.getVersionInfo())); + requestPreparation.setRequestStatus(RequestStatus.SUCCESS); + } else { + requestPreparation.setMessage("Failed to get spdx package from the provided BOM file."); + requestPreparation.setRequestStatus(RequestStatus.FAILURE); + } + } catch (InvalidSPDXAnalysisException e) { + requestPreparation.setRequestStatus(RequestStatus.FAILURE); + e.printStackTrace(); + } + return requestPreparation; + } + public RequestSummary importSpdxBOMAsRelease(File file, AttachmentContent attachmentContent) + throws SW360Exception { + return importSpdxBOM(file, attachmentContent, SW360Constants.TYPE_RELEASE); + } + + private SpdxDocument openAsSpdx(File file){ + try { + log.info("Read file: " + file.getName()); + return SpdxToolsHelper.deserializeDocument(file); + } catch (InvalidSPDXAnalysisException | IOException | InvalidFileNameException e) { + log.error("Error read file " + file.getName() + " to SpdxDocument"); + e.printStackTrace(); + return null; + } + } + + public RequestSummary importSpdxBOMAsProject(File file, AttachmentContent attachmentContent) + throws SW360Exception { + return importSpdxBOM(file, attachmentContent, SW360Constants.TYPE_PROJECT); } - public RequestSummary importSpdxBOMAsProject(InputStream inputStream, AttachmentContent attachmentContent) - throws InvalidSPDXAnalysisException, SW360Exception { - return importSpdxBOM(inputStream, attachmentContent, SW360Constants.TYPE_PROJECT); + private RequestSummary importSpdxBOM(File file, AttachmentContent attachmentContent, String type) + throws SW360Exception { + return importSpdxBOM(file, attachmentContent, type, null, null); } - private RequestSummary importSpdxBOM(InputStream inputStream, AttachmentContent attachmentContent, String type) - throws InvalidSPDXAnalysisException, SW360Exception { + private RequestSummary importSpdxBOM(File file, AttachmentContent attachmentContent, String type, String newReleaseVersion, String releaseId) + throws SW360Exception { final RequestSummary requestSummary = new RequestSummary(); - final SpdxDocument spdxDocument = openAsSpdx(inputStream); - final List describedPackages = Arrays.stream(spdxDocument.getDocumentDescribes()) - .filter(item -> item instanceof SpdxPackage) - .collect(Collectors.toList()); - - if (describedPackages.size() == 0) { - requestSummary.setTotalAffectedElements(0); - requestSummary.setTotalElements(0); - requestSummary.setMessage("The provided BOM did not contain any top level packages."); - requestSummary.setRequestStatus(RequestStatus.FAILURE); - return requestSummary; - } else if (describedPackages.size() > 1) { - requestSummary.setTotalAffectedElements(0); - requestSummary.setTotalElements(0); - requestSummary.setMessage("The provided BOM file contained multiple described top level packages. This is not allowed here."); - requestSummary.setRequestStatus(RequestStatus.FAILURE); - return requestSummary; - } + List describedPackages = new ArrayList<>(); + try { + SpdxDocument spdxDocument = openAsSpdx(file); + if (spdxDocument == null) { + requestSummary.setRequestStatus(RequestStatus.FAILURE); + return requestSummary; + } + describedPackages = spdxDocument.getDocumentDescribes().stream().collect(Collectors.toList()); + List packages = describedPackages.stream() + .filter(SpdxPackage.class::isInstance) + .collect(Collectors.toList()); + if (packages.isEmpty()) { + requestSummary.setTotalAffectedElements(0); + requestSummary.setTotalElements(0); + requestSummary.setMessage("The provided BOM did not contain any top level packages."); + requestSummary.setRequestStatus(RequestStatus.FAILURE); + return requestSummary; + } else if (packages.size() > 1) { + requestSummary.setTotalAffectedElements(0); + requestSummary.setTotalElements(0); + requestSummary.setMessage("The provided BOM file contained multiple described top level packages. This is not allowed here."); + requestSummary.setRequestStatus(RequestStatus.FAILURE); + return requestSummary; + } - final SpdxItem spdxItem = describedPackages.get(0); - final Optional response; - if (SW360Constants.TYPE_PROJECT.equals(type)) { - response = importAsProject(spdxItem, attachmentContent); - } else if (SW360Constants.TYPE_RELEASE.equals(type)) { - response = importAsRelease(spdxItem, attachmentContent); - } else { - throw new SW360Exception("Unsupported type=[" + type + "], can not import BOM"); - } + final SpdxPackage spdxElement = (SpdxPackage) packages.get(0); + final Optional response; + if (SW360Constants.TYPE_PROJECT.equals(type)) { + response = importAsProject(spdxElement, attachmentContent); + } else if (SW360Constants.TYPE_RELEASE.equals(type)) { + response = importAsRelease(spdxElement, attachmentContent); + } else { + throw new SW360Exception("Unsupported type=[" + type + "], can not import BOM"); + } - if (response.isPresent()) { - requestSummary.setRequestStatus(RequestStatus.SUCCESS); - requestSummary.setTotalAffectedElements(response.get().countAffected()); - requestSummary.setTotalElements(response.get().count()); - requestSummary.setMessage(response.get().getId()); - } else { - requestSummary.setRequestStatus(RequestStatus.FAILURE); - requestSummary.setTotalAffectedElements(-1); - requestSummary.setTotalElements(-1); - requestSummary.setMessage("Failed to import the BOM as type=[" + type + "]."); + if (response.isPresent()) { + requestSummary.setRequestStatus(RequestStatus.SUCCESS); + requestSummary.setTotalAffectedElements(response.get().countAffected()); + requestSummary.setTotalElements(response.get().count()); + requestSummary.setMessage(response.get().getId()); + } else { + requestSummary.setRequestStatus(RequestStatus.FAILURE); + requestSummary.setTotalAffectedElements(-1); + requestSummary.setTotalElements(-1); + requestSummary.setMessage("Failed to import the BOM as type=[" + type + "]."); + } + } catch (InvalidSPDXAnalysisException | NullPointerException e) { + log.error("Can not open file to SpdxDocument " +e); } return requestSummary; } - private SpdxDocument openAsSpdx(InputStream inputStream) throws InvalidSPDXAnalysisException { - String FILETYPE_SPDX_INTERNAL = "RDF/XML"; - return SPDXDocumentFactory - .createSpdxDocument(inputStream, - "http://localhost/", - FILETYPE_SPDX_INTERNAL); - } - - private Component createComponentFromSpdxPackage(SpdxPackage spdxPackage) { + private Component createComponentFromSpdxPackage(SpdxPackage spdxPackage) throws InvalidSPDXAnalysisException { final Component component = new Component(); - final String name = spdxPackage.getName(); + String name = ""; + name = getValue(spdxPackage.getName()); component.setName(name); return component; } - private SpdxBOMImporterSink.Response importAsComponent(SpdxPackage spdxPackage) throws SW360Exception { + private SpdxBOMImporterSink.Response importAsComponent(SpdxPackage spdxPackage) throws SW360Exception, InvalidSPDXAnalysisException { final Component component = createComponentFromSpdxPackage(spdxPackage); return sink.addComponent(component); } - private Release createReleaseFromSpdxPackage(SpdxPackage spdxPackage) { + private Release createReleaseFromSpdxPackage(SpdxPackage spdxPackage) throws InvalidSPDXAnalysisException { final Release release = new Release(); - final String name = spdxPackage.getName(); - final String version = spdxPackage.getVersionInfo(); + final String name = getValue(spdxPackage.getName()); + final String version = getValue(spdxPackage.getVersionInfo()); release.setName(name); release.setVersion(version); return release; } + // 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 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 importAsRelease(SpdxElement relatedSpdxElement) throws SW360Exception { + private Optional importAsRelease(SpdxElement relatedSpdxElement) throws SW360Exception, InvalidSPDXAnalysisException { return importAsRelease(relatedSpdxElement, null); } - private Optional importAsRelease(SpdxElement relatedSpdxElement, AttachmentContent attachmentContent) throws SW360Exception { + private Optional importAsRelease(SpdxElement relatedSpdxElement, AttachmentContent attachmentContent + ) throws SW360Exception, InvalidSPDXAnalysisException { if (relatedSpdxElement instanceof SpdxPackage) { final SpdxPackage spdxPackage = (SpdxPackage) relatedSpdxElement; + final Release release; + SpdxBOMImporterSink.Response component; - SpdxBOMImporterSink.Response component = importAsComponent(spdxPackage); + component = importAsComponent(spdxPackage); final String componentId = component.getId(); - - final Release release = createReleaseFromSpdxPackage(spdxPackage); + release = createReleaseFromSpdxPackage(spdxPackage); release.setComponentId(componentId); - final Relationship[] relationships = spdxPackage.getRelationships(); + final Relationship[] relationships = spdxPackage.getRelationships().toArray(new Relationship[spdxPackage.getRelationships().size()]); List releases = importAsReleases(relationships); Map releaseIdToRelationship = makeReleaseIdToRelationship(releases); release.setReleaseIdToRelationship(releaseIdToRelationship); @@ -154,8 +253,6 @@ private Optional importAsRelease(SpdxElement relat Attachment attachment = makeAttachmentFromContent(attachmentContent); release.setAttachments(Collections.singleton(attachment)); } - - final SpdxBOMImporterSink.Response response = sink.addRelease(release); response.addChild(component); return Optional.of(response); @@ -170,29 +267,26 @@ private Map makeReleaseIdToRelationship(List importAsReleases(Relationship[] relationships) throws SW360Exception { + private List importAsReleases(Relationship[] relationships) throws SW360Exception, InvalidSPDXAnalysisException { List releases = new ArrayList<>(); - - Map typeToSupplierMap = new HashMap<>(); - typeToSupplierMap.put(Relationship.RelationshipType.CONTAINS, ReleaseRelationship.CONTAINED); - + Map typeToSupplierMap = new HashMap<>(); + typeToSupplierMap.put(RelationshipType.CONTAINS, ReleaseRelationship.CONTAINED); for (Relationship relationship : relationships) { - final Relationship.RelationshipType relationshipType = relationship.getRelationshipType(); + final RelationshipType relationshipType = relationship.getRelationshipType(); if(! typeToSupplierMap.keySet().contains(relationshipType)) { log.debug("Unsupported RelationshipType: " + relationshipType.toString()); continue; } - - final SpdxElement relatedSpdxElement = relationship.getRelatedSpdxElement(); + final SpdxElement relatedSpdxElement = relationship.getRelatedSpdxElement().get(); final Optional releaseId = importAsRelease(relatedSpdxElement); releaseId.map(response -> { response.setReleaseRelationship(typeToSupplierMap.get(relationshipType)); @@ -213,13 +307,11 @@ private Map makeReleaseIdToProjectRelationsh } - private Optional importAsProject(SpdxElement spdxElement, AttachmentContent attachmentContent) throws SW360Exception { + private Optional importAsProject(SpdxElement spdxElement, AttachmentContent attachmentContent) throws SW360Exception, InvalidSPDXAnalysisException { if (spdxElement instanceof SpdxPackage) { final SpdxPackage spdxPackage = (SpdxPackage) spdxElement; - final Project project = creatProjectFromSpdxPackage(spdxPackage); - - final Relationship[] relationships = spdxPackage.getRelationships(); + final Relationship[] relationships = spdxPackage.getRelationships().toArray(new Relationship[spdxPackage.getRelationships().size()]); List releases = importAsReleases(relationships); Map releaseIdToProjectRelationship = makeReleaseIdToProjectRelationship(releases); project.setReleaseIdToUsage(releaseIdToProjectRelationship); @@ -237,4 +329,12 @@ private Optional importAsProject(SpdxElement spdxE return Optional.empty(); } } + + private String getValue(Optional value) { + if (value.isPresent()) { + return value.get(); + } else { + return ""; + } + } } 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..f26477faed 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 @@ -91,6 +91,10 @@ 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 static class Response { private final String id; private final List 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 3e4bf350ac..d8b099a64b 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 @@ -22,6 +22,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.io.File; import java.io.InputStream; import static org.junit.Assert.assertNotNull; @@ -31,6 +32,7 @@ public class SpdxBOMImporterTest { private InputStream inputStream; + private File file; private AttachmentContent attachmentContent; @Mock @@ -73,7 +75,7 @@ public void after() throws Exception { @Test public void testProject() throws Exception { - final RequestSummary requestSummary = spdxBOMImporter.importSpdxBOMAsProject(inputStream, attachmentContent); + final RequestSummary requestSummary = spdxBOMImporter.importSpdxBOMAsProject(file, attachmentContent); assertNotNull(requestSummary); verify(spdxBOMImporterSink, times(1)).addProject(ArgumentMatchers.any()); @@ -83,7 +85,7 @@ public void testProject() throws Exception { @Test public void testRelease() throws Exception { - final RequestSummary requestSummary = spdxBOMImporter.importSpdxBOMAsRelease(inputStream, attachmentContent); + final RequestSummary requestSummary = spdxBOMImporter.importSpdxBOMAsRelease(file, attachmentContent); assertNotNull(requestSummary); verify(spdxBOMImporterSink, times(4)).addComponent(ArgumentMatchers.any()); 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 431e3968c6..213bf5436c 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 @@ -619,10 +619,17 @@ 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 rdfFilePath) throws TException { + assertNotNull(attachmentContentId); + assertUser(user); + return handler.importBomFromAttachmentContent(user, attachmentContentId,rdfFilePath); } @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 cfeafd4b29..050b978720 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 @@ -219,6 +219,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/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 fc8f83e376..7094086aa6 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 @@ -502,7 +502,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"; @@ -664,6 +668,9 @@ 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 Set SET_RELATIONSHIP_TYPE; + static { Properties props = CommonUtils.loadProperties(PortalConstants.class, PROPERTIES_FILE_PATH); @@ -703,6 +710,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/Sw360Portlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/Sw360Portlet.java index dcbe519363..c199d8e9d5 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 4d044eb8c0..d041215e9c 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 @@ -240,6 +240,8 @@ public void serveResource(ResourceRequest request, ResourceResponse response) th importBom(request, response); } else if (PortalConstants.LICENSE_TO_SOURCE_FILE.equals(action)) { serveLicenseToSourceFileMapping(request, response); + } else if (PortalConstants.PREPARE_IMPORT_BOM.equals(action)) { + prepareImportBom(request, response); } else if (isGenericAction(action)) { dealWithGenericAction(request, response, action); } else if (PortalConstants.LOAD_CHANGE_LOGS.equals(action) || PortalConstants.VIEW_CHANGE_LOGS.equals(action)) { @@ -331,8 +333,9 @@ private void importBom(ResourceRequest request, ResourceResponse response) { 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,rdfFilePath); LiferayPortletURL releaseUrl = createDetailLinkTemplate(request); releaseUrl.setParameter(PortalConstants.PAGENAME, PortalConstants.PAGENAME_RELEASE_DETAIL); @@ -422,6 +425,19 @@ private void serveViewDepartment(ResourceRequest request, ResourceResponse respo include("/html/components/ajax/departmentSearch.jsp", request, response, PortletRequest.RESOURCE_PHASE); } + 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)); + } + } + private void serveCheckComponentName(ResourceRequest request, ResourceResponse response) throws IOException { List resultComponents = new ArrayList<>(); List errors = new ArrayList<>(); @@ -1029,7 +1045,7 @@ 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"); - + } catch (TException e) { if (e instanceof SW360Exception) { SW360Exception sw360Exp = (SW360Exception)e; @@ -1416,7 +1432,7 @@ private void generateReleaseMergeWizardStep1Response(ActionRequest request, Json } Map> displayInformation = new HashMap<>(); - + addToMap(displayInformation, "mainlineState", releaseTarget.getMainlineState()); addToMap(displayInformation, "mainlineState", releaseSource.getMainlineState()); addToMap(displayInformation, "repositorytype", releaseTarget.getRepository() != null ? releaseTarget.getRepository().getRepositorytype() : null); @@ -1439,7 +1455,7 @@ private void generateReleaseMergeWizardStep1Response(ActionRequest request, Json releaseToNameMap.put(release.getId(), release.getName() + " (" + release.getVersion() + ")"); } displayInformation.put("release", releaseToNameMap); - + jsonGenerator.writeStartObject(); // adding common title @@ -1479,7 +1495,7 @@ private Map getUsageInformationForReleaseMerge(String releaseSo usageInformation.put("releaseVulnerabilities", releaseVulnerabilities.size()); List projectRatings = vulnerabilityClient.getProjectVulnerabilityRatingsByReleaseId(releaseSourceId, sessionUser); usageInformation.put("projectRatings", projectRatings.size()); - + return usageInformation; } @@ -1658,7 +1674,7 @@ private void prepareReleaseDetailView(RenderRequest request, RenderResponse resp } Map permissions = release.getPermissions(); - + request.setAttribute(PortalConstants.WRITE_ACCESS_USER, permissions.get(RequestedAction.WRITE)); if (isNullOrEmpty(id)) { id = release.getComponentId(); 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 1ad5aec4e3..8247fa5fbf 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 @@ -2704,7 +2704,7 @@ private Map setLicenseInfoWithObligations(PortletR 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) { 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/includes/releases/clearingDetails.jspf b/frontend/sw360-portlet/src/main/resources/META-INF/resources/html/components/includes/releases/clearingDetails.jspf index e4550db107..cf7b5018a6 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 @@ -503,6 +503,7 @@ dataObj = {}; url.setParameter(tableData.releaseIdParameterName, releaseId); + url.setParameter(tableData.attachmentIdParameterName, attachmentId); dataObj[ '' + 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 400254872e..bd6b8e0a89 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 @@ -141,9 +141,10 @@ <%@include file="/html/components/includes/vendors/vendorDetail.jspf" %> + <%@include file="/html/utils/includes/usingProjectsTable.jspf" %> <%@include file="/html/utils/includes/usingComponentsTable.jspf"%> - +
active show"> <%@include file="/html/utils/includes/linkedReleaseDetails.jspf" %>
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 5719a478a5..9369b16b7a 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 @@ -175,7 +175,7 @@