Compare commits
No commits in common. "68462514db39f9f8055b49f8c1176c2f6ae0b7e9" and "8bcd8cb9a17b5654a0f51195f61dd2fb7f1aad63" have entirely different histories.
68462514db
...
8bcd8cb9a1
14
README.md
14
README.md
|
@ -1,4 +1,4 @@
|
||||||
# Gourmet Recipe Manager - Spring Boot - Version 2
|
# Gourmet Recipe Manager - Spring Boot
|
||||||
|
|
||||||
This is a port of Thomas Hinkle (thinkle) Gourmet Recipe Manager.
|
This is a port of Thomas Hinkle (thinkle) Gourmet Recipe Manager.
|
||||||
|
|
||||||
|
@ -85,14 +85,4 @@ employed when you run this app on your local desktop.
|
||||||
### Improved graphics support
|
### Improved graphics support
|
||||||
|
|
||||||
A lot of recipe websites publish images in webp form. Support
|
A lot of recipe websites publish images in webp form. Support
|
||||||
for webp has now been added.
|
for webp has now been added.
|
||||||
|
|
||||||
### Better session management
|
|
||||||
|
|
||||||
JSF tends to depend on session-scope context. Sessions, however
|
|
||||||
time out and this has been an annoyance when a recipe is being
|
|
||||||
displayed. To minimize this, better timeout mechanisms have been
|
|
||||||
installed and the recipe browser keeps last-search and search-type
|
|
||||||
values in long-lived cookies on the client. The server will read
|
|
||||||
and cache them, but if the server times out, it will automatically
|
|
||||||
re-read the cookies on the next request.
|
|
|
@ -22,5 +22,3 @@ spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
|
||||||
# My special properties
|
# My special properties
|
||||||
gourmet.password.file=${user.home}/.gourmetpw
|
gourmet.password.file=${user.home}/.gourmetpw
|
||||||
|
|
||||||
# This will override aplication.yml
|
|
||||||
#server.servlet.context-parameters.primefaces.THEME=le-frog
|
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -78,12 +78,6 @@
|
||||||
<artifactId>all-themes</artifactId>
|
<artifactId>all-themes</artifactId>
|
||||||
<version>1.0.10</version>
|
<version>1.0.10</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<!-- Primefaces theme won't work without this! -->
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
|
||||||
<!-- <dependency>
|
<!-- <dependency>
|
||||||
<groupId>javax.enterprise</groupId>
|
<groupId>javax.enterprise</groupId>
|
||||||
<artifactId>cdi-api</artifactId>
|
<artifactId>cdi-api</artifactId>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.mousetech.gourmetj;
|
package com.mousetech.gourmetj;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.faces.event.AjaxBehaviorEvent;
|
import jakarta.faces.event.AjaxBehaviorEvent;
|
||||||
|
@ -15,9 +14,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||||
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
||||||
|
@ -47,11 +44,8 @@ public class AdminMainBean implements Serializable {
|
||||||
private static final Logger log =
|
private static final Logger log =
|
||||||
LoggerFactory.getLogger(AdminMainBean.class);
|
LoggerFactory.getLogger(AdminMainBean.class);
|
||||||
|
|
||||||
/** Cookie delimiter */
|
|
||||||
private static final String CKDLM = ",";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persistency service for Recipes.
|
* Persistency service for Recipes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -62,24 +56,6 @@ public class AdminMainBean implements Serializable {
|
||||||
this.recipeService = service;
|
this.recipeService = service;
|
||||||
}
|
}
|
||||||
|
|
||||||
// **
|
|
||||||
@Inject
|
|
||||||
private CookieBean cookieBean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the cookieBean
|
|
||||||
*/
|
|
||||||
public CookieBean getCookieBean() {
|
|
||||||
return cookieBean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param cookieBean the cookieBean to set
|
|
||||||
*/
|
|
||||||
public void setCookieBean(CookieBean cookieBean) {
|
|
||||||
this.cookieBean = cookieBean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// **
|
// **
|
||||||
@Inject
|
@Inject
|
||||||
private UserSession userSession;
|
private UserSession userSession;
|
||||||
|
@ -105,10 +81,10 @@ public class AdminMainBean implements Serializable {
|
||||||
* @return the searchText
|
* @return the searchText
|
||||||
*/
|
*/
|
||||||
public String getSearchText() {
|
public String getSearchText() {
|
||||||
// if (this.searchResults == null) {
|
if (this.searchResults == null) {
|
||||||
// this.setSearchText(cookieBean.getSearchText());
|
// Fake around broken @PostConstruct
|
||||||
// }
|
this.setSearchText(userSession.getLastSearch());
|
||||||
this.searchText = cookieBean.getSearchText();
|
}
|
||||||
return searchText;
|
return searchText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +93,7 @@ public class AdminMainBean implements Serializable {
|
||||||
*/
|
*/
|
||||||
public void setSearchText(String searchText) {
|
public void setSearchText(String searchText) {
|
||||||
this.searchText = searchText;
|
this.searchText = searchText;
|
||||||
cookieBean.setSearchText(searchText);
|
userSession.setLastSearch(searchText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> suggestionList = null;
|
private List<String> suggestionList = null;
|
||||||
|
@ -128,7 +104,7 @@ public class AdminMainBean implements Serializable {
|
||||||
|
|
||||||
public List<String> searchSuggestionList(String query) {
|
public List<String> searchSuggestionList(String query) {
|
||||||
if (suggestionList == null) {
|
if (suggestionList == null) {
|
||||||
switch (searchtypeEnum()) {
|
switch (this.userSession.getSearchType()) {
|
||||||
case rst_BY_CATEGORY:
|
case rst_BY_CATEGORY:
|
||||||
suggestionList = recipeService.findCategories();
|
suggestionList = recipeService.findCategories();
|
||||||
break;
|
break;
|
||||||
|
@ -142,16 +118,6 @@ public class AdminMainBean implements Serializable {
|
||||||
return suggestionList;
|
return suggestionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RecipeSearchType searchtypeEnum() {
|
|
||||||
int stn = cookieBean.getSearchType();
|
|
||||||
return searchtypeEnum(stn);
|
|
||||||
}
|
|
||||||
|
|
||||||
private RecipeSearchType searchtypeEnum(int stn) {
|
|
||||||
RecipeSearchType st = RecipeSearchType.values()[stn];
|
|
||||||
return st;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
transient DataModel<Recipe> searchResults;
|
transient DataModel<Recipe> searchResults;
|
||||||
|
|
||||||
|
@ -161,7 +127,7 @@ public class AdminMainBean implements Serializable {
|
||||||
public DataModel<Recipe> getSearchResults() {
|
public DataModel<Recipe> getSearchResults() {
|
||||||
if (searchResults == null) {
|
if (searchResults == null) {
|
||||||
searchResults = new ListDataModel<Recipe>();
|
searchResults = new ListDataModel<Recipe>();
|
||||||
init(); // @PostConstruct is broken TODO: fixed??
|
init(); // @PostConstruct is broken
|
||||||
}
|
}
|
||||||
return searchResults;
|
return searchResults;
|
||||||
}
|
}
|
||||||
|
@ -181,7 +147,7 @@ public class AdminMainBean implements Serializable {
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
void init() {
|
void init() {
|
||||||
log.debug("Initializing AdminMainBean " + this);
|
log.debug("Initializing AdminMainBean " + this);
|
||||||
this.setSearchText(cookieBean.getSearchText());
|
this.setSearchText(userSession.getLastSearch());
|
||||||
// Clean up from any previous operations.
|
// Clean up from any previous operations.
|
||||||
this.userSession.setRecipe(null);
|
this.userSession.setRecipe(null);
|
||||||
doFind();
|
doFind();
|
||||||
|
@ -211,20 +177,9 @@ public class AdminMainBean implements Serializable {
|
||||||
*/
|
*/
|
||||||
public String doFind() {
|
public String doFind() {
|
||||||
List<Recipe> recipes = null;
|
List<Recipe> recipes = null;
|
||||||
if ( searchText == null ) {
|
|
||||||
setSearchText("");
|
|
||||||
}
|
|
||||||
searchText = searchText.trim();
|
searchText = searchText.trim();
|
||||||
|
|
||||||
// Persist current settings
|
switch (this.getUserSession().getSearchType()) {
|
||||||
try {
|
|
||||||
cookieBean.saveCookies();
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
// Something is really wrong if we can't create UTF-8!
|
|
||||||
log.error("Unable to save cookies!", e);
|
|
||||||
}
|
|
||||||
RecipeSearchType st = searchtypeEnum();
|
|
||||||
switch (st) {
|
|
||||||
case rst_BY_NAME:
|
case rst_BY_NAME:
|
||||||
recipes = recipeService.findByTitle(searchText);
|
recipes = recipeService.findByTitle(searchText);
|
||||||
break;
|
break;
|
||||||
|
@ -242,11 +197,12 @@ public class AdminMainBean implements Serializable {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
log.error("Invalid recipe search type: "
|
log.error("Invalid recipe search type: "
|
||||||
+ st);
|
+ this.getUserSession().getSearchType());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSearchResults().setWrappedData(recipes);
|
getSearchResults().setWrappedData(recipes);
|
||||||
|
this.userSession.setLastSearch(this.getSearchText());
|
||||||
return null; // Stay on page
|
return null; // Stay on page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,4 +243,27 @@ public class AdminMainBean implements Serializable {
|
||||||
// items.
|
// items.
|
||||||
return "recipeDetails?faces-redirect=true";
|
return "recipeDetails?faces-redirect=true";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get printable preptime. Database version is in seconds.
|
||||||
|
*
|
||||||
|
* @deprecated User {@link UserSession#formatTime(Long)}
|
||||||
|
*
|
||||||
|
* @return Formatted time. Called from EL on main page.
|
||||||
|
*/
|
||||||
|
public String formatPreptime(int timesec) {
|
||||||
|
StringBuffer sb = new StringBuffer(20);
|
||||||
|
int preptime = timesec / 60;
|
||||||
|
if (preptime > 60) {
|
||||||
|
int hours = preptime / 60;
|
||||||
|
sb.append(hours);
|
||||||
|
sb.append(" h. ");
|
||||||
|
preptime %= 60;
|
||||||
|
}
|
||||||
|
if (preptime > 0) {
|
||||||
|
sb.append(preptime);
|
||||||
|
sb.append(" min.");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
package com.mousetech.gourmetj;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import jakarta.faces.model.SelectItem;
|
|
||||||
import jakarta.inject.Named;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appplication-scope data (mostly constants)
|
|
||||||
*
|
|
||||||
* @author timh
|
|
||||||
* @since Feb 1, 2024
|
|
||||||
*/
|
|
||||||
@Named
|
|
||||||
@ApplicationScoped
|
|
||||||
public class AppBean {
|
|
||||||
|
|
||||||
public AppBean() {
|
|
||||||
// TODO Auto-generated constructor stub
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SelectItem> searchTypeList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the searchTypeList
|
|
||||||
* @see RecipeSearchType
|
|
||||||
* Used by main.xhtml
|
|
||||||
*/
|
|
||||||
public List<SelectItem> getSearchTypeList() {
|
|
||||||
if (searchTypeList == null) {
|
|
||||||
searchTypeList = loadSearchTypeList();
|
|
||||||
}
|
|
||||||
return searchTypeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SelectItem> loadSearchTypeList() {
|
|
||||||
List<SelectItem> list = new ArrayList<SelectItem>(5);
|
|
||||||
list.add(new SelectItem(RecipeSearchType.rst_BY_NAME.ordinal(),
|
|
||||||
"Title"));
|
|
||||||
list.add(new SelectItem(RecipeSearchType.rst_BY_CATEGORY.ordinal(),
|
|
||||||
"Category"));
|
|
||||||
list.add(new SelectItem(RecipeSearchType.rst_BY_CUISINE.ordinal(),
|
|
||||||
"Cuisine"));
|
|
||||||
list.add(
|
|
||||||
new SelectItem(RecipeSearchType.rst_BY_INGREDIENT.ordinal(),
|
|
||||||
"Ingredient"));
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2024, Tim Holloway
|
|
||||||
*
|
|
||||||
* Manages app data persisted client-side in cookies.
|
|
||||||
*
|
|
||||||
* Date written: Jan 31, 2024
|
|
||||||
* Author: Tim Holloway <timh@mousetech.com>
|
|
||||||
*/
|
|
||||||
package com.mousetech.gourmetj;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
|
||||||
import jakarta.faces.view.ViewScoped;
|
|
||||||
import jakarta.inject.Named;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Caching object for cookie data persistence.
|
|
||||||
*
|
|
||||||
* @author timh
|
|
||||||
* @since Jan 31, 2024
|
|
||||||
*/
|
|
||||||
@Named
|
|
||||||
@ViewScoped
|
|
||||||
public class CookieBean {
|
|
||||||
|
|
||||||
private static final String KEY_DISPLAY_ROWS = "displayRows";
|
|
||||||
|
|
||||||
private static final String KEY_SEARCH_TYPE = "searchType";
|
|
||||||
|
|
||||||
private static final String KEY_SEARCH_FOR = "searchFor";
|
|
||||||
|
|
||||||
/* Logger */
|
|
||||||
|
|
||||||
private static final Logger log =
|
|
||||||
LoggerFactory.getLogger(CookieBean.class);
|
|
||||||
|
|
||||||
private Map<String, String> cookieMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*/
|
|
||||||
public CookieBean() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void init() {
|
|
||||||
this.cookieMap = JSFUtils.getCookies();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Persist us to client cookie storage
|
|
||||||
*
|
|
||||||
* @throws UnsupportedEncodingException (which should never
|
|
||||||
* happen)
|
|
||||||
*/
|
|
||||||
public void saveCookies()
|
|
||||||
throws UnsupportedEncodingException {
|
|
||||||
final Map<String, Object> properties = new HashMap<>();
|
|
||||||
properties.put("maxAge", 31536000);
|
|
||||||
properties.put("path", "/");
|
|
||||||
properties.put("SameSite", "Strict");
|
|
||||||
|
|
||||||
for (Entry<String, String> e : cookieMap.entrySet()) {
|
|
||||||
JSFUtils.outputCookie(e.getKey(), e.getValue(),
|
|
||||||
properties);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Cookie value by name
|
|
||||||
*
|
|
||||||
* @param name Name of the cookie
|
|
||||||
* @return Value stored in the cookie
|
|
||||||
*/
|
|
||||||
public String getCookieValue(String name) {
|
|
||||||
return cookieMap.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCookieValue(String name, String value) {
|
|
||||||
cookieMap.put(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ************************
|
|
||||||
// App-specific properties
|
|
||||||
// ************************
|
|
||||||
|
|
||||||
public String getSearchText() {
|
|
||||||
return cookieMap.get(KEY_SEARCH_FOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSearchText(String value) {
|
|
||||||
cookieMap.put(KEY_SEARCH_FOR, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// **
|
|
||||||
public Integer getSearchType() {
|
|
||||||
if (!cookieMap.containsKey(KEY_SEARCH_TYPE)) {
|
|
||||||
cookieMap.put(KEY_SEARCH_TYPE, "0");
|
|
||||||
}
|
|
||||||
String st = cookieMap.get(KEY_SEARCH_TYPE);
|
|
||||||
return Integer.valueOf(String.valueOf(st));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSearchType(Integer value) {
|
|
||||||
cookieMap.put(KEY_SEARCH_TYPE, String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
// **
|
|
||||||
public Integer getDisplayListSize() {
|
|
||||||
if (!cookieMap.containsKey(KEY_DISPLAY_ROWS)) {
|
|
||||||
cookieMap.put(KEY_DISPLAY_ROWS, "30");
|
|
||||||
}
|
|
||||||
String st = cookieMap.get(KEY_DISPLAY_ROWS);
|
|
||||||
return Integer.valueOf(String.valueOf(st));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDisplayListSize(Integer value) {
|
|
||||||
cookieMap.put(KEY_DISPLAY_ROWS, String.valueOf(value));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,11 @@
|
||||||
package com.mousetech.gourmetj;
|
package com.mousetech.gourmetj;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import jakarta.faces.application.FacesMessage;
|
import jakarta.faces.application.FacesMessage;
|
||||||
import jakarta.faces.context.ExternalContext;
|
import jakarta.faces.context.ExternalContext;
|
||||||
import jakarta.faces.context.FacesContext;
|
import jakarta.faces.context.FacesContext;
|
||||||
import jakarta.faces.context.Flash;
|
import jakarta.faces.context.Flash;
|
||||||
import jakarta.servlet.http.Cookie;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -61,14 +55,6 @@ public class JSFUtils {
|
||||||
message);
|
message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void addWarningMessage(String msgString) {
|
|
||||||
FacesMessage message = new FacesMessage(
|
|
||||||
FacesMessage.SEVERITY_WARN, "WARNING", msgString);
|
|
||||||
FacesContext.getCurrentInstance().addMessage(null,
|
|
||||||
message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post an error-level message to the FacesContext where the
|
* Post an error-level message to the FacesContext where the
|
||||||
* <h:messages> tag can display it.
|
* <h:messages> tag can display it.
|
||||||
|
@ -117,36 +103,4 @@ public class JSFUtils {
|
||||||
public static void putFlash(String key, Object value) {
|
public static void putFlash(String key, Object value) {
|
||||||
flashScope().put(key, value);
|
flashScope().put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
//***********
|
|
||||||
//* COOKIE!!!
|
|
||||||
//***********
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get cookie values.
|
|
||||||
*/
|
|
||||||
public static Map<String, String> getCookies(){
|
|
||||||
Map<String, Object> m0 = getExternalContext().getRequestCookieMap();
|
|
||||||
Map<String, String>m1 = new HashMap<String, String>();
|
|
||||||
m1 = m0.entrySet()
|
|
||||||
.stream()
|
|
||||||
.collect(Collectors.toMap(
|
|
||||||
e -> e.getKey(),
|
|
||||||
e -> ((Cookie)e.getValue()).getValue()));
|
|
||||||
return m1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a cookie value in Response.
|
|
||||||
* @param name Cookie name
|
|
||||||
* @param value Cookie value
|
|
||||||
* @param properties Cookie property Map (timeout, <i>etc.</i>)
|
|
||||||
* @throws UnsupportedEncodingException
|
|
||||||
*/
|
|
||||||
public static void outputCookie(String name,
|
|
||||||
String value, Map<String, Object> properties) throws UnsupportedEncodingException {
|
|
||||||
getExternalContext().addResponseCookie(name,
|
|
||||||
URLEncoder.encode(value, "UTF-8"),
|
|
||||||
properties);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,32 @@ public class UserSession implements Serializable {
|
||||||
this.searchType = searchType;
|
this.searchType = searchType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<SelectItem> searchTypeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the searchTypeList
|
||||||
|
*/
|
||||||
|
public List<SelectItem> getSearchTypeList() {
|
||||||
|
if (searchTypeList == null) {
|
||||||
|
searchTypeList = loadSearchTypeList();
|
||||||
|
}
|
||||||
|
return searchTypeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SelectItem> loadSearchTypeList() {
|
||||||
|
List<SelectItem> list = new ArrayList<SelectItem>(5);
|
||||||
|
list.add(new SelectItem(RecipeSearchType.rst_BY_NAME,
|
||||||
|
"Title"));
|
||||||
|
list.add(new SelectItem(RecipeSearchType.rst_BY_CATEGORY,
|
||||||
|
"Category"));
|
||||||
|
list.add(new SelectItem(RecipeSearchType.rst_BY_CUISINE,
|
||||||
|
"Cuisine"));
|
||||||
|
list.add(
|
||||||
|
new SelectItem(RecipeSearchType.rst_BY_INGREDIENT,
|
||||||
|
"Ingredient"));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
// ====
|
// ====
|
||||||
|
|
||||||
public String formatCategories(Recipe r) {
|
public String formatCategories(Recipe r) {
|
||||||
|
@ -164,17 +190,16 @@ public class UserSession implements Serializable {
|
||||||
private List<Recipe> shoppingList = new ArrayList<Recipe>();
|
private List<Recipe> shoppingList = new ArrayList<Recipe>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the sessionTimeoutInterval, msec
|
* @return the sessionTimeoutInterval
|
||||||
*/
|
*/
|
||||||
public long getSessionTimeoutInterval() {
|
public long getSessionTimeoutInterval() {
|
||||||
return 5000L; //sessionTimeoutInterval;
|
return sessionTimeoutInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sessionIdleListener() {
|
public void sessionIdleListener() {
|
||||||
log.warn("Session Idle Listener fired.");
|
log.warn("Session Idle Listener fired.");
|
||||||
JSFUtils.addWarningMessage("Timeout approaching. Save your work!");
|
PrimeFaces.current()
|
||||||
// PrimeFaces.current()
|
.executeScript("sessionExpiredConfirmation.show()");
|
||||||
// .executeScript("sessionExpiredConfirmation.show()");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String logoutAction() {
|
public String logoutAction() {
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||||
xmlns:p="http://primefaces.org/ui"
|
xmlns:p="http://primefaces.org/ui"
|
||||||
>
|
>
|
||||||
<h:head></h:head>
|
<head></head>
|
||||||
<h:body>
|
<body>
|
||||||
<ui:composition>
|
<ui:composition>
|
||||||
|
<f:view>
|
||||||
<h:head>
|
<h:head>
|
||||||
<title><ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert></title>
|
<title><ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert></title>
|
||||||
<link rel="icon" type="image/vnd.microsoft.icon"
|
<link rel="icon" type="image/vnd.microsoft.icon"
|
||||||
|
@ -28,14 +29,50 @@
|
||||||
</ui:insert>
|
</ui:insert>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
(C) 2021, 2024 Tim Holloway, Licensed under the <a
|
(C) 2021 Tim Holloway, Licensed under the <a
|
||||||
href="http://www.apache.org/licenses/LICENSE-2.0"
|
href="http://www.apache.org/licenses/LICENSE-2.0"
|
||||||
>Apache License, Version 2.0</a>.
|
>Apache License, Version 2.0</a>.
|
||||||
<p>Based on Gourmet Recipe Manager by T.
|
<p>Based on Gourmet Recipe Manager by T.
|
||||||
Hinkle</p>
|
Hinkle</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
|
<h:form id="frmTimeout">
|
||||||
|
<p:idleMonitor
|
||||||
|
timeout="#{userSession.sessionTimeoutInterval}"
|
||||||
|
onidle="PF('dlgSessionExpired').show()"
|
||||||
|
>
|
||||||
|
<p:ajax event="idle"
|
||||||
|
listener="#{userSession.sessionIdleListener}"
|
||||||
|
/>
|
||||||
|
</p:idleMonitor>
|
||||||
|
<p:confirmDialog closable="false"
|
||||||
|
id="sessionExpiredDlg"
|
||||||
|
message="Your session has expired."
|
||||||
|
header="#{msgs['confirmDialog.initiatingDestroyProcess.label']}"
|
||||||
|
severity="alert"
|
||||||
|
widgetVar="dlgSessionExpired"
|
||||||
|
style="z-index: 25000"
|
||||||
|
>
|
||||||
|
<p:commandButton id="cmdExpiredOK"
|
||||||
|
value="OK" action="/main.jsf"
|
||||||
|
oncomplete="PF('dlgSessionExpired').hide()"
|
||||||
|
/>
|
||||||
|
</p:confirmDialog>
|
||||||
|
</h:form>
|
||||||
|
<!-- -->
|
||||||
|
<h:form id="frmOpErr">
|
||||||
|
<p:confirmDialog
|
||||||
|
message="Session may have expired."
|
||||||
|
header="Error"
|
||||||
|
severity="alert" widgetVar="opError"
|
||||||
|
>
|
||||||
|
<p:commandButton value="OK"
|
||||||
|
oncomplete="PF('opError').hide()"
|
||||||
|
/>
|
||||||
|
</p:confirmDialog>
|
||||||
|
</h:form>
|
||||||
</h:body>
|
</h:body>
|
||||||
|
</f:view>
|
||||||
</ui:composition>
|
</ui:composition>
|
||||||
</h:body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -395,19 +395,6 @@
|
||||||
/>
|
/>
|
||||||
</h:form>
|
</h:form>
|
||||||
</p:panel>
|
</p:panel>
|
||||||
<!-- -->
|
|
||||||
<p:growl id="growl" showDetail="true" />
|
|
||||||
<h:form id="frmTimeout">
|
|
||||||
<p:idleMonitor
|
|
||||||
timeout="#{userSession.sessionTimeoutInterval}"
|
|
||||||
>
|
|
||||||
<p:ajax id="ajaxIdle" event="idle"
|
|
||||||
listener="#{userSession.sessionIdleListener}"
|
|
||||||
update="growl"
|
|
||||||
/>
|
|
||||||
</p:idleMonitor>
|
|
||||||
</h:form>
|
|
||||||
<!-- -->
|
|
||||||
<p:dialog id="addGroupDlg" widgetVar="addGroupDlg">
|
<p:dialog id="addGroupDlg" widgetVar="addGroupDlg">
|
||||||
<h:form id="frmAddGroup">
|
<h:form id="frmAddGroup">
|
||||||
<p:panelGrid columns="1">
|
<p:panelGrid columns="1">
|
||||||
|
|
|
@ -28,10 +28,10 @@
|
||||||
/>
|
/>
|
||||||
<p:outputLabel for="@next" value="Search for " />
|
<p:outputLabel for="@next" value="Search for " />
|
||||||
<p:selectOneMenu id="ctlSearchType"
|
<p:selectOneMenu id="ctlSearchType"
|
||||||
value="#{cookieBean.searchType}"
|
value="#{userSession.searchType}"
|
||||||
>
|
>
|
||||||
<f:selectItems
|
<f:selectItems
|
||||||
value="#{appBean.searchTypeList}"
|
value="#{userSession.searchTypeList}"
|
||||||
/>
|
/>
|
||||||
<p:ajax
|
<p:ajax
|
||||||
listener="#{adminMainBean.resetSuggestions}"
|
listener="#{adminMainBean.resetSuggestions}"
|
||||||
|
@ -45,12 +45,12 @@
|
||||||
<p:commandButton value="New Recipe"
|
<p:commandButton value="New Recipe"
|
||||||
action="#{adminMainBean.doNewRecipe}"
|
action="#{adminMainBean.doNewRecipe}"
|
||||||
/>
|
/>
|
||||||
<p:commandButton value="Shopping..."
|
<p:commandButton value="More..."
|
||||||
action="#{adminMainBean.doMore}"
|
action="#{adminMainBean.doMore}"
|
||||||
/>
|
/>
|
||||||
<h:outputText id="slistSSize"
|
<h:outputText id="slistSSize"
|
||||||
style="margin-left: 2em"
|
style="margin-left: 2em"
|
||||||
value="#{cookieBean.displayListSize}"
|
value="#{userSession.shoppingList.size()}"
|
||||||
/>
|
/>
|
||||||
<h:outputLabel for="slistSize"
|
<h:outputLabel for="slistSize"
|
||||||
value=" Recipes in Shopping List"
|
value=" Recipes in Shopping List"
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
xmlns:p="http://primefaces.org/ui"
|
xmlns:p="http://primefaces.org/ui"
|
||||||
xmlns:c="http://xmlns.jcp.org/jstl"
|
xmlns:c="http://xmlns.jcp.org/jstl"
|
||||||
>
|
>
|
||||||
<!-- Tabbed page for the Mainpage "Shopping..." button -->
|
<!-- Tabbed page for the Mainpage "More..." button -->
|
||||||
<ui:define name="title">Gourmet Recipe Manager - Shopping</ui:define>
|
<ui:define name="title">Gourmet Recipe Manager - Shopping</ui:define>
|
||||||
<ui:define name="content">
|
<ui:define name="content">
|
||||||
<style>
|
<style>
|
||||||
|
@ -34,7 +34,6 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
This is the list of recipe items you've selected to shop for.
|
|
||||||
<h:messages />
|
<h:messages />
|
||||||
<p:tabView id="tabGroupClient" orientation="left"
|
<p:tabView id="tabGroupClient" orientation="left"
|
||||||
dynamic="true"
|
dynamic="true"
|
||||||
|
@ -96,11 +95,13 @@
|
||||||
value="Ingredients"
|
value="Ingredients"
|
||||||
/>
|
/>
|
||||||
</f:facet>
|
</f:facet>
|
||||||
|
<p:headerRow>
|
||||||
<p:column colspan="4">
|
<p:column colspan="4">
|
||||||
<h:outputText
|
<h:outputText
|
||||||
value="#{item.shopCat}"
|
value="#{item.shopCat}"
|
||||||
/>
|
/>
|
||||||
</p:column>
|
</p:column>
|
||||||
|
</p:headerRow>
|
||||||
<p:column label="Amt"
|
<p:column label="Amt"
|
||||||
style="width: 3em; text-align: right"
|
style="width: 3em; text-align: right"
|
||||||
>
|
>
|
||||||
|
@ -149,7 +150,6 @@
|
||||||
</p:tab>
|
</p:tab>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<p:tab id="tabPantry" title="Pantry">
|
<p:tab id="tabPantry" title="Pantry">
|
||||||
<h:outputText value="Stuff already in the pantry." />
|
|
||||||
<h:outputText value="For future implementation" />
|
<h:outputText value="For future implementation" />
|
||||||
</p:tab>
|
</p:tab>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
|
|
|
@ -26,11 +26,6 @@ server:
|
||||||
servlet:
|
servlet:
|
||||||
session:
|
session:
|
||||||
timeout: '30m'
|
timeout: '30m'
|
||||||
# Theme here pverrides jinfaces theme
|
|
||||||
# context-parameters:
|
|
||||||
# primefaces:
|
|
||||||
# THEME: vela
|
|
||||||
|
|
||||||
|
|
||||||
gourmet:
|
gourmet:
|
||||||
password:
|
password:
|
||||||
|
@ -38,4 +33,4 @@ gourmet:
|
||||||
|
|
||||||
joinfaces:
|
joinfaces:
|
||||||
primefaces:
|
primefaces:
|
||||||
theme: casablanca
|
theme: saga
|
||||||
|
|
Loading…
Reference in New Issue
Block a user