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

[NAE-1544] Time zone in Date and Datetime field #116

Open
wants to merge 10 commits into
base: release/6.4.0
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ import com.netgrif.application.engine.mail.interfaces.IMailService
import com.netgrif.application.engine.orgstructure.groups.interfaces.INextGroupService
import com.netgrif.application.engine.pdf.generator.config.PdfResource
import com.netgrif.application.engine.pdf.generator.service.interfaces.IPdfGenerator
import com.netgrif.application.engine.petrinet.domain.I18nString
import com.netgrif.application.engine.petrinet.domain.PetriNet
import com.netgrif.application.engine.petrinet.domain.Transition
import com.netgrif.application.engine.petrinet.domain.UriContentType
import com.netgrif.application.engine.petrinet.domain.UriNode
import com.netgrif.application.engine.petrinet.domain.*
import com.netgrif.application.engine.petrinet.domain.dataset.*
import com.netgrif.application.engine.petrinet.domain.dataset.logic.ChangedField
import com.netgrif.application.engine.petrinet.domain.dataset.logic.FieldBehavior
Expand Down Expand Up @@ -68,14 +64,13 @@ import org.springframework.beans.factory.annotation.Value
import org.springframework.context.i18n.LocaleContextHolder
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.FileSystemResource
import org.springframework.core.io.FileUrlResource
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable

import java.time.LocalDateTime
import java.time.ZoneId
import java.util.stream.Collectors

