Java tutorial
/* * Copyright (C) 2000 - 2018 Silverpeas * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * As a special exception to the terms and conditions of version 3.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * Open Source Software ("FLOSS") applications as described in Silverpeas's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * "http://www.silverpeas.org/docs/core/legal/floss_exception.html" * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.silverpeas.components.kmelia.export; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.odftoolkit.odfdom.dom.element.text.TextAElement; import org.odftoolkit.simple.TextDocument; import org.odftoolkit.simple.meta.Meta; import org.odftoolkit.simple.table.Row; import org.odftoolkit.simple.table.Table; import org.odftoolkit.simple.text.Paragraph; import org.odftoolkit.simple.text.Section; import org.odftoolkit.simple.text.list.ListItem; import org.silverpeas.components.kmelia.model.KmeliaPublication; import org.silverpeas.components.kmelia.service.KmeliaService; import org.silverpeas.core.admin.service.OrganizationController; import org.silverpeas.core.admin.service.OrganizationControllerProvider; import org.silverpeas.core.admin.user.model.User; import org.silverpeas.core.comment.model.Comment; import org.silverpeas.core.contribution.attachment.AttachmentServiceProvider; import org.silverpeas.core.contribution.attachment.model.SimpleDocument; import org.silverpeas.core.contribution.content.form.DataRecord; import org.silverpeas.core.contribution.content.form.Form; import org.silverpeas.core.contribution.content.form.PagesContext; import org.silverpeas.core.contribution.content.form.RecordSet; import org.silverpeas.core.contribution.content.form.RenderingContext; import org.silverpeas.core.contribution.content.wysiwyg.service.WysiwygContentTransformer; import org.silverpeas.core.contribution.content.wysiwyg.service.WysiwygController; import org.silverpeas.core.contribution.converter.DocumentFormatConverterProvider; import org.silverpeas.core.contribution.converter.HTMLConverter; import org.silverpeas.core.contribution.publication.model.PublicationDetail; import org.silverpeas.core.contribution.template.publication.PublicationTemplate; import org.silverpeas.core.contribution.template.publication.PublicationTemplateManager; import org.silverpeas.core.node.model.NodePK; import org.silverpeas.core.pdc.pdc.model.ClassifyPosition; import org.silverpeas.core.pdc.pdc.model.ClassifyValue; import org.silverpeas.core.pdc.pdc.model.Value; import org.silverpeas.core.util.Charsets; import org.silverpeas.core.util.LocalizationBundle; import org.silverpeas.core.util.ResourceLocator; import org.silverpeas.core.util.ServiceProvider; import org.silverpeas.core.util.SettingBundle; import org.silverpeas.core.util.UnitUtil; import org.silverpeas.core.util.file.FileRepositoryManager; import java.io.File; import java.util.Calendar; import java.util.List; import java.util.UUID; import java.util.regex.Pattern; import static org.silverpeas.components.kmelia.export.DocumentTemplateParts.*; import static org.silverpeas.components.kmelia.export.ODTDocumentTextTranslator.aTranslatorWith; import static org.silverpeas.components.kmelia.export.ODTDocumentsMerging.atSection; import static org.silverpeas.components.kmelia.export.ODTDocumentsMerging.decorates; import static org.silverpeas.core.contribution.converter.DocumentFormat.inFormat; import static org.silverpeas.core.contribution.converter.DocumentFormat.odt; import static org.silverpeas.core.util.DateUtil.formatDate; import static org.silverpeas.core.util.DateUtil.getOutputDate; import static org.silverpeas.core.util.StringUtil.isDefined; import static org.silverpeas.core.util.StringUtil.isInteger; /** * A builder of an ODT document based on a given template and from a specified Kmelia publication. */ public class ODTDocumentBuilder { private static final String DOCUMENT_TEMPLATE = "kmelia.export.template"; private static final SettingBundle settings = ResourceLocator .getSettingBundle("org.silverpeas.kmelia.settings.kmeliaSettings"); private User user; private String language = ""; private String topicIdToConsider; private LocalizationBundle messages; /** * Gets an instance of a builder of ODT documents. * @return an ODTDocumentBuilder instance. */ public static ODTDocumentBuilder anODTDocumentBuilder() { return new ODTDocumentBuilder(); } /** * Informs this builder the build is for the specified user. If not set, then the builds will be * performed for the publication creator. Only information the user is authorized to access will * be rendered into the ODT documents. * @param user the user for which the build of the documents should be done. * @return itself. */ public ODTDocumentBuilder forUser(final User user) { this.user = user; return this; } /** * Informs this builder the prefered language to use for the content of the documents to build. * If * the publication doesn't have a content in the specified language, then it is the default * publication's text that will be taken (whatever the language in which it is written). * @param language the language in which the text should be displayed in the built documents. * @return itself. */ public ODTDocumentBuilder inLanguage(String language) { this.language = (language == null ? "" : language); return this; } /** * Informs explicitly the topic to consider when building a document from publications. This * topic * can be provided by the caller itself as it was already computed for the publications to export * and according to the rights of the user on a such topic. If this topic isn't provided * explicitly, then it is computed directly from the publication and according to the rights of * the user on the topics the publication belongs to. * @param topicId the topic to explicitly consider. * @return itself. */ public ODTDocumentBuilder inTopic(final String topicId) { this.topicIdToConsider = topicId; return this; } /** * A convenient method to improve the readability in the call of the method * buildFromPublication(). It can be uses as: * <code>File odt = builder.buildFrom(mypublication, anODTAt("/tmp/foo.odt"));</code> * @param documentPath the path of the document to build. * @return the document path as passed as parameter. */ public static String anODTAt(String documentPath) { return documentPath; } /** * Builds an ODT document at the specified path and from the specified Kmelia publication. If an * error occurs while building the document, a runtime exception DocumentBuildException is * thrown. * @param publication the publication from which an ODT document is built. * @param documentPath the path of the ODT document to build. * @return the file corresponding to the ODT document built from the publication. */ public File buildFrom(final KmeliaPublication publication, String documentPath) { boolean isUserSet = true; try { String odtFilePath = documentPath; String extension = FilenameUtils.getExtension(odtFilePath); if (!isDefined(extension) || !"odt".equalsIgnoreCase(extension)) { odtFilePath += ".odt"; } TextDocument odtDocument = loadTemplate(); if (getUser() == null) { forUser(publication.getCreator()); isUserSet = false; } translate(odtDocument); fill(odtDocument, with(publication)); File odtFile = new File(odtFilePath); odtDocument.save(odtFile); return odtFile; } catch (Exception ex) { throw new DocumentBuildException(ex.getMessage(), ex); } finally { if (!isUserSet) { forUser(null); } } } private static KmeliaPublication with(final KmeliaPublication publication) { return publication; } private static TextDocument in(final TextDocument document) { return document; } /** * Loads the template to use in the build of ODT documents. * @return an ODT document corresponding to the loaded template. * @throws Exception if the template loading fails. */ private TextDocument loadTemplate() throws Exception { String exportTemplateDir = FileRepositoryManager.getExportTemplateRepository(); String templateDoc = settings.getString(DOCUMENT_TEMPLATE); return TextDocument.loadDocument(new File(exportTemplateDir + templateDoc)); } private void translate(final TextDocument odtDocument) { ODTDocumentTextTranslator translator = aTranslatorWith(getMessagesBundle()); translator.translate(odtDocument); } private void fill(final TextDocument odtDocument, final KmeliaPublication publication) throws Exception { buildInfoSection(in(odtDocument), with(publication)); buildContentSection(in(odtDocument), with(publication)); buildAttachmentsSection(in(odtDocument), with(publication)); buildSeeAlsoSection(in(odtDocument), with(publication)); buildPdCSection(in(odtDocument), with(publication)); buildCommentSection(in(odtDocument), with(publication)); } private void buildInfoSection(final TextDocument odtDocument, final KmeliaPublication publication) { PublicationDetail detail = publication.getDetail(); Meta metadata = odtDocument.getOfficeMetadata(); metadata.setCreator(detail.getCreatorName()); metadata.setCreationDate(Calendar.getInstance()); metadata.setSubject(detail.getDescription(getLanguage())); metadata.setDcdate(Calendar.getInstance()); metadata.setTitle(detail.getName(getLanguage())); metadata.setUserDefinedDataValue(FIELD_CREATION_DATE, getOutputDate(detail.getCreationDate(), getLanguage())); metadata.setUserDefinedDataValue(FIELD_MODIFICATION_DATE, getOutputDate(detail.getUpdateDate(), getLanguage())); metadata.setUserDefinedDataValue(FIELD_AUTHOR, publication.getCreator().getDisplayedName()); metadata.setUserDefinedDataValue(FIELD_LAST_MODIFIER, publication.getLastModifier().getDisplayedName()); metadata.setUserDefinedDataValue(FIELD_URL, publication.getURL()); metadata.setUserDefinedDataValue(FIELD_VERSION, detail.getVersion()); } private void buildCommentSection(final TextDocument odtDocument, final KmeliaPublication publication) { List<Comment> comments = publication.getComments(); if (comments.isEmpty()) { Section commentsSection = odtDocument.getSectionByName(SECTION_COMMENTS); commentsSection.remove(); } else { Table commentsTable = odtDocument.getTableByName(LIST_OF_COMMENTS); int i = 1; for (Comment comment : comments) { Row row = commentsTable.getRowByIndex(i++); row.getCellByIndex(0).setStringValue(comment.getOwnerDetail().getDisplayedName()); row.getCellByIndex(1).setStringValue(comment.getMessage()); row.getCellByIndex(2).setStringValue(formatDate(comment.getCreationDate())); row.getCellByIndex(3).setStringValue(formatDate(comment.getLastModificationDate())); } } } private void buildContentSection(final TextDocument odtDocument, final KmeliaPublication publication) throws Exception { String htmlContent = WysiwygController.load(publication.getPk().getInstanceId(), publication.getPk().getId(), getLanguage()); if (isDefined(htmlContent)) { buildWithHTMLText(htmlContent, in(odtDocument)); } else { buildWithXMLText(publication, in(odtDocument)); } } private void buildWithHTMLText(String htmlText, final TextDocument odtDocument) throws Exception { Section content = odtDocument.getSectionByName(SECTION_CONTENT); if (isDefined(htmlText)) { String text = WysiwygContentTransformer.on(htmlText).resolveVariablesDirective().transform(); String html = "<html><body>" + text + "</body></html>"; Paragraph p = content.getParagraphByIndex(1, false); if (p != null) { content.removeParagraph(p); } File htmlFile = null; File odtConvertedHtmlFile = null; try { htmlFile = new File( FileRepositoryManager.getTemporaryPath() + UUID.randomUUID().toString() + ".html"); FileUtils.writeStringToFile(htmlFile, html, Charsets.UTF_8); HTMLConverter converter = DocumentFormatConverterProvider.getHTMLConverter(); odtConvertedHtmlFile = converter.convert(htmlFile, inFormat(odt)); TextDocument htmlContent = TextDocument.loadDocument(odtConvertedHtmlFile); decorates(odtDocument).merge(htmlContent, atSection(SECTION_CONTENT)); } finally { if (htmlFile != null) { FileUtils.deleteQuietly(htmlFile); } if (odtConvertedHtmlFile != null) { FileUtils.deleteQuietly(odtConvertedHtmlFile); } } } else { content.remove(); } } private void buildWithXMLText(final KmeliaPublication publication, final TextDocument odtDocument) throws Exception { boolean removeSection = true; String templateId = publication.getDetail().getInfoId(); if (!isInteger(templateId)) { PublicationTemplate template = PublicationTemplateManager.getInstance() .getPublicationTemplate(publication.getPk().getInstanceId() + ":" + templateId); Form viewForm = template.getViewForm(); RecordSet recordSet = template.getRecordSet(); DataRecord dataRecord = recordSet.getRecord(publication.getPk().getId(), getLanguage()); if (dataRecord == null) { dataRecord = recordSet.getEmptyRecord(); dataRecord.setId(publication.getPk().getId()); } PagesContext context = new PagesContext(); context.setRenderingContext(RenderingContext.EXPORT); context.setLanguage(getLanguage()); context.setComponentId(publication.getPk().getInstanceId()); context.setObjectId(publication.getPk().getId()); context.setBorderPrinted(false); context.setContentLanguage(getLanguage()); context.setUserId(getUser().getId()); context.setNodeId(getTopicIdOf(publication)); String htmlText = viewForm.toString(context, dataRecord); if (isDefined(htmlText)) { //Suppress script tag htmlText = Pattern .compile("<script[^>]*>.*?</script>", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.DOTALL) .matcher(htmlText).replaceAll(""); buildWithHTMLText(htmlText, in(odtDocument)); removeSection = false; } } if (removeSection) { Section contentSection = odtDocument.getSectionByName(SECTION_CONTENT); contentSection.remove(); } } private void buildAttachmentsSection(final TextDocument odtDocument, final KmeliaPublication publication) { List<SimpleDocument> attachments = AttachmentServiceProvider.getAttachmentService() .listDocumentsByForeignKey(publication.getPk().toResourceReference(), getLanguage()); boolean hasNoAttachmentToDisplay = true; Table attachmentsTable = odtDocument.getTableByName(LIST_OF_ATTACHMENTS); int i = 1; for (SimpleDocument document : attachments) { SimpleDocument attachment = document.getLastPublicVersion(); if (attachment != null) { hasNoAttachmentToDisplay = false; SimpleDocumentHolder attachmentHolder = SimpleDocumentHolder.hold(attachment); Row row = attachmentsTable.getRowByIndex(i++); row.getCellByIndex(0).setStringValue(attachment.getFilename()); row.getCellByIndex(1).setStringValue(attachment.getTitle()); row.getCellByIndex(2).setStringValue(attachment.getDescription()); row.getCellByIndex(3).setStringValue(UnitUtil.formatMemSize(attachment.getSize())); row.getCellByIndex(4).setStringValue(attachmentHolder.getVersionNumber()); row.getCellByIndex(5).setStringValue(attachmentHolder.getLastModification(getLanguage())); row.getCellByIndex(6).setStringValue(attachmentHolder.getAuthorFullName()); } } if (hasNoAttachmentToDisplay) { Section attachmentsSection = odtDocument.getSectionByName(SECTION_ATTACHMENTS); attachmentsSection.remove(); } } private void buildSeeAlsoSection(final TextDocument odtDocument, final KmeliaPublication publication) { try { Section seeAlso = odtDocument.getSectionByName(SECTION_SEEALSO); List<KmeliaPublication> linkedPublications = getKmeliaService().getLinkedPublications(publication, getUser().getId()); if (linkedPublications.isEmpty()) { seeAlso.remove(); } else { Paragraph p = seeAlso.getParagraphByIndex(1, false); if (p != null) { seeAlso.removeParagraph(p); } for (KmeliaPublication aLinkedPublication : linkedPublications) { PublicationDetail publicationDetail = aLinkedPublication.getDetail(); if (publicationDetail.isValid() && !publicationDetail.getPK().getId().equals(publication.getPk().getId())) { TextAElement hyperlink = new TextAElement(odtDocument.getContentDom()); hyperlink.setXlinkHrefAttribute(aLinkedPublication.getURL()); hyperlink.setXlinkTypeAttribute("simple"); hyperlink.setTextContent(publicationDetail.getName(getLanguage())); org.odftoolkit.simple.text.list.List ul = odtDocument.addList(); ListItem li = ul.addItem(""); li.getOdfElement().getFirstChild().appendChild(hyperlink); seeAlso.getOdfElement().appendChild(ul.getOdfElement().cloneNode(true)); ul.remove(); seeAlso.addParagraph(aLinkedPublication.getLastModifier().getDisplayedName() + " - " + getOutputDate(publicationDetail.getUpdateDate(), getLanguage())); seeAlso.addParagraph(publicationDetail.getDescription(getLanguage())); } } } } catch (Exception ex) { throw new DocumentBuildException(ex.getMessage(), ex); } } private void buildPdCSection(TextDocument odtDocument, KmeliaPublication publication) { Section classification = odtDocument.getSectionByName(SECTION_CLASSIFICATION); List<ClassifyPosition> positions = publication.getPDCPositions(); int pathMaxLength = 5; int pathNodeMinNb = 2; String pathSeparator = "/"; int rank = 1; if (positions.isEmpty()) { classification.remove(); } else { Paragraph p = classification.getParagraphByIndex(1, false); if (p != null) { classification.removeParagraph(p); } for (ClassifyPosition aPosition : positions) { rank = buildPosition(odtDocument, classification, pathMaxLength, pathNodeMinNb, pathSeparator, rank, aPosition); } } } private int buildPosition(final TextDocument odtDocument, final Section classification, final int pathMaxLength, final int pathNodeMinNb, final String pathSeparator, int rank, final ClassifyPosition aPosition) { classification.addParagraph("Position " + rank++); org.odftoolkit.simple.text.list.List ul = odtDocument.addList(); for (ClassifyValue positionValue : aPosition.getValues()) { buildPositionValue(pathMaxLength, pathNodeMinNb, pathSeparator, ul, positionValue); } classification.getOdfElement().appendChild(ul.getOdfElement().cloneNode(true)); ul.remove(); return rank; } private void buildPositionValue(final int pathMaxLength, final int pathNodeMinNb, final String pathSeparator, final org.odftoolkit.simple.text.list.List ul, final ClassifyValue positionValue) { StringBuilder pathToRender = new StringBuilder(); List<Value> pathNodes = positionValue.getFullPath(); int pathLength = pathNodes.size(); if (pathLength > pathMaxLength) { for (int i = 0; i < pathNodeMinNb; i++) { pathToRender.append(pathNodes.get(i).getName(getLanguage())).append(pathSeparator); } pathToRender.append("...").append(pathSeparator); for (int i = pathNodeMinNb; i > 0; i--) { pathToRender.append(pathNodes.get(pathLength - i).getName(getLanguage())).append(pathSeparator); } } else { for (Value pathNode : pathNodes) { pathToRender.append(pathNode.getName(getLanguage())).append(pathSeparator); } } if (!pathSeparator.equals(pathToRender.toString()) && pathToRender.length() > 0) { ul.addItem(pathToRender.substring(0, pathToRender.length() - pathSeparator.length())); } } private boolean isRightsOnTopicsEnabled(final String componentInstanceId) { return Boolean.valueOf( getOrganizationService().getComponentParameterValue(componentInstanceId, "rightsOnTopics")); } private boolean isTree(final String componentInstanceId) { String isTree = getOrganizationService().getComponentParameterValue(componentInstanceId, "istree"); if (!isDefined(isTree)) { return true; } return "0".equals(isTree) || "1".equals(isTree); } private String getTopicIdOf(final KmeliaPublication publication) { String theTopicId = this.topicIdToConsider; if (theTopicId == null) { String componentId = publication.getPk().getInstanceId(); NodePK pk = getKmeliaService().getPublicationFatherPK(publication.getPk(), isTree(componentId), getUser().getId(), isRightsOnTopicsEnabled(componentId)); theTopicId = pk.getId(); } return theTopicId; } private String getLanguage() { return this.language; } private User getUser() { return this.user; } private LocalizationBundle getMessagesBundle() { if (this.messages == null) { this.messages = ResourceLocator.getLocalizationBundle("org.silverpeas.kmelia.multilang.kmeliaExport", getLanguage()); } return this.messages; } /** * Gets the Kmelia service. * @return an instance of KmeliaService. */ protected KmeliaService getKmeliaService() { return ServiceProvider.getService(KmeliaService.class); } /** * Gets the organization controller. * @return an instance of OrganizationController. */ protected OrganizationController getOrganizationService() { return OrganizationControllerProvider.getOrganisationController(); } }