Compare commits
	
		
			3 Commits
		
	
	
		
			8bcd8cb9a1
			...
			68462514db
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 68462514db | |||
| 61942f547a | |||
| 9cf0f89e76 | 
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
# Gourmet Recipe Manager - Spring Boot
 | 
					# Gourmet Recipe Manager - Spring Boot - Version 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This is a port of Thomas Hinkle (thinkle) Gourmet Recipe Manager.
 | 
					This is a port of Thomas Hinkle (thinkle) Gourmet Recipe Manager.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,4 +85,14 @@ 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,3 +22,5 @@ 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,6 +78,12 @@
 | 
				
			||||||
            <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,6 +1,7 @@
 | 
				
			||||||
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;
 | 
				
			||||||
| 
						 | 
					@ -14,7 +15,9 @@ 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;
 | 
				
			||||||
| 
						 | 
					@ -44,8 +47,11 @@ 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
 | 
				
			||||||
| 
						 | 
					@ -56,6 +62,24 @@ 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;
 | 
				
			||||||
| 
						 | 
					@ -81,10 +105,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) {
 | 
				
			||||||
			// Fake around broken @PostConstruct
 | 
					//			this.setSearchText(cookieBean.getSearchText());
 | 
				
			||||||
			this.setSearchText(userSession.getLastSearch());
 | 
					//		}
 | 
				
			||||||
		}
 | 
							this.searchText = cookieBean.getSearchText();
 | 
				
			||||||
		return searchText;
 | 
							return searchText;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,7 +117,7 @@ public class AdminMainBean implements Serializable {
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void setSearchText(String searchText) {
 | 
						public void setSearchText(String searchText) {
 | 
				
			||||||
		this.searchText = searchText;
 | 
							this.searchText = searchText;
 | 
				
			||||||
		userSession.setLastSearch(searchText);
 | 
							cookieBean.setSearchText(searchText);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private List<String> suggestionList = null;
 | 
						private List<String> suggestionList = null;
 | 
				
			||||||
| 
						 | 
					@ -104,7 +128,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 (this.userSession.getSearchType()) {
 | 
								switch (searchtypeEnum()) {
 | 
				
			||||||
			case rst_BY_CATEGORY:
 | 
								case rst_BY_CATEGORY:
 | 
				
			||||||
				suggestionList = recipeService.findCategories();
 | 
									suggestionList = recipeService.findCategories();
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
| 
						 | 
					@ -118,6 +142,16 @@ 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,7 +161,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
 | 
								init(); // @PostConstruct is broken TODO: fixed??
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return searchResults;
 | 
							return searchResults;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -147,7 +181,7 @@ public class AdminMainBean implements Serializable {
 | 
				
			||||||
	@PostConstruct
 | 
						@PostConstruct
 | 
				
			||||||
	void init() {
 | 
						void init() {
 | 
				
			||||||
		log.debug("Initializing AdminMainBean " + this);
 | 
							log.debug("Initializing AdminMainBean " + this);
 | 
				
			||||||
		this.setSearchText(userSession.getLastSearch());
 | 
							this.setSearchText(cookieBean.getSearchText());
 | 
				
			||||||
		// Clean up from any previous operations.
 | 
							// Clean up from any previous operations.
 | 
				
			||||||
		this.userSession.setRecipe(null);
 | 
							this.userSession.setRecipe(null);
 | 
				
			||||||
		doFind();
 | 
							doFind();
 | 
				
			||||||
| 
						 | 
					@ -177,9 +211,20 @@ 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();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch (this.getUserSession().getSearchType()) {
 | 
							// Persist current settings
 | 
				
			||||||
 | 
							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;
 | 
				
			||||||
| 
						 | 
					@ -197,12 +242,11 @@ public class AdminMainBean implements Serializable {
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			log.error("Invalid recipe search type: "
 | 
								log.error("Invalid recipe search type: "
 | 
				
			||||||
					+ this.getUserSession().getSearchType());
 | 
										+ st);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		getSearchResults().setWrappedData(recipes);
 | 
							getSearchResults().setWrappedData(recipes);	
 | 
				
			||||||
		this.userSession.setLastSearch(this.getSearchText());
 | 
					 | 
				
			||||||
		return null; // Stay on page
 | 
							return null; // Stay on page
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -243,27 +287,4 @@ 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();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										51
									
								
								src/main/java/com/mousetech/gourmetj/AppBean.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/main/java/com/mousetech/gourmetj/AppBean.java
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,51 @@
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
						}	
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										127
									
								
								src/main/java/com/mousetech/gourmetj/CookieBean.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/main/java/com/mousetech/gourmetj/CookieBean.java
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,127 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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,11 +1,17 @@
 | 
				
			||||||
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;
 | 
				
			||||||
| 
						 | 
					@ -55,6 +61,14 @@ 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.
 | 
				
			||||||
| 
						 | 
					@ -103,4 +117,36 @@ 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,32 +129,6 @@ 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) {
 | 
				
			||||||
| 
						 | 
					@ -190,16 +164,17 @@ public class UserSession implements Serializable {
 | 
				
			||||||
	private List<Recipe> shoppingList = new ArrayList<Recipe>();
 | 
						private List<Recipe> shoppingList = new ArrayList<Recipe>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * @return the sessionTimeoutInterval
 | 
						 * @return the sessionTimeoutInterval, msec
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public long getSessionTimeoutInterval() {
 | 
						public long getSessionTimeoutInterval() {
 | 
				
			||||||
		return sessionTimeoutInterval;
 | 
							return 5000L; //sessionTimeoutInterval;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void sessionIdleListener() {
 | 
						public void sessionIdleListener() {
 | 
				
			||||||
		log.warn("Session Idle Listener fired.");
 | 
							log.warn("Session Idle Listener fired.");
 | 
				
			||||||
		PrimeFaces.current()
 | 
							JSFUtils.addWarningMessage("Timeout approaching. Save your work!");
 | 
				
			||||||
			.executeScript("sessionExpiredConfirmation.show()");
 | 
					//		PrimeFaces.current()
 | 
				
			||||||
 | 
					//			.executeScript("sessionExpiredConfirmation.show()");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public String logoutAction() {
 | 
						public String logoutAction() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,9 @@
 | 
				
			||||||
    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"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
<head></head>
 | 
					<h:head></h:head>
 | 
				
			||||||
<body>
 | 
					<h: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"
 | 
				
			||||||
| 
						 | 
					@ -29,50 +28,14 @@
 | 
				
			||||||
                </ui:insert>
 | 
					                </ui:insert>
 | 
				
			||||||
                <!--  -->
 | 
					                <!--  -->
 | 
				
			||||||
                <div id="footer">
 | 
					                <div id="footer">
 | 
				
			||||||
                    (C) 2021 Tim Holloway, Licensed under the <a
 | 
					                    (C) 2021, 2024 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>
 | 
				
			||||||
</body>
 | 
					</h:body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
| 
						 | 
					@ -395,6 +395,19 @@
 | 
				
			||||||
                    />                    
 | 
					                    />                    
 | 
				
			||||||
                </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="#{userSession.searchType}"
 | 
					                    value="#{cookieBean.searchType}"
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <f:selectItems
 | 
					                    <f:selectItems
 | 
				
			||||||
                        value="#{userSession.searchTypeList}"
 | 
					                        value="#{appBean.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="More..."
 | 
					                <p:commandButton value="Shopping..."
 | 
				
			||||||
                    action="#{adminMainBean.doMore}"
 | 
					                    action="#{adminMainBean.doMore}"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
                <h:outputText id="slistSSize"
 | 
					                <h:outputText id="slistSSize"
 | 
				
			||||||
                    style="margin-left: 2em"
 | 
					                    style="margin-left: 2em"
 | 
				
			||||||
                    value="#{userSession.shoppingList.size()}"
 | 
					                    value="#{cookieBean.displayListSize}"
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
                <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 "More..." button -->
 | 
					    <!-- Tabbed page for the Mainpage "Shopping..." 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,6 +34,7 @@
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</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"
 | 
				
			||||||
| 
						 | 
					@ -95,13 +96,11 @@
 | 
				
			||||||
                                    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"
 | 
				
			||||||
                            >
 | 
					                            >
 | 
				
			||||||
| 
						 | 
					@ -150,6 +149,7 @@
 | 
				
			||||||
            </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,6 +26,11 @@ server:
 | 
				
			||||||
    servlet:
 | 
					    servlet:
 | 
				
			||||||
        session:
 | 
					        session:
 | 
				
			||||||
            timeout: '30m'
 | 
					            timeout: '30m'
 | 
				
			||||||
 | 
					# Theme here pverrides jinfaces theme            
 | 
				
			||||||
 | 
					#        context-parameters:
 | 
				
			||||||
 | 
					#            primefaces:
 | 
				
			||||||
 | 
					#                THEME: vela
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gourmet:
 | 
					gourmet:
 | 
				
			||||||
    password:
 | 
					    password:
 | 
				
			||||||
| 
						 | 
					@ -33,4 +38,4 @@ gourmet:
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
joinfaces:
 | 
					joinfaces:
 | 
				
			||||||
  primefaces:
 | 
					  primefaces:
 | 
				
			||||||
    theme: saga
 | 
					    theme: casablanca
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user