Commit 7cfab7b9 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '133-generate-docx-from-descriptor-list' into 'master'

133 generate docx from descriptor list

See merge request !124
parents d2be7b47 3e7e4306
......@@ -132,7 +132,7 @@
</execution>
</executions>
<configuration>
<!-- first check to see if you have locally modified files, and will
<!-- first check to see if you have locally modified files, and will
fail if there are any. -->
<doCheck>false</doCheck>
<doUpdate>false</doUpdate>
......@@ -657,5 +657,15 @@
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-all</artifactId>
<version>0.28.34</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
</dependencies>
</project>
......@@ -40,6 +40,7 @@ import org.springframework.core.io.Resource;
public class ApplicationConfig {
@Bean
@Order(Integer.MIN_VALUE)
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
final PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
propertyPlaceholderConfigurer.setIgnoreResourceNotFound(true);
......
......@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.catalog.server.components;
package org.genesys.catalog.server.config;
import javax.transaction.Transactional;
......
......@@ -19,8 +19,7 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import org.genesys.catalog.server.components.AddStuffInterceptor;
import org.genesys.catalog.server.components.StartupInitializer;
import org.genesys.catalog.server.controller.AddStuffInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
......@@ -57,7 +56,7 @@ import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
*/
@Configuration
@EnableWebMvc
@ComponentScan({ "org.genesys.catalog.server" })
@ComponentScan({ "org.genesys.catalog.server.controller", "org.genesys.catalog.server.service" })
public class WebConfiguration extends WebMvcConfigurerAdapter {
@Override
......@@ -120,11 +119,6 @@ public class WebConfiguration extends WebMvcConfigurerAdapter {
return new AddStuffInterceptor();
}
@Bean
public StartupInitializer startupInitializer() {
return new StartupInitializer();
}
@Override
public void addInterceptors(final InterceptorRegistry registry) {
final LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
......
......@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.catalog.server.components;
package org.genesys.catalog.server.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
......
/*
* Copyright 2018 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.catalog.server.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.genesys.catalog.model.traits.DescriptorList;
import org.genesys.catalog.server.service.FreemarkerTemplating;
import org.genesys.catalog.server.service.FreemarkerTemplating.FreemarkerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.vladsch.flexmark.ast.Node;
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.KeepType;
import com.vladsch.flexmark.util.options.MutableDataHolder;
import com.vladsch.flexmark.util.options.MutableDataSet;
/**
* Export Descriptor list
*/
@Component
public class DescriptorListExporter {
@Autowired
private FreemarkerTemplating freemarker;
/*@formatter:off*/
static final MutableDataHolder OPTIONS = new MutableDataSet()
.set(Parser.REFERENCES_KEEP, KeepType.LAST)
// .set(Parser.HTML_BLOCK_PARSER, true)
// .set(Parser.HTML_BLOCK_START_ONLY_ON_BLOCK_TAGS, true)
.set(HtmlRenderer.INDENT_SIZE, 2)
.set(HtmlRenderer.PERCENT_ENCODE_URLS, true)
// .set(HtmlRenderer.ESCAPE_HTML, false)
// for full GFM table compatibility add the following table extension options:
.set(TablesExtension.COLUMN_SPANS, false)
.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
.set(Parser.EXTENSIONS, Arrays.asList(
TablesExtension.create(),
StrikethroughExtension.create()
));
/*@formatter:on*/
public String markdownDescriptorList(DescriptorList descriptorList) throws FreemarkerException {
Map<String, Object> root = new HashMap<>();
root.put("descriptorList", descriptorList);
return freemarker.processTemplateResource("descriptorlist/booklet.ftl", root);
}
public String htmlDescriptorList(DescriptorList descriptorList) throws FreemarkerException {
Map<String, Object> root = new HashMap<>();
root.put("descriptorList", descriptorList);
String markdown = freemarker.processTemplateResource("descriptorlist/booklet.ftl", root);
// System.err.println(markdown);
Parser parser = Parser.builder(OPTIONS).build();
Node document = parser.parse(markdown);
HtmlRenderer renderer = HtmlRenderer.builder(OPTIONS).build();
final String html = renderer.render(document);
// System.err.println(html);
// HTML wrapper
root.clear();
root.put("title", descriptorList.getTitle());
root.put("author", descriptorList.getPublisher());
root.put("html", html);
return freemarker.processTemplateResource("html.ftl", root);
}
}
......@@ -15,13 +15,17 @@
*/
package org.genesys.catalog.server.controller.api.v0;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.genesys.catalog.model.traits.Descriptor;
import org.genesys.catalog.model.traits.DescriptorList;
import org.genesys.catalog.server.controller.DescriptorListExporter;
import org.genesys.catalog.service.DescriptorListService;
import org.genesys.catalog.service.DescriptorService;
import org.genesys.catalog.service.filters.DescriptorListFilter;
......@@ -29,7 +33,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -58,6 +64,9 @@ public class DescriptorListController {
@Autowired
private DescriptorListService descriptorListService;
@Autowired
private DescriptorListExporter exporter;
/**
* Gets the descriptor list.
......@@ -71,6 +80,21 @@ public class DescriptorListController {
return descriptorList;
}
@Transactional(readOnly = true)
@GetMapping(value = "/{UUID}/html", produces = { "text/html" })
public void generateDocument(@PathVariable("UUID") final UUID uuid, final HttpServletResponse response) throws IOException {
final DescriptorList descriptorList = descriptorListService.getDescriptorList(uuid);
final String descriptorListHtml = exporter.htmlDescriptorList(descriptorList);
response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=86400, s-maxage=86400, public, no-transform");
response.setHeader(HttpHeaders.PRAGMA, "");
response.setContentType("text/html");
// response.addHeader("Content-Disposition", String.format("attachment; filename=\"%s.html\"", descriptorList.getTitle()));
response.getWriter().write(descriptorListHtml);
response.flushBuffer();
}
/**
* Loads descriptor by uuid and version and tries to publish it.
*
......
/*
* Copyright 2018 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.catalog.server.service;
import java.io.IOException;
import java.util.Map;
/**
* The Interface FreemarkerTemplating.
*
* @author Matija Obreza
*/
public interface FreemarkerTemplating {
/**
* Process template.
*
* @param template the template
* @param root the root
* @return the string
* @throws FreemarkerException the freemarker exception
*/
String processTemplate(String template, Map<?, ?> root) throws FreemarkerException;
/**
* Process template resource.
*
* @param template the template
* @param root the root
* @return the string
* @throws FreemarkerException the freemarker exception
*/
String processTemplateResource(String template, Map<?, ?> root) throws FreemarkerException;
/**
* The Class FreemarkerException.
*/
public final static class FreemarkerException extends IOException {
private static final long serialVersionUID = -8412115361695692212L;
/**
* Instantiates a new freemarker exception.
*
* @param e the e
*/
public FreemarkerException(Throwable e) {
super(e);
}
}
}
/*
* Copyright 2018 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.catalog.server.service.impl;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Map;
import org.genesys.catalog.server.service.FreemarkerTemplating;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.TemplateNotFoundException;
/**
* The Class FreemarkerTemplatingServiceImpl.
*
* @author Matija Obreza
*/
@Service
public class FreemarkerTemplatingServiceImpl implements FreemarkerTemplating, InitializingBean {
private Configuration freeMarkerCfg;
@Override
public void afterPropertiesSet() throws Exception {
// Create your Configuration instance, and specify if up to what FreeMarker
// version (here 2.3.27) do you want to apply the fixes that are not 100%
// backward-compatible. See the Configuration JavaDoc for details.
Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
cfg.setClassForTemplateLoading(this.getClass(), "/freemarker/");
// Set the preferred charset template files are stored in. UTF-8 is
// a good choice in most applications:
cfg.setDefaultEncoding("UTF-8");
// Sets how errors will appear.
// During web page *development* TemplateExceptionHandler.HTML_DEBUG_HANDLER is
// better.
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
// Don't log exceptions inside FreeMarker that it will thrown at you anyway:
cfg.setLogTemplateExceptions(false);
this.freeMarkerCfg = cfg;
}
/**
* Process template.
*
* @param template the template
* @param root the root
* @return the string
* @throws IOException Signals that an I/O exception has occurred.
* @throws TemplateException the template exception
*/
@Override
public String processTemplate(String template, Map<?, ?> root) throws FreemarkerException {
try {
Template temp = new Template("template-" + template.hashCode(), new StringReader(template), this.freeMarkerCfg);
StringWriter out = new StringWriter(2048);
temp.process(root, out);
return out.toString();
} catch (Throwable e) {
throw new FreemarkerException(e);
}
}
/**
* Process template resource.
*
* @param template the template
* @param root the root
* @return the string
* @throws TemplateNotFoundException the template not found exception
* @throws MalformedTemplateNameException the malformed template name exception
* @throws ParseException the parse exception
* @throws IOException Signals that an I/O exception has occurred.
* @throws TemplateException the template exception
*/
@Override
public String processTemplateResource(String template, Map<?, ?> root) throws FreemarkerException {
try {
Template temp = freeMarkerCfg.getTemplate(template);
StringWriter out = new StringWriter(2048);
temp.process(root, out);
return out.toString();
} catch (Throwable e) {
throw new FreemarkerException(e);
}
}
}
<#macro isPublished published what>
<#if published>
<#else>
<div class="alert not-published"><b>Note:</b> This ${what} is not published.</div><#lt>
</#if>
</#macro>
<#macro printDescriptor descriptor>
# ${descriptor.title} <#if descriptor.uom??>[${descriptor.uom}]</#if><#lt>
<@isPublished published=descriptor.published what="descriptor" />
<#if descriptor.description??>
${descriptor.description}<#lt>
<#else>
> No description.<#lt>
</#if>
<#list descriptor.terms>
<#compress>
|Code|Term|Description|
|---|---|---|
<#items as term>
|**${term.code}**|${term.title}|${term.description!}|
</#items>
</#compress>
</#list>
<#if descriptor.vocabulary??>
<#compress>
**Note:** This descriptor uses the **${descriptor.vocabulary.title}** controlled vocabulary
<#if descriptor.vocabulary.publisher??>
published by **${descriptor.vocabulary.publisher}**
<#else>
maintained by **${descriptor.vocabulary.owner.name}**
</#if>.
</#compress>
${descriptor.vocabulary.description!}<#lt>
</#if>
</#macro>
# ${descriptorList.title}
${descriptorList.description!}
<@isPublished published=descriptorList.published what="descriptor list" />
Version: ${descriptorList.versionTag} <#lt>
<#if descriptorList.publisher??>
Published by: **${descriptorList.publisher}** <#lt>
<#else>
Maintained by: ${descriptorList.owner.name} <#lt>
</#if>
Last modified: ${descriptorList.lastModifiedDate?date} <#lt>
Date registered: ${descriptorList.createdDate?date} <#lt>
Record: ${descriptorList.uuid} v${descriptorList.version} <#lt>
<#list descriptorList.descriptors>
<#items as descriptor>
<@printDescriptor descriptor=descriptor />
</#items>
<#else>
> The descriptor list does not declare any descriptors.
</#list>
<footer>
Generated ${.now} from Genesys Catalog data.
</footer>
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes" />
<meta name="author" content="${author}" />
<style>
html {
font-size: 10pt;
font-family: Arial;
}
h1 { font-size: 1.2rem; margin: 1.4rem 0 0.6rem; }
h1:first-of-type {
text-align: center;
font-size: 2rem;
}
h2 { font-size: 1.2rem; margin: 1.2rem 0 0.6rem; }
h3 { font-size: 1.1rem; margin: 1rem 0 0.5rem; }
h4 { font-size: 1rem; margin: 0.8rem 0 0.4rem; }
pre { font-size: 0.8rem; }
body {
counter-reset: h1counter;
}
footer {
margin-top: 2rem;
text-align: center;
font-size: .8rem;
color: #a0a0a0;
}
blockquote {
border-left: .3rem solid gray;
margin: .2rem;
padding: .1rem 1rem;
}
table {
}
table th, table td {
text-align: left;
font-size: 0.9rem;
padding: .2rem .5rem;
}
h1:first-of-type:before {
content: none;
counter-reset: h1counter;
counter-reset: h2counter;
counter-reset: h3counter;
}
h1:before {
content: counter(h1counter) ".\0000a0\0000a0";
counter-increment: h1counter;
counter-reset: h2counter;
}
h2:before {
content: counter(h1counter) "." counter(h2counter) ".\0000a0\0000a0";
counter-increment: h2counter;
counter-reset: h3counter;
}
h3:before {
content: counter(h1counter) "." counter(h2counter) "." counter(h3counter) ".\0000a0\0000a0";
counter-increment: h3counter;
}
div.alert.not-published {