Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PPTX export and settings page #11

Open
wants to merge 28 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9c39eb7
Squash all the PowerPoint export changes into a single commit for eas…
tung-jin-chew-hp Feb 20, 2017
bfea7b8
Embed the pptx button in the parametric results view directly
tung-jin-chew-hp Feb 21, 2017
6afd7d1
Exclude report view CSS since the dashboard isn't in this branch.
tung-jin-chew-hp Feb 21, 2017
89987aa
Use events object instead of adding listeners on render
tung-jin-chew-hp Feb 21, 2017
e534c46
Vertically center the left/right margin buttons using translateY, ins…
tung-jin-chew-hp Feb 23, 2017
27de2b0
Use the HTML5 input event so we immediately update the margin hint wh…
tung-jin-chew-hp Feb 23, 2017
b8dc80b
Disable topicmap/list/table/sunburst powerpoint export buttons until …
tung-jin-chew-hp Feb 27, 2017
6eb5f36
Tweak margin range validation for numerical accuracy and to prevent t…
tung-jin-chew-hp Feb 28, 2017
7a74139
More tests for custom filename and invalid individual top/left/right/…
tung-jin-chew-hp Feb 28, 2017
3f55f87
Cleanup obsoleted tile method (we use the JSONP proxy instead since i…
tung-jin-chew-hp Feb 28, 2017
fab10f2
Support exporting each visualization of a composite report as a separ…
tung-jin-chew-hp Feb 28, 2017
f0f9a37
Squash the widget and powerpoint report export code into a single com…
tung-jin-chew-hp Mar 2, 2017
dce499e
Hide the powerpoint export button until the widgets have all loaded
tung-jin-chew-hp Mar 3, 2017
439756e
We have to use application/x-www-form-urlencoded instead of multipart…
tung-jin-chew-hp Mar 3, 2017
790f7fd
Retain multipart/form-data encoding since it's far more efficient (3x…
tung-jin-chew-hp Mar 7, 2017
34ea854
Support exportPPTData on the sunburst and table views to make it easi…
tung-jin-chew-hp Mar 8, 2017
63ca200
Add PowerPoint export support to sunburst widget
tung-jin-chew-hp Mar 10, 2017
0dee6a4
Since the sunburst widget doesn't show the 'Breakdown by...' message,…
tung-jin-chew-hp Mar 10, 2017
867b037
Colour the sunburst widget's exported PowerPoint chart the same colou…
tung-jin-chew-hp Mar 10, 2017
f0a177e
Sort by title, to match the widget display
tung-jin-chew-hp Mar 10, 2017
6bf62d6
Update the map view and comparison map view field titles to match Ivo…
tung-jin-chew-hp Mar 10, 2017
558a50d
Improvements to the CasperJS report generation code mean we can do aw…
tung-jin-chew-hp Mar 14, 2017
cd386fc
Wait for the new initialiseWidgetPromise instead of the savedSearchPr…
tung-jin-chew-hp Mar 14, 2017
cff87b8
Export postInitialize so the initialiseWidgetPromise for the results-…
tung-jin-chew-hp Mar 14, 2017
98e42dc
Since FIND-971 added thumbnails to the results list widget, we should…
tung-jin-chew-hp Mar 15, 2017
c5e0487
Wait for the map widget to finish loading results before allowing pow…
tung-jin-chew-hp Mar 15, 2017
15660d1
Export the depth to the leaf nodes so we know whether to apply animat…
tung-jin-chew-hp Mar 16, 2017
928ba84
[FIND-1052] Flag that only the non-hidden colours should be shown in …
tung-jin-chew-hp Mar 22, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion webapp/core/bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
"datatables.net-fixedcolumns-bs": "3.2.2",
"hp-autonomy-about-page": "0.3.0",
"bootstrap": "3.3.7",
"moment-timezone": "0.5.11"
"moment-timezone": "0.5.11",
"html2canvas": "~0.4.1"
},
"devDependencies": {
"hp-autonomy-js-testing-utils": "2.2.0",
Expand Down
5 changes: 5 additions & 0 deletions webapp/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@
<artifactId>haven-search-components-core</artifactId>
<version>${haven.search.components.version}</version>
</dependency>
<dependency>
<groupId>com.hp.autonomy.frontend.reports.powerpoint</groupId>
<artifactId>powerpoint-report</artifactId>
<version>${powerpoint.report.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.LocaleResolver;
Expand Down Expand Up @@ -65,12 +67,19 @@ public EmbeddedServletContainerCustomizer containerCustomizer() {
* since that's apparently what the servlet spec specifies,
* It's required despite URIEncoding="UTF-8" on the connector since that only works on GET parameters.
* Jetty doesn't have this problem, it seems to use UTF-8 as the default.
* It also has to be a FilterRegistrationBean and be explicitly marked HIGHEST-PRECEDENCE otherwise it'll have no
* effect if other filters run getParameter() before this filter is called.
*/
@Bean
public CharacterEncodingFilter characterEncodingFilter() {
@Order(Ordered.HIGHEST_PRECEDENCE)
public FilterRegistrationBean characterEncodingFilter() {
final CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return characterEncodingFilter;

final FilterRegistrationBean frb = new FilterRegistrationBean(characterEncodingFilter);
frb.addUrlPatterns("/*");

return frb;
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public class TomcatConfig {
@Value("${server.tomcat.resources.max-cache-kb}")
private long webResourcesCacheSize;

@Value("${server.tomcat.connector.max-post-size}")
private int connectorMaxPostSize;

@Bean
public EmbeddedServletContainerFactory servletContainer() {
final TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
Expand All @@ -40,13 +43,18 @@ public EmbeddedServletContainerFactory servletContainer() {
context.setResources(resources);
});

tomcat.addConnectorCustomizers(connector -> {
connector.setMaxPostSize(connectorMaxPostSize);
});

return tomcat;
}

private Connector createAjpConnector() {
final Connector connector = new Connector("AJP/1.3");
connector.setPort(ajpPort);
connector.setAttribute("tomcatAuthentication", false);
connector.setMaxPostSize(connectorMaxPostSize);
return connector;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ public interface FindConfig<C extends FindConfig<C, B>, B extends FindConfigBuil
Integer getTopicMapMaxResults();

B toBuilder();

PowerPointConfig getPowerPoint();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* Copyright 2015-2017 Hewlett-Packard Enterprise Development Company, L.P.
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
*/

package com.hp.autonomy.frontend.find.core.configuration;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.hp.autonomy.frontend.configuration.ConfigException;
import com.hp.autonomy.frontend.configuration.validation.OptionalConfigurationComponent;
import com.hp.autonomy.frontend.configuration.validation.ValidationResult;
import com.hp.autonomy.frontend.reports.powerpoint.PowerPointServiceImpl;
import com.hp.autonomy.frontend.reports.powerpoint.TemplateLoadException;
import com.hp.autonomy.frontend.reports.powerpoint.TemplateSettingsSource;
import com.hp.autonomy.frontend.reports.powerpoint.dto.Anchor;
import java.io.File;
import java.io.FileInputStream;
import lombok.Data;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.lang.StringUtils;

import static com.hp.autonomy.frontend.find.core.configuration.PowerPointConfig.Validation.INVALID_MARGINS;

@JsonDeserialize(builder = PowerPointConfig.Builder.class)
@Data
public class PowerPointConfig implements OptionalConfigurationComponent<PowerPointConfig> {
private final String templateFile;

private final Double marginTop, marginLeft, marginRight, marginBottom;

private PowerPointConfig(final Builder builder) {
templateFile = builder.templateFile;
marginTop = builder.marginTop;
marginLeft = builder.marginLeft;
marginRight = builder.marginRight;
marginBottom = builder.marginBottom;
}

@Override
public PowerPointConfig merge(final PowerPointConfig savedSearchConfig) {
return savedSearchConfig != null ?
new PowerPointConfig.Builder()
.setTemplateFile(templateFile == null ? savedSearchConfig.templateFile : templateFile)
.setMarginTop(marginTop == null ? savedSearchConfig.marginTop : marginTop)
.setMarginLeft(marginLeft == null ? savedSearchConfig.marginLeft : marginLeft)
.setMarginRight(marginRight == null ? savedSearchConfig.marginRight : marginRight)
.setMarginBottom(marginBottom == null ? savedSearchConfig.marginBottom : marginBottom)
.build()
: this;
}

@Override
public void basicValidate(final String section) throws ConfigException {
final ValidationResult<?> result = validate();

if (!result.isValid()) {
throw new ConfigException(section, String.valueOf(result.getData()));
}
}

@JsonIgnore
public Anchor getAnchor() {
final Anchor anchor = new Anchor();

final double tmpMarginTop = marginTop == null ? 0 : marginTop;
final double tmpMarginLeft = marginLeft == null ? 0 : marginLeft;
final double tmpMarginRight = marginRight == null ? 0 : marginRight;
final double tmpMarginBottom = marginBottom == null ? 0 : marginBottom;

anchor.setX(tmpMarginLeft);
anchor.setY(tmpMarginTop);
anchor.setWidth(1 - tmpMarginLeft - tmpMarginRight);
anchor.setHeight(1 - tmpMarginTop - tmpMarginBottom);

return anchor;
}

public ValidationResult<Validation> validate() {
if(rangeIsInvalid(marginTop)) {
return new ValidationResult<>(false, INVALID_MARGINS);
}
if(rangeIsInvalid(marginLeft)) {
return new ValidationResult<>(false, INVALID_MARGINS);
}
if(rangeIsInvalid(marginRight)) {
return new ValidationResult<>(false, INVALID_MARGINS);
}
if(rangeIsInvalid(marginBottom)) {
return new ValidationResult<>(false, INVALID_MARGINS);
}
if(differenceIsInvalid(marginLeft, marginRight)) {
return new ValidationResult<>(false, INVALID_MARGINS);
}
if(differenceIsInvalid(marginTop, marginBottom)) {
return new ValidationResult<>(false, INVALID_MARGINS);
}

if(StringUtils.isNotBlank(templateFile)) {
final File file = new File(templateFile);

if (!file.exists()) {
return new ValidationResult<>(false, Validation.TEMPLATE_FILE_NOT_FOUND);
}

final PowerPointServiceImpl service = new PowerPointServiceImpl(() -> new FileInputStream(file), TemplateSettingsSource.DEFAULT);

try {
service.validateTemplate();
}
catch(TemplateLoadException e) {
return new ValidationResult<>(false, Validation.TEMPLATE_INVALID);
}
}

return new ValidationResult<>(true, null);
}

private boolean differenceIsInvalid(final Double min, final Double max) {
double realMin = min == null ? 0 : min;
double realMax = max == null ? 0 : max;
if (realMin + realMax >= 1) {
return true;
}
return false;
}

private static boolean rangeIsInvalid(final Double value) {
return value != null && (value >= 1 || value < 0);
}

@Override
public Boolean getEnabled() {
return true;
}

@Setter
@Accessors(chain = true)
@JsonPOJOBuilder(withPrefix = "set")
public static class Builder {
private String templateFile;
private Double marginTop, marginLeft, marginRight, marginBottom;

public PowerPointConfig build() {
return new PowerPointConfig(this);
}
}

public enum Validation {
TEMPLATE_FILE_NOT_FOUND,
TEMPLATE_INVALID,
INVALID_MARGINS;

private Validation() {
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2015-2017 Hewlett-Packard Enterprise Development Company, L.P.
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
*/

package com.hp.autonomy.frontend.find.core.configuration;

import com.hp.autonomy.frontend.configuration.ConfigException;
import com.hp.autonomy.frontend.configuration.validation.ValidationResult;
import com.hp.autonomy.frontend.configuration.validation.Validator;
import org.springframework.stereotype.Component;

@Component
public class PowerPointConfigValidator implements Validator<PowerPointConfig> {
@Override
public ValidationResult<PowerPointConfig.Validation> validate(final PowerPointConfig config) {
return config.validate();
}

@Override
public Class<PowerPointConfig> getSupportedClass() {
return PowerPointConfig.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2017 Hewlett-Packard Enterprise Development Company, L.P.
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
*/

package com.hp.autonomy.frontend.find.core.configuration;

import com.hp.autonomy.frontend.reports.powerpoint.TemplateSource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping({"/api/admin/config", "/api/config/config"})
public class SamplePowerPointController {

@RequestMapping(value = "/template.pptx", method = RequestMethod.GET)
public HttpEntity<byte[]> template() throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copyLarge(TemplateSource.DEFAULT.getInputStream(), baos);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.presentationml.presentation"));
headers.set("Content-Disposition", "inline; filename=template.pptx");
return new HttpEntity<>(baos.toByteArray(), headers);
}
}
Loading