Compare commits
No commits in common. "main" and "version2" have entirely different histories.
|
@ -25,6 +25,13 @@
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
|
<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">
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<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
|
JSF View can cause a session to be created, not just Views that
|
||||||
reference View- or SessionScoped backing beans. This is alterable
|
reference View- or SessionScoped backing beans. This is alterable
|
||||||
by setting an option in the faces-config.
|
by setting an option in the faces-config.
|
||||||
force
|
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.
|
|
|
@ -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>
|
<groupId>com.mousetech.gourmet</groupId>
|
||||||
<artifactId>gourmetj</artifactId>
|
<artifactId>gourmetj</artifactId>
|
||||||
<version>2.0.37</version>
|
<version>0.2.0</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>GourmetJ</name>
|
<name>GourmetJ</name>
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>3.2.3</version>
|
<version>3.2.2</version>
|
||||||
<relativePath /> <!-- lookup parent from repository -->
|
<relativePath /> <!-- lookup parent from repository -->
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -84,6 +84,10 @@
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- <dependency>
|
||||||
|
<groupId>javax.enterprise</groupId>
|
||||||
|
<artifactId>cdi-api</artifactId>
|
||||||
|
</dependency>-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-security</artifactId>
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
@ -92,6 +96,14 @@
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>jakarta.validation</groupId>
|
<groupId>jakarta.validation</groupId>
|
||||||
<artifactId>jakarta.validation-api</artifactId>
|
<artifactId>jakarta.validation-api</artifactId>
|
||||||
|
@ -117,11 +129,21 @@
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- <dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jstl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.tomcat.embed</groupId>
|
<groupId>org.apache.tomcat.embed</groupId>
|
||||||
<artifactId>tomcat-embed-jasper</artifactId>
|
<artifactId>tomcat-embed-jasper</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- <dependency>
|
||||||
|
<groupId>org.glassfish</groupId>
|
||||||
|
<artifactId>jakarta.el</artifactId>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
https://mvnrepository.com/artifact/com.twelvemonkeys.imageio/imageio-core -->
|
https://mvnrepository.com/artifact/com.twelvemonkeys.imageio/imageio-core -->
|
||||||
|
@ -136,6 +158,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<!--
|
<!--
|
||||||
https://mvnrepository.com/artifact/com.twelvemonkeys.imageio/imageio-webp -->
|
https://mvnrepository.com/artifact/com.twelvemonkeys.imageio/imageio-webp -->
|
||||||
|
<!-- In Core??? -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio-webp</artifactId>
|
<artifactId>imageio-webp</artifactId>
|
||||||
|
@ -143,6 +166,7 @@
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- Needed only if you deploy ImageIO plugins as part of a web app.
|
<!-- Needed only if you deploy ImageIO plugins as part of a web app.
|
||||||
|
Make sure you add the IIOProviderContextListener to your web.xml.
|
||||||
-->
|
-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||||
|
@ -160,6 +184,12 @@
|
||||||
<version>8.0.30</version>
|
<version>8.0.30</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- <dependency>
|
||||||
|
<groupId>org.glassfish</groupId>
|
||||||
|
<artifactId>jakarta.faces</artifactId>
|
||||||
|
<version>${com.sun.faces.version}</version>
|
||||||
|
</dependency>-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter</artifactId>
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
@ -172,92 +202,11 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<pluginManagement>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<version>3.13.0</version>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</pluginManagement>
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
</plugin>
|
</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>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</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;
|
package com.mousetech.gourmetj;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -11,22 +12,27 @@ import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||||
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import jakarta.enterprise.context.RequestScoped;
|
||||||
import jakarta.faces.event.AjaxBehaviorEvent;
|
import jakarta.faces.event.AjaxBehaviorEvent;
|
||||||
import jakarta.faces.model.DataModel;
|
import jakarta.faces.model.DataModel;
|
||||||
import jakarta.faces.model.ListDataModel;
|
import jakarta.faces.model.ListDataModel;
|
||||||
import jakarta.faces.view.ViewScoped;
|
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.inject.Named;
|
import jakarta.inject.Named;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main control panel backing bean.
|
* 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
|
* @author timh
|
||||||
* @since Jun 28, 2012
|
* @since Jun 28, 2012
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Named
|
@Named
|
||||||
@ViewScoped
|
@RequestScoped
|
||||||
public class AdminMainBean implements Serializable {
|
public class AdminMainBean implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,6 +49,9 @@ 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.
|
||||||
*/
|
*/
|
||||||
|
@ -66,6 +75,13 @@ public class AdminMainBean implements Serializable {
|
||||||
return cookieBean;
|
return cookieBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cookieBean the cookieBean to set
|
||||||
|
*/
|
||||||
|
public void setCookieBean(CookieBean cookieBean) {
|
||||||
|
this.cookieBean = cookieBean;
|
||||||
|
}
|
||||||
|
|
||||||
// **
|
// **
|
||||||
@Inject
|
@Inject
|
||||||
private UserSession userSession;
|
private UserSession userSession;
|
||||||
|
@ -202,6 +218,13 @@ public class AdminMainBean implements Serializable {
|
||||||
}
|
}
|
||||||
searchText = searchText.trim();
|
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();
|
RecipeSearchType st = searchtypeEnum();
|
||||||
switch (st) {
|
switch (st) {
|
||||||
case rst_BY_NAME:
|
case rst_BY_NAME:
|
||||||
|
@ -239,7 +262,6 @@ public class AdminMainBean implements Serializable {
|
||||||
this.userSession.setLastEdit(null);
|
this.userSession.setLastEdit(null);
|
||||||
// Construct a blank recipe to be created.
|
// Construct a blank recipe to be created.
|
||||||
this.userSession.setRecipe(new Recipe());
|
this.userSession.setRecipe(new Recipe());
|
||||||
this.userSession.setDetailTab(0); // title tab
|
|
||||||
return "detailEdit?faces-redirect=true";
|
return "detailEdit?faces-redirect=true";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,9 +289,4 @@ public class AdminMainBean implements Serializable {
|
||||||
// items.
|
// items.
|
||||||
return "recipeDetails?faces-redirect=true";
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.faces.model.SelectItem;
|
import jakarta.faces.model.SelectItem;
|
||||||
import jakarta.inject.Named;
|
import jakarta.inject.Named;
|
||||||
|
@ -49,12 +47,5 @@ public class AppBean {
|
||||||
new SelectItem(RecipeSearchType.rst_BY_INGREDIENT.ordinal(),
|
new SelectItem(RecipeSearchType.rst_BY_INGREDIENT.ordinal(),
|
||||||
"Ingredient"));
|
"Ingredient"));
|
||||||
return list;
|
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 org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.faces.view.ViewScoped;
|
import jakarta.enterprise.context.RequestScoped;
|
||||||
import jakarta.inject.Named;
|
import jakarta.inject.Named;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,7 +28,7 @@ import jakarta.inject.Named;
|
||||||
* @since Jan 31, 2024
|
* @since Jan 31, 2024
|
||||||
*/
|
*/
|
||||||
@Named
|
@Named
|
||||||
@ViewScoped
|
@RequestScoped
|
||||||
public class CookieBean {
|
public class CookieBean {
|
||||||
|
|
||||||
private static final String KEY_DISPLAY_ROWS = "displayRows";
|
private static final String KEY_DISPLAY_ROWS = "displayRows";
|
||||||
|
@ -44,15 +44,10 @@ public class CookieBean {
|
||||||
|
|
||||||
private Map<String, String> cookieMap;
|
private Map<String, String> cookieMap;
|
||||||
|
|
||||||
final Map<String, Object> properties = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*/
|
*/
|
||||||
public CookieBean() {
|
public CookieBean() {
|
||||||
properties.put("maxAge", 31536000);
|
|
||||||
properties.put("path", "/");
|
|
||||||
properties.put("SameSite", "Strict");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
|
@ -68,6 +63,11 @@ public class CookieBean {
|
||||||
*/
|
*/
|
||||||
public void saveCookies()
|
public void saveCookies()
|
||||||
throws UnsupportedEncodingException {
|
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()) {
|
for (Entry<String, String> e : cookieMap.entrySet()) {
|
||||||
JSFUtils.outputCookie(e.getKey(), e.getValue(),
|
JSFUtils.outputCookie(e.getKey(), e.getValue(),
|
||||||
properties);
|
properties);
|
||||||
|
@ -86,13 +86,6 @@ public class CookieBean {
|
||||||
|
|
||||||
public void setCookieValue(String name, String value) {
|
public void setCookieValue(String name, String value) {
|
||||||
cookieMap.put(name, 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) {
|
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) {
|
public void setSearchType(Integer value) {
|
||||||
String st = String.valueOf(value);
|
cookieMap.put(KEY_SEARCH_TYPE, String.valueOf(value));
|
||||||
setCookieValue(KEY_SEARCH_TYPE, st);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// **
|
// **
|
||||||
public Integer getDisplayListSize() {
|
public Integer getDisplayListSize() {
|
||||||
if (!cookieMap.containsKey(KEY_DISPLAY_ROWS)) {
|
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);
|
String st = cookieMap.get(KEY_DISPLAY_ROWS);
|
||||||
return Integer.valueOf(String.valueOf(st));
|
return Integer.valueOf(String.valueOf(st));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDisplayListSize(Integer value) {
|
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)
|
* IdleMonitor backing methods (session/View timeout)
|
||||||
* Todo: move to a more general location. Currently
|
|
||||||
* only used by view editor, not Main!
|
|
||||||
*/
|
*/
|
||||||
public void sessionIdleListener() {
|
public void sessionIdleListener() {
|
||||||
log.info("Session Idle Listener fired.");
|
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.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -13,7 +12,6 @@ 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 jakarta.servlet.http.Cookie;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -128,52 +126,29 @@ public class JSFUtils {
|
||||||
/**
|
/**
|
||||||
* Get cookie values.
|
* Get cookie values.
|
||||||
*/
|
*/
|
||||||
public static Map<String, String> getCookies() {
|
public static Map<String, String> getCookies(){
|
||||||
Map<String, Object> m0 =
|
Map<String, Object> m0 = getExternalContext().getRequestCookieMap();
|
||||||
getExternalContext().getRequestCookieMap();
|
Map<String, String>m1 = new HashMap<String, String>();
|
||||||
Map<String, String> m1 = new HashMap<String, String>();
|
m1 = m0.entrySet()
|
||||||
m1 = m0.entrySet().stream()
|
.stream()
|
||||||
.collect(Collectors.toMap(e -> e.getKey(),
|
.collect(Collectors.toMap(
|
||||||
e -> ((Cookie) e.getValue()).getValue()));
|
e -> e.getKey(),
|
||||||
|
e -> ((Cookie)e.getValue()).getValue()));
|
||||||
return m1;
|
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.
|
* Set a cookie value in Response.
|
||||||
*
|
* @param name Cookie name
|
||||||
* @param name Cookie name
|
* @param value Cookie value
|
||||||
* @param value Cookie value
|
* @param properties Cookie property Map (timeout, <i>etc.</i>)
|
||||||
* @param properties Cookie property Map (timeout,
|
* @throws UnsupportedEncodingException
|
||||||
* <i>etc.</i>)
|
|
||||||
* @throws UnsupportedEncodingException
|
|
||||||
*/
|
*/
|
||||||
public static void outputCookie(String name, String value,
|
public static void outputCookie(String name,
|
||||||
Map<String, Object> properties)
|
String value, Map<String, Object> properties) throws UnsupportedEncodingException {
|
||||||
throws UnsupportedEncodingException {
|
getExternalContext().addResponseCookie(name,
|
||||||
// getExternalContext().addResponseCookie(name,
|
URLEncoder.encode(value, "UTF-8"),
|
||||||
// URLEncoder.encode(value, "UTF-8"),
|
properties);
|
||||||
// properties);
|
|
||||||
|
|
||||||
Cookie cookie = new Cookie(name, value);
|
|
||||||
cookie.setMaxAge(31536000);
|
|
||||||
cookie.setPath("/");
|
|
||||||
jakarta.servlet.http.HttpServletResponse resp =
|
|
||||||
(HttpServletResponse) getExternalContext()
|
|
||||||
.getResponse();
|
|
||||||
resp.addCookie(cookie);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,9 +164,5 @@ public class JSFUtils {
|
||||||
log.warn("Session did not exist.");
|
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.faces.view.ViewScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.inject.Named;
|
import jakarta.inject.Named;
|
||||||
|
import jakarta.servlet.http.Part;
|
||||||
import jakarta.faces.event.AjaxBehaviorEvent;
|
import jakarta.faces.event.AjaxBehaviorEvent;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.primefaces.event.FileUploadEvent;
|
import org.primefaces.event.FileUploadEvent;
|
||||||
|
import org.primefaces.model.file.UploadedFile;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.orm.jpa.JpaSystemException;
|
|
||||||
|
|
||||||
import com.mousetech.gourmetj.persistence.model.Category;
|
import com.mousetech.gourmetj.persistence.model.Category;
|
||||||
import com.mousetech.gourmetj.persistence.model.Ingredient;
|
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
|
* After construction and injection, we obtain the recipe ID
|
||||||
* passed to us, if any, and load the recipe. It's also stored
|
* passed to us, if any and load the recipe. It's also stored
|
||||||
* in @see UserSession for the detail editor.
|
* in @see UserSession for the detail editor and
|
||||||
*
|
*
|
||||||
* @see PictureController.
|
* @see PictureController.
|
||||||
*/
|
*/
|
||||||
|
@ -243,7 +245,7 @@ public class RecipeDetailBean implements Serializable {
|
||||||
this.shop = this.getUserSession().getShoppingList()
|
this.shop = this.getUserSession().getShoppingList()
|
||||||
.contains(recipe);
|
.contains(recipe);
|
||||||
|
|
||||||
log.debug("Set recipe: " + this.recipe);
|
log.info("Set recipe: " + this.recipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -358,8 +360,8 @@ public class RecipeDetailBean implements Serializable {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
String s = instructions.replace("\r\n", "<p/>")
|
String s = instructions.replace("\r\n", "<p/>")
|
||||||
.replace("\n\n", "<p/>")
|
.replace("\n\n", "<p/>");
|
||||||
.replace("\n", "<br/>");
|
s = s.replace("\n", "<br/>");
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,30 +645,21 @@ public class RecipeDetailBean implements Serializable {
|
||||||
* @see #addIngredientList(String)
|
* @see #addIngredientList(String)
|
||||||
*/
|
*/
|
||||||
public void addIngredient(String ingredientText) {
|
public void addIngredient(String ingredientText) {
|
||||||
log.debug("Ingredient line: \"" + ingredientText + "\"");
|
log.info("Ingredient line: \"" + ingredientText + "\"");
|
||||||
Ingredient ing =
|
Ingredient ing =
|
||||||
IngredientDigester.digest(ingredientText);
|
IngredientDigester.digest(ingredientText);
|
||||||
|
|
||||||
String ingkey = ing.getIngkey();
|
String ingkey = ing.getIngkey();
|
||||||
if (!StringUtils.isEmpty(ingkey)) {
|
if (!StringUtils.isEmpty(ingkey)) {
|
||||||
try {
|
Shopcat scat = this.recipeService
|
||||||
Shopcat scat = this.recipeService
|
.findShopcatForIngredientKey(ingkey);
|
||||||
.findShopcatForIngredientKey(ingkey);
|
ing.setShopCat(scat);
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// get ing list size, set ing position, append
|
// get ing list size, set ing position, append
|
||||||
List<IngredientUI> ingredients = getWrappedIngredients();
|
List<IngredientUI> ingredients = getWrappedIngredients();
|
||||||
int lsize = ingredients.size();
|
int lsize = ingredients.size();
|
||||||
ing.setPosition(lsize + 1);
|
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())) {
|
if (recipeService.save(this.getRecipe())) {
|
||||||
return "recipeDetails?faces-redirect=true";
|
userSession.setRecipe(null);
|
||||||
|
return "recipeDetails";
|
||||||
} else {
|
} else {
|
||||||
JSFUtils.addErrorMessage("Save recipe failed");
|
JSFUtils.addErrorMessage("Save recipe failed");
|
||||||
return null;
|
return null;
|
||||||
|
@ -977,7 +971,7 @@ public class RecipeDetailBean implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ajaxUpdateShopcat(IngredientUI item) {
|
public void ajaxUpdateShopcat(IngredientUI item) {
|
||||||
log.debug("SHOPCAT2 ");
|
log.warn("SHOPCAT2 ");
|
||||||
updateShopcat(item);
|
updateShopcat(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -986,7 +980,7 @@ public class RecipeDetailBean implements Serializable {
|
||||||
|
|
||||||
public String editDescription() {
|
public String editDescription() {
|
||||||
this.setDetailTab(0);
|
this.setDetailTab(0);
|
||||||
return "detailEdit.xhtml?faces-redirect=true";
|
return "detailEdit?faces-redirect=true";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String editIngredients() {
|
public String editIngredients() {
|
||||||
|
|
|
@ -21,11 +21,9 @@ import org.springframework.http.HttpStatus;
|
||||||
"com.mousetech.gourmetj.persistence.model" })
|
"com.mousetech.gourmetj.persistence.model" })
|
||||||
public class SpringPrimeFacesApplication {
|
public class SpringPrimeFacesApplication {
|
||||||
|
|
||||||
final String homePage = "/main.jsf?viewExpired=true";
|
|
||||||
final String errorPage = "/error/error.html";
|
final String errorPage = "/error/error.html";
|
||||||
final String error404Page = "/error/error404.jsp";
|
final String error404Page = "/error/error404.html";
|
||||||
final String error400Page = "/error/error400.jsp";
|
final String expiredPage = "/main.xhtml";
|
||||||
final String expiredPage = "/error/viewExpired.xhtml";
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(SpringPrimeFacesApplication.class,
|
SpringApplication.run(SpringPrimeFacesApplication.class,
|
||||||
|
@ -64,10 +62,8 @@ public class SpringPrimeFacesApplication {
|
||||||
registry.addErrorPages(new ErrorPage(
|
registry.addErrorPages(new ErrorPage(
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
errorPage));
|
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.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
|
import jakarta.servlet.DispatcherType;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
public class SpringSecurityConfig {
|
public class SpringSecurityConfig {
|
||||||
|
@ -69,7 +71,6 @@ public class SpringSecurityConfig {
|
||||||
|| (pwLine.charAt(0) == '#')) {
|
|| (pwLine.charAt(0) == '#')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//log.error("======== PWLINE="+pwLine);
|
|
||||||
String[] creds = parseCreds(pwLine);
|
String[] creds = parseCreds(pwLine);
|
||||||
UserDetailsManagerConfigurer<AuthenticationManagerBuilder,
|
UserDetailsManagerConfigurer<AuthenticationManagerBuilder,
|
||||||
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>>
|
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>>
|
||||||
|
@ -98,24 +99,21 @@ public class SpringSecurityConfig {
|
||||||
return ocreds;
|
return ocreds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain securityFilterChain(HttpSecurity http)
|
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
http.csrf(AbstractHttpConfigurer::disable)
|
http
|
||||||
.cors(AbstractHttpConfigurer::disable)
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
.formLogin(login -> login.loginPage("/login.jsf")
|
.cors(AbstractHttpConfigurer::disable)
|
||||||
.permitAll()
|
.formLogin(Customizer.withDefaults())
|
||||||
.failureUrl("/login.jsf?error=true"))
|
.authorizeHttpRequests((authorize)-> authorize
|
||||||
.logout(logout -> logout
|
.dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
|
||||||
.logoutSuccessUrl("/login.jsf"))
|
.anyRequest().authenticated()
|
||||||
.httpBasic(Customizer.withDefaults())
|
);
|
||||||
.authorizeHttpRequests((authorize) -> authorize
|
|
||||||
.anyRequest().authenticated());
|
|
||||||
|
|
||||||
return http.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces old antMatchers for determining secured URLs.
|
* Replaces old antMatchers for determining secured URLs.
|
||||||
* @return customizer
|
* @return customizer
|
||||||
|
@ -123,19 +121,13 @@ public class SpringSecurityConfig {
|
||||||
@Bean
|
@Bean
|
||||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||||
return (web) -> web.ignoring().requestMatchers(
|
return (web) -> web.ignoring().requestMatchers(
|
||||||
"/jakarta.faces.resource/**",
|
"/javax.faces.resource/**",
|
||||||
"/",
|
"/",
|
||||||
"/index.html",
|
"/index.jsf",
|
||||||
// "/login",
|
"/index.xhtml",
|
||||||
// "/login.jsf", // Leave them for the authenticator!
|
|
||||||
// "/login.xhtml",
|
|
||||||
"/main.jsf",
|
"/main.jsf",
|
||||||
"/main.xhtml",
|
|
||||||
"/img/**",
|
"/img/**",
|
||||||
"/error/**",
|
|
||||||
"/RES_NOT_FOUND",
|
|
||||||
"/recipeDetails.jsf",
|
"/recipeDetails.jsf",
|
||||||
"/recipeDetails.xhtml",
|
|
||||||
"/shoppingList.jsf",
|
"/shoppingList.jsf",
|
||||||
"/recipePrint.jsf");
|
"/recipePrint.jsf");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
package com.mousetech.gourmetj;
|
package com.mousetech.gourmetj;
|
||||||
|
|
||||||
import java.io.Serializable;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jakarta.enterprise.context.SessionScoped;
|
import jakarta.enterprise.context.SessionScoped;
|
||||||
|
import jakarta.faces.model.SelectItem;
|
||||||
import jakarta.inject.Named;
|
import jakarta.inject.Named;
|
||||||
|
|
||||||
|
import org.primefaces.PrimeFaces;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -148,54 +146,6 @@ public class UserSession implements Serializable {
|
||||||
return sb.toString();
|
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.
|
* @Deprecated Using TimeConverter.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -11,7 +11,7 @@ public class WelcomePageRedirect implements WebMvcConfigurer {
|
||||||
@Override
|
@Override
|
||||||
public void addViewControllers(ViewControllerRegistry registry) {
|
public void addViewControllers(ViewControllerRegistry registry) {
|
||||||
registry.addViewController("/")
|
registry.addViewController("/")
|
||||||
.setViewName("forward:/index.html");
|
.setViewName("forward:/index.xhtml");
|
||||||
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
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.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import com.mousetech.gourmetj.persistence.model.Category;
|
||||||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +24,7 @@ import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||||
public interface RecipeRepository
|
public interface RecipeRepository
|
||||||
extends JpaRepository<Recipe, Long> {
|
extends JpaRepository<Recipe, Long> {
|
||||||
|
|
||||||
List<Recipe> findByTitleContainingIgnoreCase(String searchText);
|
List<Recipe> findByTitleContaining(String searchText);
|
||||||
|
|
||||||
@EntityGraph(value="Recipe.findWorkingSet")
|
@EntityGraph(value="Recipe.findWorkingSet")
|
||||||
public Recipe findDetailsById(Long recipeId);
|
public Recipe findDetailsById(Long recipeId);
|
||||||
|
@ -31,11 +32,11 @@ public interface RecipeRepository
|
||||||
@Query(name = "Recipe.findCusines", nativeQuery = true)
|
@Query(name = "Recipe.findCusines", nativeQuery = true)
|
||||||
List<String> FindCuisinesNative();
|
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);
|
String searchText);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class RecipeService implements Serializable {
|
||||||
|
|
||||||
public List<Recipe> findByTitle(String searchText) {
|
public List<Recipe> findByTitle(String searchText) {
|
||||||
return recipeRepository
|
return recipeRepository
|
||||||
.findByTitleContainingIgnoreCase(searchText);
|
.findByTitleContaining(searchText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipe findByPrimaryKey(Long recipeId) {
|
public Recipe findByPrimaryKey(Long recipeId) {
|
||||||
|
@ -120,19 +120,14 @@ public class RecipeService implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Recipe> findByCategoryLike(String searchText) {
|
public List<Recipe> findByCategoryLike(String searchText) {
|
||||||
return recipeRepository
|
return recipeRepository.findByCategories_CategoryContains(searchText);
|
||||||
.findByCategories_CategoryContainsIgnoreCase(
|
|
||||||
searchText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Recipe> findByCuisineLike(String searchText) {
|
public List<Recipe> findByCuisineLike(String searchText) {
|
||||||
return recipeRepository
|
return recipeRepository.findByCuisineContains(searchText);
|
||||||
.findByCuisineContainsIgnoreCase(searchText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Recipe> findByIngredientLike(String searchText) {
|
public List<Recipe> findByIngredientLike(String searchText) {
|
||||||
return recipeRepository
|
return recipeRepository.findDistinctByIngredientHash_ItemContains(searchText);
|
||||||
.findDistinctByIngredientHash_ItemContainsIgnoreCase(
|
|
||||||
searchText);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@ import java.io.InputStream;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.model.Recipe;
|
||||||
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpSession;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author timh
|
* @author timh
|
||||||
* @since Nov 26, 2021
|
* @since Nov 26, 2021
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<navigation-case>
|
<navigation-case>
|
||||||
<description>Go Home</description>
|
<description>Go Home</description>
|
||||||
<from-outcome>home</from-outcome>
|
<from-outcome>home</from-outcome>
|
||||||
<to-view-id>/main.xhtml?faces-redirect=true</to-view-id>
|
<to-view-id>/main</to-view-id>
|
||||||
<redirect />
|
<redirect />
|
||||||
</navigation-case>
|
</navigation-case>
|
||||||
</navigation-rule>
|
</navigation-rule>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</h:head>
|
</h:head>
|
||||||
<h:body>
|
<h:body>
|
||||||
<h1>
|
<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>
|
</h1>
|
||||||
<p:ajaxStatus onerror="PF('opError').show()"/>
|
<p:ajaxStatus onerror="PF('opError').show()"/>
|
||||||
<ui:insert name="content">
|
<ui:insert name="content">
|
||||||
|
@ -28,11 +28,11 @@
|
||||||
</ui:insert>
|
</ui:insert>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<h:outputText value="Version #{appBean.appVersion}"/><br/>
|
(C) 2021, 2024 Tim Holloway, Licensed under the <a
|
||||||
(C) 2021, 2024 Tim Holloway, Licensed under
|
href="http://www.apache.org/licenses/LICENSE-2.0"
|
||||||
the Common Development and Distribution License (CDDL).
|
>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:body>
|
</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;
|
font-weight: bold;
|
||||||
background-color: green;
|
background-color: green;
|
||||||
}
|
}
|
||||||
.noBorders .noBorders tr, .noBorders td {
|
|
||||||
background: none !important;
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<h:messages id="messages" />
|
<h:messages id="messages" />
|
||||||
<p:panel id="editorPanel"
|
<p:panel id="editorPanel"
|
||||||
|
@ -59,8 +55,129 @@
|
||||||
<p:tab id="overviewTab"
|
<p:tab id="overviewTab"
|
||||||
title="Description"
|
title="Description"
|
||||||
>
|
>
|
||||||
<ui:include
|
<p:panelGrid columns="2"
|
||||||
src="/WEB-INF/layout/panels/editRecipe/overview.xhtml"/>
|
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>
|
||||||
<p:tab id="ingredientsTab"
|
<p:tab id="ingredientsTab"
|
||||||
title="Ingredients"
|
title="Ingredients"
|
||||||
|
@ -245,8 +362,8 @@
|
||||||
id="ctlAddIng"
|
id="ctlAddIng"
|
||||||
value="+ Add"
|
value="+ Add"
|
||||||
onclick="ingButton(); return false;"
|
onclick="ingButton(); return false;"
|
||||||
update=":growl"
|
>
|
||||||
/>
|
</p:commandButton>
|
||||||
</h:panelGroup>
|
</h:panelGroup>
|
||||||
</p:panel>
|
</p:panel>
|
||||||
</p:tab>
|
</p:tab>
|
||||||
|
@ -260,9 +377,7 @@
|
||||||
rows="30" cols="120"
|
rows="30" cols="120"
|
||||||
escape="false"
|
escape="false"
|
||||||
value="#{recipeDetailBean.recipe.instructions}"
|
value="#{recipeDetailBean.recipe.instructions}"
|
||||||
>
|
/>
|
||||||
<p:focus />
|
|
||||||
</h:inputTextarea>
|
|
||||||
</div>
|
</div>
|
||||||
</p:panel>
|
</p:panel>
|
||||||
</p:tab>
|
</p:tab>
|
||||||
|
@ -272,24 +387,18 @@
|
||||||
rows="30" cols="120"
|
rows="30" cols="120"
|
||||||
escape="false"
|
escape="false"
|
||||||
value="#{recipeDetailBean.recipe.modifications}"
|
value="#{recipeDetailBean.recipe.modifications}"
|
||||||
>
|
/>
|
||||||
<p:focus/>
|
|
||||||
</h:inputTextarea>
|
|
||||||
</p:panel>
|
</p:panel>
|
||||||
</p:tab>
|
</p:tab>
|
||||||
</p:tabView>
|
</p:tabView>
|
||||||
<p:commandButton id="doSave" value="Save"
|
<p:commandButton id="doSave" value="Save" icon="ui-icon-pencil" ajax="false" disabled="{not recipeDetailBean.dirty}" action="#{recipeDetailBean.doSave}" />
|
||||||
icon="ui-icon-pencil" ajax="false"
|
|
||||||
disabled="{not recipeDetailBean.dirty}"
|
|
||||||
action="#{recipeDetailBean.doSave}"
|
|
||||||
/>
|
|
||||||
<p:commandButton id="doCancel" value="Cancel"
|
<p:commandButton id="doCancel" value="Cancel"
|
||||||
ajax="false" immediate="true"
|
ajax="false" immediate="true"
|
||||||
action="recipeDetails.jsf"
|
action="recipeDetails.jsf"
|
||||||
/>
|
/>
|
||||||
<p:commandButton id="doHome" value="Home"
|
<p:commandButton id="doHome" value="Home"
|
||||||
icon="ui-icon-home" ajax="false"
|
icon="ui-icon-home" ajax="false"
|
||||||
immediate="true" action="home"
|
immediate="true" action="main.jsf"
|
||||||
/>
|
/>
|
||||||
</h:form>
|
</h:form>
|
||||||
</p:panel>
|
</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"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE html >
|
|
||||||
<ui:composition template="/WEB-INF/layout/layout.xhtml"
|
<ui:composition template="/WEB-INF/layout/layout.xhtml"
|
||||||
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
xmlns:h="http://xmlns.jcp.org/jsf/html"
|
||||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||||
|
@ -11,22 +10,25 @@
|
||||||
<h:messages />
|
<h:messages />
|
||||||
<h:form id="form1">
|
<h:form id="form1">
|
||||||
<div>
|
<div>
|
||||||
<p:focus />
|
|
||||||
<p:autoComplete id="searchFor" size="45"
|
<p:autoComplete id="searchFor" size="45"
|
||||||
placeholder="Recipe title/cuisine/category, etc.)"
|
placeholder="Recipe title/cuisine/category, etc.)"
|
||||||
value="#{adminMainBean.searchText}"
|
value="#{adminMainBean.searchText}"
|
||||||
completeMethod="#{adminMainBean.searchSuggestionList}"
|
completeMethod="#{adminMainBean.searchSuggestionList}"
|
||||||
onfocus="jQuery('#form1\\:searchFor_input').select();"
|
|
||||||
>
|
>
|
||||||
<f:ajax event="change" execute="@this"
|
<f:ajax event="change" execute="@this"
|
||||||
render="form2:table1"
|
render="form2:table1"
|
||||||
listener="#{adminMainBean.ajaxUpdateList}"
|
listener="#{adminMainBean.ajaxUpdateList}"
|
||||||
/>
|
/>
|
||||||
</p:autoComplete>
|
</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"
|
<p:selectOneMenu id="ctlSearchType"
|
||||||
value="#{cookieBean.searchType}"
|
value="#{cookieBean.searchType}"
|
||||||
onchange="jQuery('#form1\\:searchFor_input').trigger('focus');"
|
|
||||||
>
|
>
|
||||||
<f:selectItems
|
<f:selectItems
|
||||||
value="#{appBean.searchTypeList}"
|
value="#{appBean.searchTypeList}"
|
||||||
|
@ -35,12 +37,6 @@
|
||||||
listener="#{adminMainBean.resetSuggestions}"
|
listener="#{adminMainBean.resetSuggestions}"
|
||||||
/>
|
/>
|
||||||
</p:selectOneMenu>
|
</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"
|
<p:commandButton id="ctlClear" value="Clear"
|
||||||
icon="ui-icon-close"
|
icon="ui-icon-close"
|
||||||
update="@form:searchFor :form2:table1"
|
update="@form:searchFor :form2:table1"
|
||||||
|
@ -57,10 +53,7 @@
|
||||||
value="#{cookieBean.displayListSize}"
|
value="#{cookieBean.displayListSize}"
|
||||||
/>
|
/>
|
||||||
<h:outputLabel for="slistSize"
|
<h:outputLabel for="slistSize"
|
||||||
value=" Recipes in Shopping List "
|
value=" Recipes in Shopping List"
|
||||||
/>
|
|
||||||
<p:commandButton id="logout" value="Logout"
|
|
||||||
action="#{adminMainBean.doLogout}"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</h:form>
|
</h:form>
|
||||||
|
@ -98,7 +91,7 @@
|
||||||
/>
|
/>
|
||||||
</p:column>
|
</p:column>
|
||||||
<p:column headerText="Source">
|
<p:column headerText="Source">
|
||||||
<h:outputText value="#{userSession.formatSource(row)}" />
|
<h:outputText value="#{row.source}" />
|
||||||
</p:column>
|
</p:column>
|
||||||
<p:column headerText="Prep Time">
|
<p:column headerText="Prep Time">
|
||||||
<h:outputText value="#{row.preptime}"
|
<h:outputText value="#{row.preptime}"
|
||||||
|
|
|
@ -35,98 +35,71 @@
|
||||||
/>
|
/>
|
||||||
</f:facet>
|
</f:facet>
|
||||||
<p:panel id="leftCol" style="width: auto;">
|
<p:panel id="leftCol" style="width: auto;">
|
||||||
<p:panelGrid>
|
<p:panelGrid columns="2">
|
||||||
<p:row>
|
<p:panelGrid id="picButtons" columns="2">
|
||||||
<p:column style="width: 136px;">
|
<img id="bigpix"
|
||||||
<p:graphicImage id="bigpix"
|
style="width: 132px;"
|
||||||
style="width: 132px;"
|
src="/img/picture/#{recipeDetailBean.recipe.id}"
|
||||||
value="/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:commandButton ajax="false"
|
||||||
<p:column style="width: 18em;">
|
value="Print"
|
||||||
<p:panelGrid id="pnlButtons"
|
icon="ui-icon-print"
|
||||||
columns="2"
|
action="recipePrint.jsf"
|
||||||
>
|
styleClass="ui-button-print"
|
||||||
<!-- TODO: ask if we should save -->
|
immediate="true"
|
||||||
<p:commandButton value="Home"
|
/>
|
||||||
ajax="false"
|
<p:commandButton id="ctlShop"
|
||||||
icon="ui-icon-home"
|
icon="ui-icon-cart"
|
||||||
action="home"
|
value="Shop" immediate="true"
|
||||||
immediate="true"
|
styleClass="#{recipeDetailBean.shop ? 'greenButton' : null}"
|
||||||
/>
|
action="#{recipeDetailBean.doShop}"
|
||||||
<p:commandButton ajax="false"
|
update="ctlShop"
|
||||||
value="Print"
|
/>
|
||||||
icon="ui-icon-print"
|
<h:outputText value="" />
|
||||||
action="/recipePrint.jsf?faces-redirect=true"
|
<p:outputLabel for="@next"
|
||||||
styleClass="ui-button-print"
|
value="Categories:"
|
||||||
immediate="true"
|
/>
|
||||||
/>
|
<h:outputText label="Category: "
|
||||||
<p:commandButton id="ctlShop"
|
value="#{userSession.formatCategories(recipeDetailBean.recipe)}"
|
||||||
icon="ui-icon-cart"
|
/>
|
||||||
value="Shop"
|
<p:outputLabel for="@next"
|
||||||
immediate="true"
|
value="Cuisine:"
|
||||||
styleClass="#{recipeDetailBean.shop ? 'greenButton' : null}"
|
/>
|
||||||
action="#{recipeDetailBean.doShop}"
|
<h:outputText label="Cuisine: "
|
||||||
update="ctlShop"
|
value="#{recipeDetailBean.recipe.cuisine}"
|
||||||
/>
|
/>
|
||||||
<p:commandButton
|
<p:outputLabel for="@next"
|
||||||
icon="ui-icon-wrench"
|
value="Prep Time:"
|
||||||
value="Edit"
|
/>
|
||||||
action="#{recipeDetailBean.editDescription}"
|
<h:outputText label="Prep Time: "
|
||||||
/>
|
value="#{recipeDetailBean.recipe.preptime}"
|
||||||
<p:outputLabel for="@next"
|
converter="com.mousetech.gourmetj.utils.TimeConverter"
|
||||||
value="Categories:"
|
/>
|
||||||
/>
|
<p:outputLabel for="@next"
|
||||||
<h:outputText
|
value="Cook Time:"
|
||||||
label="Category: "
|
/>
|
||||||
value="#{userSession.formatCategories(recipeDetailBean.recipe)}"
|
<h:outputText label="Cook Time: "
|
||||||
/>
|
value="#{recipeDetailBean.recipe.cooktime}"
|
||||||
<p:outputLabel for="@next"
|
converter="com.mousetech.gourmetj.utils.TimeConverter"
|
||||||
value="Cuisine:"
|
/>
|
||||||
/>
|
<h:outputText value="" />
|
||||||
<h:outputText
|
<p:commandButton
|
||||||
label="Cuisine: "
|
icon="ui-icon-wrench"
|
||||||
value="#{recipeDetailBean.recipe.cuisine}"
|
value="Edit"
|
||||||
/>
|
action="#{recipeDetailBean.editDescription}"
|
||||||
<p:outputLabel for="@next"
|
/>
|
||||||
value="Prep Time:"
|
</p:panelGrid>
|
||||||
/>
|
</p:panelGrid>
|
||||||
<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:panelGrid>
|
</p:panelGrid>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<p:panel id="pnlInstr">
|
<p:panel id="pnlInstr">
|
||||||
|
|
|
@ -9,106 +9,101 @@
|
||||||
<!-- Print Recipe -->
|
<!-- Print Recipe -->
|
||||||
<ui:define name="title">Gourmet Recipe Manager</ui:define>
|
<ui:define name="title">Gourmet Recipe Manager</ui:define>
|
||||||
<ui:define name="content">
|
<ui:define name="content">
|
||||||
<h:form id="printForm" style="font-size: 12pt;">
|
<h:form id="printForm">
|
||||||
<h:messages />
|
<h:messages />
|
||||||
<p:commandButton value="Back" immediate="true"
|
<p:panelGrid
|
||||||
ajax="false" icon="ui-icon-arrowthick-1-w"
|
style="margin-bottom: 5px; border-style: none"
|
||||||
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:column width="50%">
|
<p:column>
|
||||||
<img id="bigpix"
|
<img id="bigpix"
|
||||||
SRC="/img/picture/#{recipeDetailBean.recipe.id}"
|
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>
|
||||||
<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">
|
<p:commandButton value="Back"
|
||||||
<f:facet name="header">
|
immediate="true" ajax="false"
|
||||||
<h:outputText styleClass="subtitle"
|
icon="ui-icon-arrowthick-1-w"
|
||||||
value="Ingredients"
|
style="margin-left: 2em"
|
||||||
/>
|
styleClass="noprint"
|
||||||
</f:facet>
|
action="recipeDetails.jsf?faces-redirect=true"
|
||||||
<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:column>
|
</p:column>
|
||||||
</p:panelGrid>
|
</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 id="ingredientsc">
|
||||||
<p:panelGrid columns="1" style="width: 100%; font-size: 12pt;">
|
<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">
|
<f:facet name="header">
|
||||||
<h:outputText styleClass="subtitle"
|
<h:outputText styleClass="subtitle"
|
||||||
value="Instructions"
|
value="Instructions"
|
||||||
|
@ -119,7 +114,7 @@
|
||||||
value="#{recipeDetailBean.instructions}"
|
value="#{recipeDetailBean.instructions}"
|
||||||
/>
|
/>
|
||||||
</p:panelGrid>
|
</p:panelGrid>
|
||||||
<p:panelGrid columns="1" style="width: 100%; font-size: 12pt"
|
<p:panelGrid columns="1" style="width: 100%"
|
||||||
rendered="#{not empty recipeDetailBean.modifications}"
|
rendered="#{not empty recipeDetailBean.modifications}"
|
||||||
>
|
>
|
||||||
<f:facet name="header">
|
<f:facet name="header">
|
||||||
|
@ -131,9 +126,6 @@
|
||||||
value="#{recipeDetailBean.modifications}"
|
value="#{recipeDetailBean.modifications}"
|
||||||
/>
|
/>
|
||||||
</p:panelGrid>
|
</p:panelGrid>
|
||||||
<h:outputText
|
|
||||||
value="Recipe ID: #{recipeDetailBean.recipe.id}"
|
|
||||||
/>
|
|
||||||
</h:form>
|
</h:form>
|
||||||
</ui:define>
|
</ui:define>
|
||||||
</ui:composition>
|
</ui:composition>
|
|
@ -42,7 +42,7 @@
|
||||||
<p:tab id="overviewTab" title="Shopping List">
|
<p:tab id="overviewTab" title="Shopping List">
|
||||||
<h:form id="form1">
|
<h:form id="form1">
|
||||||
<p:dataTable id="tblRecipes"
|
<p:dataTable id="tblRecipes"
|
||||||
style="width: 60em"
|
style="width: 40em"
|
||||||
value="#{shoppingListBean.recipeList}"
|
value="#{shoppingListBean.recipeList}"
|
||||||
var="item"
|
var="item"
|
||||||
>
|
>
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
>
|
>
|
||||||
<p:dataTable id="tblShopIngredients"
|
<p:dataTable id="tblShopIngredients"
|
||||||
value="#{shoppingListBean.ingredientList}"
|
value="#{shoppingListBean.ingredientList}"
|
||||||
style="width: 60em;"
|
style="width: 40em;"
|
||||||
sortBy="#{item.shopCat}" var="item"
|
sortBy="#{item.shopCat}" var="item"
|
||||||
>
|
>
|
||||||
<f:facet name="header">
|
<f:facet name="header">
|
||||||
|
|
|
@ -21,16 +21,10 @@ spring:
|
||||||
ddl-auto: none
|
ddl-auto: none
|
||||||
database-platform: org.hibernate.dialect.MySQLDialect
|
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:
|
server:
|
||||||
servlet:
|
servlet:
|
||||||
session:
|
session:
|
||||||
timeout: '30m'
|
timeout: '30m'
|
||||||
tracking-modes: 'cookie'
|
|
||||||
# Theme here overrides joinfaces theme
|
# Theme here overrides joinfaces theme
|
||||||
# context-parameters:
|
# context-parameters:
|
||||||
# primefaces:
|
# primefaces:
|
||||||
|
@ -40,18 +34,7 @@ server:
|
||||||
gourmet:
|
gourmet:
|
||||||
password:
|
password:
|
||||||
file: .gourmetpw
|
file: .gourmetpw
|
||||||
|
|
||||||
joinfaces:
|
joinfaces:
|
||||||
primefaces:
|
primefaces:
|
||||||
theme: bluesky
|
theme: casablanca
|
||||||
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
|
|
||||||
|
|
|
@ -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