Commit 5bb0c9f2 authored by Alexander Basov's avatar Alexander Basov Committed by Matija Obreza

Use external org.genesys2.transifex-client depedency

parent 3f3e7979
......@@ -112,6 +112,13 @@
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.genesys2</groupId>
<artifactId>transifex-client</artifactId>
<version>0.1</version>
</dependency>
<!--Test dependencies -->
<dependency>
<groupId>junit</groupId>
......@@ -527,6 +534,11 @@
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
......
......@@ -18,10 +18,21 @@ package org.genesys2.server.persistence.domain;
import org.genesys2.server.model.impl.Article;
import org.genesys2.server.model.impl.ClassPK;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface ArticleRepository extends JpaRepository<Article, Long> {
Article findByClassPkAndTargetIdAndSlugAndLang(ClassPK classPk, Long id, String slug, String lang);
@Query("select a from Article a where a.lang = :lang")
Page<Article> getAllByLang(@Param("lang") String lang, Pageable pageable);
@Query("select a from Article a where a.slug = :slug and a.lang = :lang")
Article findBySlugAndLang(@Param("slug")String slug,
@Param("lang")String lang);
}
......@@ -47,6 +47,8 @@ public interface ContentService {
Article getArticle(EntityId entity, String slug, Locale locale);
Article getArticleBySlugAndLang(String slug, String lang);
/**
* Global articles have ClassPK of Article.class and targetId of null
*
......@@ -70,6 +72,8 @@ public interface ContentService {
Page<Article> listArticles(Pageable pageable);
Page<Article> listArticlesByLang(String lang, Pageable pageable);
void save(Iterable<Article> articles);
/**
......
......@@ -16,13 +16,6 @@
package org.genesys2.server.service.impl;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
......@@ -48,6 +41,13 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@Service
@Transactional(readOnly = true)
public class ContentServiceImpl implements ContentService {
......@@ -86,9 +86,14 @@ public class ContentServiceImpl implements ContentService {
return articleRepository.findAll(pageable);
}
@Override
@Override
public Page<Article> listArticlesByLang(String lang, Pageable pageable) {
return articleRepository.getAllByLang(lang, pageable);
}
@Override
@Transactional(readOnly = false)
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
// @PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
@CacheEvict(value = "contentcache", allEntries = true)
public void save(Iterable<Article> articles) {
articleRepository.save(articles);
......@@ -115,7 +120,12 @@ public class ContentServiceImpl implements ContentService {
return getArticle(entity.getClass(), entity.getId(), slug, locale, true);
}
@Override
@Override
public Article getArticleBySlugAndLang(String slug, String lang) {
return articleRepository.findBySlugAndLang(slug, lang);
}
@Override
@Cacheable(value = "contentcache", key = "'article-' + #clazz.name + '-' + #id + '-' + #slug + '-' + #locale.language + '-' + #useDefault")
public Article getArticle(Class<?> clazz, Long id, String slug, Locale locale, boolean useDefault) {
Article article = articleRepository.findByClassPkAndTargetIdAndSlugAndLang(getClassPk(clazz), id, slug, locale.getLanguage());
......
/**
* Copyright 2014 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.genesys2.server.servlet.controller.transifex;
import java.io.IOException;
import java.util.Locale;
import org.genesys2.server.model.impl.Article;
import org.genesys2.server.service.ContentService;
import org.genesys2.server.servlet.controller.BaseController;
import org.genesys2.transifex.client.TransifexService;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.HttpClientErrorException;
/**
* Transifex web hook listener.
* TODO Move other (non-hook) methods to ArticleController!
*
* @author matijaobreza
*
*/
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@Controller
@RequestMapping(value = "/transifex")
public class TransifexAPIController extends BaseController {
@Autowired
private ContentService contentService;
@Autowired
private TransifexService transifexService;
@Value("${transifex.content.template}")
private String contentTemplate;
@Value("${transifex.min.translated}")
private int transifexMinTranslated;
@Value("${transifex.hook.key}")
private Object transifexHookKey;
@RequestMapping(value = "/deleteContent/{slug}")
public String deleteResource(@PathVariable String slug, Model model) {
Article article = contentService.getGlobalArticle(slug, getLocale());
if (articleExists(article)) {
if (transifexService.deleteResource("article-".concat(article.getSlug()))) {
model.addAttribute("result", "deleted ok");
}
} else {
model.addAttribute("result", "not exists");
}
model.addAttribute("title", article.getTitle());
model.addAttribute("article", article);
return "/content/article-edit";
}
/**
* Use "magic" SHA1 hash value just for some security purposes
*
* Note: the very same value should be in Transifex preferences
*
* @see classpath:spring/spring-security.xml
*/
@RequestMapping(value = "/hook/{hookKey:.+}", method = RequestMethod.POST)
public @ResponseBody
String webHookHandle(@PathVariable("hookKey") String hookKey, @RequestParam(value = "project") String projectSlug,
@RequestParam(value = "resource") String resource, @RequestParam(value = "language") String language,
@RequestParam(value = "translated") Integer translatedPercentage, Model model) {
if (!transifexHookKey.equals(hookKey)) {
_logger.error("Invalid key provided for Transifex callback hook: " + hookKey);
throw new RuntimeException("I don't know you!");
}
// TODO remove once confirmed to work
_logger.warn("project=" + projectSlug + " resource=" + resource + " lang=" + language + " translated=" + translatedPercentage);
if (!resource.startsWith("article-")) {
_logger.warn("Ignoring Transifex'd hook for " + resource);
return "Ignored";
}
String slug = resource.split("-")[1];
_logger.warn("Transifex'd article slug=" + slug);
if (translatedPercentage != null && translatedPercentage.intValue() >= transifexMinTranslated) {
// fetch updated resource from Transifex
updateArticle(slug, language);
return "Thanks!";
} else {
return "Not sufficiently translated";
}
}
/**
* Get translated contents from Transifex and update the local Article.
*
* @param lang
* @param slug
* @param model
* @return
*/
@RequestMapping(value = "/translate/{slug}/{lang}", method = RequestMethod.GET)
public String translateArticles(@PathVariable String lang, @PathVariable String slug, Model model) {
Article article = updateArticle(slug, lang);
model.addAttribute("title", article.getTitle());
model.addAttribute("article", article);
return "/content/article-edit";
}
private Article updateArticle(String slug, String lang) {
_logger.info("Fetching updated translation for article " + slug + " lang=" + lang);
Locale locale = new Locale(lang);
String resourceBody = transifexService.getTranslatedResource("article-".concat(slug), locale);
String title = resourceBody.split("<title>")[1].split("</title>")[0];
String body = resourceBody.split("<body>")[1].split("</body>")[0];
// Extract article from database we need (correct locale + do not use
// default (EN) language)
Article article = contentService.getGlobalArticle(slug, locale, false);
if (article == null) {
// No article for selected locale
article = contentService.createGlobalArticle(slug, locale, title, body);
} else {
// Update article for locale
article = contentService.updateArticle(article.getId(), article.getSlug(), title, body);
}
_logger.info("Updated translation for article " + slug + " lang=" + lang);
return article;
}
@RequestMapping(value = "/postContent/{slug:.+}")
public String postResourceToTransifex(@PathVariable String slug, Model model) throws IOException {
// Extract article from database we need
Article article = contentService.getGlobalArticle(slug, contentService.getDefaultLocale(), false);
if (!articleExists(article)) {
_logger.info("Posting to transifex slug=" + slug);
// This is template our xhtml
String content = String.format(contentTemplate, article.getTitle(), article.getBody());
try {
transifexService.createXhtmlResource("article-".concat(article.getSlug()), article.getTitle(), content);
model.addAttribute("responseFromTransifex", "Resource added");
} catch (HttpClientErrorException e) {
_logger.error(e.getMessage());
_logger.error(e.getResponseBodyAsString());
model.addAttribute("responseFromTransifex", "fail");
}
} else {
// TODO Update contents on Transifex!
model.addAttribute("responseFromTransifex", "already exists");
}
model.addAttribute("title", article.getTitle());
model.addAttribute("article", article);
return "/content/article-edit";
}
// This method verify if article already exists in Transifex
boolean articleExists(Article article) {
return transifexService.resourceExists("article-".concat(article.getSlug()));
}
@Autowired
private ContentService contentService;
@Autowired
private TransifexService transifexService;
@Value("${transifex.project}")
private String projectSlug;
@Value("${transifex.username}")
private String trasifexUserName;
@Value("${transifex.password}")
private String transifexPassord;
@Value("${transifex.base.api.url}")
private String baseApiURL;
@Value("${transifex.content.template}")
private String contentTemplate;
@Value("${transifex.min.translated}")
private int transifexMinTranslated;
/**
* Use "magic" SHA1 hash value just for some security purposes
* <p/>
* Note: the very same value should be in Transifex preferences
*
* @see classpath:spring/spring-security.xml
*/
@RequestMapping(value = "/hooks/40874cca86ca396169a5f4e6ebf6e4bf7199c4e7", method = RequestMethod.POST)
public void webHookHandle(@RequestParam(value = "project") String projectSlug,
@RequestParam(value = "resource") String resource,
@RequestParam(value = "language") String language,
@RequestParam(value = "translated") int translatedPercentage) throws UnsupportedEncodingException {
//TODO may be revisit this
//currently we do this due to nature of Transifex hook
String articleSlug = resource.split("-")[1];
Article article = new Article();
JSONObject jsonObject;
JSONParser jsonParser = new JSONParser();
String translatedResource = transifexService.getTranslatedResource(resource, new Locale(language));
String content;
String title = null;
String body = null;
try {
jsonObject = (JSONObject) jsonParser.parse(translatedResource);
content = (String) jsonObject.get("content");
title = content.split("<title>")[1].split("</title>")[0];
body = content.split("<body>")[1].split("</body>")[0];
} catch (ParseException e) {
e.printStackTrace();
}
article.setTitle(title);
article.setBody(body);
article.setSlug(articleSlug);
article.setLang(language);
article.setClassPk(contentService.ensureClassPK(article.getClass()));
List<Article> articles = new ArrayList<>();
articles.add(article);
contentService.save(articles);
}
}
......@@ -32,13 +32,13 @@
package org.genesys2.spring;
import java.util.Locale;
import org.genesys2.server.servlet.filter.LocaleURLFilter;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.genesys2.server.servlet.filter.LocaleURLFilter;
import org.springframework.web.servlet.LocaleResolver;
import java.util.Locale;
import java.util.Set;
/**
* Implementation of LocaleResolver that uses a locale attribute in the user's
......@@ -62,6 +62,7 @@ import org.springframework.web.servlet.LocaleResolver;
public class RequestAttributeLocaleResolver implements LocaleResolver {
private Locale defaultLocale;
private Set<String> supportedLocales;
@Override
public Locale resolveLocale(HttpServletRequest request) {
......@@ -79,7 +80,15 @@ public class RequestAttributeLocaleResolver implements LocaleResolver {
throw new UnsupportedOperationException("Cannot set locale!");
}
public void setDefaultLocale(Locale defaultLocale) {
public Set<String> getSupportedLocales() {
return supportedLocales;
}
public void setSupportedLocales(Set<String> supportedLocales) {
this.supportedLocales = supportedLocales;
}
public void setDefaultLocale(Locale defaultLocale) {
this.defaultLocale=defaultLocale;
}
}
......@@ -16,24 +16,18 @@
package org.genesys2.spring.config;
import javax.persistence.Entity;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.genesys2.spring.validation.oval.spring.SpringOvalValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Validator;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import javax.persistence.Entity;
@EnableAspectJAutoProxy
@ComponentScan(basePackages = { "org.genesys2.spring", "org.genesys2.server" }, excludeFilters = {
......
......@@ -16,34 +16,25 @@
package org.genesys2.spring.config;
import java.util.HashSet;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import org.genesys2.spring.AddStuffInterceptor;
import org.genesys2.spring.RequestAttributeLocaleResolver;
import org.genesys2.spring.validation.oval.spring.SpringOvalValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.*;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.theme.CookieThemeResolver;
import org.springframework.web.servlet.theme.ThemeChangeInterceptor;
import java.util.HashSet;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
@Import({ SpringProperties.class })
@EnableWebMvc
@EnableAspectJAutoProxy
......@@ -56,7 +47,7 @@ public class SpringServletConfig extends WebMvcConfigurerAdapter {
@Autowired
private AddStuffInterceptor addStuffInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
......@@ -119,8 +110,7 @@ public class SpringServletConfig extends WebMvcConfigurerAdapter {
@Bean
public RequestAttributeLocaleResolver localeResolver() {
final Set<String> supportedLocales = new HashSet<String>();
final Set<String> supportedLocales = new HashSet<String>();
supportedLocales.add("en");
supportedLocales.add("ar");
supportedLocales.add("de");
......@@ -133,7 +123,7 @@ public class SpringServletConfig extends WebMvcConfigurerAdapter {
final RequestAttributeLocaleResolver resolver = new RequestAttributeLocaleResolver();
resolver.setDefaultLocale(new Locale("en"));
// resolver.setSupportedLocales(supportedLocales);
resolver.setSupportedLocales(supportedLocales);
return resolver;
}
......@@ -143,4 +133,4 @@ public class SpringServletConfig extends WebMvcConfigurerAdapter {
cookieThemeResolver.setDefaultThemeName(defaultThemeName);
return cookieThemeResolver;
}
}
}
\ No newline at end of file
/**
* Copyright 2014 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.genesys2.transifex.client;
public class TransifexException extends Exception {
public TransifexException(String message, Throwable e) {
super(message, e);
}
}
/**
* Copyright 2014 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.genesys2.transifex.client;
import java.io.IOException;
import java.util.Locale;
/**
* Transifex client
*
* @author matijaobreza
*
*/
public interface TransifexService {
boolean resourceExists(String slug);
boolean deleteResource(String slug);
String getTranslatedResource(String slug, Locale locale);