/**
* ActionDelegate class contains Actions API methods.
*/
Expand Down Expand Up @@ -676,6 +671,8 @@ class ActionDelegate {
ChangedField changedField = new ChangedField(field.stringId)
if (field instanceof I18nField) {
changedField.attributes.put("value", value)
} else if (field instanceof DateTimeField) {
changedField.addAttribute("value", FieldFactory.parseDateTime(((LocalDateTime) value).toDate()))
} else {
changedField.addAttribute("value", value)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.netgrif.application.engine.configuration.ApplicationContextProvider;
import com.netgrif.application.engine.configuration.JsonRootRelProvider;
import com.netgrif.application.engine.configuration.locale.CustomLocaleResolver;
import com.netgrif.application.engine.petrinet.domain.version.StringToVersionConverter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
Expand All @@ -19,6 +20,8 @@
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.server.LinkRelationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -57,6 +60,12 @@ ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}

@Bean
LocaleResolver localeResolver() {
mazarijuraj marked this conversation as resolved.
Show resolved Hide resolved
SessionLocaleResolver l = new SessionLocaleResolver();
return new CustomLocaleResolver(l);
}

public static void main(String[] args) {
SpringApplication.run(ApplicationEngine.class, args);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.netgrif.application.engine.configuration;

import com.netgrif.application.engine.auth.service.interfaces.IUserService;
import com.netgrif.application.engine.configuration.locale.LocaleInterceptor;
import com.netgrif.application.engine.configuration.properties.LocaleConfigurationProperties;
import com.netgrif.application.engine.security.service.ISecurityContextService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.PostConstruct;
import java.util.TimeZone;

/**
* Configuration for LocaleInterceptor to extract time zone offset from request headers
* and managing default time zone in system.
* */
@Configuration
public class LocaleConfiguration implements WebMvcConfigurer {

private final LocaleConfigurationProperties properties;

public LocaleConfiguration(@Autowired LocaleConfigurationProperties properties) {
this.properties = properties;
}

@PostConstruct
void init() {
TimeZone.setDefault(TimeZone.getTimeZone(properties.getDefaultTimeZone()));
}

/**
* Adds new interceptor that extracts time zone from request header.
* @param registry the interceptor registry of Spring MVC
* */
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(new LocaleInterceptor())
.addPathPatterns(properties.getDefaultPathPatterns());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.netgrif.application.engine.configuration.locale;

import org.springframework.context.i18n.LocaleContext;
import org.springframework.web.servlet.LocaleContextResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

/**
* A custom implementation of locale resolver to store the time zone offset in
* request context
* */
public class CustomLocaleResolver implements LocaleContextResolver {
renczesstefan marked this conversation as resolved.
Show resolved Hide resolved
private final LocaleContextResolver localeContextResolver;
private final AcceptHeaderLocaleResolver acceptHeaderLocaleResolver;

public CustomLocaleResolver(LocaleContextResolver localeContextResolver) {
this.localeContextResolver = localeContextResolver;
acceptHeaderLocaleResolver = new AcceptHeaderLocaleResolver();
acceptHeaderLocaleResolver.setDefaultLocale(Locale.getDefault());
}

@Override
public LocaleContext resolveLocaleContext(HttpServletRequest request) {
return localeContextResolver.resolveLocaleContext(request);
}

@Override
public void setLocaleContext(HttpServletRequest request, HttpServletResponse response, LocaleContext localeContext) {
localeContextResolver.setLocaleContext(request, response, localeContext);

}

@Override
public Locale resolveLocale(HttpServletRequest request) {
return acceptHeaderLocaleResolver.resolveLocale(request);
}

@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
acceptHeaderLocaleResolver.setLocale(request, response, locale);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.netgrif.application.engine.configuration.locale;

import com.netgrif.application.engine.auth.domain.LoggedUser;
import com.netgrif.application.engine.auth.service.interfaces.IUserService;
import com.netgrif.application.engine.security.service.ISecurityContextService;
import org.springframework.context.i18n.SimpleTimeZoneAwareLocaleContext;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.LocaleContextResolver;
import org.springframework.web.servlet.support.RequestContextUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.ZoneOffset;
import java.util.Locale;
import java.util.TimeZone;

/**
* The interceptor that extracts time zone from request headers.
* It's configuration can be found in {@link com.netgrif.application.engine.configuration.LocaleConfiguration}
* */
public class LocaleInterceptor implements HandlerInterceptor {

private static final String TIMEZONE_OFFSET_HEADER_NAME = "X-Timezone-Offset";
renczesstefan marked this conversation as resolved.
Show resolved Hide resolved

/**
* The handle function that is being called to extract time zone offset from request header.
* @param request the request object
* @param response the response object
* @param handler the handler object
* @return boolean whether the chain should be continued with next interceptor
* */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (request.getHeader(TIMEZONE_OFFSET_HEADER_NAME) == null) {
return true;
}
/*Here retrieve the offset from request headers*/
String timeZoneOffset = request.getHeader(TIMEZONE_OFFSET_HEADER_NAME);
/*Retrieve the locale from request headers*/
Locale locale = request.getLocale();
/*Convert zoneOffset to TimeZone*/
ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(-Integer.parseInt(timeZoneOffset) * 60);
TimeZone timeZone = TimeZone.getTimeZone(zoneOffset);
/*Set the locale to requestContextUtils*/
LocaleContextResolver localeResolver = (LocaleContextResolver) RequestContextUtils.getLocaleResolver(request);
if (localeResolver != null) {
localeResolver.setLocaleContext(request, response, new SimpleTimeZoneAwareLocaleContext(locale, timeZone));
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.netgrif.application.engine.configuration.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Data
@Component
@ConfigurationProperties(prefix = "nae.locale")
public class LocaleConfigurationProperties {

/**
* Defines the default server time zone
* */
private String defaultTimeZone = "UTC";

/**
* Defines the default server patters where {@link com.netgrif.application.engine.configuration.locale.LocaleInterceptor}
renczesstefan marked this conversation as resolved.
Show resolved Hide resolved
* will execute {@link com.netgrif.application.engine.configuration.locale.LocaleInterceptor#preHandle(HttpServletRequest, HttpServletResponse, Object)}
* function
* */
private String defaultPathPatterns = "/api/task/{id}/data";

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
import com.netgrif.application.engine.workflow.service.interfaces.IDataValidationExpressionEvaluator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.support.RequestContextUtils;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
Expand Down Expand Up @@ -49,6 +53,9 @@ public final class FieldFactory {
@Autowired
private IDataValidationExpressionEvaluator dataValidationExpressionEvaluator;

@Autowired
private HttpServletRequest request;

// TODO: refactor this shit
Field getField(Data data, Importer importer) throws IllegalArgumentException, MissingIconKeyException {
Field field;
Expand Down Expand Up @@ -658,10 +665,13 @@ public static LocalDateTime parseDateTime(Object value) {

if (value instanceof LocalDate)
return LocalDateTime.of((LocalDate) value, LocalTime.NOON);
else if (value instanceof String)
return parseDateTimeFromString((String) value);
else if (value instanceof Date)
return LocalDateTime.ofInstant(((Date) value).toInstant(), ZoneId.systemDefault());
else if (value instanceof String) {
LocalDateTime localDateTime = parseDateTimeFromString((String) value);
if (localDateTime == null)
mazarijuraj marked this conversation as resolved.
Show resolved Hide resolved
return null;
return localDateTime.atZone(getTimeZoneId()).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
} else if (value instanceof Date)
return LocalDateTime.ofInstant(((Date) value).toInstant(), getTimeZoneId());
return (LocalDateTime) value;
}

Expand Down Expand Up @@ -712,6 +722,18 @@ public static String parseEnumerationMapValue(Case useCase, String fieldId) {
return value != null ? value.toString() : null;
}

public static ZoneId getTimeZoneId() {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return ZoneId.systemDefault();
}
TimeZone timeZone = RequestContextUtils.getTimeZone(requestAttributes.getRequest());
if (timeZone == null) {
return ZoneId.systemDefault();
}
return timeZone.toZoneId();
}

private void parseFileValue(FileField field, Case useCase, String fieldId) {
Object value = useCase.getFieldValue(fieldId);
if (value == null)
Expand Down