diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..707d3a305
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,174 @@
+
+# Created by https://www.toptal.com/developers/gitignore/api/intellij,eclipse
+# Edit at https://www.toptal.com/developers/gitignore?templates=intellij,eclipse
+
+### Eclipse ###
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# CDT- autotools
+.autotools
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Annotation Processing
+.apt_generated/
+.apt_generated_test/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+
+# Uncomment this line if you wish to ignore the project description file.
+# Typically, this file would be tracked if it contains build/dependency configurations:
+#.project
+
+### Eclipse Patch ###
+# Spring Boot Tooling
+.sts4-cache/
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+# https://plugins.jetbrains.com/plugin/7973-sonarlint
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator-enh.xml
+.idea/**/markdown-navigator/
+
+# Cache file creation bug
+# See https://youtrack.jetbrains.com/issue/JBR-2257
+.idea/$CACHE_FILE$
+
+# CodeStream plugin
+# https://plugins.jetbrains.com/plugin/12206-codestream
+.idea/codestream.xml
+
+# End of https://www.toptal.com/developers/gitignore/api/intellij,eclipse
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 000000000..fee26ec3c
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/../../../../../../../:\Users\dmhol\dev\CaseStudy\Blog\.idea/dataSources/
+/dataSources.local.xml
+# Zeppelin ignored files
+/ZeppelinRemoteNotebooks/
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 000000000..efd261bf7
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+demo
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 000000000..a55e7a179
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 000000000..e65307f0f
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 000000000..63e900193
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 000000000..48cf38dc5
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..436120054
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..0fb971708
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
new file mode 100644
index 000000000..bed0bd396
--- /dev/null
+++ b/.idea/sqldialects.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 000000000..e76d1f324
--- /dev/null
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if(mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if(mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if(!outputFile.getParentFile().exists()) {
+ if(!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 000000000..2cc7d4a55
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 000000000..642d572ce
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/HELP.md b/HELP.md
new file mode 100644
index 000000000..263675bae
--- /dev/null
+++ b/HELP.md
@@ -0,0 +1,33 @@
+# Getting Started
+
+### Reference Documentation
+For further reference, please consider the following sections:
+
+* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
+* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/maven-plugin/reference/html/)
+* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/maven-plugin/reference/html/#build-image)
+* [Spring Web](https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications)
+* [Thymeleaf](https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-template-engines)
+* [Spring Data JPA](https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/reference/htmlsingle/#boot-features-jpa-and-spring-data)
+* [JDBC API](https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/reference/htmlsingle/#boot-features-sql)
+* [Spring Data JDBC](https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/)
+* [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/reference/htmlsingle/#using-boot-devtools)
+* [Spring Configuration Processor](https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/reference/htmlsingle/#configuration-metadata-annotation-processor)
+* [Spring Security](https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/reference/htmlsingle/#boot-features-security)
+
+### Guides
+The following guides illustrate how to use some features concretely:
+
+* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
+* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
+* [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/)
+* [Handling Form Submission](https://spring.io/guides/gs/handling-form-submission/)
+* [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/)
+* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)
+* [Accessing Relational Data using JDBC with Spring](https://spring.io/guides/gs/relational-data-access/)
+* [Managing Transactions](https://spring.io/guides/gs/managing-transactions/)
+* [Using Spring Data JDBC](https://github.com/spring-projects/spring-data-examples/tree/master/jdbc/basics)
+* [Securing a Web Application](https://spring.io/guides/gs/securing-web/)
+* [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/)
+* [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/)
+
diff --git a/README.md b/README.md
index 8cdfa1bdd..b49268125 100644
--- a/README.md
+++ b/README.md
@@ -1,69 +1,25 @@
-# Full Stack Web Application
-
-* **Objective** - to create an implementation of a web service
-* **Purpose** - to demonstrate the construction of a full-stacked web-application
-* **Description**
- * This Case Study is your first foray into building a full-stack application. You'll be building a Spring MVC based application, which means you'll learn about what it takes to build a functional application from the ground up yourself.
- * This is exciting! It's a lot, but we've given you the tools to be able to build what you need, and you get to decide what you do with it. You also get to be creative in choosing what sort of application you want to build!
- * You will be working individually to design your app. We hope you'll exercise creativity on this project, sketch some wireframes before you start, make sure you have time to run these ideas by your instructors to get their feedback before you dive too deep into coding! Remember to keep things small and focus on mastering the fundamentals.
-* **Additional Resources**
- * [The Original Case Study Document](./case-study.pdf)
- * [Case Study Outline](./case-study-outline.pdf)
- * [Case Study Deliverables](./README_deliverables.md)
- * [Identifying Plagiarism](./README_plagiarism.md)
- * [Suggested Project Topics](./README_suggested-project-topics.md)
-
-
-
-## Minimum Features
-* `RESTful` web service which consumes requests from a front-end web application and caches each request and the respective response to a database.
-* The application must support a login functionality.
-
-
-
-
-## Developmental Notes
-
-### Tech Stack Selection
-* Select at least 1 technology from each of the following categories:
- * **Version Control System**
- 1. Github
- 2. Bitbucket
-
- * **Wireframe**
- 1. Mockflow
- 2. Balsamiq
- 3. Lucidcharts
-
- * **Frontend**
- 1. Java Server Pages
-
- * **Business Logic**
- 1. Java
- 2. TypeScript
-
- * **WebServer Implementation**
- 1. Spring Boot
- 2. At least 1 [backing service](https://12factor.net/backing-services) API
-
- * **Data Layer**
- 1. MySQL
- 2. PostgreSQL
- 3. MariaDB
-
- * **Web Server Cloud Deployment**
- 1. Heroku
- 2. AWS EC2 Instance
-
- * **Web Application Cloud Deployment**
- 1. Netlify
- 2. AWS EC2 Instance
-
-
-
-
-### Installation
-* It is advised that you make install each of the following technologies to ensure that are at least accessible
- * Install [NodeJs](https://nodejs.org/en/).
- * Install [Angular](http://angular.io/).
- * Install [AngularCli](https://cli.angular.io/).
+# My Blog
+
+* **Description** - This is my blog that I got from https://nakov.com/blog/2016/08/05/creating-a-blog-system-with-spring-mvc-thymeleaf-jpa-and-mysql/
+* **Things Added** - The tutorial being old and largely unfinished, I added security, create/edit/index of posts along with controllers.
+* **Features**- This is a skeleton/vanilla blog system and it allows freedom of speech.
+
+
+
+### Project Configuration
+* Ensure that you have mysql database installed on port 3306
+ * Go inside of the resources/application.properties
+ * Change the user to root
+ * Put in your own password
+ *Set server.ports to what you like
+ 
+
+### Problems
+* Project will not run on same database of two seperate deployments, will give errors: 'User name does not have a default'
+ * Passwords are not set to be very secure, so it is up to you to make strong passwords
+ * Google will say 'Passwords for localhost:8080 have been breached.'
+ * Put in your own password
+ * Not set to delete users
+ * Permissions are not set to anything but Admin, so anybody with access can delete other peoples posts
+ * Need to set up permissions over posts. This is done through setting roles in spring security and changing permissions on edit, index, create pages.
+ *Visitors can only see the last 5 posts. This can be enhanced.
\ No newline at end of file
diff --git a/README_deliverables.md b/README_deliverables.md
deleted file mode 100644
index 4bb20a97f..000000000
--- a/README_deliverables.md
+++ /dev/null
@@ -1,52 +0,0 @@
-## Deliverables
-
-### Trello Board
-* To begin your case study, create a Trello board
-* Please ensure the instructor has access to this Trello board.
-* This board will be used to track progress and evaluate required effort of tasks.
-* Ensure the board has at least 5 columns accounting for each of the following
- * `Ideation`
- * This column will first contain cards describing a potential application to be built.
- * Upon concept approval, this column will be re-purposed to describe potential features of the approved application.
- * `Backlog`
- * This column will contain cards describing work that **will not be** done in a near-future, but **must be done** eventually for the product to be considered completed
- * `Upcoming`
- * This column will contain cards describing work that **will not be** done in the current-sprint commitment, but **will be committed to** in the upcoming-sprint commitment.
- * `Current`
- * This column will contain cards describing work that **will be** done in the current-sprint commitment.
- * `In Review
- * This column will contain cards describing work that **need to be reviewed** to be considered **done**.
- * `Completed`
- * This column will contain cards describing work that has been reviewed and is considered completed
-
-### Database
-* MariaDB is the suggested dialect
-* This deliverable includes creating a database that reflects the website you have decided to create
-* Schema diagram: a visual representation of your database
-* Database file creation (`.sql`): Queries used to create the database
-
-
-### Core Java & JPA
-* This deliverable includes creating a back-end environment that is composed of different Java classes
- * Models: Java classes that represent an entity and are used to transfer data related to an entity, create multiple queries, and represent the database as an object-oriented model
- * Persistence.xml: This file configures the Java classes that are going to be using JPA to interact with the database.
- * Persistence Java Class: A static class that allows the application to create a persistent object which can be used to interact with the database.
- * Service Class or Data Access Objects (DAO): Java classes that are composed of one or more functions and have direct access to the database by using JPA persistent object. Each function in a DAO class interacts with the database differently.
- * Custom Exceptions: Java classes that allow you to describe an error while the application is running.
- * Utilizes: Java classes that hold constant variables (Variables that never change from its initial value). The value of these variables can be requested parameters, Database queries used in the DAO, name of HTML pages, or URL patterns to forward a request to.
-
-
-### HTML5/CSS3
-* This deliverable includes creating every page required by the given case study
-* HTML5: Use HTML for static and dynamic pages and markup the structure of every page.
-* CSS3: Use CSS3 to style your HTML pages and make sure to take into consideration the knowledge acquired from the visual design lessons.
-
-
-### Spring MVC
-* This deliverable includes connecting no. 1, 2 and 3 deliverables to function together
- * Spring MVC: Responsible for responding to a request made by the user. This can be login, registration, etc. When using Spring MVC make sure to use at least the following functionalities: different type of session management, annotation-based controller, exception handling, models, model attributes.
-
-
-### Junit (Test all DAO classes)
-* This deliverable includes creating a test class for each DAO available and creating a test case in the test class for each function inside the DAO
- * Junit: A Java framework responsible for performing unit testing against every DAO class available. There should be a test class for every DAO and inside the test classes, there should be at least one test case for every function inside the DAO classes. When using JUnit make sure to use the following functionalities: Suite classes, Runner, Feature life cycle, Test, Parameterized classes, Java Hamcrest library.
diff --git a/README_plagiarism.md b/README_plagiarism.md
deleted file mode 100644
index 5b071dae3..000000000
--- a/README_plagiarism.md
+++ /dev/null
@@ -1,20 +0,0 @@
-### A Note on Plagiarism
-* You are encouraged to ask others, including students, instructors, and StackOverflow, for help. However, it is NOT ACCEPTABLE TO COPY another person's code and submit it as your own. More importantly, it is detrimental to your learning and growth.
-* All of the following are considered plagiarism or cheating:
- * Turning in work that is not your own.
- * Turning in someone else's work as your own.
- * Hiring, or paying someone to do your work for you.
- * Copying words or code without giving credit.
- * Building or copying someone else’s idea without their knowledge or giving credit.
- * Giving incorrect information about a source.
- * Changing words, variable names, etc. but copying the code or files of a source without giving credit.
- * Copying so many ideas or code blocks from a source that it makes up the majority of your work, whether you give credit or not.
- * Failing to put a quotation in quotation marks.
-
-
-* In an effort to not plagiarize, credit for this content goes to:
-* [Plagiarism.org](plagiarism.org), specifically the “plagiarism 101” section. The content was adapted for the code. For more information, please see:
- * [What is Plagiarism](http://www.plagiarism.org/plagiarism-101/what-is-plagiarism)
- * [Types of Plagiarism](http://www.plagiarism.org/plagiarism-101/types-of-plagiarism)
- * [How do I safely write code in my own 'words' and not plagiarize?](http://programmers.stackexchange.com/questions/80167/how-do-i-safely-write-code-in-my-own-words-and-not-plagiarize)
- * [Avoiding Plagiarism: Writing Computer Code](http://www.upenn.edu/academicintegrity/ai_computercode.html)
diff --git a/README_suggested-project-topics.md b/README_suggested-project-topics.md
deleted file mode 100644
index 9b2ac965e..000000000
--- a/README_suggested-project-topics.md
+++ /dev/null
@@ -1,54 +0,0 @@
-
-## Suggested Project Topics
-
-### (Suggested Project Topic 1) TCP Application
-
-#### User Stories to Fulfill
-* As a client, (not logged in) I
- * can send messages to a _peer_.
- * can view default channels
- * can view all accessible channels
- * can view messages live as they are received
-
-
-
-
-
-
-
-### (Suggested Project Topic 2) Blog Application
-
-#### User Stories to Fulfill
-* As a client, (not logged in) I
- * can create new blog posts
- * A blog post can consist of images and text
- * can view list of all blog posts
- * can view blog posts filtered by blog-tag
- * can view new blog posts upon refreshing the DOM
-
-
-
-
-
-
-
-### (Suggested Project Topic 3) Application
-
-#### User Stories to Fulfill
-* As a client, (not logged in) I
- * can upload new videos
- * can view list of all videos
- * can post simple text-comments on a video
-
-
-
-
-
-### (Suggested Project Topic 4) Money Management Application
-
-#### User Stories to Fulfill
-* As a client, (not logged in) I
- * can create new accounts
- * deposit money to each account
- * withdrawl money from each account
- * transfer money to and from any 2 accounts
diff --git a/demo.iml b/demo.iml
new file mode 100644
index 000000000..2bc1a8afd
--- /dev/null
+++ b/demo.iml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mvnw b/mvnw
new file mode 100644
index 000000000..a16b5431b
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 000000000..c8d43372c
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 000000000..e3771e0d4
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+ spring
+ blog
+ 0.0.1-SNAPSHOT
+ jar
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.0.3.RELEASE
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+
+ org.springframework.data
+ spring-data-jpa
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ mysql
+ mysql-connector-java
+ 8.0.11
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+
+
+ org.thymeleaf.extras
+ thymeleaf-extras-springsecurity4
+
+
+
+
+
+
+ com.h2database
+ h2
+ test
+
+
+
+ 1.8
+
+
diff --git a/spring-security.xml b/spring-security.xml
new file mode 100644
index 000000000..bf38e5fec
--- /dev/null
+++ b/spring-security.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/dmholland/demo/Configure/WebMvcConfig.java b/src/main/java/com/dmholland/demo/Configure/WebMvcConfig.java
new file mode 100644
index 000000000..e2054ed02
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/Configure/WebMvcConfig.java
@@ -0,0 +1,27 @@
+package com.dmholland.demo.Configure;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+
+ public void addViewControllers(ViewControllerRegistry registry){
+ registry.addViewController("/home").setViewName("index");
+ registry.addViewController("/").setViewName("index");
+ registry.addViewController("/login").setViewName("users/login");
+ registry.addViewController("/users/registration").setViewName("users/registration");
+ registry.addViewController("/backoffice").setViewName("users/backoffice");
+
+ //registry.addViewController("/posts").setViewName("posts/index");
+ }
+
+ @Bean
+ public BCryptPasswordEncoder passwordEncoder(){
+ return new BCryptPasswordEncoder();
+ }
+
+}
diff --git a/src/main/java/com/dmholland/demo/Configure/WebSecurityConfig.java b/src/main/java/com/dmholland/demo/Configure/WebSecurityConfig.java
new file mode 100644
index 000000000..821a40f50
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/Configure/WebSecurityConfig.java
@@ -0,0 +1,79 @@
+package com.dmholland.demo.Configure;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+
+import javax.sql.DataSource;
+
+@Configuration
+@EnableWebSecurity
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ /**
+ * Needed to read credentials parse password
+ * TODO https://medium.com/@gustavo.ponce.ch/spring-boot-spring-mvc-spring-security-mysql-a5d8545d837d
+ */
+ @Autowired
+ private DataSource dataSource;
+ @Autowired
+ private BCryptPasswordEncoder bCryptPasswordEncoder;
+ /**
+ * Read query statement from application.properties file
+ */
+ @Value("${spring.queries.users-query}")
+ private String usersQuery;
+
+ @Value("${spring.queries.roles-query}")
+ private String rolesQuery;
+
+ /**
+ * Perform a query against the database with user name and password fields
+ */
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception{
+ auth.jdbcAuthentication()
+ .usersByUsernameQuery(usersQuery)
+ .authoritiesByUsernameQuery(rolesQuery)
+ .dataSource(dataSource)
+ .passwordEncoder(bCryptPasswordEncoder);
+ }
+ /**
+ * Configure HTTP permissions
+ */
+ @Override
+ protected void configure(HttpSecurity http) throws Exception{
+
+ http.authorizeRequests()
+ .antMatchers("/", "/home", "/error/**", "/posts", "/posts/view/**", "/users/logout", "/users/registration", "/users/login").permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .loginPage("/users/login").failureUrl("/users/login?error=true").defaultSuccessUrl("/")
+ .usernameParameter("username").passwordParameter("hashPassword")
+ .and()
+ .logout()
+ .invalidateHttpSession(true)
+ .clearAuthentication(true)
+ .logoutRequestMatcher(new AntPathRequestMatcher("/users/logout"))
+ .logoutSuccessUrl("/").and().exceptionHandling()
+ .accessDeniedPage("/error/403");
+ }
+ /**
+ * Configure Web permissions (images, css, js, etc.)
+ */
+ @Override
+ public void configure(WebSecurity web) throws Exception{
+ web.ignoring()
+ .antMatchers("/resources/**", "/static/**", "/css/**", "/img/**", "/js/**");
+
+ }
+
+}
diff --git a/src/main/java/com/dmholland/demo/application.java b/src/main/java/com/dmholland/demo/application.java
new file mode 100644
index 000000000..f26abf8e5
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/application.java
@@ -0,0 +1,11 @@
+package com.dmholland.demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class application {
+ public static void main(String[] args) {
+ SpringApplication.run(application.class, args);
+ }
+}
diff --git a/src/main/java/com/dmholland/demo/controllers/BackOfficeController.java b/src/main/java/com/dmholland/demo/controllers/BackOfficeController.java
new file mode 100644
index 000000000..415cb751c
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/controllers/BackOfficeController.java
@@ -0,0 +1,32 @@
+package com.dmholland.demo.controllers;
+
+import com.dmholland.demo.models.User;
+import com.dmholland.demo.services.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+
+@Controller
+public class BackOfficeController {
+
+ @Autowired
+ private UserService userService;
+
+
+ @RequestMapping("/users/backoffice")
+ public String index(Model model, @PageableDefault(sort = {"username"}, value = 5) Pageable pageable){
+ // Get the content of the table, TODO. find a way to paginate
+ Page users = this.userService.findAll(pageable);
+ // Define variables to be passed to view
+ model.addAttribute("users", users);
+ // Return the view model itself
+ return "users/backoffice";
+ }
+
+ }
+
diff --git a/src/main/java/com/dmholland/demo/controllers/ErrorController.java b/src/main/java/com/dmholland/demo/controllers/ErrorController.java
new file mode 100644
index 000000000..44da07990
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/controllers/ErrorController.java
@@ -0,0 +1,39 @@
+package com.dmholland.demo.controllers;
+
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletResponse;
+
+@RestController
+public class ErrorController implements org.springframework.boot.web.servlet.error.ErrorController {
+
+@RequestMapping("/error")
+public ModelAndView handleError(HttpServletResponse response){
+ModelAndView modelAndView = new ModelAndView();
+
+if(response.getStatus() == HttpStatus.NOT_FOUND.value()){
+ modelAndView.setViewName("Error/404");
+}
+ if(response.getStatus() == HttpStatus.FORBIDDEN.value()){
+ modelAndView.setViewName("Error/403");
+ }
+ if(response.getStatus() == HttpStatus.INTERNAL_SERVER_ERROR.value()){
+ modelAndView.setViewName("Error/500");
+ }
+ else {
+ modelAndView.setViewName("Error/other");
+ }
+
+ return modelAndView;
+}
+
+
+ @Override
+ public String getErrorPath() {
+ return "/error";
+ }
+}
diff --git a/src/main/java/com/dmholland/demo/controllers/HomeController.java b/src/main/java/com/dmholland/demo/controllers/HomeController.java
new file mode 100644
index 000000000..d3c2ee994
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/controllers/HomeController.java
@@ -0,0 +1,31 @@
+package com.dmholland.demo.controllers;
+
+
+import com.dmholland.demo.models.Post;
+import com.dmholland.demo.services.PostServiceInterface;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Controller
+public class HomeController {
+
+ @Autowired
+ private PostServiceInterface postService;
+
+ @RequestMapping(value= { "/", "/home" } )
+ public String index(Model model) {
+ List latest5Posts = postService.findLatest5();
+ model.addAttribute("latest5Posts",latest5Posts);
+
+ List latest3Posts = latest5Posts.stream()
+ .limit(3)
+ .collect(Collectors.toList());
+ model.addAttribute("latest3Posts",latest3Posts);
+ return "index";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/dmholland/demo/controllers/LoginController.java b/src/main/java/com/dmholland/demo/controllers/LoginController.java
new file mode 100644
index 000000000..6b924c98a
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/controllers/LoginController.java
@@ -0,0 +1,37 @@
+package com.dmholland.demo.controllers;
+
+import com.dmholland.demo.services.NotificationService;
+import com.dmholland.demo.services.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@Controller
+public class LoginController {
+
+ @Autowired
+ private NotificationService notifyService;
+
+ @Autowired
+ private UserService userService;
+
+
+
+ //Due to using the spring security, login form is not required in method
+ @RequestMapping("/users/login")
+ public String loginPage() {
+ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ if ((auth instanceof AnonymousAuthenticationToken)) {
+ return "users/login";
+ } else {
+ notifyService.addErrorMessage("Credentials are not correct");
+ return "redirect:/";
+ }
+ }
+
+ }
+
+
diff --git a/src/main/java/com/dmholland/demo/controllers/PostsController.java b/src/main/java/com/dmholland/demo/controllers/PostsController.java
new file mode 100644
index 000000000..d164ab8b0
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/controllers/PostsController.java
@@ -0,0 +1,143 @@
+package com.dmholland.demo.controllers;
+
+
+import com.dmholland.demo.models.Post;
+import com.dmholland.demo.models.User;
+import com.dmholland.demo.services.NotificationService;
+import com.dmholland.demo.services.PostService;
+import com.dmholland.demo.services.PostServiceInterface;
+import com.dmholland.demo.services.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.validation.Valid;
+
+@Controller
+public class PostsController {
+ @Autowired
+ NotificationService notifyService;
+
+ @Autowired
+ private PostServiceInterface postService;
+
+ @Autowired
+ private UserService userService;
+
+ @RequestMapping("/posts/view/{id}")
+ public String view(@PathVariable("id") Long id, Model model){
+ Post post = this.postService.findById(id);
+ if( post == null ){
+ notifyService.addErrorMessage("Cannot find post #" + id);
+ return "redirect:/";
+ }
+ model.addAttribute("post", post);
+ // To have something like src/main/resources/templates//
+ return "posts/view";
+ }
+
+@RequestMapping("/posts/create")
+
+public ModelAndView createPosts() {
+ ModelAndView mv=new ModelAndView();
+ Post post = new Post();
+ mv.addObject(post);
+ mv.setViewName("posts/create");
+ return mv;
+}
+
+@RequestMapping(value = "posts/create", method = RequestMethod.POST)
+public ModelAndView createPosts(@Valid Post post, BindingResult bindingResult){
+ ModelAndView mv=new ModelAndView();
+ Authentication verify = SecurityContextHolder.getContext().getAuthentication();
+
+ //So the errors will reject and show reasoning
+ if( post.getTitle().isEmpty() ){
+ bindingResult.rejectValue("title", "error.post", "Title cannot be empty");
+ }
+ if( post.getBody().isEmpty() ){
+ bindingResult.rejectValue("body", "error.post", "Content cannot be empty");
+ }
+ User user =this.userService.findByUserName(verify.getName());//gets name of current user
+ if(null == user){
+ bindingResult.rejectValue("author","error.author","Can not create post, your User Name is not Verified");
+ }
+ if( !bindingResult.hasErrors() ){
+ post.setAuthor(user);
+ this.postService.create(post);
+ mv.addObject("successMessage","Post has been created");
+ mv.addObject("post",new Post());
+ }
+ return mv;
+}
+
+
+
+@RequestMapping("/posts/delete/{id}")
+public String delete(@PathVariable("id")Long id){
+ Post post = this.postService.findById(id);
+ if(null==post){
+ notifyService.addErrorMessage("Can not find post # "+id);
+ }else{
+ this.postService.deleteById(id);
+ }
+ return "redirect:/posts/";
+}
+
+@RequestMapping("/posts/edit/{id}")
+public String edit(@PathVariable("id") Long id, Model model){
+ Post post = this.postService.findById(id);
+ if( null==post ){
+ notifyService.addErrorMessage("Cannot find post #" + id);
+ return "redirect:/posts/";
+ }
+ model.addAttribute("post", post);
+ return "posts/edit";
+}
+
+
+@RequestMapping(value = "/posts/edit", method = RequestMethod.POST)
+public ModelAndView edit(@Valid Post post, BindingResult bindingResult){
+ ModelAndView mv = new ModelAndView();
+ Authentication verify = SecurityContextHolder.getContext().getAuthentication();
+ mv.setViewName("posts/edit");
+ //Since this was just like the creating of a post I copied and altered from above
+ if( post.getTitle().isEmpty() ){
+ bindingResult.rejectValue("title", "error.post", "Title cannot be empty");
+ }
+ if( post.getBody().isEmpty() ){
+ bindingResult.rejectValue("body", "error.post", "Content cannot be empty");
+ }
+ User user =this.userService.findByUserName(verify.getName());//gets name of current user
+ if(null == user){
+ bindingResult.rejectValue("author","error.author","Can not edit post, your User Name is not Verified");
+ }
+ if( !bindingResult.hasErrors() ){
+ post.setAuthor(user);
+ this.postService.create(post);
+ mv.addObject("successMessage","Post has been edited");
+ mv.addObject("post",new Post());
+ }
+ return mv;
+}
+
+@RequestMapping("/posts")
+public String index(Model model, @PageableDefault(sort = {"id"},value= 5) Pageable pageable){
+ //not exactly sure how above line worked need to see if 6 posts will print
+
+ Page posts = this.postService.findAll(pageable);
+
+ model.addAttribute("posts",posts);
+ return "posts/index";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/dmholland/demo/controllers/RegistrationController.java b/src/main/java/com/dmholland/demo/controllers/RegistrationController.java
new file mode 100644
index 000000000..18faa3001
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/controllers/RegistrationController.java
@@ -0,0 +1,53 @@
+package com.dmholland.demo.controllers;
+
+
+import com.dmholland.demo.models.User;
+import com.dmholland.demo.services.NotificationService;
+import com.dmholland.demo.services.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.validation.Valid;
+
+@Controller
+public class RegistrationController {
+ @Autowired
+ private UserService userService;
+
+
+ @RequestMapping("/users/registration")
+ public ModelAndView registration(){
+ ModelAndView mv= new ModelAndView();
+ User user=new User();
+ // User user=repository.createUser(new User());
+ //I had get mapping and used the repository
+ mv.addObject(user);
+ mv.setViewName("users/registration");
+ return mv;
+ }
+
+ @RequestMapping(value="users/registration",method = RequestMethod.POST)
+ public ModelAndView registration(@Valid User user, BindingResult validate){
+ ModelAndView mv=new ModelAndView();
+ mv.setViewName("users/registration");
+ if(this.userService.userCheck(user)){//user exists
+ validate.rejectValue("username", "error.user", "User exists");
+ return mv;
+ }
+ if(!validate.hasErrors()) {
+ this.userService.createUser(user);
+ mv.addObject("successMessage", "User has been created");
+ mv.addObject("user", new User());
+ }
+ return mv;
+
+ }
+
+}
diff --git a/src/main/java/com/dmholland/demo/models/Post.java b/src/main/java/com/dmholland/demo/models/Post.java
new file mode 100644
index 000000000..00a99ded0
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/models/Post.java
@@ -0,0 +1,92 @@
+package com.dmholland.demo.models;
+
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+
+@Entity
+@Table(name="posts")
+public class Post {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(nullable = false, length = 30)
+ private String title;
+
+ @Lob //Recognized as a Blob(binary body), this helps get large pieces of data
+ @Column(nullable = false)
+ private String body;
+
+ @ManyToOne(optional = false,fetch = FetchType.LAZY)//This means that without an author, an error will cause it not to persist, also lazy means it will only fetch when chosen
+ private User author;
+
+ @NotNull
+ @Column(nullable = false)
+ private Date date = new Date();
+
+ public Post() {
+ }
+
+
+ public Post(Long id, String title, String body, User author) {
+ this.id = id;
+ this.author = author;
+ this.title = title;
+ this.body = body;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ /**
+ * @return the title
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public void setBody(String body) {
+ this.body = body;
+ }
+
+ public User getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(User author) {
+ this.author = author;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ @Override
+ public String toString() {
+ return "Posts{" + "id= " + id + ", title='" + title + '\'' +
+ ", body='" + body + '\'' +
+ ", author='" + author + '\'' +
+ ", date='" + date + '}';
+ }
+
+
+}
diff --git a/src/main/java/com/dmholland/demo/models/User.java b/src/main/java/com/dmholland/demo/models/User.java
new file mode 100644
index 000000000..9ca7ba3fc
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/models/User.java
@@ -0,0 +1,96 @@
+package com.dmholland.demo.models;
+
+import org.hibernate.validator.constraints.Length;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotEmpty;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Entity
+@Table(name = "users")
+public class User {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)//Identity because it auto increments
+ private Long id;
+
+ @Column(length = 60)
+ @Length(min = 5, message = "Your password must have at least 5 characters")
+ @NotEmpty(message = "Please provide your password")
+ private String hashPassword;
+
+
+ @Column(nullable = false,length = 60, unique = true)
+ @NotEmpty(message = "Please provide a username")
+ private String username;
+
+ @Column(length = 60)
+ @Length(min = 5, message = "Your name must have at least 5 characters")
+ @NotEmpty(message = "You must enter your name")
+ private String fullName;
+
+ @OneToMany(mappedBy = "author") //Must be a container
+ private Set posts = new HashSet<>();
+
+ public User() {
+ }
+
+
+
+ public User(Long id, String username, String fullName) {
+ this.id = id;
+ this.username = username;
+ this.fullName = fullName;
+
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getHashPassword() {
+ return hashPassword;
+ }
+
+ public void setHashPassword(String hashPassword) {
+ this.hashPassword = hashPassword;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getFullName() {
+ return fullName;
+ }
+
+ public void setFullName(String fullName) {
+ this.fullName = fullName;
+ }
+
+ public Set getPosts() {
+ return posts;
+ }
+
+ public void setPosts(Set posts) {
+ this.posts = posts;
+ }
+
+ @Override
+ public String toString() {
+ return "User{" + "id= " + id + ", username='" + username + '\'' +
+ ", hashPassword='" + hashPassword + '\'' +
+ ", fullName='" + fullName + '\'' + '}';
+ }
+}
diff --git a/src/main/java/com/dmholland/demo/repository/PostRepository.java b/src/main/java/com/dmholland/demo/repository/PostRepository.java
new file mode 100644
index 000000000..304e4d9cf
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/repository/PostRepository.java
@@ -0,0 +1,17 @@
+package com.dmholland.demo.repository;
+
+import com.dmholland.demo.models.Post;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface PostRepository extends JpaRepository {
+
+
+ @Query(value="SELECT p.* FROM posts p ORDER BY p.date DESC", nativeQuery = true)
+ List findLatest5Posts(Pageable pageable);
+}
diff --git a/src/main/java/com/dmholland/demo/repository/UserRepository.java b/src/main/java/com/dmholland/demo/repository/UserRepository.java
new file mode 100644
index 000000000..0188f6e39
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/repository/UserRepository.java
@@ -0,0 +1,11 @@
+package com.dmholland.demo.repository;
+
+
+import com.dmholland.demo.models.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface UserRepository extends JpaRepository {
+ User findByusername(String username);
+}
diff --git a/src/main/java/com/dmholland/demo/services/NotificationService.java b/src/main/java/com/dmholland/demo/services/NotificationService.java
new file mode 100644
index 000000000..0ebce3d0b
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/services/NotificationService.java
@@ -0,0 +1,8 @@
+package com.dmholland.demo.services;
+
+
+
+public interface NotificationService {
+ void addInfoMessage(String msg);
+ void addErrorMessage(String msg);
+}
diff --git a/src/main/java/com/dmholland/demo/services/NotificationServiceImpl.java b/src/main/java/com/dmholland/demo/services/NotificationServiceImpl.java
new file mode 100644
index 000000000..5a7c45e10
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/services/NotificationServiceImpl.java
@@ -0,0 +1,66 @@
+package com.dmholland.demo.services;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpSession;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class NotificationServiceImpl implements NotificationService{
+ public static final String NOTIFY_MSG_SESSION_KEY = "siteNotificationMessages";
+
+ @Autowired
+ private HttpSession httpSession;
+
+ public enum NotificationMessageType{
+ INFO,
+ ERROR
+ }
+ @Override
+ public void addInfoMessage(String msg) {
+ addNotificationMessage(NotificationMessageType.INFO, msg);
+ }
+
+
+ @Override
+ public void addErrorMessage(String msg) {
+ addNotificationMessage(NotificationMessageType.ERROR, msg);
+ }
+
+
+private void addNotificationMessage(NotificationMessageType type, String msg){
+
+ List notifyMessages;
+ notifyMessages=(List)httpSession.getAttribute(NOTIFY_MSG_SESSION_KEY);
+ if(notifyMessages == null) {
+ notifyMessages = new ArrayList();
+ }
+ notifyMessages.add(new NotificationMessage(type,msg));
+ httpSession.setAttribute(NOTIFY_MSG_SESSION_KEY,notifyMessages);
+}
+
+
+
+ public static class NotificationMessage {
+
+ NotificationMessageType type;
+ String text;
+ public NotificationMessage(NotificationMessageType type, String text) {
+ this.type=type;
+ this.text=text;
+ }
+
+ public NotificationMessageType getType() {
+ return type;
+ }
+
+ public String getText(){
+ return text;
+ }
+ }
+
+
+}
diff --git a/src/main/java/com/dmholland/demo/services/PostService.java b/src/main/java/com/dmholland/demo/services/PostService.java
new file mode 100644
index 000000000..cb5829e2f
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/services/PostService.java
@@ -0,0 +1,70 @@
+package com.dmholland.demo.services;
+
+import com.dmholland.demo.models.Post;
+import com.dmholland.demo.repository.PostRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+
+
+
+/**
+ * Implement the PostService and UserService to Use the DB
+ * Just add new implementations for the UserService and PostService,
+ * annotated with @Primary.
+ * This will tell the Spring Framework to use these implementations instead of the old stubs.
+ *
+ */
+@Service
+public class PostService implements PostServiceInterface {
+
+ @Autowired
+ private PostRepository postRepository;
+
+ @Override
+ public List findAll() {
+ return this.postRepository.findAll();
+ }
+
+ @Override
+ public List findLatest5() {
+ // Create our own query
+ // return this.postRepository.findLatest5Posts( PageRequest.of(0,5) );
+ //Using Streams also worked Descending order
+ return this.postRepository.findAll( PageRequest.of(0, 2) ).stream().sorted( (a,b) -> a.getDate().compareTo(b.getDate()) ).collect(Collectors.toList());
+ }
+ @Override
+ public Post findById(Long id) {
+ return this.postRepository.findById(id).orElse(null);
+ // return this.postRepository.getOne(id);
+ }
+
+ @Override
+ public Post create(Post post) {
+ return this.postRepository.save(post);
+ }
+ @Override
+ public Post edit(Post post) {
+ return this.postRepository.save(post);
+ }
+ @Override
+ public void deleteById(Long id) {
+ this.postRepository.deleteById(id);
+ }
+
+ @Override
+ public boolean isValid(Post post){
+ if(null == post.getTitle() ||null == post.getBody() || null == post.getAuthor()){return false;}
+ return true;
+ }
+
+ @Override
+ public Page findAll(Pageable pageable) {
+ return this.postRepository.findAll(pageable);
+ }
+}
diff --git a/src/main/java/com/dmholland/demo/services/PostServiceInterface.java b/src/main/java/com/dmholland/demo/services/PostServiceInterface.java
new file mode 100644
index 000000000..4c05dd43b
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/services/PostServiceInterface.java
@@ -0,0 +1,21 @@
+package com.dmholland.demo.services;
+
+import com.dmholland.demo.models.Post;
+import com.dmholland.demo.repository.PostRepository;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+
+public interface PostServiceInterface {
+ List findAll();
+ List findLatest5();
+ Post findById(Long id);
+ Post create(Post post);
+ Post edit(Post post);
+ void deleteById(Long id);
+ boolean isValid(Post post);
+ Page findAll(Pageable pageable);
+}
diff --git a/src/main/java/com/dmholland/demo/services/UserService.java b/src/main/java/com/dmholland/demo/services/UserService.java
new file mode 100644
index 000000000..53b69ca05
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/services/UserService.java
@@ -0,0 +1,78 @@
+package com.dmholland.demo.services;
+
+import com.dmholland.demo.models.Post;
+import com.dmholland.demo.models.User;
+import com.dmholland.demo.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Set;
+
+@Service
+@Primary
+public class UserService implements UserServiceInterface{
+
+ @Autowired
+ private UserRepository repository;
+
+ @Autowired
+ private BCryptPasswordEncoder bCryptPasswordEncoder;
+
+ @Override
+ public List findAll() {
+ return this.repository.findAll();
+ }
+
+ @Override
+ public Page findAll(Pageable pageable) {
+ return this.repository.findAll(pageable);
+ }
+
+
+ @Override
+ public User createUser(User user) {
+ user.setHashPassword(bCryptPasswordEncoder.encode(user.getHashPassword()));
+ return this.repository.save(user);
+ }
+
+
+ @Override
+ public void deleteUser(Long Id) {
+ this.repository.delete(findById(Id));
+ }
+
+ @Override
+ public User findById(Long id) {
+ return this.repository.getOne(id);
+ }
+
+ @Override
+ public User findByName(String name) {
+ return findAll().stream()
+ .filter(x->x.getFullName().equals(name))
+ .findFirst().orElse(null);
+ }
+
+ @Override
+ public User editUser(User user) {
+ return this.repository.save(user);
+ }
+
+ @Override
+ public Set findAllPosts(User user) {
+ return user.getPosts();
+ }
+
+ @Override
+ public User findByUserName(String username) {
+ return this.repository.findByusername(username);}
+
+ public boolean userCheck(User user){
+ return (null != findByName(user.getUsername()));
+ }
+}
diff --git a/src/main/java/com/dmholland/demo/services/UserServiceInterface.java b/src/main/java/com/dmholland/demo/services/UserServiceInterface.java
new file mode 100644
index 000000000..2581dc41b
--- /dev/null
+++ b/src/main/java/com/dmholland/demo/services/UserServiceInterface.java
@@ -0,0 +1,21 @@
+package com.dmholland.demo.services;
+
+import com.dmholland.demo.models.Post;
+import com.dmholland.demo.models.User;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.List;
+import java.util.Set;
+
+public interface UserServiceInterface {
+ List findAll();
+ Page findAll(Pageable pageable);
+ User createUser(User user);
+ void deleteUser(Long id);
+ User findById(Long id);
+ User findByName(String name);
+ User editUser(User user);
+ Set findAllPosts(User user);
+User findByUserName(String username);
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 000000000..088ef43eb
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,58 @@
+# ==============================================================
+# src/main/resources/application.properties
+# ==============================================================
+# = Server port to use
+# ==============================================================
+server.port=8080
+
+# ===============================
+# = Thymeleaf configurations
+# ===============================
+spring.thymeleaf.cache=false
+
+# ==============================================================
+# = Disable the Whitelabel Error Page
+# ==============================================================
+server.error.whitelabel.enabled=false
+
+# ==============================================================
+# = DATA SOURCE - MySQL
+# ==============================================================
+#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
+spring.datasource.url=jdbc:mysql://localhost:3306/blog2_db?createDatabaseIfNotExist=true&serverTimezone=UTC&useLegacyDatetimeCode=true
+spring.datasource.username=root
+spring.datasource.password=
+
+# ==============================================================
+# = Spring Security / Queries for AuthenticationManagerBuilder
+# ==============================================================
+spring.queries.users-query=select username, hash_password, id from users where username=?
+#spring.queries.roles-query=select u.user_name, r.role from users u inner join user_role ur on(u.user_id=ur.user_id) inner join role r on(ur.role_id=r.role_id) where u.user_name=?
+# Don't want to use roles for now so set all as ADMIN
+# ==============================================================
+# if you want to use hasRole('ADMIN'), use this:
+#spring.queries.roles-query=select user_name, 'ROLE_ADMIN' AS 'role' from users where user_name=?
+# ==============================================================
+# if you want to use hasAuthority('ADMIN'), use this:
+spring.queries.roles-query=select username, 'ADMIN' AS 'role' from users where username=?
+
+# ==============================================================
+# = JPA / HIBERNATE
+# ==============================================================
+# Configure Hibernate DDL mode: create / update
+# spring.jpa.properties.hibernate.hbm2ddl.auto = create
+# spring.jpa.properties.hibernate.hbm2ddl.auto = update
+#
+# Create the Database with hbm2ddl.auto
+# Ensure the hbm2ddl is enabled (value "create"). NOTE, this needs to be done once !!!, after creating the initial DB setup change to update
+# This will drop the database at application startup and will re-create the database tables according to the entity classes found in the project.
+# ==============================================================
+spring.jpa.show-sql=true
+spring.jpa.properties.hibernate.hbm2ddl.auto=update
+
+
+spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
+spring.jpa.hibernate.ddl-auto=update
+spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
+#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect
+
diff --git a/src/main/resources/public/css/styles.css b/src/main/resources/public/css/styles.css
new file mode 100644
index 000000000..46008595c
--- /dev/null
+++ b/src/main/resources/public/css/styles.css
@@ -0,0 +1,77 @@
+body>header {
+ background: #eee;
+ padding: 5px;
+}
+
+body>header>a>img, body>header a{
+ display: inline-block;
+ vertical-align: middle;
+ padding: 0px 5px;
+ font-size: 1.2em;
+
+}
+
+body>footer{
+ background: #eee;
+ padding: 5px;
+ margin: 10px 0;
+ text-align:center;
+}
+
+#logged-in-info {
+ float: right;
+ margin-top: 18px;
+}
+
+#logged-in-info form{
+ display: inline-block;
+ margin-right: 10px;
+}
+
+body>aside {
+ float: right;
+ width: 200px;
+ background: #CCC;
+ padding: 15px;
+ margin-left: 20px;
+ margin-top: 10px;
+}
+
+body>aside h2 {
+ margin: 5px 0 15px 0;
+}
+
+body>aside a {
+ display: block;
+ margin: 5px 0;
+ text-decoration: none;
+}
+
+body>main:after {
+ content: '';
+ display: block;
+ clear: both;
+}
+
+ul#messages li {
+ display: block;
+ width: 80%;
+ margin: 5px auto;
+ text-align: center;
+ padding: 5px;
+ border-radius: 5px;
+}
+
+ul#messages li.info{
+ background: #7f7;
+}
+
+ul#messages li.error{
+ background: #d00;
+ color:white;
+}
+
+.formError {
+ color: red;
+ font-style:italic;
+}
\ No newline at end of file
diff --git a/src/main/resources/public/img/Messedup.png b/src/main/resources/public/img/Messedup.png
new file mode 100644
index 000000000..ba4b2d1b3
Binary files /dev/null and b/src/main/resources/public/img/Messedup.png differ
diff --git a/src/main/resources/public/img/favicon.ico b/src/main/resources/public/img/favicon.ico
new file mode 100644
index 000000000..710c06a3a
Binary files /dev/null and b/src/main/resources/public/img/favicon.ico differ
diff --git a/src/main/resources/public/img/not_there.png b/src/main/resources/public/img/not_there.png
new file mode 100644
index 000000000..72e3e1b90
Binary files /dev/null and b/src/main/resources/public/img/not_there.png differ
diff --git a/src/main/resources/public/img/notauthorized2.png b/src/main/resources/public/img/notauthorized2.png
new file mode 100644
index 000000000..88875ed45
Binary files /dev/null and b/src/main/resources/public/img/notauthorized2.png differ
diff --git a/src/main/resources/public/img/oops.png b/src/main/resources/public/img/oops.png
new file mode 100644
index 000000000..2fe0efaab
Binary files /dev/null and b/src/main/resources/public/img/oops.png differ
diff --git a/src/main/resources/public/img/page-not-found.png b/src/main/resources/public/img/page-not-found.png
new file mode 100644
index 000000000..51e8f702c
Binary files /dev/null and b/src/main/resources/public/img/page-not-found.png differ
diff --git a/src/main/resources/public/img/sad-face.jpg b/src/main/resources/public/img/sad-face.jpg
new file mode 100644
index 000000000..a7e693456
Binary files /dev/null and b/src/main/resources/public/img/sad-face.jpg differ
diff --git a/src/main/resources/public/img/site-logo.png b/src/main/resources/public/img/site-logo.png
new file mode 100644
index 000000000..64f5f91ef
Binary files /dev/null and b/src/main/resources/public/img/site-logo.png differ
diff --git a/src/main/resources/public/img/sunglasses-face.jpg b/src/main/resources/public/img/sunglasses-face.jpg
new file mode 100644
index 000000000..f8cdf2942
Binary files /dev/null and b/src/main/resources/public/img/sunglasses-face.jpg differ
diff --git a/src/main/resources/public/js/blog-scripts.js b/src/main/resources/public/js/blog-scripts.js
new file mode 100644
index 000000000..9883fd9d6
--- /dev/null
+++ b/src/main/resources/public/js/blog-scripts.js
@@ -0,0 +1,8 @@
+$(function() {
+ $('#messages li').cick(function(){
+ $(this).fadeOut();
+ });
+ setTimeout(function() {
+ $('#messages li.info').fadeOut();
+ })
+})
\ No newline at end of file
diff --git a/src/main/resources/public/js/jquery-3.1.0.min.js b/src/main/resources/public/js/jquery-3.1.0.min.js
new file mode 100644
index 000000000..949e9d0ce
--- /dev/null
+++ b/src/main/resources/public/js/jquery-3.1.0.min.js
@@ -0,0 +1,10872 @@
+/*!
+ * jQuery JavaScript Library v3.5.1
+ * https://jquery.com/
+ *
+ * Includes Sizzle.js
+ * https://sizzlejs.com/
+ *
+ * Copyright JS Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2020-05-04T22:49Z
+ */
+( function( global, factory ) {
+
+ "use strict";
+
+ if ( typeof module === "object" && typeof module.exports === "object" ) {
+
+ // For CommonJS and CommonJS-like environments where a proper `window`
+ // is present, execute the factory and get jQuery.
+ // For environments that do not have a `window` with a `document`
+ // (such as Node.js), expose a factory as module.exports.
+ // This accentuates the need for the creation of a real `window`.
+ // e.g. var jQuery = require("jquery")(window);
+ // See ticket #14549 for more info.
+ module.exports = global.document ?
+ factory( global, true ) :
+ function( w ) {
+ if ( !w.document ) {
+ throw new Error( "jQuery requires a window with a document" );
+ }
+ return factory( w );
+ };
+ } else {
+ factory( global );
+ }
+
+// Pass this if window is not defined yet
+} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
+// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
+// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
+// enough that all such attempts are guarded in a try block.
+ "use strict";
+
+ var arr = [];
+
+ var getProto = Object.getPrototypeOf;
+
+ var slice = arr.slice;
+
+ var flat = arr.flat ? function( array ) {
+ return arr.flat.call( array );
+ } : function( array ) {
+ return arr.concat.apply( [], array );
+ };
+
+
+ var push = arr.push;
+
+ var indexOf = arr.indexOf;
+
+ var class2type = {};
+
+ var toString = class2type.toString;
+
+ var hasOwn = class2type.hasOwnProperty;
+
+ var fnToString = hasOwn.toString;
+
+ var ObjectFunctionString = fnToString.call( Object );
+
+ var support = {};
+
+ var isFunction = function isFunction( obj ) {
+
+ // Support: Chrome <=57, Firefox <=52
+ // In some browsers, typeof returns "function" for HTML