diff --git a/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/LicenseInfoHandler.java b/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/LicenseInfoHandler.java index 73abed993f..72b29611a4 100644 --- a/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/LicenseInfoHandler.java +++ b/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/LicenseInfoHandler.java @@ -94,7 +94,7 @@ protected LicenseInfoHandler(AttachmentDatabaseHandler attachmentDatabaseHandler new TextGenerator(DISCLOSURE, "License Disclosure as TEXT"), new XhtmlGenerator(DISCLOSURE, "License Disclosure as XHTML"), new DocxGenerator(DISCLOSURE, "License Disclosure as DOCX"), - new DocxGenerator(REPORT, "License Report as DOCX") + new DocxGenerator(REPORT, "Project Clearing Report as DOCX") ); // @formatter:on } diff --git a/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/outputGenerators/DocxGenerator.java b/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/outputGenerators/DocxGenerator.java index 63dd39e41c..8bd5aebf42 100644 --- a/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/outputGenerators/DocxGenerator.java +++ b/backend/src/src-licenseinfo/src/main/java/org/eclipse/sw360/licenseinfo/outputGenerators/DocxGenerator.java @@ -15,6 +15,7 @@ import org.apache.log4j.Logger; import org.apache.poi.xwpf.usermodel.*; import org.apache.thrift.TException; +import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.SW360Utils; @@ -29,6 +30,8 @@ import org.eclipse.sw360.datahandler.thrift.licenses.LicenseService; import org.eclipse.sw360.datahandler.thrift.licenses.Todo; import org.eclipse.sw360.datahandler.thrift.projects.Project; +import org.eclipse.sw360.datahandler.thrift.projects.ProjectLink; +import org.eclipse.sw360.datahandler.thrift.projects.ProjectService; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.users.UserService; @@ -47,17 +50,24 @@ public class DocxGenerator extends OutputGenerator { private static final Logger LOGGER = Logger.getLogger(DocxGenerator.class); private static final String UNKNOWN_LICENSE_NAME = "Unknown license name"; private static final String UNKNOWN_FILE_NAME = "Unknown file name"; + private static final String UNKNOWN_LICENSE = "Unknown"; private static final String TODO_DEFAULT_TEXT = "todo not determined so far."; private static final String DOCX_TEMPLATE_FILE = "/templateFrontpageContent.docx"; private static final String DOCX_TEMPLATE_REPORT_FILE = "/templateReport.docx"; private static final String DOCX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; private static final String DOCX_OUTPUT_TYPE = "docx"; - public static final String UNKNOWN_LICENSE = "Unknown"; + private static final long ADDITIONAL_REQ_THRESHOLD = 3; - public static final int ADDITIONAL_REQ_TABLE_INDEX = 5; + + public static final int OVERVIEW_TABLE_INDEX = 0; + public static final int SPECIAL_OSS_RISKS_TABLE_INDEX = 1; public static final int DEV_DETAIL_TABLE_INDEX = 2; + public static final int THIRD_PARTY_COMPONENT_OVERVIEW_TABLE_INDEX = 3; private static final int COMMON_RULES_TABLE_INDEX = 4; + public static final int ADDITIONAL_REQ_TABLE_INDEX = 5; + + public DocxGenerator(OutputFormatVariant outputFormatVariant, String description) { super(DOCX_OUTPUT_TYPE, description, true, DOCX_MIME_TYPE, outputFormatVariant); @@ -136,7 +146,7 @@ private void fillDisclosureDocument( fillReleaseBulletList(document, projectLicenseInfoResults); fillReleaseDetailList(document, projectLicenseInfoResults, includeObligations); fillLicenseList(document, projectLicenseInfoResults); - } + } private void fillReportDocument( XWPFDocument document, @@ -147,6 +157,7 @@ private void fillReportDocument( Collection obligationResults, User user) throws XmlException, TException { + String businessUnit = project.getBusinessUnit(); String projectName = project.getName(); String projectVersion = project.getVersion(); String obligationsText = project.getObligationsText(); @@ -157,11 +168,13 @@ private void fillReportDocument( String deliveryChannelsText = project.getDeliveryChannels(); String remarksAdditionalRequirementsText = project.getRemarksAdditionalRequirements(); String projectDescription = project.getDescription(); - + // extract licenses that appear at least ADDITIONAL_REQ_THRESHOLD times + Set mostLicenses = extractMostCommonLicenses(obligationResults, ADDITIONAL_REQ_THRESHOLD); fillOwnerGroup(document, project); fillAttendeesTable(document, project); + replaceText(document, "$bunit", businessUnit); replaceText(document, "$license-info-header", licenseInfoHeaderText); replaceText(document, "$project-name", projectName); replaceText(document, "$project-version", projectVersion); @@ -175,13 +188,13 @@ private void fillReportDocument( replaceText(document, "$product-description", projectDescription); fillSpecialOSSRisksTable(document, project, obligationResults); - fillDevelopmentDetailsTable(document, project, user); + fillDevelopmentDetailsTable(document, project, user, projectLicenseInfoResults); fillOverview3rdPartyComponentTable(document, projectLicenseInfoResults); + fillCommonRulesTable(document, project); - fillAdditionalRequirementsTable(document, obligationResults); + replaceText(document, "$list_comma_sep_licenses_above_threshold", String.join(", ", mostLicenses)); + fillAdditionalRequirementsTable(document, obligationResults, mostLicenses); - // because of the impossible API component subsections must be the last thing in the docx file - // the rest of the sections must be generated after this writeComponentSubsections(document, projectLicenseInfoResults, obligationResults); } @@ -194,9 +207,9 @@ private void fillOwnerGroup(XWPFDocument document, Project project) throws XmlEx } private void fillAttendeesTable(XWPFDocument document, Project project) throws XmlException, TException { - XWPFTable table = document.getTables().get(0); + XWPFTable table = document.getTables().get(OVERVIEW_TABLE_INDEX); - int currentRow = 6; + int currentRow = 7; UserService.Iface userClient = new ThriftClients().makeUserClient(); @@ -220,7 +233,12 @@ private void fillAttendeesTable(XWPFDocument document, Project project) throws X continue; } - User user = userClient.getByEmail(email); + User user = null; + try { + user = userClient.getByEmail(email); + } catch (TException te) { + // a resulting null user object is handled below by replacing with email + } XWPFTableRow row = table.insertNewTableRow(currentRow++); String name = email; @@ -229,7 +247,7 @@ private void fillAttendeesTable(XWPFDocument document, Project project) throws X } String department = "N.A."; if(user != null) { - name = user.getDepartment(); + department = user.getDepartment(); } row.addNewTableCell().setText(name); @@ -241,7 +259,7 @@ private void fillAttendeesTable(XWPFDocument document, Project project) throws X } private void fillSpecialOSSRisksTable(XWPFDocument document, Project project, Collection obligationResults) throws XmlException, TException { - XWPFTable table = document.getTables().get(1); + XWPFTable table = document.getTables().get(SPECIAL_OSS_RISKS_TABLE_INDEX); final int[] currentRow = new int[]{0}; obligationResults.stream() @@ -260,7 +278,7 @@ private void fillSpecialOSSRisksTable(XWPFDocument document, Project project, Co } private void fillOverview3rdPartyComponentTable(XWPFDocument document, Collection projectLicenseInfoResults) throws XmlException { - XWPFTable table = document.getTables().get(3); + XWPFTable table = document.getTables().get(THIRD_PARTY_COMPONENT_OVERVIEW_TABLE_INDEX); int currentRow = 1; for(LicenseInfoParsingResult result : projectLicenseInfoResults) { @@ -292,17 +310,31 @@ private static Optional obligationsForRelease(Release r return obligationResults.stream().filter(opr -> opr.getRelease() == release).findFirst(); } - private void writeComponentSubsections(XWPFDocument document, Collection projectLicenseInfoResults, Collection obligationResults) throws XmlException { + private void writeComponentSubsections(XWPFDocument document, Collection projectLicenseInfoResults, Collection obligationResults) throws SW360Exception, XmlException { + XmlCursor cursor = document.getTables().get(ADDITIONAL_REQ_TABLE_INDEX).getCTTbl().newCursor(); + cursor.toEndToken(); for (LicenseInfoParsingResult result : projectLicenseInfoResults) { + while (cursor.currentTokenType() != XmlCursor.TokenType.START && cursor.hasNextToken()) { + cursor.toNextToken(); + } - XWPFParagraph title = document.createParagraph(); + if (cursor.currentTokenType() != XmlCursor.TokenType.START) { + throw new SW360Exception("Corrupt template; unable find start token"); + } + + XWPFParagraph title = document.insertNewParagraph(cursor); title.setStyle(STYLE_HEADING_3); title.setNumID(new BigInteger("2")); XWPFRun titleRun = title.createRun(); titleRun.setText(result.getVendor() + " " + result.getName()); - XWPFParagraph description = document.createParagraph(); + if (cursor.hasNextToken()) { + cursor.toNextToken(); + } else { + throw new SW360Exception("Corrupt template; unable to proceed to next token"); + } + XWPFParagraph description = document.insertNewParagraph(cursor); XWPFRun descriptionRun = description.createRun(); LicenseInfo licenseInfo = result.getLicenseInfo(); @@ -331,7 +363,7 @@ private void writeComponentSubsections(XWPFDocument document, Collection obligations = obligationsResult.getObligations(); - XWPFTable table = document.createTable(); + XWPFTable table = document.insertNewTbl(cursor); for (Obligation o : obligations) { XWPFTableRow row = table.insertNewTableRow(currentRow++); String licensesString = String.join(" ", o.getLicenseIDs()); @@ -343,29 +375,34 @@ private void writeComponentSubsections(XWPFDocument document, Collection projectLicenseInfoResults) throws TException { XWPFTable table = document.getTables().get(DEV_DETAIL_TABLE_INDEX); int currentRow = 1; - ComponentService.Iface compClient = new ThriftClients().makeComponentClient(); - List rll = compClient.getLinkedReleases(project.getReleaseIdToUsage()); + for(LicenseInfoParsingResult result : projectLicenseInfoResults) { + if (result.getStatus() != LicenseInfoRequestStatus.SUCCESS) { + // this error handling is for extra safety since projectLicenseInfoResults is provided by the caller + // and we assume valid input so we silently ignoring it. + continue; + } - for (ReleaseLink rl : rll) { - Release r = compClient.getReleaseById(rl.getId(), user); - Component component = compClient.getComponentById(r.getComponentId(), user); + Release r = result.getRelease(); + if (r == null) { + continue; + } XWPFTableRow row = table.insertNewTableRow(currentRow++); - row.addNewTableCell().setText(component.getName()); + row.addNewTableCell().setText(r.getName()); - String operatingSystems = component.getOperatingSystemsSize() == 0 ? "Unknown operating systems" : String.join(" ", component.getOperatingSystems()); + String operatingSystems = r.getOperatingSystemsSize() == 0 ? "N/A" : String.join(" ", r.getOperatingSystems()); row.addNewTableCell().setText(operatingSystems); - String langs = component.getLanguagesSize() == 0 ? "Unknown languages" : String.join(" ", component.getLanguages()); + String langs = r.getLanguagesSize() == 0 ? "N/A" : String.join(" ", r.getLanguages()); row.addNewTableCell().setText(langs); - String platforms = component.getSoftwarePlatformsSize() == 0 ? "Unknown platforms" : String.join(" ", component.getSoftwarePlatforms()); + String platforms = r.getSoftwarePlatformsSize() == 0 ? "N/A" : String.join(" ", r.getSoftwarePlatforms()); row.addNewTableCell().setText(platforms); } } @@ -379,13 +416,11 @@ protected static Set extractMostCommonLicenses(Collection entry.getValue().longValue() >= threshold) .map(entry -> entry.getKey()) + .map(license -> license.replace("\n", "").replace("\r", "")) .collect(Collectors.toSet()); } - private void fillAdditionalRequirementsTable(XWPFDocument document, Collection obligationResults) throws XmlException { - // extract licenses that appear at least ADDITIONAL_REQ_THRESHOLD times - Set mostLicenses = extractMostCommonLicenses(obligationResults, ADDITIONAL_REQ_THRESHOLD); - + private void fillAdditionalRequirementsTable(XWPFDocument document, Collection obligationResults, Set mostLicenses) throws XmlException { XWPFTable table = document.getTables().get(ADDITIONAL_REQ_TABLE_INDEX); final int[] currentRow = new int[]{0}; @@ -393,7 +428,7 @@ private void fillAdditionalRequirementsTable(XWPFDocument document, Collection opr.getStatus() == ObligationInfoRequestStatus.SUCCESS) .flatMap(opr -> opr.getObligations().stream()) .filter(o -> o.getLicenseIDs().stream() - .anyMatch(lid -> mostLicenses.parallelStream().anyMatch(mlid -> mlid.equals(lid)))) + .anyMatch(lid -> mostLicenses.parallelStream().anyMatch(mlid -> mlid.equals(lid.replace("\n", "").replace("\r", ""))))) .forEach(o -> { currentRow[0] = currentRow[0] + 1; XWPFTableRow row = table.insertNewTableRow(currentRow[0]); diff --git a/backend/src/src-licenseinfo/src/main/resources/templateReport.docx b/backend/src/src-licenseinfo/src/main/resources/templateReport.docx index d81e0ef1ae..cd3b1438fa 100644 Binary files a/backend/src/src-licenseinfo/src/main/resources/templateReport.docx and b/backend/src/src-licenseinfo/src/main/resources/templateReport.docx differ 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 d84abbc6e4..d973d34d55 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 @@ -320,8 +320,9 @@ private void downloadLicenseInfo(ResourceRequest request, ResourceResponse respo } private void sendLicenseInfoResponse(ResourceRequest request, ResourceResponse response, Project project, LicenseInfoFile licenseInfoFile) throws IOException { - OutputFormatInfo outputFormatInfo = licenseInfoFile.getOutputFormatInfo(); - String filename = String.format("LicenseInfo-%s%s-%s.%s", project.getName(), + OutputFormatInfo outputFormatInfo = licenseInfoFile.getOutputFormatInfo(); + String documentVariant = licenseInfoFile.getOutputFormatInfo().getVariant() == OutputFormatVariant.DISCLOSURE ? "LicenseInfo" : "ProjectClearingReport"; + String filename = String.format("%s-%s%s-%s.%s", documentVariant, project.getName(), StringUtils.isBlank(project.getVersion()) ? "" : "-" + project.getVersion(), SW360Utils.getCreatedOnTime().replaceAll("\\s", "_").replace(":", "_"), outputFormatInfo.getFileExtension()); diff --git a/frontend/sw360-portlet/src/main/webapp/html/components/editRelease.jsp b/frontend/sw360-portlet/src/main/webapp/html/components/editRelease.jsp index 9e4d08af4e..033846c82e 100644 --- a/frontend/sw360-portlet/src/main/webapp/html/components/editRelease.jsp +++ b/frontend/sw360-portlet/src/main/webapp/html/components/editRelease.jsp @@ -53,6 +53,7 @@ + @@ -179,6 +180,7 @@ Liferay.on('allPortletsReady', function() { autocomplete.prepareForMultipleHits('programminglanguages', ${programmingLanguages}); autocomplete.prepareForMultipleHits('op_systems', ${operatingSystemsAutoC}); + autocomplete.prepareForMultipleHits('platformsTB', ${platformsAutoC}); sw360Validate.validateWithInvalidHandlerNoIgnore('#releaseEditForm'); diff --git a/frontend/sw360-portlet/src/main/webapp/html/components/includes/releases/editReleaseInformation.jspf b/frontend/sw360-portlet/src/main/webapp/html/components/includes/releases/editReleaseInformation.jspf index 8efa9c9e8c..08923a484b 100644 --- a/frontend/sw360-portlet/src/main/webapp/html/components/includes/releases/editReleaseInformation.jspf +++ b/frontend/sw360-portlet/src/main/webapp/html/components/includes/releases/editReleaseInformation.jspf @@ -16,7 +16,7 @@ - + @@ -52,6 +52,17 @@ placeholder="e.g.,Linux,MAC,Windows,..." value=""/> + + + + + - - - - - - + + +
Release SummaryRelease Summary
+ + "/> +
"/> - -
+ +