Compare commits
No commits in common. "main" and "version2" have entirely different histories.
|
@ -25,6 +25,13 @@
|
|||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="test" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="optional" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
|
|
24
README.md
24
README.md
|
@ -107,26 +107,4 @@ Note that by default, JSF caches ViewState in a session so every
|
|||
JSF View can cause a session to be created, not just Views that
|
||||
reference View- or SessionScoped backing beans. This is alterable
|
||||
by setting an option in the faces-config.
|
||||
force
|
||||
|
||||
### Developer/deployer note.
|
||||
Because of caching, updated installations
|
||||
may not render properly. Manually request the "/main.xtml" resource
|
||||
and that should flush out stale info being used by "/main.jsf".
|
||||
|
||||
Followup: Excess payloads were being added to the welcome page
|
||||
that caused HTTP "400" errors fetching resources. A fix has
|
||||
been made, although the ultimate solution will probably be more
|
||||
JSF-friendly.
|
||||
|
||||
Security note: The gourmetj password file is a simple CSV file
|
||||
with "userid","password" plain text, NOT htpasswd form. Modify the
|
||||
SpringSecurityConfig if you want something more rigorous.
|
||||
|
||||
## Minor release 2.0.1
|
||||
|
||||
This release can build an installable RPM for Red Hat-compatible
|
||||
systems, including CentOS and its relatives and Fedora.
|
||||
|
||||
Just run ``mvn install`` and the RPM will be produced under the
|
||||
``/target/rpm`` directory.
|
||||
force
|
|
@ -1,96 +0,0 @@
|
|||
# GourmetJ application Architecture.
|
||||
|
||||
This is a Spring Boot self-hosted website built on standard JEE
|
||||
components like JavaServer Faces (JSF) and the Java Persistence
|
||||
Architecture (JPA). It is built and can be tested using Maven.
|
||||
|
||||
The Maven pom.xml file determines the build and what dependencies
|
||||
it has. The core dependencies and their versions are determined
|
||||
in the `dependencyManagement` section of the POM. Actual inclusion
|
||||
of dependencies is done in the `dependencies` section.
|
||||
|
||||
Of the 3 dependencyManagement dependencies, the JoinFaces BOM is
|
||||
the most important. It ensures that the proper version of Spring
|
||||
Boot and its dependencies is brought into the `dependencies` as
|
||||
well as allowing the option to select which JSF extension libraries
|
||||
are to be used. For this project, PrimeFaces is the primary JSF
|
||||
framework.
|
||||
|
||||
## Source Code components
|
||||
|
||||
Source code files come in 4 major types:
|
||||
|
||||
1. Spring/Spring Boot support
|
||||
1. JSF Backing Beans
|
||||
1. Data Persistence
|
||||
1. General Utilities (for example data format converters and parsers)
|
||||
|
||||
### Spring and Spring Boot Support
|
||||
|
||||
The application code root package is `com.mousetech.gourmetj`. The
|
||||
Spring configuration classes are located in this package. There
|
||||
are two of them: `SpringPrimeFacesApplication'java`, which is the
|
||||
main class for the app, and `SpringSecurityConfig.java`, which
|
||||
defines security. Security credentials (userid/password) are taken
|
||||
from an external password file whose location is defined by the
|
||||
`application.yml` properties file. There are no security roles, and
|
||||
all site pages are accessible without logging in except for the
|
||||
ones that can modify the database (create/edit recipe).
|
||||
|
||||
Secondary Spring characteristics come from the `WelcomePageRedirect.java`
|
||||
file which defines the site's welcome page (index.html).
|
||||
|
||||
A small bit of Spring Web in the `springweb.PicureController` class.
|
||||
This Controller resolves the image URLs for recipe images and
|
||||
thumbnails.
|
||||
|
||||
### JSF BackingBeans
|
||||
|
||||
The heart of the webapp is in JavaServer Faces Backing Beans which
|
||||
provide the Models for their corresponding View Template (.xhtml)
|
||||
files. The most important beans are the adminMain bean, which
|
||||
generates the recipe list page and the recipeDetail bean, which
|
||||
back the display and editing of a recipe. The CookieBean is
|
||||
a utility class that allows keeping search information in user-side
|
||||
cookies between user sessions and JSFUtils allows access to
|
||||
HTTP/Servlet soecific resources in a way that isolates them from
|
||||
the general application code. That allows platform independence
|
||||
and easier unit testing.
|
||||
|
||||
### Persistence
|
||||
|
||||
Persistence is done via JPA and has 3 layers:
|
||||
|
||||
1. Service layer. This layer fetches and updates "working sets" of
|
||||
related database entity objects. All service methods are
|
||||
Transactions. Working sets passed in or out of the service layer
|
||||
to the higher (business) layer of the app are detached objects,
|
||||
so the Service layer also ensures detaching and re-attaching
|
||||
(merging) as needed for the lower layer functions.
|
||||
|
||||
1. DAO layer. This layer does find/CRUD functions for a single
|
||||
Entity type or sometimes a parent/child Entity set. I've coded this
|
||||
using explicit logic in times past, but in this app, I've leveraged
|
||||
Spring's Repository feature to let it write the grunt code.
|
||||
|
||||
1. Model layer. This layer contains the actual Entity classes,
|
||||
which are all POJOs and have no executable logic except get/set.
|
||||
|
||||
### Utilities
|
||||
|
||||
Most utility services are in the ``utils`` package, except for
|
||||
JSFUtils, which serves as a liason between JSF server-independent
|
||||
code and server platform-specific services.
|
||||
|
||||
## Future considerations
|
||||
|
||||
There's probably still some lint in the POM and perhaps some dead
|
||||
code from deadend solutions that weren't viable. That's how apps
|
||||
are in the Real World.
|
||||
|
||||
Adding an I18N Bundle would probably be nice.
|
||||
|
||||
A Schema file was recently added to the project and it needs to
|
||||
be checked for functionality. A similar `data.sql` is probably
|
||||
needed to be able to fully set up a database from scratch. My
|
||||
current copy was simply cloned from an existing database.
|
115
pom.xml
115
pom.xml
|
@ -7,7 +7,7 @@
|
|||
|
||||
<groupId>com.mousetech.gourmet</groupId>
|
||||
<artifactId>gourmetj</artifactId>
|
||||
<version>2.0.37</version>
|
||||
<version>0.2.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>GourmetJ</name>
|
||||
|
@ -17,7 +17,7 @@
|
|||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<version>3.2.2</version>
|
||||
<relativePath /> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
|
@ -84,6 +84,10 @@
|
|||
<artifactId>gson</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>javax.enterprise</groupId>
|
||||
<artifactId>cdi-api</artifactId>
|
||||
</dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
|
@ -92,6 +96,14 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<!--
|
||||
https://mvnrepository.com/artifact/jakarta.persistence/jakarta.persistence-api -->
|
||||
<!-- <dependency>
|
||||
<groupId>javax.persistence</groupId>
|
||||
<artifactId>javax.persistence-api</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
|
@ -117,11 +129,21 @@
|
|||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jstl</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-jasper</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>jakarta.el</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!--
|
||||
https://mvnrepository.com/artifact/com.twelvemonkeys.imageio/imageio-core -->
|
||||
|
@ -136,6 +158,7 @@
|
|||
</dependency>
|
||||
<!--
|
||||
https://mvnrepository.com/artifact/com.twelvemonkeys.imageio/imageio-webp -->
|
||||
<!-- In Core??? -->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-webp</artifactId>
|
||||
|
@ -143,6 +166,7 @@
|
|||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- Needed only if you deploy ImageIO plugins as part of a web app.
|
||||
Make sure you add the IIOProviderContextListener to your web.xml.
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||
|
@ -160,6 +184,12 @@
|
|||
<version>8.0.30</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>jakarta.faces</artifactId>
|
||||
<version>${com.sun.faces.version}</version>
|
||||
</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
|
@ -172,92 +202,11 @@
|
|||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>rpm-maven-plugin</artifactId>
|
||||
<version>2.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-rpm</id>
|
||||
<goals>
|
||||
<goal>rpm</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<license>GPL (c) 2024, mousetech.com</license>
|
||||
<distribution>Gourmetj</distribution>
|
||||
<group>Application/Collectors</group>
|
||||
<packager>Tom Holloway</packager>
|
||||
<prefix>/usr/local</prefix>
|
||||
<changelogFile>src/changelog</changelogFile>
|
||||
<defineStatements>
|
||||
<defineStatement>_unpackaged_files_terminate_build 0</defineStatement>
|
||||
</defineStatements>
|
||||
<mappings>
|
||||
<mapping>
|
||||
<directory>/opt/mousetech/gourmetj</directory>
|
||||
<filemode>750</filemode>
|
||||
<username>gourmetj</username>
|
||||
<groupname>gourmetj</groupname>
|
||||
<sources>
|
||||
<source>
|
||||
<location>
|
||||
target/${project.build.finalName}.jar</location>
|
||||
</source>
|
||||
<softlinkSource>
|
||||
<destination>gourmetj.jar</destination>
|
||||
<location>
|
||||
${project.build.finalName}.jar</location>
|
||||
</softlinkSource>
|
||||
</sources>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<directory>/opt/mousetech/gourmetj</directory>
|
||||
<configuration>true</configuration>
|
||||
<filemode>640</filemode>
|
||||
<username>gourmetj</username>
|
||||
<groupname>gourmetj</groupname>
|
||||
<sources>
|
||||
<source>
|
||||
<location>src/main/conf</location>
|
||||
</source>
|
||||
</sources>
|
||||
</mapping>
|
||||
<mapping>
|
||||
<directory>/etc/systemd/system/</directory>
|
||||
<directoryIncluded>false</directoryIncluded>
|
||||
<configuration>true</configuration>
|
||||
<filemode>740</filemode>
|
||||
<sources>
|
||||
<source>
|
||||
<location>src/main/systemd/gourmetj.service</location>
|
||||
</source>
|
||||
</sources>
|
||||
</mapping>
|
||||
</mappings>
|
||||
<preinstallScriptlet>
|
||||
<script>echo "installing ${project.name} now"
|
||||
/usr/bin/getent passwd gourmetj || /usr/sbin/useradd -r -d /opt/mousetech/gourmetj -s /sbin/nologin gourmetj
|
||||
</script>
|
||||
</preinstallScriptlet>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
# Application user password file
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
# THIS is the application properties used when testing in the IDE
|
||||
# or running stand-alone from the command line.
|
||||
# It augments/overrides application.yml in the JAR
|
||||
joinfaces.jsf.webapp-resources-directory=/resources
|
||||
server.servlet.session.timeout=30m
|
||||
|
||||
spring.thymeleaf.enabled=false
|
||||
server.error.whitelabel.enabled=false
|
||||
|
||||
spring.datasource.url=jdbc:mysql://dbase/recipes
|
||||
#jdbc:sqlite:${home}/recipes.db
|
||||
spring.datasource.username=recipes
|
||||
pring.datasource.password=yumyumyum
|
||||
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
|
||||
|
||||
#Runtime lies and says no longer required, but it defaults to MySQL5.5.0!:
|
||||
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
|
||||
#org.sqlite.hibernate.dialect.SQLiteDialect
|
||||
#spring.jpa.show-sql: true
|
||||
|
||||
# My special properties
|
||||
gourmet.password.file=.gourmetpw
|
||||
|
||||
# This will override aplication.yml
|
||||
#server.servlet.context-parameters.primefaces.THEME=le-frog
|
||||
|
||||
### HttpSession timeout (note effects on detailEdit idleMonitors)
|
||||
server.servlet.session.timeout=35m
|
|
@ -1,6 +1,7 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -11,22 +12,27 @@ import com.mousetech.gourmetj.persistence.model.Recipe;
|
|||
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.enterprise.context.RequestScoped;
|
||||
import jakarta.faces.event.AjaxBehaviorEvent;
|
||||
import jakarta.faces.model.DataModel;
|
||||
import jakarta.faces.model.ListDataModel;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
|
||||
/**
|
||||
* Main control panel backing bean.
|
||||
*
|
||||
* The rare and fabled RequestScope, which is otherwise
|
||||
* useless 90% of the time. Here we maintain no session
|
||||
* state. so we can better support the session timeout
|
||||
* for editing functions.
|
||||
*
|
||||
* @author timh
|
||||
* @since Jun 28, 2012
|
||||
*/
|
||||
|
||||
@Named
|
||||
@ViewScoped
|
||||
@RequestScoped
|
||||
public class AdminMainBean implements Serializable {
|
||||
|
||||
/**
|
||||
|
@ -43,6 +49,9 @@ public class AdminMainBean implements Serializable {
|
|||
private static final Logger log =
|
||||
LoggerFactory.getLogger(AdminMainBean.class);
|
||||
|
||||
/** Cookie delimiter */
|
||||
private static final String CKDLM = ",";
|
||||
|
||||
/**
|
||||
* Persistency service for Recipes.
|
||||
*/
|
||||
|
@ -66,6 +75,13 @@ public class AdminMainBean implements Serializable {
|
|||
return cookieBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cookieBean the cookieBean to set
|
||||
*/
|
||||
public void setCookieBean(CookieBean cookieBean) {
|
||||
this.cookieBean = cookieBean;
|
||||
}
|
||||
|
||||
// **
|
||||
@Inject
|
||||
private UserSession userSession;
|
||||
|
@ -202,6 +218,13 @@ public class AdminMainBean implements Serializable {
|
|||
}
|
||||
searchText = searchText.trim();
|
||||
|
||||
// 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:
|
||||
|
@ -239,7 +262,6 @@ public class AdminMainBean implements Serializable {
|
|||
this.userSession.setLastEdit(null);
|
||||
// Construct a blank recipe to be created.
|
||||
this.userSession.setRecipe(new Recipe());
|
||||
this.userSession.setDetailTab(0); // title tab
|
||||
return "detailEdit?faces-redirect=true";
|
||||
}
|
||||
|
||||
|
@ -267,9 +289,4 @@ public class AdminMainBean implements Serializable {
|
|||
// items.
|
||||
return "recipeDetails?faces-redirect=true";
|
||||
}
|
||||
|
||||
public String doLogout() {
|
||||
JSFUtils.logout();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ package com.mousetech.gourmetj;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.faces.model.SelectItem;
|
||||
import jakarta.inject.Named;
|
||||
|
@ -49,12 +47,5 @@ public class AppBean {
|
|||
new SelectItem(RecipeSearchType.rst_BY_INGREDIENT.ordinal(),
|
||||
"Ingredient"));
|
||||
return list;
|
||||
}
|
||||
|
||||
@Value("${appVersion}")
|
||||
String appVersion = "Not Supplied.";
|
||||
|
||||
public String getAppVersion() {
|
||||
return appVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.enterprise.context.RequestScoped;
|
||||
import jakarta.inject.Named;
|
||||
|
||||
/**
|
||||
|
@ -28,7 +28,7 @@ import jakarta.inject.Named;
|
|||
* @since Jan 31, 2024
|
||||
*/
|
||||
@Named
|
||||
@ViewScoped
|
||||
@RequestScoped
|
||||
public class CookieBean {
|
||||
|
||||
private static final String KEY_DISPLAY_ROWS = "displayRows";
|
||||
|
@ -44,15 +44,10 @@ public class CookieBean {
|
|||
|
||||
private Map<String, String> cookieMap;
|
||||
|
||||
final Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CookieBean() {
|
||||
properties.put("maxAge", 31536000);
|
||||
properties.put("path", "/");
|
||||
properties.put("SameSite", "Strict");
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
|
@ -68,6 +63,11 @@ public class CookieBean {
|
|||
*/
|
||||
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);
|
||||
|
@ -86,13 +86,6 @@ public class CookieBean {
|
|||
|
||||
public void setCookieValue(String name, String value) {
|
||||
cookieMap.put(name, value);
|
||||
try {
|
||||
JSFUtils.outputCookie(name, value, properties);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Should never happen. But...
|
||||
log.error("Unable to encode cookie", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// ************************
|
||||
|
@ -104,7 +97,7 @@ public class CookieBean {
|
|||
}
|
||||
|
||||
public void setSearchText(String value) {
|
||||
setCookieValue(KEY_SEARCH_FOR, value);
|
||||
cookieMap.put(KEY_SEARCH_FOR, value);
|
||||
}
|
||||
|
||||
// **
|
||||
|
@ -117,27 +110,24 @@ public class CookieBean {
|
|||
}
|
||||
|
||||
public void setSearchType(Integer value) {
|
||||
String st = String.valueOf(value);
|
||||
setCookieValue(KEY_SEARCH_TYPE, st);
|
||||
cookieMap.put(KEY_SEARCH_TYPE, String.valueOf(value));
|
||||
}
|
||||
|
||||
// **
|
||||
public Integer getDisplayListSize() {
|
||||
if (!cookieMap.containsKey(KEY_DISPLAY_ROWS)) {
|
||||
cookieMap.put(KEY_DISPLAY_ROWS, "0");
|
||||
cookieMap.put(KEY_DISPLAY_ROWS, "30");
|
||||
}
|
||||
String st = cookieMap.get(KEY_DISPLAY_ROWS);
|
||||
return Integer.valueOf(String.valueOf(st));
|
||||
}
|
||||
|
||||
public void setDisplayListSize(Integer value) {
|
||||
setCookieValue(KEY_DISPLAY_ROWS, String.valueOf(value));
|
||||
cookieMap.put(KEY_DISPLAY_ROWS, String.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* IdleMonitor backing methods (session/View timeout)
|
||||
* Todo: move to a more general location. Currently
|
||||
* only used by view editor, not Main!
|
||||
*/
|
||||
public void sessionIdleListener() {
|
||||
log.info("Session Idle Listener fired.");
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package com.mousetech.gourmetj;
|
||||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -13,7 +12,6 @@ import jakarta.faces.context.ExternalContext;
|
|||
import jakarta.faces.context.FacesContext;
|
||||
import jakarta.faces.context.Flash;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
@ -128,52 +126,29 @@ public class JSFUtils {
|
|||
/**
|
||||
* 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()));
|
||||
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;
|
||||
}
|
||||
|
||||
public static String getCookie(String cookieName) {
|
||||
Map<String, Object> map =
|
||||
getExternalContext().getRequestCookieMap();
|
||||
if (map == null) {
|
||||
return null; // no cookies at all
|
||||
}
|
||||
Cookie cookie = (Cookie) map.get(cookieName);
|
||||
if (cookie == null) {
|
||||
return null;
|
||||
}
|
||||
return cookie.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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);
|
||||
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
cookie.setMaxAge(31536000);
|
||||
cookie.setPath("/");
|
||||
jakarta.servlet.http.HttpServletResponse resp =
|
||||
(HttpServletResponse) getExternalContext()
|
||||
.getResponse();
|
||||
resp.addCookie(cookie);
|
||||
public static void outputCookie(String name,
|
||||
String value, Map<String, Object> properties) throws UnsupportedEncodingException {
|
||||
getExternalContext().addResponseCookie(name,
|
||||
URLEncoder.encode(value, "UTF-8"),
|
||||
properties);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,9 +164,5 @@ public class JSFUtils {
|
|||
log.warn("Session did not exist.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static HttpSession getSession(boolean create) {
|
||||
return (HttpSession) getExternalContext().getSession(create);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,13 +14,15 @@ import jakarta.faces.model.ListDataModel;
|
|||
import jakarta.faces.view.ViewScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.servlet.http.Part;
|
||||
import jakarta.faces.event.AjaxBehaviorEvent;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.primefaces.event.FileUploadEvent;
|
||||
import org.primefaces.model.file.UploadedFile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
|
||||
|
||||
import com.mousetech.gourmetj.persistence.model.Category;
|
||||
import com.mousetech.gourmetj.persistence.model.Ingredient;
|
||||
|
@ -206,8 +208,8 @@ public class RecipeDetailBean implements Serializable {
|
|||
|
||||
/**
|
||||
* After construction and injection, we obtain the recipe ID
|
||||
* passed to us, if any, and load the recipe. It's also stored
|
||||
* in @see UserSession for the detail editor.
|
||||
* passed to us, if any and load the recipe. It's also stored
|
||||
* in @see UserSession for the detail editor and
|
||||
*
|
||||
* @see PictureController.
|
||||
*/
|
||||
|
@ -243,7 +245,7 @@ public class RecipeDetailBean implements Serializable {
|
|||
this.shop = this.getUserSession().getShoppingList()
|
||||
.contains(recipe);
|
||||
|
||||
log.debug("Set recipe: " + this.recipe);
|
||||
log.info("Set recipe: " + this.recipe);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -358,8 +360,8 @@ public class RecipeDetailBean implements Serializable {
|
|||
return "";
|
||||
}
|
||||
String s = instructions.replace("\r\n", "<p/>")
|
||||
.replace("\n\n", "<p/>")
|
||||
.replace("\n", "<br/>");
|
||||
.replace("\n\n", "<p/>");
|
||||
s = s.replace("\n", "<br/>");
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -643,30 +645,21 @@ public class RecipeDetailBean implements Serializable {
|
|||
* @see #addIngredientList(String)
|
||||
*/
|
||||
public void addIngredient(String ingredientText) {
|
||||
log.debug("Ingredient line: \"" + ingredientText + "\"");
|
||||
log.info("Ingredient line: \"" + ingredientText + "\"");
|
||||
Ingredient ing =
|
||||
IngredientDigester.digest(ingredientText);
|
||||
|
||||
String ingkey = ing.getIngkey();
|
||||
if (!StringUtils.isEmpty(ingkey)) {
|
||||
try {
|
||||
Shopcat scat = this.recipeService
|
||||
.findShopcatForIngredientKey(ingkey);
|
||||
ing.setShopCat(scat);
|
||||
} catch (JpaSystemException ex) {
|
||||
String msg = String.format(
|
||||
"Database Error: Unable to fetch info on \"%s\".",
|
||||
ingkey);
|
||||
log.error(msg);
|
||||
JSFUtils.addErrorMessage(msg);
|
||||
|
||||
}
|
||||
Shopcat scat = this.recipeService
|
||||
.findShopcatForIngredientKey(ingkey);
|
||||
ing.setShopCat(scat);
|
||||
}
|
||||
// get ing list size, set ing position, append
|
||||
List<IngredientUI> ingredients = getWrappedIngredients();
|
||||
int lsize = ingredients.size();
|
||||
ing.setPosition(lsize + 1);
|
||||
ingredients.add(new IngredientUI(ing));
|
||||
ingredients.add(new IngredientUI(ing));
|
||||
}
|
||||
|
||||
// ===
|
||||
|
@ -720,7 +713,8 @@ public class RecipeDetailBean implements Serializable {
|
|||
}
|
||||
|
||||
if (recipeService.save(this.getRecipe())) {
|
||||
return "recipeDetails?faces-redirect=true";
|
||||
userSession.setRecipe(null);
|
||||
return "recipeDetails";
|
||||
} else {
|
||||
JSFUtils.addErrorMessage("Save recipe failed");
|
||||
return null;
|
||||
|
@ -977,7 +971,7 @@ public class RecipeDetailBean implements Serializable {
|
|||
}
|
||||
|
||||
public void ajaxUpdateShopcat(IngredientUI item) {
|
||||
log.debug("SHOPCAT2 ");
|
||||
log.warn("SHOPCAT2 ");
|
||||
updateShopcat(item);
|
||||
}
|
||||
|
||||
|
@ -986,7 +980,7 @@ public class RecipeDetailBean implements Serializable {
|
|||
|
||||
public String editDescription() {
|
||||
this.setDetailTab(0);
|
||||
return "detailEdit.xhtml?faces-redirect=true";
|
||||
return "detailEdit?faces-redirect=true";
|
||||
}
|
||||
|
||||
public String editIngredients() {
|
||||
|
|
|
@ -21,11 +21,9 @@ import org.springframework.http.HttpStatus;
|
|||
"com.mousetech.gourmetj.persistence.model" })
|
||||
public class SpringPrimeFacesApplication {
|
||||
|
||||
final String homePage = "/main.jsf?viewExpired=true";
|
||||
final String errorPage = "/error/error.html";
|
||||
final String error404Page = "/error/error404.jsp";
|
||||
final String error400Page = "/error/error400.jsp";
|
||||
final String expiredPage = "/error/viewExpired.xhtml";
|
||||
final String error404Page = "/error/error404.html";
|
||||
final String expiredPage = "/main.xhtml";
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringPrimeFacesApplication.class,
|
||||
|
@ -64,10 +62,8 @@ public class SpringPrimeFacesApplication {
|
|||
registry.addErrorPages(new ErrorPage(
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
errorPage));
|
||||
registry.addErrorPages(new ErrorPage(
|
||||
HttpStatus.BAD_REQUEST,
|
||||
error400Page));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
|||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import jakarta.servlet.DispatcherType;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SpringSecurityConfig {
|
||||
|
@ -69,7 +71,6 @@ public class SpringSecurityConfig {
|
|||
|| (pwLine.charAt(0) == '#')) {
|
||||
continue;
|
||||
}
|
||||
//log.error("======== PWLINE="+pwLine);
|
||||
String[] creds = parseCreds(pwLine);
|
||||
UserDetailsManagerConfigurer<AuthenticationManagerBuilder,
|
||||
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>>
|
||||
|
@ -98,24 +99,21 @@ public class SpringSecurityConfig {
|
|||
return ocreds;
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http)
|
||||
throws Exception {
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
|
||||
http.csrf(AbstractHttpConfigurer::disable)
|
||||
.cors(AbstractHttpConfigurer::disable)
|
||||
.formLogin(login -> login.loginPage("/login.jsf")
|
||||
.permitAll()
|
||||
.failureUrl("/login.jsf?error=true"))
|
||||
.logout(logout -> logout
|
||||
.logoutSuccessUrl("/login.jsf"))
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
.anyRequest().authenticated());
|
||||
|
||||
return http.build();
|
||||
}
|
||||
http
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.cors(AbstractHttpConfigurer::disable)
|
||||
.formLogin(Customizer.withDefaults())
|
||||
.authorizeHttpRequests((authorize)-> authorize
|
||||
.dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces old antMatchers for determining secured URLs.
|
||||
* @return customizer
|
||||
|
@ -123,19 +121,13 @@ public class SpringSecurityConfig {
|
|||
@Bean
|
||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
return (web) -> web.ignoring().requestMatchers(
|
||||
"/jakarta.faces.resource/**",
|
||||
"/javax.faces.resource/**",
|
||||
"/",
|
||||
"/index.html",
|
||||
// "/login",
|
||||
// "/login.jsf", // Leave them for the authenticator!
|
||||
// "/login.xhtml",
|
||||
"/index.jsf",
|
||||
"/index.xhtml",
|
||||
"/main.jsf",
|
||||
"/main.xhtml",
|
||||
"/img/**",
|
||||
"/error/**",
|
||||
"/RES_NOT_FOUND",
|
||||
"/recipeDetails.jsf",
|
||||
"/recipeDetails.xhtml",
|
||||
"/shoppingList.jsf",
|
||||
"/recipePrint.jsf");
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.enterprise.context.SessionScoped;
|
||||
import jakarta.faces.model.SelectItem;
|
||||
import jakarta.inject.Named;
|
||||
|
||||
import org.primefaces.PrimeFaces;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -148,54 +146,6 @@ public class UserSession implements Serializable {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display source. If no "source" and there's a URL,
|
||||
* use that.
|
||||
*
|
||||
* @param ltime
|
||||
* @return
|
||||
*/
|
||||
public String formatSource(Recipe r) {
|
||||
String s = r.getSource();
|
||||
if ( s != null && ! s.isBlank()) {
|
||||
return s;
|
||||
}
|
||||
s = r.getLink();
|
||||
return urlToSource(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a URL and strip it of everything but
|
||||
* the base domain name (including ".com")
|
||||
* @param s URL string.
|
||||
* @return Processed domain name.
|
||||
* TestedBy: UserSessionTest
|
||||
*/
|
||||
String urlToSource(String s) {
|
||||
if ( s == null || s.isBlank()) {
|
||||
return ""; // no source, no URL
|
||||
}
|
||||
try {
|
||||
if ( ! s.startsWith("http")) {
|
||||
s = "http://" + s; // Convert to absolute URI
|
||||
}
|
||||
URL u = new URI(s).toURL();
|
||||
String s1 = u.getHost();
|
||||
if ( s1.startsWith("www.")) {
|
||||
s1 = s1.substring("www.".length());
|
||||
}
|
||||
if ( s1.endsWith(".com")) {
|
||||
// mousetech.com
|
||||
s1 = s1.substring(0, s1.length() - ".com".length());
|
||||
}
|
||||
return s1;
|
||||
} catch (MalformedURLException e) {
|
||||
return s;
|
||||
} catch (URISyntaxException e) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Deprecated Using TimeConverter.
|
||||
*/
|
||||
|
|
|
@ -11,7 +11,7 @@ public class WelcomePageRedirect implements WebMvcConfigurer {
|
|||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/")
|
||||
.setViewName("forward:/index.html");
|
||||
.setViewName("forward:/index.xhtml");
|
||||
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.mousetech.gourmetj.persistence.model.Category;
|
||||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||
|
||||
/**
|
||||
|
@ -23,7 +24,7 @@ import com.mousetech.gourmetj.persistence.model.Recipe;
|
|||
public interface RecipeRepository
|
||||
extends JpaRepository<Recipe, Long> {
|
||||
|
||||
List<Recipe> findByTitleContainingIgnoreCase(String searchText);
|
||||
List<Recipe> findByTitleContaining(String searchText);
|
||||
|
||||
@EntityGraph(value="Recipe.findWorkingSet")
|
||||
public Recipe findDetailsById(Long recipeId);
|
||||
|
@ -31,11 +32,11 @@ public interface RecipeRepository
|
|||
@Query(name = "Recipe.findCusines", nativeQuery = true)
|
||||
List<String> FindCuisinesNative();
|
||||
|
||||
List<Recipe> findByCategories_CategoryContainsIgnoreCase(String searchText);
|
||||
List<Recipe> findByCategories_CategoryContains(String searchText);
|
||||
|
||||
List<Recipe> findByCuisineContainsIgnoreCase(String searchText);
|
||||
List<Recipe> findByCuisineContains(String searchText);
|
||||
|
||||
List<Recipe> findDistinctByIngredientHash_ItemContainsIgnoreCase(
|
||||
List<Recipe> findDistinctByIngredientHash_ItemContains(
|
||||
String searchText);
|
||||
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class RecipeService implements Serializable {
|
|||
|
||||
public List<Recipe> findByTitle(String searchText) {
|
||||
return recipeRepository
|
||||
.findByTitleContainingIgnoreCase(searchText);
|
||||
.findByTitleContaining(searchText);
|
||||
}
|
||||
|
||||
public Recipe findByPrimaryKey(Long recipeId) {
|
||||
|
@ -120,19 +120,14 @@ public class RecipeService implements Serializable {
|
|||
}
|
||||
|
||||
public List<Recipe> findByCategoryLike(String searchText) {
|
||||
return recipeRepository
|
||||
.findByCategories_CategoryContainsIgnoreCase(
|
||||
searchText);
|
||||
return recipeRepository.findByCategories_CategoryContains(searchText);
|
||||
}
|
||||
|
||||
public List<Recipe> findByCuisineLike(String searchText) {
|
||||
return recipeRepository
|
||||
.findByCuisineContainsIgnoreCase(searchText);
|
||||
return recipeRepository.findByCuisineContains(searchText);
|
||||
}
|
||||
|
||||
public List<Recipe> findByIngredientLike(String searchText) {
|
||||
return recipeRepository
|
||||
.findDistinctByIngredientHash_ItemContainsIgnoreCase(
|
||||
searchText);
|
||||
return recipeRepository.findDistinctByIngredientHash_ItemContains(searchText);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ import java.io.InputStream;
|
|||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import jakarta.servlet.http.Part;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -34,9 +37,6 @@ import com.mousetech.gourmetj.UserSession;
|
|||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
/**
|
||||
* @author timh
|
||||
* @since Nov 26, 2021
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<navigation-case>
|
||||
<description>Go Home</description>
|
||||
<from-outcome>home</from-outcome>
|
||||
<to-view-id>/main.xhtml?faces-redirect=true</to-view-id>
|
||||
<to-view-id>/main</to-view-id>
|
||||
<redirect />
|
||||
</navigation-case>
|
||||
</navigation-rule>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</h:head>
|
||||
<h:body>
|
||||
<h1>
|
||||
<ui:insert name="title">Gourmet Recipe Manager (web version #{appBean.appVersion})</ui:insert>
|
||||
<ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert>
|
||||
</h1>
|
||||
<p:ajaxStatus onerror="PF('opError').show()"/>
|
||||
<ui:insert name="content">
|
||||
|
@ -28,11 +28,11 @@
|
|||
</ui:insert>
|
||||
<!-- -->
|
||||
<div id="footer">
|
||||
<h:outputText value="Version #{appBean.appVersion}"/><br/>
|
||||
(C) 2021, 2024 Tim Holloway, Licensed under
|
||||
the Common Development and Distribution License (CDDL).
|
||||
(C) 2021, 2024 Tim Holloway, Licensed under the <a
|
||||
href="http://www.apache.org/licenses/LICENSE-2.0"
|
||||
>Apache License, Version 2.0</a>.
|
||||
<p>Based on Gourmet Recipe Manager by T.
|
||||
Hinkle.</p>
|
||||
Hinkle</p>
|
||||
</div>
|
||||
<!-- -->
|
||||
</h:body>
|
||||
|
|
|
@ -1,163 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<ui:composition
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:c="http://xmlns.jcp.org/jstl"
|
||||
>
|
||||
<!-- Overview tab body -->
|
||||
<p:panelGrid columns="2"
|
||||
columnClasses="deDescl, deDescr"
|
||||
>
|
||||
<f:facet name="header">Description</f:facet>
|
||||
<p:row>
|
||||
<p:column styleClass="deDescl">
|
||||
<p:outputLabel value="Title" />
|
||||
</p:column>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
<p:inputText id="rtitle" size="45"
|
||||
required="true"
|
||||
placeholder="A recipe title is required."
|
||||
value="#{recipeDetailBean.recipe.title}"
|
||||
>
|
||||
<p:focus />
|
||||
<f:ajax execute="rtitle"
|
||||
render="editorPanel" />
|
||||
</p:inputText>
|
||||
</p:column>
|
||||
</p:row>
|
||||
|
||||
<!-- -->
|
||||
<p:row>
|
||||
<p:column styleClass="deDescl">
|
||||
<p:outputLabel value="Category" />
|
||||
</p:column>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
<p:inputText id="rcategory"
|
||||
label="Category"
|
||||
value="#{recipeDetailBean.category}"
|
||||
tip="One or more categories, separated by commas (ex: Entree, Soup)" />
|
||||
<p:commandButton
|
||||
value="<- Suggest"
|
||||
>
|
||||
<f:ajax execute="rcategory bxCat"
|
||||
listener="#{recipeDetailBean.ajaxSuggestCategory}"
|
||||
render="rcategory" />
|
||||
</p:commandButton>
|
||||
<p:selectOneMenu id="bxCat"
|
||||
value="#{recipeDetailBean.catToAdd}"
|
||||
tip="Recipe category suggestions, based on previous selections"
|
||||
>
|
||||
<f:selectItems
|
||||
value="#{recipeDetailBean.suggestCategory}" />
|
||||
</p:selectOneMenu>
|
||||
</p:column>
|
||||
</p:row>
|
||||
|
||||
<!-- -->
|
||||
<p:row>
|
||||
<p:column styleClass="deDescl">
|
||||
<p:outputLabel value="Cuisine" />
|
||||
</p:column>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
<p:autoComplete id="rcuisine"
|
||||
value="#{recipeDetailBean.recipe.cuisine}"
|
||||
completeMethod="#{recipeDetailBean.cuisineSuggestions}" />
|
||||
</p:column>
|
||||
</p:row>
|
||||
<p:row>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
<p:outputLabel value="Prep Time" />
|
||||
<p:inputText id="rpreptime" max="10"
|
||||
value="#{recipeDetailBean.recipe.preptime}"
|
||||
>
|
||||
<f:converter
|
||||
converterId="com.mousetech.gourmetj.utils.TimeConverter" />
|
||||
</p:inputText>
|
||||
</p:column>
|
||||
</p:row>
|
||||
|
||||
<!-- -->
|
||||
<p:row>
|
||||
<p:column styleClass="deDescl">
|
||||
<p:outputLabel value="Cooking Time" />
|
||||
</p:column>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
|
||||
<p:inputText id="rcooktime" max="10"
|
||||
value="#{recipeDetailBean.recipe.cooktime}"
|
||||
>
|
||||
<f:converter
|
||||
converterId="com.mousetech.gourmetj.utils.TimeConverter" />
|
||||
</p:inputText>
|
||||
</p:column>
|
||||
</p:row>
|
||||
<p:row>
|
||||
<p:column styleClass="deDescl">
|
||||
<p:outputLabel value="Servings" />
|
||||
</p:column>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
<p:inputText id="rserves" max="10"
|
||||
value="#{recipeDetailBean.recipe.servings}" />
|
||||
</p:column>
|
||||
</p:row>
|
||||
<p:row>
|
||||
<p:column styleClass="deDescl">
|
||||
<p:outputLabel value="Rating" />
|
||||
</p:column>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
<p:rating id="rrating" max="10"
|
||||
value="#{recipeDetailBean.recipe.rating}" />
|
||||
</p:column>
|
||||
</p:row>
|
||||
<p:row>
|
||||
<p:column styleClass="deDescl">
|
||||
<p:outputLabel value="Source" />
|
||||
</p:column>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
<p:inputText id="rsource" size="45"
|
||||
value="#{recipeDetailBean.recipe.source}" />
|
||||
</p:column>
|
||||
</p:row>
|
||||
<p:row>
|
||||
<p:column styleClass="deDescl">
|
||||
<p:outputLabel value="URL" />
|
||||
</p:column>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
<p:inputText id="rurl" size="45"
|
||||
value="#{recipeDetailBean.recipe.link}" />
|
||||
</p:column>
|
||||
</p:row>
|
||||
<p:row>
|
||||
<p:column styleClass="deDescl" colspan="3">
|
||||
<p:outputLabel value="Description" />
|
||||
</p:column>
|
||||
<p:column styleClass="deDescr" colspan="3">
|
||||
<p:inputTextarea id="description"
|
||||
rows="10" cols="45"
|
||||
value="#{recipeDetailBean.recipe.description}" />
|
||||
</p:column>
|
||||
</p:row>
|
||||
</p:panelGrid>
|
||||
<p:panelGrid id="ppGrid" columns="2">
|
||||
<p:panel id="picPanel">
|
||||
<img id="bigPix"
|
||||
src="/img/picture/?dt=#{recipeDetailBean.currentTime}" />
|
||||
</p:panel>
|
||||
<p:panelGrid id="picButtonPanel" columns="1">
|
||||
<p:fileUpload id="ctlUpload"
|
||||
label="Upload Image"
|
||||
listener="#{recipeDetailBean.ajaxUploadImage}"
|
||||
global="true" mode="advanced" multiple="false"
|
||||
update="picPanel" auto="true" sizeLimit="1000000"
|
||||
allowTypes="/(\.|\/)(gif|jpe?g|png|webp)$/" />
|
||||
<p:commandButton id="ctlDelImg"
|
||||
value="Delete Image"
|
||||
action="#{recipeDetailBean.ajaxDeleteImage}"
|
||||
update="picPanel" immediate="true" />
|
||||
</p:panelGrid>
|
||||
</p:panelGrid>
|
||||
</ui:composition>
|
|
@ -42,10 +42,6 @@
|
|||
font-weight: bold;
|
||||
background-color: green;
|
||||
}
|
||||
.noBorders .noBorders tr, .noBorders td {
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
</style>
|
||||
<h:messages id="messages" />
|
||||
<p:panel id="editorPanel"
|
||||
|
@ -59,8 +55,129 @@
|
|||
<p:tab id="overviewTab"
|
||||
title="Description"
|
||||
>
|
||||
<ui:include
|
||||
src="/WEB-INF/layout/panels/editRecipe/overview.xhtml"/>
|
||||
<p:panelGrid columns="2"
|
||||
columnClasses="deDescl, deDescr"
|
||||
>
|
||||
<f:facet name="header">Description</f:facet>
|
||||
<p:outputLabel for="@next"
|
||||
value="Title"
|
||||
/>
|
||||
<p:inputText id="rtitle"
|
||||
size="45" required="true"
|
||||
focus="true"
|
||||
placeholder="A recipe title is required."
|
||||
value="#{recipeDetailBean.recipe.title}"
|
||||
>
|
||||
<f:ajax execute="rtitle"
|
||||
render="editorPanel"
|
||||
/>
|
||||
</p:inputText>
|
||||
<p:outputLabel for="@next"
|
||||
value="Category"
|
||||
/>
|
||||
<p:inputText id="rcategory"
|
||||
label="Category"
|
||||
value="#{recipeDetailBean.category}"
|
||||
tip="One or more categories, separated by commas (ex: Entree, Soup)"
|
||||
/>
|
||||
<p:commandButton
|
||||
value="<- Suggest"
|
||||
>
|
||||
<f:ajax
|
||||
execute="rcategory bxCat"
|
||||
listener="#{recipeDetailBean.ajaxSuggestCategory}"
|
||||
render="rcategory"
|
||||
/>
|
||||
</p:commandButton>
|
||||
<p:selectOneMenu id="bxCat"
|
||||
value="#{recipeDetailBean.catToAdd}"
|
||||
tip="Recipe category suggestions, based on previous selections"
|
||||
>
|
||||
<f:selectItems
|
||||
value="#{recipeDetailBean.suggestCategory}"
|
||||
/>
|
||||
</p:selectOneMenu>
|
||||
<p:outputLabel for="@next"
|
||||
value="Cuisine"
|
||||
/>
|
||||
<p:autoComplete id="rcuisine"
|
||||
value="#{recipeDetailBean.recipe.cuisine}"
|
||||
completeMethod="#{recipeDetailBean.cuisineSuggestions}"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Prep Time"
|
||||
/>
|
||||
<p:inputText id="rpreptime"
|
||||
max="10"
|
||||
value="#{recipeDetailBean.recipe.preptime}"
|
||||
>
|
||||
<f:converter
|
||||
converterId="com.mousetech.gourmetj.utils.TimeConverter"
|
||||
/>
|
||||
</p:inputText>
|
||||
<p:outputLabel for="@next"
|
||||
value="Cooking Time"
|
||||
/>
|
||||
<p:inputText id="rcooktime"
|
||||
max="10"
|
||||
value="#{recipeDetailBean.recipe.cooktime}"
|
||||
>
|
||||
<f:converter
|
||||
converterId="com.mousetech.gourmetj.utils.TimeConverter"
|
||||
/>
|
||||
</p:inputText>
|
||||
<p:outputLabel for="@next"
|
||||
value="Rating"
|
||||
/>
|
||||
<p:rating id="rrating" max="10"
|
||||
value="#{recipeDetailBean.recipe.rating}"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Source"
|
||||
/>
|
||||
<p:inputText id="rsource"
|
||||
size="45"
|
||||
value="#{recipeDetailBean.recipe.source}"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="URL"
|
||||
/>
|
||||
<p:inputText id="rurl" size="45"
|
||||
value="#{recipeDetailBean.recipe.link}"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Description"
|
||||
/>
|
||||
<p:inputTextarea id="description"
|
||||
rows="10" cols="45"
|
||||
value="#{recipeDetailBean.recipe.description}"
|
||||
/>
|
||||
</p:panelGrid>
|
||||
<p:panel id="picPanel">
|
||||
<img id="bigPix"
|
||||
src="/img/picture/?dt=#{recipeDetailBean.currentTime}"
|
||||
/>
|
||||
</p:panel>
|
||||
<p:panelGrid id="picButtonPanel"
|
||||
columns="2"
|
||||
>
|
||||
<p:fileUpload id="ctlUpload"
|
||||
label="Upload Image"
|
||||
listener="#{recipeDetailBean.ajaxUploadImage}"
|
||||
global="true" mode="advanced"
|
||||
multiple="false"
|
||||
update=":messages picPanel"
|
||||
auto="true"
|
||||
sizeLimit="1000000"
|
||||
allowTypes="/(\.|\/)(gif|jpe?g|png|webp)$/"
|
||||
/>
|
||||
<p:commandButton id="ctlDelImg"
|
||||
value="Delete Image"
|
||||
action="#{recipeDetailBean.ajaxDeleteImage}"
|
||||
update="picPanel"
|
||||
immediate="true"
|
||||
/>
|
||||
</p:panelGrid>
|
||||
</p:tab>
|
||||
<p:tab id="ingredientsTab"
|
||||
title="Ingredients"
|
||||
|
@ -245,8 +362,8 @@
|
|||
id="ctlAddIng"
|
||||
value="+ Add"
|
||||
onclick="ingButton(); return false;"
|
||||
update=":growl"
|
||||
/>
|
||||
>
|
||||
</p:commandButton>
|
||||
</h:panelGroup>
|
||||
</p:panel>
|
||||
</p:tab>
|
||||
|
@ -260,9 +377,7 @@
|
|||
rows="30" cols="120"
|
||||
escape="false"
|
||||
value="#{recipeDetailBean.recipe.instructions}"
|
||||
>
|
||||
<p:focus />
|
||||
</h:inputTextarea>
|
||||
/>
|
||||
</div>
|
||||
</p:panel>
|
||||
</p:tab>
|
||||
|
@ -272,24 +387,18 @@
|
|||
rows="30" cols="120"
|
||||
escape="false"
|
||||
value="#{recipeDetailBean.recipe.modifications}"
|
||||
>
|
||||
<p:focus/>
|
||||
</h:inputTextarea>
|
||||
/>
|
||||
</p:panel>
|
||||
</p:tab>
|
||||
</p:tabView>
|
||||
<p:commandButton id="doSave" value="Save"
|
||||
icon="ui-icon-pencil" ajax="false"
|
||||
disabled="{not recipeDetailBean.dirty}"
|
||||
action="#{recipeDetailBean.doSave}"
|
||||
/>
|
||||
<p:commandButton id="doSave" value="Save" icon="ui-icon-pencil" ajax="false" disabled="{not recipeDetailBean.dirty}" action="#{recipeDetailBean.doSave}" />
|
||||
<p:commandButton id="doCancel" value="Cancel"
|
||||
ajax="false" immediate="true"
|
||||
action="recipeDetails.jsf"
|
||||
/>
|
||||
<p:commandButton id="doHome" value="Home"
|
||||
icon="ui-icon-home" ajax="false"
|
||||
immediate="true" action="home"
|
||||
immediate="true" action="main.jsf"
|
||||
/>
|
||||
</h:form>
|
||||
</p:panel>
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<%@ page language="java" contentType="text/html; charset=US-ASCII"
|
||||
pageEncoding="US-ASCII" isErrorPage="true"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
|
||||
<title>Error 400 Page - Bad request</title>
|
||||
</head>
|
||||
<body>
|
||||
<font color="red">Error: exception.getMessage() </font><br>
|
||||
</body>
|
||||
</html>
|
12
src/main/resources/META-INF/resources/error/error404.html
Normal file
12
src/main/resources/META-INF/resources/error/error404.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<html>
|
||||
<head>
|
||||
<title>ERROR - Page Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Page Not Found</h1>
|
||||
<p>This URL is invalid.</p>
|
||||
<p><a href="/main.jsf">Return to Main Page</a></p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,12 +0,0 @@
|
|||
<%@ page language="java" contentType="text/html; charset=US-ASCII"
|
||||
pageEncoding="US-ASCII" isErrorPage="true"%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>ERROR - Page Not Found</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Page Not Found</h1>
|
||||
<p><a href="/main.jsf">Return to Main Page</a></p>
|
||||
</body>
|
||||
</html>
|
14
src/main/resources/META-INF/resources/error/viewExpired.html
Normal file
14
src/main/resources/META-INF/resources/error/viewExpired.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>ERROR - Page Expired</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Page Expired</h1>
|
||||
<p>The page state could not be restored because it was
|
||||
left idle too long.</p>
|
||||
<p>
|
||||
<a href="/main.jsf">Return to Main Page</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Gourmet Recipe Manager</title><head>
|
||||
<body>
|
||||
<h1>Gourmet Recipe Manager</h1>
|
||||
<p>This is an implementation of Thomas Hinkle's
|
||||
Gourmet Recipe Manager, originally a desktop
|
||||
application but now available as a Java Web
|
||||
application.</p>
|
||||
<p><a href="main.jsf">
|
||||
Go to Main Page</a></p>
|
||||
<hr/>
|
||||
<p>Copyright © 2021, 2024 Tim Holloway. All Rights Reserved.
|
||||
<p>This is an open-source application under the
|
||||
Common Development and Distribution License (CDDL).
|
||||
</p>
|
||||
<body>
|
||||
</html>
|
26
src/main/resources/META-INF/resources/index.xhtml
Normal file
26
src/main/resources/META-INF/resources/index.xhtml
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0"?>
|
||||
<ui:composition template="/WEB-INF/layout/layout.xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:c="http://xmlns.jcp.org/jstl"
|
||||
>
|
||||
<!-- Print Recipe -->
|
||||
<ui:define name="title">Gourmet Recipe Manager</ui:define>
|
||||
<ui:define name="content">
|
||||
<h:form id="printForm">
|
||||
<h:messages />
|
||||
<p>This is an implementation of Thomas Hinkle's
|
||||
Gourmet Recipe Manager, originally a desktop
|
||||
application but now available as a Java Web
|
||||
application.</p>
|
||||
<h:outputLink
|
||||
value="/main.jsf"
|
||||
>Go to Main Page</h:outputLink>
|
||||
</h:form>
|
||||
<div style="height: 20px">
|
||||
<h:outputText value="" />
|
||||
</div>
|
||||
</ui:define>
|
||||
</ui:composition>
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:h="http://java.sun.com/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:p="http://primefaces.org/ui"
|
||||
xmlns:pe="http://primefaces.org/ui/extensions"
|
||||
>
|
||||
<h:head>
|
||||
<title>Login</title>
|
||||
</h:head>
|
||||
<h:body>
|
||||
<h:form prependId="false" style="width:100%">
|
||||
<p:panelGrid columns="3" style="width:100%"
|
||||
styleClass="ui-fluid center ui-noborder"
|
||||
>
|
||||
<h:outputText style="width:33%;" value=" " />
|
||||
<p:panelGrid columns="1" id="grid1">
|
||||
<h2>Please login</h2>
|
||||
<p:outputLabel value="Login failed!"
|
||||
styleClass="red"
|
||||
rendered="${!empty param['error']}"
|
||||
/>
|
||||
<p:outputLabel for="username">User ID</p:outputLabel>
|
||||
<p:inputText id="username"
|
||||
placeholder="User name"
|
||||
/>
|
||||
<p:outputLabel for="password">Password</p:outputLabel>
|
||||
<p:password id="password" placeholder="Password" />
|
||||
<p:commandButton value="Login" ajax="false" />
|
||||
</p:panelGrid>
|
||||
<h:outputText style="width:33%;" value=" " />
|
||||
</p:panelGrid>
|
||||
</h:form>
|
||||
</h:body>
|
||||
</html>
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html >
|
||||
<ui:composition template="/WEB-INF/layout/layout.xhtml"
|
||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
|
@ -11,22 +10,25 @@
|
|||
<h:messages />
|
||||
<h:form id="form1">
|
||||
<div>
|
||||
<p:focus />
|
||||
<p:autoComplete id="searchFor" size="45"
|
||||
placeholder="Recipe title/cuisine/category, etc.)"
|
||||
value="#{adminMainBean.searchText}"
|
||||
completeMethod="#{adminMainBean.searchSuggestionList}"
|
||||
onfocus="jQuery('#form1\\:searchFor_input').select();"
|
||||
>
|
||||
<f:ajax event="change" execute="@this"
|
||||
render="form2:table1"
|
||||
listener="#{adminMainBean.ajaxUpdateList}"
|
||||
/>
|
||||
</p:autoComplete>
|
||||
<p:outputLabel for="@next" value=" In " />
|
||||
<p:defaultCommand target="find" />
|
||||
<p:commandButton id="find" value="Find"
|
||||
icon="ui-icon-search"
|
||||
action="#{adminMainBean.doFind}"
|
||||
update=":form2:table1"
|
||||
/>
|
||||
<p:outputLabel for="@next" value="Search for " />
|
||||
<p:selectOneMenu id="ctlSearchType"
|
||||
value="#{cookieBean.searchType}"
|
||||
onchange="jQuery('#form1\\:searchFor_input').trigger('focus');"
|
||||
>
|
||||
<f:selectItems
|
||||
value="#{appBean.searchTypeList}"
|
||||
|
@ -35,12 +37,6 @@
|
|||
listener="#{adminMainBean.resetSuggestions}"
|
||||
/>
|
||||
</p:selectOneMenu>
|
||||
<p:defaultCommand target="find" />
|
||||
<p:commandButton id="find" value="Find"
|
||||
icon="ui-icon-search"
|
||||
action="#{adminMainBean.doFind}"
|
||||
update=":form2:table1"
|
||||
/>
|
||||
<p:commandButton id="ctlClear" value="Clear"
|
||||
icon="ui-icon-close"
|
||||
update="@form:searchFor :form2:table1"
|
||||
|
@ -57,10 +53,7 @@
|
|||
value="#{cookieBean.displayListSize}"
|
||||
/>
|
||||
<h:outputLabel for="slistSize"
|
||||
value=" Recipes in Shopping List "
|
||||
/>
|
||||
<p:commandButton id="logout" value="Logout"
|
||||
action="#{adminMainBean.doLogout}"
|
||||
value=" Recipes in Shopping List"
|
||||
/>
|
||||
</div>
|
||||
</h:form>
|
||||
|
@ -98,7 +91,7 @@
|
|||
/>
|
||||
</p:column>
|
||||
<p:column headerText="Source">
|
||||
<h:outputText value="#{userSession.formatSource(row)}" />
|
||||
<h:outputText value="#{row.source}" />
|
||||
</p:column>
|
||||
<p:column headerText="Prep Time">
|
||||
<h:outputText value="#{row.preptime}"
|
||||
|
|
|
@ -35,98 +35,71 @@
|
|||
/>
|
||||
</f:facet>
|
||||
<p:panel id="leftCol" style="width: auto;">
|
||||
<p:panelGrid>
|
||||
<p:row>
|
||||
<p:column style="width: 136px;">
|
||||
<p:graphicImage id="bigpix"
|
||||
style="width: 132px;"
|
||||
value="/img/picture/#{recipeDetailBean.recipe.id}"
|
||||
<p:panelGrid columns="2">
|
||||
<p:panelGrid id="picButtons" columns="2">
|
||||
<img id="bigpix"
|
||||
style="width: 132px;"
|
||||
src="/img/picture/#{recipeDetailBean.recipe.id}"
|
||||
/>
|
||||
<p:panelGrid id="pnlButtons"
|
||||
columns="2" style="width: 220px;"
|
||||
>
|
||||
<!-- TODO: ask if we should save -->
|
||||
<p:commandButton value="Back"
|
||||
ajax="false"
|
||||
icon="ui-icon-arrowthick-1-w"
|
||||
action="home"
|
||||
immediate="true"
|
||||
/>
|
||||
</p:column>
|
||||
<p:column style="width: 18em;">
|
||||
<p:panelGrid id="pnlButtons"
|
||||
columns="2"
|
||||
>
|
||||
<!-- TODO: ask if we should save -->
|
||||
<p:commandButton value="Home"
|
||||
ajax="false"
|
||||
icon="ui-icon-home"
|
||||
action="home"
|
||||
immediate="true"
|
||||
/>
|
||||
<p:commandButton ajax="false"
|
||||
value="Print"
|
||||
icon="ui-icon-print"
|
||||
action="/recipePrint.jsf?faces-redirect=true"
|
||||
styleClass="ui-button-print"
|
||||
immediate="true"
|
||||
/>
|
||||
<p:commandButton id="ctlShop"
|
||||
icon="ui-icon-cart"
|
||||
value="Shop"
|
||||
immediate="true"
|
||||
styleClass="#{recipeDetailBean.shop ? 'greenButton' : null}"
|
||||
action="#{recipeDetailBean.doShop}"
|
||||
update="ctlShop"
|
||||
/>
|
||||
<p:commandButton
|
||||
icon="ui-icon-wrench"
|
||||
value="Edit"
|
||||
action="#{recipeDetailBean.editDescription}"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Categories:"
|
||||
/>
|
||||
<h:outputText
|
||||
label="Category: "
|
||||
value="#{userSession.formatCategories(recipeDetailBean.recipe)}"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Cuisine:"
|
||||
/>
|
||||
<h:outputText
|
||||
label="Cuisine: "
|
||||
value="#{recipeDetailBean.recipe.cuisine}"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Prep Time:"
|
||||
/>
|
||||
<h:outputText
|
||||
label="Prep Time: "
|
||||
value="#{recipeDetailBean.recipe.preptime}"
|
||||
converter="com.mousetech.gourmetj.utils.TimeConverter"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Cook Time:"
|
||||
/>
|
||||
<h:outputText
|
||||
label="Cook Time: "
|
||||
value="#{recipeDetailBean.recipe.cooktime}"
|
||||
converter="com.mousetech.gourmetj.utils.TimeConverter"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Servings:"
|
||||
/>
|
||||
<h:outputText
|
||||
value="#{recipeDetailBean.recipe.servings}"
|
||||
/>
|
||||
</p:panelGrid>
|
||||
</p:column>
|
||||
<p:column>
|
||||
<p:panel id="sources" style="width: fit-content;">
|
||||
<h:outputText
|
||||
value="#{recipeDetailBean.recipe.source}"
|
||||
/>
|
||||
<br />
|
||||
<h:outputLink
|
||||
value="#{recipeDetailBean.recipe.link}"
|
||||
rendered="#{not empty recipeDetailBean.recipe.link}"
|
||||
>
|
||||
#{recipeDetailBean.recipe.link}
|
||||
</h:outputLink>
|
||||
</p:panel>
|
||||
</p:column>
|
||||
</p:row>
|
||||
<p:commandButton ajax="false"
|
||||
value="Print"
|
||||
icon="ui-icon-print"
|
||||
action="recipePrint.jsf"
|
||||
styleClass="ui-button-print"
|
||||
immediate="true"
|
||||
/>
|
||||
<p:commandButton id="ctlShop"
|
||||
icon="ui-icon-cart"
|
||||
value="Shop" immediate="true"
|
||||
styleClass="#{recipeDetailBean.shop ? 'greenButton' : null}"
|
||||
action="#{recipeDetailBean.doShop}"
|
||||
update="ctlShop"
|
||||
/>
|
||||
<h:outputText value="" />
|
||||
<p:outputLabel for="@next"
|
||||
value="Categories:"
|
||||
/>
|
||||
<h:outputText label="Category: "
|
||||
value="#{userSession.formatCategories(recipeDetailBean.recipe)}"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Cuisine:"
|
||||
/>
|
||||
<h:outputText label="Cuisine: "
|
||||
value="#{recipeDetailBean.recipe.cuisine}"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Prep Time:"
|
||||
/>
|
||||
<h:outputText label="Prep Time: "
|
||||
value="#{recipeDetailBean.recipe.preptime}"
|
||||
converter="com.mousetech.gourmetj.utils.TimeConverter"
|
||||
/>
|
||||
<p:outputLabel for="@next"
|
||||
value="Cook Time:"
|
||||
/>
|
||||
<h:outputText label="Cook Time: "
|
||||
value="#{recipeDetailBean.recipe.cooktime}"
|
||||
converter="com.mousetech.gourmetj.utils.TimeConverter"
|
||||
/>
|
||||
<h:outputText value="" />
|
||||
<p:commandButton
|
||||
icon="ui-icon-wrench"
|
||||
value="Edit"
|
||||
action="#{recipeDetailBean.editDescription}"
|
||||
/>
|
||||
</p:panelGrid>
|
||||
</p:panelGrid>
|
||||
</p:panelGrid>
|
||||
<!-- -->
|
||||
<p:panel id="pnlInstr">
|
||||
|
|
|
@ -9,106 +9,101 @@
|
|||
<!-- Print Recipe -->
|
||||
<ui:define name="title">Gourmet Recipe Manager</ui:define>
|
||||
<ui:define name="content">
|
||||
<h:form id="printForm" style="font-size: 12pt;">
|
||||
<h:form id="printForm">
|
||||
<h:messages />
|
||||
<p:commandButton value="Back" immediate="true"
|
||||
ajax="false" icon="ui-icon-arrowthick-1-w"
|
||||
style="margin-left: 2em" styleClass="noprint"
|
||||
action="recipeDetails.jsf?faces-redirect=true"
|
||||
/>
|
||||
<p:panelGrid id="pTopGrid"
|
||||
columns="2" style="margin-bottom: 5px; border-style: none"
|
||||
<p:panelGrid
|
||||
style="margin-bottom: 5px; border-style: none"
|
||||
>
|
||||
<p:column width="50%">
|
||||
<p:column>
|
||||
<img id="bigpix"
|
||||
SRC="/img/picture/#{recipeDetailBean.recipe.id}"
|
||||
/>
|
||||
<p:panelGrid columns="2" id="pGrid2">
|
||||
<h:outputLabel for="@next"
|
||||
value="Category: "
|
||||
/>
|
||||
<h:outputText
|
||||
value="#{userSession.formatCategories(recipeDetailBean.recipe)}"
|
||||
/>
|
||||
<h:outputLabel for="@next"
|
||||
value="Cuisine: "
|
||||
/>
|
||||
<h:outputText
|
||||
value="#{recipeDetailBean.recipe.cuisine}"
|
||||
/>
|
||||
<h:outputLabel for="@next"
|
||||
value="Prep Time: "
|
||||
/>
|
||||
<h:outputText
|
||||
value="#{userSession.formatTime(recipeDetailBean.recipe.preptime)}"
|
||||
/>
|
||||
<h:outputLabel for="@next"
|
||||
value="Cook Time: "
|
||||
/>
|
||||
<h:outputText label="Cook Time: "
|
||||
value="#{userSession.formatTime(recipeDetailBean.recipe.cooktime)}"
|
||||
/>
|
||||
</p:panelGrid>
|
||||
</p:column>
|
||||
<p:column
|
||||
width="50%" style="vertical-align: middle; text-align: left; border: none"
|
||||
style="vertical-align: middle; text-align: left; border: none"
|
||||
>
|
||||
<p:panelGrid id="ingredientsc" columns="1">
|
||||
<f:facet name="header">
|
||||
<h:outputText styleClass="subtitle"
|
||||
value="Ingredients"
|
||||
/>
|
||||
</f:facet>
|
||||
<p:column style="padding: 0px 4px">
|
||||
<p:dataTable id="ingredients"
|
||||
showDirectLinksArrows="true"
|
||||
value="#{recipeDetailBean.ingredients}"
|
||||
var="ingredient"
|
||||
style="width: 100%; font-size: 12pt;"
|
||||
>
|
||||
<p:column
|
||||
style="text-align: right; width: 2em"
|
||||
>
|
||||
<f:facet name="header">
|
||||
Amt.
|
||||
</f:facet>
|
||||
<h:outputText
|
||||
value="#{ingredient.displayAmount}"
|
||||
/>
|
||||
</p:column>
|
||||
<p:column style="width: 6em">
|
||||
<f:facet name="header">Units</f:facet>
|
||||
<h:outputText
|
||||
value="#{ingredient.unit}"
|
||||
/>
|
||||
</p:column>
|
||||
<p:column>
|
||||
<f:facet name="header">
|
||||
Item
|
||||
</f:facet>
|
||||
<h:outputText
|
||||
value="#{ingredient.item}"
|
||||
/>
|
||||
</p:column>
|
||||
<p:column align="center"
|
||||
style="width: 2em"
|
||||
>
|
||||
<f:facet name="header">
|
||||
Opt.
|
||||
</f:facet>
|
||||
<p:selectBooleanCheckbox
|
||||
readonly="true"
|
||||
value="#{ingredient.optionalCB}"
|
||||
/>
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
</p:column>
|
||||
</p:panelGrid>
|
||||
<p:commandButton value="Back"
|
||||
immediate="true" ajax="false"
|
||||
icon="ui-icon-arrowthick-1-w"
|
||||
style="margin-left: 2em"
|
||||
styleClass="noprint"
|
||||
action="recipeDetails.jsf?faces-redirect=true"
|
||||
/>
|
||||
</p:column>
|
||||
</p:panelGrid>
|
||||
<p:panelGrid columns="2">
|
||||
<h:outputLabel for="@next" value="Category: " />
|
||||
<h:outputText
|
||||
value="#{userSession.formatCategories(recipeDetailBean.recipe)}"
|
||||
/>
|
||||
<h:outputLabel for="@next" value="Cuisine: " />
|
||||
<h:outputText
|
||||
value="#{recipeDetailBean.recipe.cuisine}"
|
||||
/>
|
||||
<h:outputLabel for="@next" value="Prep Time: " />
|
||||
<h:outputText
|
||||
value="#{userSession.formatTime(recipeDetailBean.recipe.preptime)}"
|
||||
/>
|
||||
<h:outputLabel for="@next" value="Cook Time: " />
|
||||
<h:outputText label="Cook Time: "
|
||||
value="#{userSession.formatTime(recipeDetailBean.recipe.cooktime)}"
|
||||
/>
|
||||
</p:panelGrid>
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
<p:panelGrid columns="1" style="width: 100%; font-size: 12pt;">
|
||||
<p:panelGrid id="ingredientsc">
|
||||
<f:facet name="header">
|
||||
<h:outputText styleClass="subtitle"
|
||||
value="Ingredients"
|
||||
/>
|
||||
</f:facet>
|
||||
<p:column style="padding: 0px 4px">
|
||||
<p:dataTable id="ingredients"
|
||||
showDirectLinksArrows="true"
|
||||
value="#{recipeDetailBean.ingredients}"
|
||||
var="ingredient"
|
||||
>
|
||||
<p:column
|
||||
style="text-align: right; width: 2em"
|
||||
>
|
||||
<f:facet name="header">
|
||||
Amt.
|
||||
</f:facet>
|
||||
<h:outputText
|
||||
value="#{ingredient.displayAmount}"
|
||||
/>
|
||||
</p:column>
|
||||
<p:column style="width: 6em">
|
||||
<f:facet name="header">Units</f:facet>
|
||||
<h:outputText
|
||||
value="#{ingredient.unit}"
|
||||
/>
|
||||
</p:column>
|
||||
<p:column>
|
||||
<f:facet name="header">
|
||||
Item
|
||||
</f:facet>
|
||||
<h:outputText
|
||||
value="#{ingredient.item}"
|
||||
/>
|
||||
</p:column>
|
||||
<p:column align="center"
|
||||
style="width: 2em"
|
||||
>
|
||||
<f:facet name="header">
|
||||
Opt.
|
||||
</f:facet>
|
||||
<p:selectBooleanCheckbox
|
||||
readonly="true"
|
||||
value="#{ingredient.optionalCB}"
|
||||
/>
|
||||
</p:column>
|
||||
</p:dataTable>
|
||||
</p:column>
|
||||
</p:panelGrid>
|
||||
<h:outputText
|
||||
value="Recipe ID: #{recipeDetailBean.recipe.id}"
|
||||
/>
|
||||
<p:panelGrid columns="1" style="width: 100%">
|
||||
<f:facet name="header">
|
||||
<h:outputText styleClass="subtitle"
|
||||
value="Instructions"
|
||||
|
@ -119,7 +114,7 @@
|
|||
value="#{recipeDetailBean.instructions}"
|
||||
/>
|
||||
</p:panelGrid>
|
||||
<p:panelGrid columns="1" style="width: 100%; font-size: 12pt"
|
||||
<p:panelGrid columns="1" style="width: 100%"
|
||||
rendered="#{not empty recipeDetailBean.modifications}"
|
||||
>
|
||||
<f:facet name="header">
|
||||
|
@ -131,9 +126,6 @@
|
|||
value="#{recipeDetailBean.modifications}"
|
||||
/>
|
||||
</p:panelGrid>
|
||||
<h:outputText
|
||||
value="Recipe ID: #{recipeDetailBean.recipe.id}"
|
||||
/>
|
||||
</h:form>
|
||||
</ui:define>
|
||||
</ui:composition>
|
|
@ -42,7 +42,7 @@
|
|||
<p:tab id="overviewTab" title="Shopping List">
|
||||
<h:form id="form1">
|
||||
<p:dataTable id="tblRecipes"
|
||||
style="width: 60em"
|
||||
style="width: 40em"
|
||||
value="#{shoppingListBean.recipeList}"
|
||||
var="item"
|
||||
>
|
||||
|
@ -87,7 +87,7 @@
|
|||
>
|
||||
<p:dataTable id="tblShopIngredients"
|
||||
value="#{shoppingListBean.ingredientList}"
|
||||
style="width: 60em;"
|
||||
style="width: 40em;"
|
||||
sortBy="#{item.shopCat}" var="item"
|
||||
>
|
||||
<f:facet name="header">
|
||||
|
|
|
@ -21,16 +21,10 @@ spring:
|
|||
ddl-auto: none
|
||||
database-platform: org.hibernate.dialect.MySQLDialect
|
||||
|
||||
# From Maven POM:
|
||||
appVersion: "@project.version@"
|
||||
|
||||
# Tracking-modes prevent URL rewrite jsessionid on Primecases
|
||||
# resources. Which causes "400" errors on initial main.jsf fetch.
|
||||
server:
|
||||
servlet:
|
||||
session:
|
||||
timeout: '30m'
|
||||
tracking-modes: 'cookie'
|
||||
# Theme here overrides joinfaces theme
|
||||
# context-parameters:
|
||||
# primefaces:
|
||||
|
@ -40,18 +34,7 @@ server:
|
|||
gourmet:
|
||||
password:
|
||||
file: .gourmetpw
|
||||
|
||||
|
||||
joinfaces:
|
||||
primefaces:
|
||||
theme: bluesky
|
||||
faces:
|
||||
project-stage: Production
|
||||
facelets-libraries: /tags/tags.taglib.xml
|
||||
|
||||
#logging:
|
||||
# level:
|
||||
# org.springframework.security: TRACE
|
||||
# org.apache.catalina: TRACE
|
||||
# jakarta.faces: TRACE
|
||||
# com.sun.faces: TRACE
|
||||
# jakarta.servlet: TRACE
|
||||
theme: casablanca
|
||||
|
|
|
@ -1,281 +0,0 @@
|
|||
-- MariaDB dump 10.19 Distrib 10.5.23-MariaDB, for Linux (x86_64)
|
||||
--
|
||||
-- Host: dbase Database: recipes
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 10.3.35-MariaDB
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `categories`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `categories`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `categories` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`recipe_id` int(11) DEFAULT NULL,
|
||||
`category` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `FK_CATEGORY_RECIPE` (`recipe_id`),
|
||||
CONSTRAINT `FK_CATEGORY_RECIPE` FOREIGN KEY (`recipe_id`) REFERENCES `recipe` (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=233 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `convtable`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `convtable`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `convtable` (
|
||||
`id` int(11) NOT NULL,
|
||||
`ckey` varchar(150) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`value` varchar(150) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `crossunitdict`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `crossunitdict`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `crossunitdict` (
|
||||
`id` int(11) NOT NULL,
|
||||
`cukey` varchar(150) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`value` varchar(150) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `density`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `density`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `density` (
|
||||
`id` int(11) NOT NULL,
|
||||
`dkey` varchar(150) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`value` varchar(150) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `info`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `info`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `info` (
|
||||
`version_super` int(11) DEFAULT NULL,
|
||||
`version_major` int(11) DEFAULT NULL,
|
||||
`version_minor` int(11) DEFAULT NULL,
|
||||
`last_access` int(11) DEFAULT NULL,
|
||||
`rowid` int(11) NOT NULL,
|
||||
PRIMARY KEY (`rowid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `ingredients`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `ingredients`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `ingredients` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`recipe_id` int(11) DEFAULT NULL,
|
||||
`refid` int(11) DEFAULT NULL,
|
||||
`unit` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`amount` float DEFAULT NULL,
|
||||
`rangeamount` float DEFAULT NULL,
|
||||
`item` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`ingkey` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`optional` tinyint(1) DEFAULT NULL,
|
||||
`shopoptional` int(11) DEFAULT NULL,
|
||||
`inggroup` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`position` int(11) DEFAULT NULL,
|
||||
`deleted` tinyint(1) DEFAULT NULL,
|
||||
`shopCat_id` bigint(20) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `refid` (`refid`),
|
||||
KEY `FK_INGREDIENT_RECIPE` (`recipe_id`),
|
||||
CONSTRAINT `FK_INGREDIENT_RECIPE` FOREIGN KEY (`recipe_id`) REFERENCES `recipe` (`id`),
|
||||
CONSTRAINT `CONSTRAINT_1` CHECK (`deleted` in (0,1)),
|
||||
CONSTRAINT `CONSTRAINT_2` CHECK (`optional` in (0,1))
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3540 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `keylookup`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `keylookup`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `keylookup` (
|
||||
`id` int(11) NOT NULL,
|
||||
`word` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`item` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`ingkey` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`count` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `pantry`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `pantry`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `pantry` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`ingkey` text COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`pantry` tinyint(1) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `CONSTRAINT_1` CHECK (`pantry` in (0,1))
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `plugin_info`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `plugin_info`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `plugin_info` (
|
||||
`plugin` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`id` int(11) NOT NULL,
|
||||
`version_super` int(11) DEFAULT NULL,
|
||||
`version_major` int(11) DEFAULT NULL,
|
||||
`version_minor` int(11) DEFAULT NULL,
|
||||
`plugin_version` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `recipe`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `recipe`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `recipe` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`title` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`instructions` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`modifications` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`cuisine` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`rating` int(11) DEFAULT NULL,
|
||||
`description` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`source` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`preptime` int(11) DEFAULT NULL,
|
||||
`cooktime` int(11) DEFAULT NULL,
|
||||
`servings` float DEFAULT NULL,
|
||||
`yields` float DEFAULT NULL,
|
||||
`yield_unit` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`image` mediumblob DEFAULT NULL,
|
||||
`thumb` blob DEFAULT NULL,
|
||||
`deleted` tinyint(1) DEFAULT NULL,
|
||||
`recipe_hash` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`ingredient_hash` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`link` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`last_modified` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `CONSTRAINT_1` CHECK (`deleted` in (0,1))
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=535 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `recipe_ingredients`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `recipe_ingredients`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `recipe_ingredients` (
|
||||
`Recipe_id` int(11) NOT NULL,
|
||||
`ingredientHash_id` int(11) NOT NULL,
|
||||
UNIQUE KEY `ingredientHash_id` (`ingredientHash_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `shopcats`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `shopcats`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `shopcats` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`ingkey` text COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`shopcategory` mediumtext COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`position` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=679 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `shopcatsorder`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `shopcatsorder`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `shopcatsorder` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`shopcategory` text COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`position` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `unitdict`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `unitdict`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `unitdict` (
|
||||
`id` int(11) NOT NULL,
|
||||
`ukey` varchar(150) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`value` varchar(150) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2024-02-18 17:34:22
|
|
@ -1,17 +0,0 @@
|
|||
[Unit]
|
||||
Description=Gourmet Recipe Manager
|
||||
Documentation=https://gogs.mousetech.com/gourmetj
|
||||
Requires=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=gourmetj
|
||||
WorkingDirectory=/opt/mousetech/gourmetj
|
||||
ExecStart=/usr/bin/java -jar gourmetj.jar
|
||||
#Restart=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=gourmetj.service
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class UserSessionTest {
|
||||
|
||||
@Test
|
||||
void testformatSource() {
|
||||
UserSession us = new UserSession();
|
||||
|
||||
assertEquals("mousetech",
|
||||
us.urlToSource("www.mousetech.com"));
|
||||
assertEquals("google",
|
||||
us.urlToSource("google.com"));
|
||||
assertEquals("foobar",
|
||||
us.urlToSource("foobar"));
|
||||
assertEquals("",
|
||||
us.urlToSource("\t"));
|
||||
assertEquals("",
|
||||
us.urlToSource(null));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user