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

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());
......
......@@ -18,99 +18,188 @@ package org.genesys2.server.servlet.controller;
import org.genesys2.server.model.impl.Article;
import org.genesys2.server.service.ContentService;
import org.genesys2.spring.RequestAttributeLocaleResolver;
import org.genesys2.spring.ResourceNotFoundException;
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.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
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.*;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
@Controller
@RequestMapping("/content")
public class ArticleController extends BaseController {
@Autowired
private ContentService contentService;
@RequestMapping
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
public String list(ModelMap model, @RequestParam(value = "page", defaultValue = "1") int page) {
model.addAttribute("pagedData", contentService.listArticles(new PageRequest(page - 1, 50, new Sort("slug"))));
return "/content/index";
}
@RequestMapping("{url:.+}")
public String view(ModelMap model, @PathVariable(value = "url") String slug) {
_logger.debug("Viewing article " + slug);
final Article article = contentService.getGlobalArticle(slug, getLocale());
if (article == null) {
if (hasRole("ADMINISTRATOR")) {
return "redirect:/content/" + slug + "/edit";
}
throw new ResourceNotFoundException();
}
model.addAttribute("title", article.getTitle());
model.addAttribute("article", article);
return "/content/article";
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
@RequestMapping("{url}/edit")
public String edit(ModelMap model, @PathVariable(value = "url") String slug) {
_logger.debug("Editing article " + slug);
Article article = contentService.getGlobalArticle(slug, getLocale(), false);
if (article == null) {
article = new Article();
article.setSlug(slug);
article.setLang(getLocale().getLanguage());
}
model.addAttribute("article", article);
return "/content/article-edit";
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
@RequestMapping(value = "/save-article", method = { RequestMethod.POST })
public String createNewGlobalArticle(ModelMap model, @RequestParam("slug") String slug, @RequestParam("title") String title,
@RequestParam("body") String body) {
contentService.createGlobalArticle(slug, getLocale(), title, body);
return "redirect:/content/" + slug;
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
@RequestMapping(value = "/save-article", params = { "id" }, method = { RequestMethod.POST })
public String saveExistingGlobalArticle(ModelMap model, @RequestParam("id") long id, @RequestParam("slug") String slug,
@RequestParam("title") String title, @RequestParam("body") String body) {
contentService.updateArticle(id, slug, title, body);
return "redirect:/content/" + slug;
}
@RequestMapping(value = "/blurp/update-blurp", method = { RequestMethod.POST })
public String updateBlurp(ModelMap model, @RequestParam("id") long id, @RequestParam(required = false, value = "title") String title,
@RequestParam("body") String body) {
contentService.updateArticle(id, null, title, body);
return "redirect:/";
}
@RequestMapping(value = "/blurp/create-blurp", method = { RequestMethod.POST })
public String createBlurp(ModelMap model, @RequestParam("clazz") String clazz, @RequestParam("entityId") long entityId,
@RequestParam(required = false, value = "title") String title, @RequestParam("body") String body) throws ClassNotFoundException {
contentService.updateArticle(Class.forName(clazz), entityId, "blurp", title, body, getLocale());
return "redirect:/";
}
@Autowired
private ContentService contentService;
@Autowired
private TransifexService transifexService;
@Autowired
private RequestAttributeLocaleResolver localeResolver;
@RequestMapping(value = "/postToTransifex/{slug}", method = RequestMethod.GET)
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
public String postTotransifex(@PathVariable("slug") String slug, Model model) {
Article article = contentService.getGlobalArticle(slug, getLocale());
try {
transifexService.createXhtmlResource("article-" + slug, article.getTitle(), article.getBody());
} catch (IOException e) {
model.addAttribute("responseFromTransifex", "fail");
model.addAttribute("article", article);
e.printStackTrace();
}
model.addAttribute("responseFromTransifex", "resource added");
model.addAttribute("article", article);
return "/content/article-edit";
}
@RequestMapping(value = "/deleteFromTransifex", method = RequestMethod.POST)
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
public String deleteFromtransifex(@RequestParam("articleSlug") String slug, Model model) {
Article article = contentService.getGlobalArticle(slug, getLocale());
if (transifexService.deleteResource("article-" + slug)) {
model.addAttribute("result", "deleted");
model.addAttribute("article", article);
} else {
model.addAttribute("result", "fail");
model.addAttribute("article", article);
}
return "/content/article-edit";
}
@RequestMapping(value = "/translate/{slug}/{lang}", method = RequestMethod.GET)
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
public String fetchFromtransifex(@PathVariable("slug") String slug, @PathVariable String lang, Model model) {
Article article = contentService.getGlobalArticle(slug, getLocale());
JSONObject jsonObject;
JSONParser jsonParser = new JSONParser();
String translatedResource = transifexService.getTranslatedResource("article-" + slug, new Locale(lang));
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.setLang(lang);
model.addAttribute("article", article);
return "/content/article-edit";
}
@RequestMapping
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
public String list(ModelMap model, @RequestParam(value = "page", defaultValue = "1") int page,
@RequestParam(value = "language", defaultValue = "") String lang) {
if (!lang.isEmpty()) {
model.addAttribute("pagedData", contentService.listArticlesByLang(lang, new PageRequest(page - 1, 50, new Sort("slug"))));
} else {
model.addAttribute("pagedData", contentService.listArticles(new PageRequest(page - 1, 50, new Sort("slug"))));
}
//todo full name of locales
Map<String, String> locales = new TreeMap<>();
for (String language : localeResolver.getSupportedLocales()) {
locales.put(new Locale(language).getDisplayName(), language);
}
model.addAttribute("languages", locales);
return "/content/index";
}
@RequestMapping("{url:.+}")
public String view(ModelMap model, @PathVariable(value = "url") String slug) {
_logger.debug("Viewing article " + slug);
final Article article = contentService.getGlobalArticle(slug, getLocale());
if (article == null) {
if (hasRole("ADMINISTRATOR")) {
return "redirect:/content/" + slug + "/edit";
}
throw new ResourceNotFoundException();
}
model.addAttribute("title", article.getTitle());
model.addAttribute("article", article);
return "/content/article";
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
@RequestMapping("{url}/edit/{language}")
public String edit(ModelMap model, @PathVariable(value = "url") String slug,
@PathVariable("language") String language) {
_logger.debug("Editing article " + slug);
Article article = contentService.getArticleBySlugAndLang(slug, language);
if (article == null) {
article = new Article();
article.setSlug(slug);
article.setLang(getLocale().getLanguage());
}
model.addAttribute("article", article);
return "/content/article-edit";
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
@RequestMapping(value = "/save-article", method = {RequestMethod.POST})
public String createNewGlobalArticle(ModelMap model, @RequestParam("slug") String slug, @RequestParam("title") String title,
@RequestParam("body") String body) {
contentService.createGlobalArticle(slug, getLocale(), title, body);
return "redirect:/content/" + slug;
}
@PreAuthorize("hasRole('ADMINISTRATOR') or hasRole('CONTENTMANAGER')")
@RequestMapping(value = "/save-article", params = {"id"}, method = {RequestMethod.POST})
public String saveExistingGlobalArticle(ModelMap model, @RequestParam("id") long id, @RequestParam("slug") String slug,
@RequestParam("title") String title, @RequestParam("body") String body) {
contentService.updateArticle(id, slug, title, body);
return "redirect:/content/" + slug;
}
@RequestMapping(value = "/blurp/update-blurp", method = {RequestMethod.POST})
public String updateBlurp(ModelMap model, @RequestParam("id") long id, @RequestParam(required = false, value = "title") String title,
@RequestParam("body") String body) {
contentService.updateArticle(id, null, title, body);
return "redirect:/";
}
@RequestMapping(value = "/blurp/create-blurp", method = {RequestMethod.POST})
public String createBlurp(ModelMap model, @RequestParam("clazz") String clazz, @RequestParam("entityId") long entityId,
@RequestParam(required = false, value = "title") String title, @RequestParam("body") String body) throws ClassNotFoundException {
contentService.updateArticle(Class.forName(clazz), entityId, "blurp", title, body, getLocale());
return "redirect:/";
}
}
/**
* 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)) {