Commit 8aae447f authored by Ubbo Veentjer's avatar Ubbo Veentjer
Browse files

Merge branch 'release/3.1.3' into main

parents db928454 5b38791a
Pipeline #141257 passed with stages
in 7 minutes and 32 seconds
package info.textgrid.rep;
import java.util.Locale;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
import info.textgrid.rep.service.TgrepConfigurationService;
/**
* Add ModelAttributes to all Controllers which are needed for
* all Models like i18n (the translation map) or the config
*
* @author Ubbo Veentjer
*/
@ControllerAdvice
public class CommonModelControllerAdvice {
private TgrepConfigurationService tgrepConfig;
private I18NProvider i18nProvider;
@Autowired
public CommonModelControllerAdvice(I18NProvider i18nProvider, TgrepConfigurationService tgrepConfig) {
this.i18nProvider = i18nProvider;
this.tgrepConfig = tgrepConfig;
}
@ModelAttribute
public void handleRequest(Model model, Locale locale) {
I18N i18n = i18nProvider.getI18N(locale);
// translation array
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
// config
model.addAttribute("config", this.tgrepConfig);
}
}
......@@ -2,37 +2,24 @@ package info.textgrid.rep;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
@Controller
public class TGRepErrorController implements ErrorController {
@Autowired
private I18NProvider i18nProvider;
@RequestMapping("/error")
public String handleError(Model model, Locale locale, HttpServletRequest request) {
I18N i18n = i18nProvider.getI18N(locale);
if(request.getAttribute("javax.servlet.error.exception") != null) {
Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
model.addAttribute("exception", exception.getMessage());
model.addAttribute("stacktrace", exception.getStackTrace());
}
model.addAttribute("statusCode", request.getAttribute("javax.servlet.error.status_code"));
// translation array
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
return "error";
}
......
package info.textgrid.rep.advancedsearch;
import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
@Controller
public class AdvancedSearchController {
private static final Log log = LogFactory.getLog(AdvancedSearchController.class);
private I18NProvider i18nProvider;
@Autowired
public AdvancedSearchController(I18NProvider i18nProvider) {
this.i18nProvider = i18nProvider;
}
@GetMapping("/advanced-search")
public String render(
Locale locale,
Model model) {
I18N i18n = i18nProvider.getI18N(locale);
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
public String render(Model model) {
return "advancedsearch";
}
......
package info.textgrid.rep.browse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.TreeMap;
import javax.ws.rs.client.ClientBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
......@@ -16,32 +20,49 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import info.textgrid.clients.tgcrud.CrudClientException;
import info.textgrid.namespaces.middleware.tgsearch.ResultType;
import info.textgrid.namespaces.middleware.tgsearch.Revisions;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
import info.textgrid.rep.service.tgsearch.AggregatorClientService;
import info.textgrid.rep.service.tgsearch.TgrepConfigurationService;
import info.textgrid.rep.service.tgsearch.TgsearchClientService;
import info.textgrid.rep.markdown.MarkdownRenderService;
import info.textgrid.rep.service.AggregatorClientService;
import info.textgrid.rep.service.TgcrudClientService;
import info.textgrid.rep.service.TgrepConfigurationService;
import info.textgrid.rep.service.TgsearchClientService;
import info.textgrid.rep.shared.Utils;
import info.textgrid.rep.shared.ViewMode;
import info.textgrid.rep.shared.ToolLink;
@Controller
public class BrowseController {
@Autowired
private TgsearchClientService tgsearchClientService;
@Autowired
private TgrepConfigurationService tgrepConfig;
@Autowired
private AggregatorClientService aggregatorClient;
@Autowired
private TgcrudClientService tgcrudClient;
private TgsearchClientService tgsearchClient;
private MarkdownRenderService markdownRenderService;
private I18NProvider i18nProvider;
private static final Log log = LogFactory.getLog(BrowseController.class);
@Autowired
public BrowseController(
TgrepConfigurationService tgrepConfig,
AggregatorClientService aggregatorClient,
TgcrudClientService tgcrudClient,
TgsearchClientService tgsearchClient,
MarkdownRenderService markdownRenderService,
I18NProvider i18nProvider) {
this.tgrepConfig = tgrepConfig;
this.aggregatorClient = aggregatorClient;
this.tgcrudClient = tgcrudClient;
this.tgsearchClient = tgsearchClient;
this.markdownRenderService = markdownRenderService;
this.i18nProvider = i18nProvider;
}
@GetMapping("/browse/{id}")
public String browse(
Locale locale,
......@@ -53,22 +74,18 @@ public class BrowseController {
I18N i18n = i18nProvider.getI18N(locale);
// common variables for browse-root aggregations and browse single items
model.addAttribute("handleHost", this.tgrepConfig.getHandleHost());
model.addAttribute("textgridHost", this.tgrepConfig.getTextgridHost());
model.addAttribute("mode", mode);
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
// if no id, browse all root aggregations
if (id == null || id.equals("root")) {
List<ViewMode> viewmodes = new ArrayList<ViewMode>();
List<ToolLink> viewmodes = new ArrayList<ToolLink>();
viewmodes
.add(new ViewMode(i18n.get("list"), "/browse/root?mode=list", mode.equals("list")));
viewmodes.add(new ViewMode(i18n.get("gallery"), "/browse/root?mode=gallery",
.add(new ToolLink(i18n.get("list"), "/browse/root?mode=list", mode.equals("list")));
viewmodes.add(new ToolLink(i18n.get("gallery"), "/browse/root?mode=gallery",
mode.equals("gallery")));
model.addAttribute("viewmodes", viewmodes);
List<ResultType> results = this.tgsearchClientService.listRootCollections().getResult();
List<ResultType> results = this.tgsearchClient.listRootCollections().getResult();
model.addAttribute("results", results);
model.addAttribute("browseRootAggregations", true);
return "browse";
......@@ -81,7 +98,7 @@ public class BrowseController {
model.addAttribute("textgridUri", id);
ResultType metadata = this.tgsearchClientService.getMetadata(id);
ResultType metadata = this.tgsearchClient.getMetadata(id);
model.addAttribute("metadata", metadata);
// for path, todo: create a tagfile for path
......@@ -100,8 +117,13 @@ public class BrowseController {
handleXml(model, i18n, id, metadata, fragment);
} else if (format.contains("image")) {
handleImages(model, id);
} else if (format.equals("text/markdown")) {
handleMarkdown(model, id);
}
// add revisions info
listRevisions(model, id, metadata);
return "browse";
}
......@@ -116,7 +138,7 @@ public class BrowseController {
*/
private void handleXml(Model model, I18N i18n, String id, ResultType metadata, String fragment) {
List<ViewMode> tools = new ArrayList<ViewMode>();
List<ToolLink> tools = new ArrayList<ToolLink>();
// identifier@type of trier dfg-viewer mets is METSXMLID
// TODO: look into relation/tg:rootElementNamespace
......@@ -129,7 +151,7 @@ public class BrowseController {
try {
String tgcrudUrl4DFGViewer = URLEncoder.encode(
tgrepConfig.getTextgridHost() + "/1.0/tgcrud-public/rest/" + id + "/data", "UTF-8");
tools.add(new ViewMode("DFG-Viewer",
tools.add(new ToolLink("DFG-Viewer",
"http://dfg-viewer.de/v3/?set[zoom]=min&set[mets]=" + tgcrudUrl4DFGViewer, false));
} catch (UnsupportedEncodingException e) {
log.error("error encoding url", e);
......@@ -152,8 +174,8 @@ public class BrowseController {
// Mirador link may be shown for METS/MODS or TEI files (if manifest available for this file)
if (hasIiifManifest(metadata.getObject().getGeneric().getGenerated().getProject().getId())) {
tools.add(new ViewMode("Mirador", tgrepConfig.getToolMiradorHost() + "/?uri=" + id, false));
tools.add(new ViewMode(
tools.add(new ToolLink("Mirador", tgrepConfig.getToolMiradorHost() + "/?uri=" + id, false));
tools.add(new ToolLink(
"<img style='margin-top:2px; height: 21px;' title='Drop icon on Mirador to open manifest' src='" + tgrepConfig.getTextgridHost() + "/1.0/iiif/manifests/img/iiif-logo.svg'>",
tgrepConfig.getTextgridHost() + "/1.0/iiif/manifests/" + id + "/manifest.json", false
));
......@@ -167,30 +189,33 @@ public class BrowseController {
* @param tools
* @param id
*/
private List<ViewMode> addTeiTools(String id) {
private List<ToolLink> addTeiTools(String id) {
// CLARIN Language Resource Switchboard (LRS)
List<ViewMode> tools = new ArrayList<ViewMode>();
List<ToolLink> tools = new ArrayList<ToolLink>();
try {
// XML
String xml4switchboard = URLEncoder.encode(tgrepConfig.getTextgridHost() + "/1.0/tgcrud-public/rest/" + id + "/data", "UTF-8");
tools.add(new ViewMode("Switchboard (TEI)", tgrepConfig.getToolSwitchboardHost() + "/" + xml4switchboard + "/application%2Ftei+xml", false));
tools.add(new ToolLink("Switchboard (TEI)", tgrepConfig.getToolSwitchboardHost() + "/" + xml4switchboard + "/application%2Ftei+xml", false));
// txt
String text4switchboard = URLEncoder.encode(tgrepConfig.getTextgridHost() + "/1.0/aggregator/text/" + id , "UTF-8");
tools.add(new ViewMode("Switchboard (txt)", tgrepConfig.getToolSwitchboardHost() + "/" + text4switchboard + "/text%2Fplain", false));
tools.add(new ToolLink("Switchboard (txt)", tgrepConfig.getToolSwitchboardHost() + "/" + text4switchboard + "/text%2Fplain", false));
} catch (UnsupportedEncodingException e) {
log.error("error encoding url for switchboard", e);
}
// Voyant
tools.add(new ViewMode("Voyant", tgrepConfig.getToolVoyantHost() + "/?input=" + tgrepConfig.getTextgridHost() + "/1.0/tgcrud-public/rest/" + id + "/data", false));
tools.add(
new ToolLink("Voyant", tgrepConfig.getVoyantHost() + "/?input=" + tgrepConfig.getTextgridHost() + "/1.0/tgcrud-public/rest/" + id + "/data", false)
.setHelpLink("/docs/voyant")
);
// Annotate
tools.add(new ViewMode("Annotate", tgrepConfig.getToolAnnotateHost() + "/data.html?uri=" + id, false, "annotation-button"));
tools.add(new ToolLink("Annotate", tgrepConfig.getToolAnnotateHost() + "/data.html?uri=" + id, false, "annotation-button"));
return tools;
}
/**
* setup browsing of aggreagtions
* setup browsing of aggregations
*
* @param model
* @param i18n
......@@ -199,15 +224,15 @@ public class BrowseController {
* @param mode
*/
private void handleAggregations(Model model, I18N i18n, String id, String format, String mode) {
List<ViewMode> viewmodes = new ArrayList<ViewMode>();
List<ToolLink> viewmodes = new ArrayList<ToolLink>();
viewmodes
.add(new ViewMode(i18n.get("list"), Utils.browseUrl(id) + "?mode=list", mode.equals("list")));
viewmodes.add(new ViewMode(i18n.get("gallery"), Utils.browseUrl(id) + "?mode=gallery",
.add(new ToolLink(i18n.get("list"), Utils.browseUrl(id) + "?mode=list", mode.equals("list")));
viewmodes.add(new ToolLink(i18n.get("gallery"), Utils.browseUrl(id) + "?mode=gallery",
mode.equals("gallery")));
if (format.equals("text/tg.edition+tg.aggregation+xml")) {
viewmodes.add(new ViewMode("TEI-Corpus", Utils.browseUrl(id) + "?mode=xml", mode.equals("xml")));
viewmodes.add(new ToolLink("TEI-Corpus", Utils.browseUrl(id) + "?mode=xml", mode.equals("xml")));
}
model.addAttribute("viewmodes", viewmodes);
......@@ -220,16 +245,18 @@ public class BrowseController {
} else {
log.info("listing agg: " + id);
List<ResultType> results;
if (format.contains("text/tg.work+xml")) {
List<String> editionFilter =
Arrays.asList(new String[] {"format:text/tg.edition+tg.aggregation+xml"});
results = this.tgsearchClientService
.search("isEditionOf:\"" + id + "\"", "relevance", 0, 20, editionFilter, true)
results = this.tgsearchClient.getSearchClient()
.searchQuery()
.setQuery("*")
.addFilter("edition.isEditionOf:"+id)
.addFilter("format:text/tg.edition+tg.aggregation+xml")
.execute()
.getResult();
} else {
results = this.tgsearchClientService.listAggregation(id).getResult();
results = this.tgsearchClient.listAggregation(id).getResult();
}
model.addAttribute("results", results);
}
......@@ -242,11 +269,53 @@ public class BrowseController {
*/
private void handleImages(Model model, String id) {
model.addAttribute("image", true);
List<ViewMode> tools = new ArrayList<ViewMode>();
tools.add(new ViewMode("Digilib", tgrepConfig.getToolDigilibHost() + "/digilib.html?fn=" + id, false));
List<ToolLink> tools = new ArrayList<ToolLink>();
tools.add(new ToolLink("Digilib", tgrepConfig.getToolDigilibHost() + "/digilib.html?fn=" + id, false));
model.addAttribute("tools", tools);
}
private void handleMarkdown(Model model, String id) {
InputStream contentStream;
String content;
try {
contentStream = this.tgcrudClient.read(id);
content = markdownRenderService.renderHtml(contentStream);
} catch (CrudClientException e) {
//TODO: nicer error strings
log.error("error reading data from crud", e);
content = "error reading data from crud";
} catch (IOException e) {
//TODO: nicer error strings
log.error("error parsing markdown", e);
content = "error parsing markdown";
}
model.addAttribute("htmlContent", content);
}
private void listRevisions(Model model, String id, ResultType metadata) {
String baseUri = id.substring(0, id.indexOf("."));
Revisions revisions = tgsearchClient.listRevisions(id);
int displayRev = metadata.getObject().getGeneric().getGenerated().getRevision();
BigInteger latestRev = Collections.max(revisions.getRevision());
if(BigInteger.valueOf(displayRev).compareTo(latestRev) < 0) {
model.addAttribute("higherRevisionAvailable", true);
model.addAttribute("latestRevision", latestRev);
model.addAttribute("latestRevisionUri", baseUri+"."+latestRev);
}
if(revisions.getRevision().size() > 0) {
TreeMap revmap = new TreeMap<BigInteger, String>();
for(BigInteger rev : revisions.getRevision()) {
revmap.put(rev, baseUri + "." + rev);
}
model.addAttribute("revisions", revmap);
}
}
private boolean hasIiifManifest(String id) {
IIIFProjects iiip = ClientBuilder.newBuilder().build()
......
......@@ -12,21 +12,20 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import info.textgrid.namespaces.middleware.tgsearch.FacetType;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
import info.textgrid.rep.service.tgsearch.TgsearchClientService;
import info.textgrid.rep.service.TgsearchClientService;
@Controller
public class BrowseFacetController {
@Autowired
private TgsearchClientService tgsearchClientService;
@Autowired
private I18NProvider i18nProvider;
private static final Log log = LogFactory.getLog(BrowseFacetController.class);
@Autowired
public BrowseFacetController(TgsearchClientService tgsearchClientService) {
this.tgsearchClientService = tgsearchClientService;
}
@GetMapping("/facet/{facet}")
public String render(
Locale locale,
......@@ -35,9 +34,7 @@ public class BrowseFacetController {
@RequestParam(value="limit", required=false, defaultValue="0") int limit,
@RequestParam(value="order", required=false, defaultValue="count.desc") String order) {
I18N i18n = i18nProvider.getI18N(locale);
// boolean sandbox = Utils.userWantsSandbox(PortalUtil.getUserId(renderRequest));
boolean sandbox = false;
// list facets configured for this portlet
......@@ -46,8 +43,6 @@ public class BrowseFacetController {
List<FacetType> facets = tgsearchClientService.listFacet(facetList, limit, order, sandbox)
.getFacetGroup().get(0).getFacet();
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
model.addAttribute("facets", facets);
model.addAttribute("facet", facet);
return "browsefacet";
......
package info.textgrid.rep.markdown;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
......@@ -21,14 +17,23 @@ import info.textgrid.rep.i18n.I18NProvider;
@Controller
public class DocController {
@Autowired
private ResourceLoader resourceLoader;
@Autowired
private I18NProvider i18nProvider;
private MarkdownRenderService markdownRenderService;
private static final Log log = LogFactory.getLog(DocController.class);
@Autowired
public DocController(
ResourceLoader resourceLoader,
I18NProvider i18nProvider,
MarkdownRenderService markdownRenderService) {
this.resourceLoader = resourceLoader;
this.i18nProvider = i18nProvider;
this.markdownRenderService = markdownRenderService;
}
@GetMapping("/")
public String renderIndex(
Model model,
......@@ -51,19 +56,13 @@ public class DocController {
String content;
try {
InputStreamReader reader = new InputStreamReader(res.getInputStream());
Parser parser = Parser.builder().build();
Node document = parser.parseReader(reader);
HtmlRenderer renderer = HtmlRenderer.builder().build();
content = renderer.render(document);
content = markdownRenderService.renderHtml(res.getInputStream());
} catch (IOException e) {
log.error("error rendering file " + doc, e);
content = "Sorry, there was an error opening file " + doc;
}
model.addAttribute("content", content);
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
return "markdown";
......
package info.textgrid.rep.markdown;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.commonmark.Extension;
import org.commonmark.ext.front.matter.YamlFrontMatterExtension;
import org.commonmark.ext.front.matter.YamlFrontMatterVisitor;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.ext.heading.anchor.HeadingAnchorExtension;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
@Service
public class MarkdownRenderService {
// renderer and parser are meant to be threadsafe
// https://github.com/atlassian/commonmark-java#thread-safety
private static Parser parser;
private static HtmlRenderer renderer;
private static YamlFrontMatterVisitor yamlVisitor;
public MarkdownRenderService() {
List<Extension> extensions = Arrays.asList(
HeadingAnchorExtension.create(),
TablesExtension.create(),
YamlFrontMatterExtension.create()
);
parser = Parser.builder()
.extensions(extensions).build();
renderer = HtmlRenderer.builder()
.extensions(extensions).build();
yamlVisitor = new YamlFrontMatterVisitor();
}
public String renderHtml(InputStream in) throws IOException {
InputStreamReader reader = new InputStreamReader(in);
Node document = parser.parseReader(reader);
document.accept(yamlVisitor);
return renderer.render(document);