From 07b54e93c64c23b804f4e04dcc393db654450cef Mon Sep 17 00:00:00 2001 From: Abdul Kapti Date: Wed, 23 Mar 2022 14:07:09 +0530 Subject: [PATCH] feat(UI): Display Licenses from Scanner findings in ISR attachments Signed-off-by: Abdul Kapti --- .../sw360/licenseinfo/parsers/SPDXParser.java | 13 ++- .../licenseinfo/parsers/SPDXParserTools.java | 65 +++++++++-- .../licenseinfo/parsers/SPDXParserTest.java | 2 +- .../sw360/portal/common/PortalConstants.java | 3 + .../portlets/FossologyAwarePortlet.java | 2 +- .../portlets/components/ComponentPortlet.java | 73 +++++++++--- .../portlets/projects/ProjectPortlet.java | 97 ++++++++++++++- .../includes/releases/clearingDetails.jspf | 110 ++++++++++++++---- .../includes/projects/clearingStatus.jsp | 23 ++-- .../html/utils/ajax/linkedProjectsRows.jspf | 7 +- .../ajax/linkedReleasesClearingStatusRows.jsp | 7 +- .../html/utils/includes/scannerFindings.jspf | 88 ++++++++++++++ .../resources/content/Language.properties | 17 +++ .../resources/content/Language_ja.properties | 17 +++ .../resources/content/Language_vi.properties | 17 +++ 15 files changed, 470 insertions(+), 71 deletions(-) create mode 100644 frontend/sw360-portlet/src/main/resources/META-INF/resources/html/utils/includes/scannerFindings.jspf 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 getApplicableFileExtensions() { @Override public List 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 List 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 LicenseInfoParsingResult getLicenseInfo(Attachment attachment, boolean includeConcludedLicense, User user, + public LicenseInfoParsingResult getLicenseInfo(Attachment attachment, boolean includeConcludedLicense, boolean includeFileInformation, User user, T context) throws TException { AttachmentContent attachmentContent = attachmentContentProvider.getAttachmentContent(attachment); @@ -78,8 +79,10 @@ public 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 getLicenseTextFromMetadata(Document doc) { + private static HashMap getLicenseTextFromMetadata(Document doc, boolean includeFileInformation) { HashMap uriLicenseMap = new HashMap(); String[] licenseTags = { SPDX_EXTRACTED_LICENSING_INFO, SPDX_LICENSE }; @@ -346,17 +357,49 @@ private static HashMap 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 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 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 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/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..225ad39a44 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"; 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 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/components/ComponentPortlet.java b/frontend/sw360-portlet/src/main/java/org/eclipse/sw360/portal/portlets/components/ComponentPortlet.java index fe5abb9ce5..4563aa71af 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 @@ -14,6 +14,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -71,7 +72,6 @@ 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; @@ -573,35 +573,56 @@ 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> licenseToSrcFilesMap = new LinkedHashMap<>(); boolean includeConcludedLicense = new Boolean(request.getParameter(PortalConstants.INCLUDE_CONCLUDED_LICENSE)); ComponentService.Iface componentClient = thriftClients.makeComponentClient(); LicenseInfoService.Iface licenseInfoClient = thriftClients.makeLicenseInfoClient(); - Set concludedLicenseIds = new HashSet<>(); - Set mainLicenseNames = new HashSet(); - Set otherLicenseNames = new HashSet(); + final Set concludedLicenseIds = new TreeSet(); + Set mainLicenseNames = new TreeSet(); + Set otherLicenseNames = new TreeSet(); + AttachmentType attachmentType = AttachmentType.OTHER; + Predicate filterLicenseResult = result -> (null != result.getLicenseInfo() && null != result.getLicenseInfo().getLicenseNamesWithTexts()); + long totalFileCount = 0; try { Release release = componentClient.getReleaseById(releaseId, user); List licenseInfoResult = licenseInfoClient.getLicenseInfoForAttachment(release, attachmentContentId, includeConcludedLicense, user); + attachmentType = release.getAttachments().stream().filter(att -> attachmentContentId.equals(att.getAttachmentContentId())).map(Attachment::getAttachmentType).findFirst().orElse(null); List licenseWithTexts = licenseInfoResult.stream() + .filter(filterLicenseResult) .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(Objects::nonNull).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); @@ -626,7 +647,15 @@ private void loadSpdxLicenseInfo(ResourceRequest request, ResourceResponse respo 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)); + } + for (Map.Entry> entry : licenseToSrcFilesMap.entrySet()) { + jsonGenerator.writeArrayFieldStart(entry.getKey()); + entry.getValue().forEach(srcFile -> wrapException(() -> { jsonGenerator.writeString(srcFile); })); + jsonGenerator.writeEndArray(); + } jsonGenerator.writeEndObject(); jsonGenerator.close(); @@ -1544,10 +1573,18 @@ private String createFossologyJobViewLink(ExternalToolProcessStep processStep, private void setSpdxAttachmentsInRequest(RenderRequest request, Release release) { Set attachments = CommonUtils.nullToEmptySet(release.getAttachments()); - Set 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 attTypes = attachments.stream().map(Attachment::getAttachmentType).collect(Collectors.toUnmodifiableSet()); + Set 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); } 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..23d6e63086 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)) { @@ -2706,14 +2709,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 +2749,96 @@ 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 isISR = attachment -> AttachmentType.INITIAL_SCAN_REPORT.equals(attachment.getAttachmentType()); + + Set licenseNameWithTexts = new HashSet(); + String attachmentContentId = ""; + String attachmentName = ""; + Set concludedLicenseIds = new TreeSet(); + Set otherLicenseNames = new TreeSet(); + AttachmentType attachmentType = AttachmentType.OTHER; + JsonGenerator jsonGenerator = JSON_FACTORY.createGenerator(response.getWriter()); + Predicate filterLicenseResult = result -> (null != result.getLicenseInfo() && null != result.getLicenseInfo().getLicenseNamesWithTexts()); + Predicate filterConcludedLicense = result -> (null != result.getLicenseInfo() && null != result.getLicenseInfo().getConcludedLicenseIds()); + long totalFileCount = 0; + try { + Release release = componentClient.getReleaseById(releaseId, user); + Set 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 licenseInfoResult = licenseInfoClient.getLicenseInfoForAttachment(release, + attachmentContentId, true, user); + List licenseWithTexts = licenseInfoResult.stream() + .filter(filterLicenseResult) + .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(Objects::nonNull).mapToInt(Set::size).sum(); + concludedLicenseIds = licenseInfoResult.stream() + .filter(filterConcludedLicense) + .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); + } + try { + 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"); + } + } else { + jsonGenerator.writeStartObject(); + jsonGenerator.writeStringField(SW360Constants.STATUS, SW360Constants.FAILURE); + if (attachments.size() > 1) { + jsonGenerator.writeStringField(SW360Constants.MESSAGE, LanguageUtil.get(resourceBundle, "multiple.isr.are.found.in.the.release")); + } else if (attachments.isEmpty()) { + jsonGenerator.writeStringField(SW360Constants.MESSAGE, LanguageUtil.get(resourceBundle, "isr.attachment.not.found.in.the.release")); + } else { + jsonGenerator.writeStringField(SW360Constants.MESSAGE, LanguageUtil.get(resourceBundle, "license.information.not.found.in.isr")); + } + jsonGenerator.writeEndObject(); + jsonGenerator.close(); + } + } catch (TException e) { + log.error("Cannot retrieve license information 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/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..a55123f142 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,19 @@ - +
class="actions d-inline" > + + +
+ + + + data-release-id="${release.id}" data-attachment-id="${spdxAttachment.attachmentContentId}" data-attachment-type="${spdxAttachment.attachmentType}"> - + @@ -217,9 +223,10 @@ <%--for javascript library loading --%> <%@ include file="/html/utils/includes/requirejs.jspf" %> +<%@ include file="/html/utils/includes/pageSpinner.jspf" %> diff --git a/frontend/sw360-portlet/src/main/resources/content/Language.properties b/frontend/sw360-portlet/src/main/resources/content/Language.properties index cdcdc67980..cbf4e25933 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! @@ -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 Assessor Department and Assessement Date 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=*** This is auto-generated comment ***
+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 UNCHECKED. 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 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: ] in db for the project
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. 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..771c701686 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=クリアリングリクエストを更新するのに失敗しました! @@ -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=Assessor DepartmentAssessement Date のフィールドも同様に取り込まれます。 +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=*** This is auto-generated comment ***
+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=このライセンスはUNCHECKEDです。 this.obligation.is.not.associated.with.linked.release=このオブリゲーションは、リンクされたリリースと無関係 this.project.contains=このプロジェクトには以下のものが含まれています。 @@ -1301,7 +1314,9 @@ this.release.x.contains.1=今回のリリース 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: ] プロジェクトのデータベース
再試行してください. 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..05172b569b 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! @@ -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 Người giám địnhNgày đánh giá 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=*** This is auto-generated comment ***
+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à CHƯA KIỂM TRA. 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 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: ] in db for the project
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.