Commit 597e1b38 authored by Ubbo Veentjer's avatar Ubbo Veentjer
Browse files

Merge branch 'feature/shelf' into 'develop'

Feature/shelf

Closes #30

See merge request !8
parents 2a1cb394 be8c1a88
Pipeline #134424 passed with stages
in 8 minutes and 54 seconds
package info.textgrid.rep.basket;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/service/shelf")
public class BasketAjaxController {
@RequestMapping("/add")
public long addToBasket(
HttpSession session,
@RequestParam("uri") String textgridUri
) throws IOException {
List<String> basket;
if(session.getAttribute("basket") == null) {
basket = new ArrayList<String>();
} else {
basket = (List<String>) session.getAttribute("basket");
}
basket.add(textgridUri);
session.setAttribute("basket", basket);
return basket.size();
}
@RequestMapping("/remove")
public long removeFromBasket(
HttpSession session,
@RequestParam("uri") String textgridUri
) throws IOException {
List<String> basket = (List<String>) session.getAttribute("basket");
basket.remove(textgridUri);
session.setAttribute("basket", basket);
return basket.size();
}
@RequestMapping("/clear")
public long clearBasket(
HttpSession session
) throws IOException {
session.setAttribute("basket", new ArrayList<String>());
return basketCount(session);
}
@GetMapping("/count")
public long basketCount(HttpSession session) {
long count;
if (session.getAttribute("basket")== null) {
count = 0;
} else {
count = ((List<String>) session.getAttribute("basket")).size();
}
return count;
}
}
package info.textgrid.rep.basket;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpSession;
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 org.springframework.web.bind.annotation.RequestParam;
import info.textgrid.namespaces.middleware.tgsearch.ResultType;
import info.textgrid.rep.i18n.I18N;
import info.textgrid.rep.i18n.I18NProvider;
import info.textgrid.rep.service.tgsearch.TgrepConfigurationService;
import info.textgrid.rep.service.tgsearch.TgsearchClientService;
import info.textgrid.rep.shared.Utils;
import info.textgrid.rep.shared.ViewMode;
@Controller
public class BasketController {
private I18NProvider i18nProvider;
private TgsearchClientService tgsearchClientService;
private TgrepConfigurationService tgrepConfig;
private I18NProvider i18nProvider;
private static final Log log = LogFactory.getLog(BasketController.class);
@Autowired
public BasketController(I18NProvider i18nProvider) {
public BasketController(TgsearchClientService tgsearchClientService, TgrepConfigurationService tgrepConfig, I18NProvider i18nProvider) {
this.tgsearchClientService = tgsearchClientService;
this.tgrepConfig = tgrepConfig;
this.i18nProvider = i18nProvider;
}
@GetMapping("/shelf")
public String render(
Model model,
Locale locale) {
Locale locale,
HttpSession session,
@RequestParam(value="mode", required=false) String mode) {
I18N i18n = i18nProvider.getI18N(locale);
model.addAttribute("mode", mode);
List<ResultType> results = new ArrayList<ResultType>();
List<String> basket = (List<String>) session.getAttribute("basket");
if(basket != null) {
for(String textgridUri : basket) {
ResultType res = tgsearchClientService.getMetadata(textgridUri);
results.add(res);
}
}
/*
List<ViewMode> tools = new ArrayList<ViewMode>();
tools.add(new ViewMode("Voyant v1", "#", false, "digivoy-basket-button"));
tools.add(new ViewMode("Voyant v2", "#", false, "digivoy2-basket-button"));
model.addAttribute("tools", tools);
*/
List<ViewMode> viewmodes = new ArrayList<ViewMode>();
viewmodes.add(new ViewMode(i18n.get("list"), "/shelf?mode=list", false));
viewmodes.add(new ViewMode(i18n.get("gallery"), "/shelf?mode=gallery", false));
model.addAttribute("viewmodes", viewmodes);
model.addAttribute("results", results);
//model.addAttribute("basket", Utils.getBasketItems(renderRequest));
model.addAttribute("aggregatorUrl", tgrepConfig.getTextgridHost()+"/1.0/aggregator");
model.addAttribute("basketItemString", Utils.getBasketItemsAsString(session));
model.addAttribute("textgridHost", this.tgrepConfig.getTextgridHost());
// translation array
model.addAttribute("i18n", i18n.getTranslationMap());
model.addAttribute("language", i18n.getLanguage());
......
......@@ -12,6 +12,7 @@ import java.net.URLEncoder;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3._1999._02._22_rdf_syntax_ns_.RdfType;
......@@ -106,38 +107,19 @@ public class Utils {
return sb.toString();
}
/*
public static List<String> getBasketItems(PortletRequest request) {
long userId = PortalUtil.getUserId(request);
if(userId > 0) {
return BasketLocalServiceUtil.getUrlsByUser(userId);
} else {
PortletSession session = request.getPortletSession();
return (List<String>) session.getAttribute("basket", PortletSession.APPLICATION_SCOPE);
}
}
public static String getBasketItemsAsString(PortletRequest request) {
return join(getBasketItems(request), ",");
public static List<String> getBasketItems(HttpSession session) {
return (List<String>) session.getAttribute("basket");
}
// http://stackoverflow.com/a/63274
public static String join(List<String> list, String delim) {
StringBuilder sb = new StringBuilder();
String loopDelim = "";
if(list != null) {
for(String s : list) {
sb.append(loopDelim);
sb.append(s);
loopDelim = delim;
}
}
return sb.toString();
public static String getBasketItemsAsString(HttpSession session) {
String itemstring = "";
if(getBasketItems(session) != null) {
itemstring = String.join(",", getBasketItems(session));
}
return itemstring;
}
*/
public static String urlencode(String string) {
try {
string = URLEncoder.encode(string, "UTF-8");
......
......@@ -40,6 +40,20 @@
<script src="/js/jquery.min.js"></script>
<script src="/js/theme.js"></script>
<script type="text/javascript">
/* show number of items in basket */
document.addEventListener("DOMContentLoaded", event => {
fetch("/service/shelf/count")
.then(response => response.text())
.then(cnt => setBasketCount(cnt));
});
function setBasketCount(num) {
[].forEach.call(document.getElementsByClassName("topbox_shelf-count"), function(elem){
elem.textContent = num;
})
}
</script>
</div>
</body>
</html>
......@@ -6,7 +6,7 @@
<li>
<a href="/shelf" class="tg topbox_link -shelf">
${i18n['shelf']}
<span class="tg topbox_shelf-count"> </span>
<span class="tg topbox_shelf-count">0</span>
</a>
</li>
<!--
......
......@@ -7,28 +7,132 @@
<%@ include file="base/head.jsp" %>
<%@ include file="components/js-incl.jsp" %>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
$(document).on('click', '.-remove-from-shelf', function(e) {
var $this = $(this);
$.ajax({
url: "/service/shelf/remove" ,
type: 'POST',
dataType:'json',
data: {
uri: $this.attr('data-textgrid-targeturi')
},
success: function(data) {
$this.closest('li.tgrep.result').remove();
$this.closest('li.tgrep.gallery-item').remove();
$('.tgrep.header_count').html(data);
setBasketCount(data);
}
});
});
$(document).on('click', '.tgrep.header_button.-clear', function(e) {
var $this = $(this);
$.ajax({
url: "/service/shelf/clear" ,
type: 'POST',
dataType:'json',
data: {
empty: "empty"
},
success: function(data) {
$('.tgrep.results_list').empty();
$('.tgrep.results_gallery').empty();
$('.tgrep.header_count').html(0);
setBasketCount(data);
}
});
});
</script>
<div class="tgrep wrap">
<div class="tgrep sidebar-toggle">
<button class="tgrep sidebar-toggle_button -show">${i18n['show-sidebar']}</button>
<button class="tgrep sidebar-toggle_button -hide">${i18n['hide-sidebar']}</button>
</div>
<div class="tgrep sidebar-toggle">
<button class="tgrep sidebar-toggle_button -show">${i18n['show-sidebar']}</button>
<button class="tgrep sidebar-toggle_button -hide">${i18n['hide-sidebar']}</button>
</div>
<aside class="tgrep sidebar">
<aside class="tgrep sidebar">
</aside>
<c:if test="${viewmodes != null}">
<section class="tgrep sidebar_panel">
<h3 class="tgrep sidebar_subheading">${i18n['views']}</h3>
<ul class="tgrep sidebar_list">
<c:forEach items="${viewmodes}" var="viewmode">
<li class="tgrep sidebar_item">
<a href="${viewmode.url}" rel="noindex nofollow" class="tgrep sidebar_link">${viewmode.label}</a>
</li>
</c:forEach>
</ul>
<c:if test="${viewmodes.size() > 6}">
<button class="tgrep sidebar_expand">${i18n['expand']}</button>
</c:if>
</section>
</c:if>
<main class="tgrep main">
<h1 class="tgrep main_heading">${i18n['shelf']}</h1>
<c:if test="${tools != null and tools.size() > 0}">
<section class="tgrep sidebar_panel">
<h3 class="tgrep sidebar_subheading">${i18n['tools']}</h3>
<ul class="tgrep sidebar_list">
<c:forEach items="${tools}" var="tool">
<li class="tgrep sidebar_item">
<a href="${tool.url}" rel="noindex nofollow" class="tgrep sidebar_link ${tool.cssClass}">${tool.label}</a>
</li>
</c:forEach>
</ul>
<c:if test="${tools.size() > 6}">
<button class="tgrep sidebar_expand">${i18n['expand']}</button>
</c:if>
</section>
</c:if>
</aside>
<header class="tgrep header">
<h2 class="tgrep header_heading">The shelf is temporarely disabled. It will be back soon.</h2>
</header>
<main class="tgrep main">
<h1 class="tgrep main_heading">${i18n['shelf']}</h1>
<header class="tgrep header">
<h2 class="tgrep header_heading">${i18n['manage-the-shelf']}</h2>
<div class="tgrep header_info">
${i18n['there-are-currently']} <span class="tgrep header_count">${results.size()}</span> ${i18n['objects-on-your-shelf']}
</div>
<div class="tgrep header_actions">
<button class="tgrep header_button -expand-all">${i18n['expand-all']}</button>
<button class="tgrep header_button -collapse-all">${i18n['collapse-all']}</button>
<button class="tgrep header_button -clear">${i18n['clear']}</button>
<div class="tg dropdown" role="group">
<a class="tg dropdown_toggle">${i18n['download-all']}</a>
<ul class="tg dropdown_menu">
<li class="tg dropdown_item"><a class="tg dropdown_link" href="${aggregatorUrl}/epub/${basketItemString}" data-type="ebook">E-Book</a></li>
<li class="tg dropdown_item"><a class="tg dropdown_link" href="${aggregatorUrl}/zip/${basketItemString}" data-type="zip">ZIP</a></li>
<li class="tg dropdown_item"><a class="tg dropdown_link" href="${aggregatorUrl}/teicorpus/${basketItemString}" data-type="teicorpus">TEI-Corpus</a></li>
</ul>
</div>
</div>
</header>
</main>
<c:choose>
<c:when test="${mode eq 'gallery'}">
<ul class="tgrep results_gallery">
<c:forEach items="${results}" var="result">
<%@ include file="components/singleGalleryResult.jsp" %>
</c:forEach>
</ul>
</c:when>
<c:otherwise>
<ul class="tgrep results_list">
<c:forEach items="${results}" var="result">
<%@ include file="components/singleListResult.jsp" %>
</c:forEach>
</ul>
</c:otherwise>
</c:choose>
</main>
</div>
<%@ include file="base/foot.jsp" %>
......@@ -4,11 +4,6 @@
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="http://textgrid.info/rep/utils" prefix="utils" %>
<!--
<portlet:defineObjects />
<portlet:resourceURL id="addToBasket" var="addToBasketUrl" ></portlet:resourceURL>
<portlet:resourceURL id="removeFromBasket" var="removeFromBasketUrl" ></portlet:resourceURL>
-->
<%@ include file="base/head.jsp" %>
<%@ include file="components/js-incl.jsp" %>
......
......@@ -2,10 +2,11 @@
document.addEventListener("DOMContentLoaded", function(event) {
/* the buttons */
$(document).on('click', '.-add-to-shelf', function(e) {
var $this = $(this);
$.ajax({
url: "${addToBasketUrl}" ,
url: "/service/shelf/add" ,
type: 'POST',
dataType:'json',
data: {
......@@ -15,7 +16,7 @@
$this.removeClass('-add-to-shelf');
$this.addClass('-remove-from-shelf');
$this.html('${i18n['remove-from-shelf']}');
setBasketCount(data.basketSize);
setBasketCount(data);
}
});
});
......@@ -23,7 +24,7 @@
$(document).on('click', '.-remove-from-shelf', function(e) {
var $this = $(this);
$.ajax({
url: "${removeFromBasketUrl}" ,
url: "/service/shelf/remove" ,
type: 'POST',
dataType:'json',
data: {
......@@ -33,11 +34,12 @@
$this.removeClass('-remove-from-shelf');
$this.addClass('-add-to-shelf');
$this.html('${i18n['add-to-shelf']}');
setBasketCount(data.basketSize);
setBasketCount(data);
}
});
});
});
});
</script>
package info.textgrid.rep.search;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@SpringBootTest
@AutoConfigureMockMvc
public class BasketControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void showBasket() throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders
.get("/shelf")
.accept(MediaType.TEXT_HTML);
mvc.perform(builder)
.andExpect(view().name("basket"))
.andExpect(forwardedUrl("/WEB-INF/jsp/basket.jsp"))
.andExpect(status().isOk());
}
@Test
public void addToBasket() throws Exception {
final MockHttpSession mockHttpSession = new MockHttpSession();
// add alice
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders
.post("/service/shelf/add")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.session(mockHttpSession)
.param("uri", "textgrid:kv2q.0");
mvc.perform(builder)
.andExpect(status().isOk())
.andExpect(content().string("1"));
// add another
builder = MockMvcRequestBuilders
.post("/service/shelf/add")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.session(mockHttpSession)
.param("uri", "textgrid:wfw9.0");
mvc.perform(builder)
.andExpect(status().isOk())
.andExpect(content().string("2"));
// view html
builder = MockMvcRequestBuilders
.get("/shelf")
.session(mockHttpSession)
.accept(MediaType.TEXT_HTML);
mvc.perform(builder)
.andExpect(view().name("basket"))
.andExpect(forwardedUrl("/WEB-INF/jsp/basket.jsp"))
.andExpect(status().isOk())
.andExpect(model().attribute("results", hasSize(2)))
.andExpect(model().attribute("basketItemString", is("textgrid:kv2q.0,textgrid:wfw9.0")));
//.andExpect(model().attribute("results", containsString("Alice im Wunderland")));
// remove one
builder = MockMvcRequestBuilders
.post("/service/shelf/remove")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.session(mockHttpSession)
.param("uri", "textgrid:wfw9.0");
mvc.perform(builder)
.andExpect(status().isOk())
.andExpect(content().string("1"));
// clear shelf
builder = MockMvcRequestBuilders
.post("/service/shelf/clear")
.session(mockHttpSession);
mvc.perform(builder)
.andExpect(status().isOk())
.andExpect(content().string("0"));
}
}
package info.textgrid.rep.search;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
......@@ -12,14 +15,12 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
import info.textgrid.rep.shared.ViewMode;
@SpringBootTest
@AutoConfigureMockMvc
public class BrowseControllterTest {
public class BrowseControllerTest {
@Autowired
private MockMvc mvc;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment