diff --git a/build.gradle b/build.gradle index f6fab35009a6f72fd446eedd67ad1cd73686f040..cb5f573cc10ea35ff24ce651c551df82c32873e7 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.mariadb.jdbc:mariadb-java-client:2.1.2' - testImplementation 'junit:junit:4.13.1' + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'junit:junit:4.13.1' compileOnly 'org.projectlombok:lombok' testImplementation 'org.projectlombok:lombok:1.18.28' compileOnly 'org.projectlombok:lombok' @@ -35,6 +36,8 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.1.RELEASE' // https://mvnrepository.com/artifact/org.webjars/openlayers implementation group: 'org.webjars', name: 'openlayers', version: '5.2.0' } @@ -42,6 +45,9 @@ dependencies { tasks.named('bootBuildImage') { builder = 'paketobuildpacks/builder-jammy-base:latest' } +test { + useJUnitPlatform() +} tasks.named('test') { useJUnitPlatform() diff --git a/src/main/java/Team5/SmartTowns/rewards/RewardsRepositoryJDBC.java b/src/main/java/Team5/SmartTowns/rewards/RewardsRepositoryJDBC.java index 65eaa9843ec3af33e994ea34ed8d219ce712ffaf..74c97a5f29319ab6dbefc042bcbc10e364b4bd03 100644 --- a/src/main/java/Team5/SmartTowns/rewards/RewardsRepositoryJDBC.java +++ b/src/main/java/Team5/SmartTowns/rewards/RewardsRepositoryJDBC.java @@ -10,7 +10,6 @@ import java.util.List; @Repository public class RewardsRepositoryJDBC implements RewardsRepository { private final JdbcTemplate jdbc; - private RowMapper<Badge> badgeMapper; private RowMapper<Sticker> stickerMapper; private RowMapper<Pack> packMapper; @@ -51,8 +50,8 @@ public class RewardsRepositoryJDBC implements RewardsRepository { @Override public List<Sticker> getAllStickersFromPack(int packID){ - String sql= "SELECT * FROM stickers WHERE packID="+packID; - return jdbc.query(sql, stickerMapper); + String sql= "SELECT * FROM stickers WHERE packID=?"; + return jdbc.query(sql, stickerMapper, packID); } @Override @@ -60,14 +59,14 @@ public class RewardsRepositoryJDBC implements RewardsRepository { /* FINDS ALL STICKERS UNLOCKED BY THE GIVEN USER */ String sql= "SELECT * FROM stickers LEFT JOIN stickerprogress " + "ON (stickers.id, stickers.packID) = (stickerprogress.stickerID, stickerprogress.packID) " + - "WHERE stickerprogress.userID="+userID; - return jdbc.query(sql, stickerMapper); + "WHERE stickerprogress.username = ? "; + return jdbc.query(sql, stickerMapper, userID); } @Override public Pack findPackByID(int id){ - String sql= "SELECT * FROM packs WHERE id="+id; - List<Pack> result = jdbc.query(sql, packMapper); + String sql= "SELECT * FROM packs WHERE id= ?"; + List<Pack> result = jdbc.query(sql, packMapper, id); return result.isEmpty() ? null : result.get(0); } } diff --git a/src/main/java/Team5/SmartTowns/security/SecurityConfiguration.java b/src/main/java/Team5/SmartTowns/security/SecurityConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..afce617e22c6e82aa040896794e144ba0558ae37 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/security/SecurityConfiguration.java @@ -0,0 +1,51 @@ +package Team5.SmartTowns.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.JdbcUserDetailsManager; +import org.springframework.security.provisioning.UserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +import javax.sql.DataSource; + + +@Configuration +@EnableWebSecurity +public class SecurityConfiguration { + /* Configures the longin features and tracks logged on users on the page */ + + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((requests) -> requests + .requestMatchers("/user/**", "/userProfile").authenticated() + .anyRequest().permitAll() + ) + .formLogin((login) -> login + .loginPage("/login").permitAll() + .defaultSuccessUrl("/userProfile") + ) + .logout((logout) -> logout.permitAll()); + + return http.build(); + } + @Bean + public PasswordEncoder passwordEncoder(){ + /* Nothing here yet, this just saves passwords in plaintext. TODO password encryption */ + return NoOpPasswordEncoder.getInstance(); + } + + @Bean + public UserDetailsManager userDetailsManager(DataSource dataSource){ + JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource); + return manager; + } + + +} diff --git a/src/main/java/Team5/SmartTowns/users/NewUser.java b/src/main/java/Team5/SmartTowns/users/NewUser.java new file mode 100644 index 0000000000000000000000000000000000000000..aa47adac9931083fb0d199375e50032ce30ccd60 --- /dev/null +++ b/src/main/java/Team5/SmartTowns/users/NewUser.java @@ -0,0 +1,26 @@ +package Team5.SmartTowns.users; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class NewUser { + + + @NotEmpty(message = "You must type in a username.") + @NotNull + String name; + + @NotEmpty(message = "You must type in a password.") + @NotNull + String password; + + @NotEmpty(message = "You must type in an email.") + @NotNull + String email; +} diff --git a/src/main/java/Team5/SmartTowns/users/User.java b/src/main/java/Team5/SmartTowns/users/User.java index 4ef65c3b288f9433e37d60e8e984b869f3a8d53e..96949fb1b8543684407118138878b21db47826fe 100644 --- a/src/main/java/Team5/SmartTowns/users/User.java +++ b/src/main/java/Team5/SmartTowns/users/User.java @@ -27,8 +27,7 @@ public class User { this.dragonProgress = dragonProgress; imgPath = findImagePath(); } - public User(int id, String email, String name) { - this.id = id; + public User(String email, String name) { this.email = email; this.name = name; imgPath = findImagePath(); diff --git a/src/main/java/Team5/SmartTowns/users/UserController.java b/src/main/java/Team5/SmartTowns/users/UserController.java index 989d13b7161f783dc75bdcb9dbf379808b1584e4..e1acd4b66f1546db506ec5313c5680122e05587d 100644 --- a/src/main/java/Team5/SmartTowns/users/UserController.java +++ b/src/main/java/Team5/SmartTowns/users/UserController.java @@ -4,10 +4,15 @@ package Team5.SmartTowns.users; import Team5.SmartTowns.rewards.Pack; import Team5.SmartTowns.rewards.RewardsRepository; import Team5.SmartTowns.rewards.Sticker; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import java.util.List; @@ -17,45 +22,87 @@ public class UserController { @Autowired private UserRepository userRepository; + @Autowired private RewardsRepository rewardsRepository; + /* LOGIN MAPPING & FUNCTIONS */ @GetMapping("/login") public ModelAndView getLoginPage() { ModelAndView mav = new ModelAndView("users/login"); -// List<User> users = userRepository.getAllUsers(); -// mav.addObject("users", users); + mav.addObject("user", new NewUser( "", "", "")); + mav.addObject("error", ""); + mav.addObject("status", ""); + System.out.println(userRepository.findUserByName("Admin").getName()); return mav; } - @GetMapping("/userList") - public ModelAndView userList() { - ModelAndView mav = new ModelAndView("towns/userData"); - List<User> users = userRepository.getAllUsers(); - mav.addObject("users", users); +// @GetMapping("/logout") +// public ModelAndView getLogOutPage(){ +// ModelAndView mav = new ModelAndView("users/logout"); +// return mav; +// } + + @PostMapping("/login/register") + public ModelAndView registerUser(@Valid @ModelAttribute("user") NewUser user, BindingResult bindingResult, Model model) { + ModelAndView mav = new ModelAndView("users/login", model.asMap()); + // TODO VALIDATE EMAIL INPUT + mav.addObject("status", "active"); + if (bindingResult.hasErrors()) { + ModelAndView modelAndView = new ModelAndView("users/login"); + modelAndView.addObject("errors", bindingResult); + return modelAndView; + } + + if ( userRepository.doesUserExist(user.getEmail()) ) { + mav.addObject("errors", "Email already in use"); + return mav; + } + + try { + userRepository.addUser(user.name, user.email, user.password); + mav.addObject("error", ""); + //TODO return user creation success + return mav; + } catch (DataAccessException e) { + mav.addObject("error", "User exists"); + } return mav; } - @GetMapping("/user/{id}") - public ModelAndView getUserPage(@PathVariable int id) { + @GetMapping("/userProfile") + public ModelAndView userProfile(){ ModelAndView mav = new ModelAndView("users/userProfile"); List<Pack> allPacks = rewardsRepository.getAllPacks(); - mav.addObject("user", userRepository.getUserById(id)); mav.addObject("packs", allPacks); - mav.addAllObjects(getPackInfo(id, 1).getModelMap()); + User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + mav.addObject("user", userRepository.findUserByName("Admin")); + mav.addAllObjects(getPackInfo("Admin", 1).getModelMap()); + return mav; + } + + /* USER MAPPING & FUNCTIONS */ + @GetMapping("/user/{username}") + public ModelAndView getUserPage(@PathVariable String username) { + ModelAndView mav = new ModelAndView("users/userProfile"); + List<Pack> allPacks = rewardsRepository.getAllPacks(); + mav.addObject("user", userRepository.findUserByName("Admin")); + mav.addObject("packs", allPacks); + mav.addAllObjects(getPackInfo(username, 1).getModelMap()); return mav; } - @GetMapping("/packInfo/{userID}/{packID}") - public ModelAndView getPackInfo(@PathVariable int userID, @PathVariable int packID) { + @GetMapping("/packInfo/{username}/{packID}") + public ModelAndView getPackInfo(@PathVariable String username, @PathVariable int packID) { /* Displays on page the stickers present in the pack and colour the ones the * user has acquired */ ModelAndView mav = new ModelAndView("users/userFrags :: stickersBox"); List<Sticker> allStickers = rewardsRepository.getAllStickersFromPack(packID); - List<Long> userStickers = userRepository.getUserStickersFromPack(userID, packID); + List<Long> userStickers = userRepository.getUserStickersFromPack(username, packID); + System.out.println(userStickers); @@ -86,4 +133,7 @@ public class UserController { return displayedStickers; } + + + } diff --git a/src/main/java/Team5/SmartTowns/users/UserRepository.java b/src/main/java/Team5/SmartTowns/users/UserRepository.java index f0f01269786128dd9afaa8d3becd5b3ada4944fa..097598e24dce331a62b34346c3ec2326fa215934 100644 --- a/src/main/java/Team5/SmartTowns/users/UserRepository.java +++ b/src/main/java/Team5/SmartTowns/users/UserRepository.java @@ -5,7 +5,10 @@ import java.util.List; public interface UserRepository { List<User> getAllUsers(); - List<Long> getUserStickersFromPack(int userID, int packID); - User getUserById(int userID); - boolean unlockSticker(int userID, int packID, int stickerID); + List<Long> getUserStickersFromPack(String username, int packID); + boolean unlockSticker(String username, int packID, int stickerID); + boolean addUser(String username, String email, String password); + boolean doesUserExist(String email); + User findUserByEmail(String email); + User findUserByName(String name); } diff --git a/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java b/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java index 8b46fc3a568455e91c180b66fc91b65f20d20c81..a88ac6e1c91fc85e5eb19fb6f0f7fcef3b9cb833 100644 --- a/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java +++ b/src/main/java/Team5/SmartTowns/users/UserRepositoryJDBC.java @@ -1,16 +1,23 @@ //Implements the users repository using JDBC package Team5.SmartTowns.users; +import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.object.SqlQuery; import org.springframework.stereotype.Repository; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.HashMap; import java.util.List; +import java.util.Objects; @Repository public class UserRepositoryJDBC implements UserRepository{ - private JdbcTemplate jdbc; + private final JdbcTemplate jdbc; private RowMapper<User> userMapper; public UserRepositoryJDBC(JdbcTemplate aJdbc){ @@ -20,9 +27,8 @@ public class UserRepositoryJDBC implements UserRepository{ private void setUserMapper(){ userMapper = (rs, i) -> new User( - rs.getInt("id"), rs.getString("email"), - rs.getString("name") + rs.getString("username") ); } @@ -34,23 +40,57 @@ public class UserRepositoryJDBC implements UserRepository{ @Override - public User getUserById(int userID){ - String sql= "SELECT * FROM users WHERE id="+userID; - List<User> result = jdbc.query(sql, userMapper); - return result.isEmpty() ? null : result.get(0); + public List<Long> getUserStickersFromPack(String username, int packID) { + /* Returns a list with the stickerIDs of stickers that the specified user has unlocked from the given pack */ + String sql = "SELECT stickerID FROM stickerprogress WHERE (username, packID)= (?,?)"; + return jdbc.queryForList(sql, Long.class, username, packID); } @Override - public List<Long> getUserStickersFromPack(int userID, int packID) { - String sql = "SELECT stickerID FROM stickerprogress WHERE (userID, packID)= (" + userID + "," + packID + ")"; - return jdbc.queryForList(sql, Long.class); + public boolean unlockSticker(String username, int packID, int stickerID){ + /* Adds entry in the stickerprogress database, effectively unlocking the sticker for the user + * Returns false if no sticker is unlocked */ + String query = "SELECT COUNT(id) FROM stickers WHERE (stickerID, packID) = (?, ?)"; + + int stickerCount = jdbc.queryForObject(query, Integer.class, stickerID, packID); + + if (stickerCount == 1){ //Checks if sticker exists + String sql = "INSERT INTO stickerprogress (username, packID, stickerID) VALUES (?,?,?)"; + jdbc.update(sql, username, packID, stickerID); + return true; + } + return false; } @Override - public boolean unlockSticker(int userID, int packID, int stickerID){ - String sql = "INSERT INTO stickerprogress (userID, packID, stickerID) VALUES (" + - userID + ", " + packID + "," + stickerID + ")"; - jdbc.update(sql); + public boolean addUser(String username, String email, String password) throws DataAccessException{ + /* Adds new user to the database */ + String query = "INSERT INTO users (username, email, password) VALUES (?, ?, ?);"; + String query2= "INSERT INTO authorities (username, authority) VALUES (?,?);"; + jdbc.update(query, username, email, password); + jdbc.update(query2, username, "USER"); return true; } + @Override + public boolean doesUserExist(String email){ + /* Returns true if a user with given email already exists in the database */ + String query = "SELECT COUNT(email) FROM users WHERE (email) = (?)"; + return !(jdbc.queryForObject(query, Integer.class, email) == 0); + } + + @Override + public User findUserByEmail(String email) { + /* Finds user matching given email */ + String query = "SELECT * FROM users WHERE (email) = (?)"; + List<User> result = jdbc.query(query, userMapper, email); + return result.isEmpty() ? null : result.get(0); + } + @Override + public User findUserByName(String name) { + /* Finds user matching given name */ + String query = "SELECT * FROM users WHERE (username) = (?)"; + List<User> result = jdbc.query(query, userMapper, name); + return result.isEmpty() ? null : result.get(0); + } + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 95f46c69ab2c6b38e1c520a5b8438c8f87aba0a3..52e81d219c68d4702643665e6f9d58c7aebb8308 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,3 +3,4 @@ spring.datasource.username=root spring.datasource.password=comsc spring.sql.init.mode=always +spring.sql.init.data-locations=classpath:data.sql, classpath:/static/sql/user-data.sql, classpath:/static/sql/user-progress-data.sql diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000000000000000000000000000000000000..b91b4f71db6b6d4005596c17a8c75a2a9f178d34 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,52 @@ +delete from trails; +insert into trails ( Name,tru) value ( 'Caerphilly Coffee Trail',false); +insert into trails ( Name,tru) value ( 'Penarth Dragon Trail',true); + +delete from locations; +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'St Cenydd','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Castle','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Medieval Trades','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Queen''s War','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Green Lady','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Armoury','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Architecture','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( '21st Century Landmark','','Location description here','Caerphilly',0101, true); + +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'JD Wetherspoons-Malcolm Uphill','','Location description here','Caerphilly',0102, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Caerphilly Cwtch','','Location description here','Caerphilly',0102, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Caerphilly Conservative Club','','Location description here','Caerphilly',0102, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The King''s Arms','','Location description here','Caerphilly',0102, true); + +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Caerphilly Bus Station','','Location description here','Caerphilly',0103, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Medieval Courthouse','','Location description here','Caerphilly',0103, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ('Caerphilly Castle','','Location description here','Caerphilly',0103, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Ty Vaughan House','','Location description here','Caerphilly',0103, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Risca Colliery','','Location description here','Risca',0201, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Black Vein Colliery Disaster','','Location description here','Risca',0201, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Esplanade','','Location description here','Penarth',0301, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Old Swimming Baths','','Location description here','Penarth',0301, true); + + + + + +DELETE FROM packs; +INSERT INTO packs (name, description) VALUE ('Wales Football Team', 'Pack of Welsh Football Players in the National Team'); +INSERT INTO packs (name, description) VALUE ('Wales Rugby Team', 'Pack of Welsh Rugby Players in the National Team'); +INSERT INTO packs (name, description) VALUE ('Welsh Heritage', 'Pack About Welsh Heritage'); + +DELETE FROM stickers; +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 1, 'wayne_hennessey', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 2, 'neco_williams', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 3, 'joe_morrell', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 4, 'ethan_ampadu', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 5, 'connor_roberts', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 1, 'Taine_Basham', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 2, 'Adam Beard', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 3, 'Elliot Dee', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 4, 'Corey Domachowski', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 5, 'Ryan Elias', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (3, 1, 'Welsh Lady', 'Welsh Heritage', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (3, 2, 'Welsh Outline', 'Welsh Heritage', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (3, 3, 'Welsh Spoon', 'Welsh Heritage', '1'); + diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000000000000000000000000000000000000..403c5bf4ba23873ea9d6e8da956823c5830d2353 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,113 @@ +/* DELETES AND RECREATES DATABASE EVERY TIME THE SYSTEM IS BOOTED*/ +DROP DATABASE IF EXISTS towns; +CREATE DATABASE IF NOT EXISTS towns; +USE towns; +/****************************************************************/ + +/* DROPS ALL TABLES IF THEY EXIST (they wont but just in case) */ +DROP TABLE IF EXISTS trails; +DROP TABLE IF EXISTS locations; +DROP TABLE IF EXISTS users; +DROP TABLE IF EXISTS stickers; +DROP TABLE IF EXISTS packs; +DROP TABLE IF EXISTS stickerProgress; +/****************************************************************/ + +/* CREATES ALL TABLES */ + +CREATE TABLE IF NOT EXISTS trails ( + trailID bigint auto_increment primary key, + name varchar(128), + tru boolean +) engine=InnoDB; + +drop table if exists locationCoordinates; +drop table if exists locations; +create table if not exists locations +( + locationID bigint auto_increment primary key, + locationName varchar(128), + locationEmail varchar(128), + locationDescription longtext, + locationPlace varchar(255), + locationTrailID varchar(128), + locationApproved boolean +) engine=InnoDB; + + +CREATE TABLE IF NOT EXISTS users ( + username varchar(30) primary key NOT NULL, + id bigint auto_increment unique, /*DEPRECATED COLUMN, LEFT IN WHILE SOME OTHER FUNCTIONS STILL USE IT*/ + email varchar(128), + password varchar(30) NOT NULL, + enabled boolean default true +); + +CREATE TABLE IF NOT EXISTS authorities ( + id bigint primary key auto_increment NOT NULL, + username varchar(30) NOT NULL , + authority varchar(45) NOT NULL +); + +CREATE TABLE IF NOT EXISTS packs ( + id bigint auto_increment primary key, + name varchar(20) NOT NULL, + description text +); + +CREATE TABLE IF NOT EXISTS stickers ( + id bigint auto_increment primary key, + packID bigint NOT NULL, + FOREIGN KEY (packID) REFERENCES packs(id) + ON DELETE CASCADE + ON UPDATE RESTRICT, + stickerID bigint NOT NULL, /*STICKER ID NUMBER WITHIN ITS OWN PACK*/ + name varchar(30) NOT NULL, + description text NOT NULL, + rarity tinyint +); + +CREATE TABLE IF NOT EXISTS stickerProgress ( + id bigint auto_increment primary key, + username varchar(30) NOT NULL, + FOREIGN KEY (username) REFERENCES users(username) + ON DELETE CASCADE + ON UPDATE RESTRICT, + packID bigint NOT NULL, + FOREIGN KEY (packID) REFERENCES packs(id) + ON DELETE CASCADE + ON UPDATE RESTRICT, + stickerID bigint NOT NULL, + FOREIGN KEY (stickerID) REFERENCES stickers(id) + ON DELETE CASCADE + ON UPDATE RESTRICT +); + +create table if not exists locationCoordinates +( + locationCoordID bigint auto_increment primary key, + locationID bigint, + Foreign Key (locationID) REFERENCES locations(locationID) + ON DELETE CASCADE + ON UPDATE RESTRICT, + locationCoordsLat DECIMAL(8,6), + locationCoordsLong DECIMAL(8,6) + + +)engine=InnoDB; + + +drop table if exists townsWithTrails; +create table if not exists townsWithTrails +( + townID bigint auto_increment primary key, + townName varchar(128), + townCentreCoordsLat varchar(128), + townCentreCoordsLong varchar(128), + townUppermostCoordsLat varchar(128), + townLowermostCoordsLat varchar(128), + townLeftmostCoordsLong varchar(128), + townRightmostCoordsLong varchar(128) + +)engine=InnoDB; + diff --git a/src/main/resources/static/css/login.css b/src/main/resources/static/css/login.css new file mode 100644 index 0000000000000000000000000000000000000000..023566c253e9354e789e85744cd1e4989d523845 --- /dev/null +++ b/src/main/resources/static/css/login.css @@ -0,0 +1,372 @@ +@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap'); + +:root { + --container-colour: #2a2a2a; + --details-colour: var(--primary-light); + --details-light: #512da8; + + --font-buttons: 14px; + --font-size-1: 14px; + --font-size-2: 200px; + --font-size-3: 300px; + + --error-colour: red; +} + +*{ + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Montserrat', sans-serif; + color: white; +} + +body{ + align-items: center; + height: 100svh; +} + +main { + height: 90%; + width: 90%; + display: flex; + align-items: center; + justify-content: center; +} + +@keyframes move{ + 0%, 49.99%{ + opacity: 0; + z-index: 1; + } + 50%, 100%{ + opacity: 1; + z-index: 5; + } +} + +@media only screen +and (min-device-width: 650px) { + .container{ + background-color: var(--container-colour); + border-radius: 30px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.35); + position: relative; + overflow: hidden; + min-width: 768px; + max-width: 100%; + min-height: 480px; + } + + .container p{ + font-size: 14px; + line-height: 20px; + letter-spacing: 0.3px; + margin: 20px 0; + } + + + .container a{ + font-size: var(--font-size-1); + text-decoration: none; + margin: 15px 0 10px; + } + + .container button{ + background-color: var(--details-colour)/*#512da8*/; + color: #fff; + font-size: var(--font-buttons); + padding: 10px 45px; + border: 1px solid transparent; + border-radius: 8px; + font-weight: 600; + letter-spacing: 0.5px; + text-transform: uppercase; + margin-top: 10px; + cursor: pointer; + } + + .container button.hidden{ + background-color: transparent; + border-color: #fff; + } + + .container form{ + background-color: var(--container-colour); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 0 40px; + height: 100%; + } + + .container input{ + background-color: #eee; + border: none; + margin: 8px 0; + padding: 10px 15px; + font-size: 13px; + border-radius: 8px; + width: 100%; + outline: none; + } + + .form-container{ + position: absolute; + top: 0; + height: 100%; + transition: all 0.6s ease-in-out; + } + + .sign-in{ + left: 0; + width: 50%; + z-index: 2; + } + + .container.active .sign-in{ + transform: translateX(100%); + } + + .sign-up{ + left: 0; + width: 50%; + opacity: 0; + z-index: 1; + } + + .container.active .sign-up{ + transform: translateX(100%); + opacity: 1; + z-index: 5; + animation: move 0.6s; + } + + + + .toggle-container{ + position: absolute; + top: 0; + left: 50%; + width: 50%; + height: 100%; + overflow: hidden; + transition: all 0.6s ease-in-out; + border-radius: 150px 0 0 100px; + z-index: 1000; + } + + .container.active .toggle-container{ + transform: translateX(-100%); /*BG THING*/ + border-radius: 0 150px 100px 0; + } + + .toggle{ + background-color: var(--details-colour); + height: 100%; + background: linear-gradient(to right, var(--details-light), var(--details-colour)); + color: #fff; + position: relative; + left: -100%; + height: 100%; + width: 200%; + transform: translateX(0); + transition: all 0.6s ease-in-out; + } + + .container.active .toggle{ + transform: translateX(50%); + } + + .toggle-panel{ + position: absolute; + width: 50%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + padding: 0 30px; + text-align: center; + top: 0; + transform: translateX(0); + transition: all 0.6s ease-in-out; + } + + .toggle-left{ + transform: translateX(-200%); + } + + .container.active .toggle-left{ + transform: translateX(0); + } + + .toggle-right{ + right: 0; + transform: translateX(0); + } + + .container.active .toggle-right{ + transform: translateX(200%); + } + .input { + color: black; + } + +} +@media only screen +and (max-device-width: 640px) { + .container { + position: relative; + + background-color: var(--container-colour); + border-radius: 30px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.35); + + overflow: hidden; + + width: 100%; + height: 100%; + + display: flex; + flex-direction: column; + justify-content: space-evenly; + } + .form-container { + height: 100%; + transition: all 0.6s ease-in-out; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + .form-container form { + display: flex; + + flex-direction: column; + align-items: center; + font-size: 3em; + margin-inline: 5%; + } + .form-container input { + flex: 1 1; + font-size: 1em; + color: black; + margin-block: 5%; + padding: 2%; + border-radius: 30px; + } + + .form-container button { + flex: 1 1; + font-size: 1em; + } + .toggle-container { + position: absolute; + top: 0; + width: 100%; + height: 50%; + transition: all 0.6s ease-in-out; + overflow: hidden; + border-radius: 0 0 150px 150px; + z-index: 1000; + + } + .container.active .toggle-container{ + transform: translateY(100%); /*BG THING*/ + border-radius: 150px 150px 0 0; + } + .toggle { + position: absolute; + width: 100%; + height: 200%; + display: flex; + flex-direction: column-reverse; + align-items: center; + justify-content: space-evenly; + background: linear-gradient(to bottom, var(--details-light), var(--details-colour)); + /*background-color: red;*/ + } + .toggle-panel { + flex: 1 1; + width: 100%; + } + .container .toggle-right { + transform: translateY(0); + opacity: 1; + } + .container .toggle-left { + transform: translateY(-100%); + opacity: 0; + } + .container.active .toggle-right{ + transform: translateY(-100%); + opacity: 0; + } + .container.active .toggle-left{ + transform: translateY(-100%); + opacity: 1; + } + + .container .sign-up { + opacity: 0; + z-index: -1; + transform: translateY(100%); + } + .container .sign-in { + opacity: 1; + z-index: 5; + } + + .container.active .sign-in{ + transform: translateY(-100%); + opacity: 0; + z-index: -1; + } + .container.active .sign-up{ + animation: move 0.6s; + opacity: 1; + z-index: 5; + transform: translateY(0); + } + + + .toggle-panel { + transition: all 0.6s ease-in-out; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: xxx-large; + + } + .toggle-panel p { + margin-inline: 10%; + margin-block: 5%; + line-height: 1.5em; + } + .container button{ + background-color: var(--details-colour)/*#512da8*/; + color: #fff; + font-size: xxx-large; + padding: 40px 40px; + border: 5px solid transparent; + border-radius: 14px; + font-weight: 600; + letter-spacing: 1px; + text-transform: uppercase; + margin-top: 30px; + cursor: pointer; + } + + .container button.hidden{ + background-color: transparent; + border-color: #fff; + } +} +.alert { + color: var(--error-colour); + text-shadow: var(--error-colour) 0 0 10px; +} + diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index 08273bc1bba5a74c518361f71f7d88bb53774811..934cac13d9ed4bfebab932cc72b6839e0869b25b 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -1,210 +1,87 @@ * { - box-sizing: border-box; - padding: 0; - margin: 0; + margin: 0; + padding: 0; + box-sizing: border-box; } -#homeHead{ - color: inherit; - text-decoration: none; - padding: 0; - margin: 0;} +* { + /*COLOUR PALETTE*/ + @media (prefers-color-scheme: dark) { + --primary-darker: hsl(272, 100%, 10%); + --primary-dark: hsl(271, 100%, 20%); + --primary-colour: hsl(271, 100%, 30%); + --primary-light: hsl(271, 100%, 40%); + --primary-lighter: hsl(271, 100%, 50%); + + --secondary-colour: hsl(12, 81%, 46%); + --accent-colour: hsl(12, 82%, 32%); + --accent-border: hsl(12, 81%, 25%); + + --accent-shadow: rgba(0, 0, 0, 0.7) 0 0.5em 1em -0.5em; + + color: white; -.center { - text-align: center; -} -body { - background-color: rgb(41, 41, 41); - margin: 0%; -} -.headerBlock { - background-color: rgb(15, 15, 15); - padding-bottom: 20px; - box-shadow: 0 10px 20px black; - .headerTitle { - text-align: center; - padding: 10px 10px 0 10px; - margin: 0 50px 0 50px; - letter-spacing: 10px; - font-size: 50px; - font-style: italic; - color: blueviolet; - text-shadow: black 3px 3px 5px; - border-bottom: 3px blueviolet solid; - } - .headerBanner { - display: flex; - flex-direction: column; - justify-content: center; - overflow: hidden; - position: relative; - height: 200px; - border-top: grey solid 2px; - border-bottom: grey solid 2px; - - .bannerBack { - opacity: 0.6; - width: 100%; - left: 0; - top: 0; - position: absolute - } - .bigTitle { - color:white; - text-align: center; - position: relative; - font-size: 50px; - margin: 20px 20px 5px; - } - .smallTitle { - position: relative; - color:white; - text-align: center; - font-size: 20px; - margin: 5px 20px 20px; - } } -} + @media (prefers-color-scheme: light) { + --primary-darker: hsl(272, 100%, 10%); + --primary-dark: hsl(271, 100%, 20%); + --primary-colour: hsl(271, 100%, 30%); + --primary-light: hsl(271, 100%, 40%); + --primary-lighter: hsl(271, 100%, 50%); + --secondary-colour: hsl(12, 81%, 46%); + --accent-colour: hsl(12, 82%, 32%); + --accent-border: hsl(12, 81%, 25%); + + --accent-shadow: rgba(0, 0, 0, 0.7) 0 0.5em 1em -0.5em; + + color: white; -.mainTrails{ - overflow: hidden; - position: relative; - height: 200px; - border-top: grey solid 2px; - border-bottom: grey solid 2px; -} -.trailList { - .ulHeader { - margin: 30px; - display: flex; - list-style: none; - justify-content: center; - padding: 0px; - border-bottom: solid black 5px; - - .liHeader { - - flex: 1 1 0px; - padding-left: 10px; - padding-right: 10px; - color: white; - text-align: center; - } - .liHeader:hover { - color: blueviolet; - border-bottom: solid blueviolet 2px; - } - .selected { - flex: 1 1 0px; - padding-left: 10px; - padding-right: 10px; - color: rgb(154, 69, 234); - border-bottom: solid blueviolet 2px; - text-align: center; - } } - } -.mainBlock { + +body { + background: linear-gradient(to bottom right, + var(--primary-darker), + var(--primary-dark), + var(--primary-darker)); + display: flex; - min-width: 300px; - flex-wrap: wrap; - margin-top: 20px; - margin-bottom: 100px; + flex-direction: column; + justify-content: center; + align-content: center; } -.mainBlock .trailStats{ - background-color: rgb(206, 153, 253); - flex: 0 0 0px; - min-width: 400px; - height: 600px; - margin: auto; - margin-bottom: 20px; - box-shadow: 0px 0px 20px rgb(20, 20, 20); - border-radius: 30px; - .stats { - display: block; - width: 200px; - margin: auto; - } - .textStats { - display: block; - text-align: left; - margin: 20px; - } -} -.mainBlock .trailInfo{ - background-color: rgb(206, 153, 253); - flex: 0 0 0px; - min-width: 400px; - height: 600px; - margin: auto; - margin-bottom: 20px; - box-shadow: 0px 10px 10px rgb(20, 20, 20); - border-radius: 30px; - .trailInfoTxt { - margin: 15px; - } -} -.titleH1 { - padding: 0px; - margin: 10px 30px 20px 30px; - text-align: center; - font-size: 40px; - font-style: italic; - border-bottom: solid black 1px; -} -.mainBlock p { - font-size: 25px; - text-align: left; - padding: 1px; -} -main .badgesBlock{ - bottom: 0%; - background-color: rgb(222, 75, 255); - flex: 0 0 0px; - min-width: 500px; - min-height: 100px; - margin: auto; - margin-bottom: 10px; - box-shadow: 0px 10px 10px rgb(20, 20, 20); - border-radius: 30px; -} -.badgesList { +.centerAll { display: flex; -} -.badgeImg { - flex: 0 1 0px; - width: 60px; - margin: auto; - margin-bottom: 20px; - + flex-direction: column; + align-items: center; + justify-content: center; } -footer { - z-index: 99; - bottom: 0%; - left: 0%; - position: fixed; - width: 100%; - min-width: 300px; -} -footer .footerBar { - display: flex; - list-style: none; - padding: 0; - margin: 0; - +/*PHONES PORTRAIT*/ +@media only screen +and (min-device-width: 320px) +and (max-device-width: 640px) { + body { + position: fixed; + width: 100vw; + height: 100svh; + } + html { + position: fixed; + width: 100vw; + height: 100svh; + } } -footer .footerButton { - padding: 20px; - text-align: center; - flex: 1 1 0px; - color:crimson; - background-color: white; - + +/*PHONES LANDSCAPE*/ +@media only screen +and (min-device-width: 640px) +and (max-device-width: 1000px) { + + } -footer .footerButton:hover { - background-color: black; -} \ No newline at end of file + +/*LARGER SCREENS*/ +@media only screen and (min-device-width: 1000px) {} \ No newline at end of file diff --git a/src/main/resources/static/css/userProfile.css b/src/main/resources/static/css/userProfile.css deleted file mode 100644 index 878ab054fc53828cb76726775109ca7d3f4bcc80..0000000000000000000000000000000000000000 --- a/src/main/resources/static/css/userProfile.css +++ /dev/null @@ -1,408 +0,0 @@ -/* AUTHOR: Gabriel Copat*/ -@import url('https://fonts.googleapis.com/css2?family=MedievalSharp&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=MedievalSharp&display=swap'); - -/*COLOUR PALETTE*/ -* { - --main-colour: #e47201; - --secondary-colour: #e47201; - --accent-colour: #e47201; - --accent-border: #b25901; - - --accent-shadow: rgba(0, 0, 0, 0.7) 0 0.5em 1em -0.5em; -} - - -/*FONTS, TYPOGRAPHY & BACKGROUNDS*/ -* { - margin: 0; - padding: 0; - - & h1, & h2 { - letter-spacing: 0.25vw; - line-height: 1.3; - text-align: center; - color: white; - text-justify: inter-word; - } - & label { - color: white; - } - -} -@media only screen and (max-device-width: 500px) { - /*ADJUSTING FOR SMALLER SCREENS*/ - * { - & h1, & h2 { text-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh 1svh;} - & p { line-height: 1.1; color: white;} - } -} -html{ - height: 100%; - @media only screen and (min-device-width: 1500px) { - height: auto; - } -} -body { - background: linear-gradient(135deg, #f7e8fd, #9914d1); - height: 100%; - display: flex; - flex-direction: column; - justify-content: space-evenly; - @media only screen and (min-device-width: 1500px) { - height: auto; - } -} -main { - background: linear-gradient(to bottom, #1e1e1e 10%, darkgoldenrod 50%, #1e1e1e 90%); - border-radius: 1vw; - margin-inline: 5%; - /*margin-block: 5%;*/ - width: auto; - padding-block: 2svh; - margin-top: 6em; - padding-inline: 1vw; - box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh max(1vw, 1em); - transition: all linear 2s; - overflow-y: scroll; - height: 90%; - @media only screen and (min-device-width: 1500px) { - padding-inline: 20%; - overflow: visible; - } -} -.rewards { - position: relative; -} -.userInfo { - display: flex; - flex-direction: column; - /*padding: min(2vw, 4em);*/ - text-align: center; - - & #userPicture { - width: min(30vw, 30em); - margin-inline: auto; - border-radius: 100%; - border: solid #a2a2a2 4px; - box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh max(1vw, 1em); - } - - & h1 { - font-size: max(5vw, 2em); - margin: 1svh 25%; - color:white; - border-bottom: #36454F solid 2px; - border-radius: 5vw; - box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh 1vw -1vw; - } -} -#packsBar::-webkit-scrollbar { - display: none; - -ms-scrollbar-darkshadow-color: transparent; -} -#packsBar { - position: static; - display: grid; - grid-template-areas: - "header" - "packs"; - overflow-x: scroll; - overflow-y: hidden; - color: white; - padding-bottom: 2%; - @media only screen and (min-device-width: 501px) { - height: 24vw; - } - & h2 { - position: absolute; - grid-area: header; - margin-inline: 5vw; - padding-inline: 2vw; - margin-block: -1svh; - box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh 1vw -1vw; - border-bottom: #36454F solid 2px; - font-size: 4vw; - width: 7em; - height: 1.2em; - - } - & #allPacksContainer { - margin-top: 3svh; - grid-area: packs; - height: 12svh; - width: 100%; - display: flex; - justify-content: space-between; - @media only screen and (min-device-width: 501px) { - height: 20vw; - margin-top: 6vw; - } - } - & .packContainer{ - position: relative; - height: 12svh; - width: 20vw; - display: flex; - flex-direction: column; - text-align: center; - justify-content: center; - overflow: visible; - } - - & .packImg { - position: relative; - margin-inline: 3vw; - height: 8svh; - /*width: 25%;*/ - z-index: 50; - @media only screen and (min-device-width: 501px) { - height: 15vw; - } - transition: 0.3s ease-out 100ms; - } - & .packImg:hover { - /*box-shadow: 0 0 20px 10px #bbbb00;*/ - transform: scale(1.5,1.5); - - } - & .packName { - height: 4svh; - display: flex; - justify-content: center; - font-size: 2em; - overflow: hidden; - align-items: flex-end; - - } -} - -#stickersBox { - padding-top: 5%; - display: flex; - flex-direction: column; - /* border-bottom-left-radius: 2vw; */ - /* border-bottom-right-radius: 2vw; */ - /*background: linear-gradient(to bottom, darkgoldenrod, transparent 90%);*/ - margin-top: -1%; - height: 500px; - & h2 { - font-size: 4em; - text-align: center; - box-shadow: rgba(0, 0, 0, 0.7) 0 2vw 2vw -2vw; - border-bottom: #36454F solid 2px; - margin-block: 1svh; - margin-inline: 25%; - } - & .stickersContainer { - margin-block: 1svh; - display: flex; - flex-wrap: wrap; - justify-content: space-around; - width: 100%; - - & .stickerImg { - width: 20vw; - margin-block: 1em; - - } - } -} -.locked { - filter: grayscale(100%); -} - -.dragonProgression { - position: relative; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; - height: 16svh; - box-sizing: content-box; - /*background: linear-gradient(to bottom, transparent -50%, darkgoldenrod 50%);*/ - width: 100%; - /*padding-top: 1svh;*/ - - @media only screen and (min-device-width: 501px) { - height: 28vw; - margin-bottom: 0; - padding-bottom: 5svh; - } - & h1 { - font-size: 3em; - font-family: 'MedievalSharp', cursive; - letter-spacing: 1vw; - box-shadow: rgba(0, 0, 0, 0.7) 0 2vw 2vw -2vw; - border-bottom: #36454F solid 2px; - border-top: #36454F solid 2px; - margin-inline: 15%; - margin-bottom: 1%; - } - & .dragonContainer { - position: relative; - margin: auto; - } - & .dragonImg { - height: 10svh; - width: 16svh; - @media only screen and (min-device-width: 501px) { - height: 22vw; - width: 30vw; - } - - } - & .dragonFill { - position: absolute; - overflow: hidden; - width: 40%; - } - & .dragonOut { - /*position: absolute;*/ - overflow: hidden; - } -} -header { - z-index: 99; - top: 0.5svh; - left: 0; - position: fixed; - width: 100vw; - justify-content: center; - display: flex; -} -header .footerBar { - display: flex; - list-style: none; - border-radius: 1vw; - overflow: hidden; - justify-content: space-evenly; - background-color: rgba(0, 0, 0, 0.7); -} -header .footerButton { - padding: 1vw; - text-align: center; - /*flex: 1 1;*/ - color:crimson; - background-color: rgba(31, 31, 31, 0.7); - font-size: 2.5em; - width: 15vw; -} -header .footerButton:hover { - background-color: #36454F; -} - -.grayedOut { - filter: grayscale(1); -} - -.solidBg { - background: #1e1e1e; - display: flex; -} -.loginWrapper { - margin-inline: auto; - margin-block: 5svh; - display: flex; - text-align: center; - justify-content: center; - background: rgba(196, 92, 239, 0.75); - padding: 2em; - flex: 0 0; - border-radius: 1vw; - box-shadow: rgba(0, 0, 0, 0.7) 0 0.5svh max(1vw, 1em); - & h2 { - margin-left: 0; - margin-right: auto; - margin-bottom: 0.5em; - } - & form{ - margin-block: auto; - font-size: 3em; - display: flex; - flex-direction: column; - } - & label { - position: relative; - margin-top: 1em; - /*width: fit-content;*/ - font-size: 0.8em; - & b { - float: left; - } - & a { - position: absolute; - font-size: 0.4em; - right: 0.2em; - bottom: 0.2em; - height: fit-content; - } - - } - & input { - font-size: 0.6em; - height: 1.5em; - width: 40vw; - border-radius: 0.2em; - padding-inline: 0.4em; - border: transparent solid 0.1em; - margin-bottom: 1em; - } - & button { - font-size: 1em; - height: 2em; - width: 4em; - box-shadow: var(--accent-shadow); - margin:auto; - margin-top: 1em; - background-color: var(--accent-colour); - border: 0.1em solid var(--accent-border); - border-radius: 1em; - color: white; - - } - & button:hover{ - background-color: var(--accent-border); - border: 0.1em solid var(--accent-colour); - } -} -.label { - position: relative; -} -.invalid-tooltip { - color: red; - width: fit-content; - opacity: 0; - font-size: 0.6em; - text-shadow: red 0 0.2em 1em; - transition: 0.3s ease-in-out 1ms; - padding:0.1em; - position: absolute; - right: 0.2em; - bottom: 0.2em; - height: fit-content; -} -.invalid-field { - box-shadow: red 0 0 1em; - transition: 0.3s ease-in-out 1ms; -} -.valid-field { - box-shadow: #40ff00 0 0 1em; - border: #40ff00 solid 0.1em; - transition: 0.3s ease-in-out 1ms; -} - - -#invalidLogin { - color: red; - text-shadow: black 0 0.1em 0.2em; - width: auto; - height: auto; - font-size: 0.6em; - opacity: 0; - transition: 0.5s ease-in-out 1ms; -} - -.rewards { - transition: 1s ease-in-out 1ms; -} \ No newline at end of file diff --git a/src/main/resources/static/css/userProfile2.css b/src/main/resources/static/css/userProfile2.css index 0728cbc6e973473051420fd817d243d5d484c489..0c5c0e9c4aef739bda2f4d38039fa4735a3e30bb 100644 --- a/src/main/resources/static/css/userProfile2.css +++ b/src/main/resources/static/css/userProfile2.css @@ -2,46 +2,7 @@ @import url('https://fonts.googleapis.com/css2?family=MedievalSharp&display=swap'); @import url('https://fonts.googleapis.com/css2?family=MedievalSharp&display=swap'); -* { - margin: 0; - padding: 0; - box-sizing: border-box; - /*COLOUR PALETTE*/ - @media (prefers-color-scheme: dark) { - --primary-darker: hsl(272, 100%, 10%); - --primary-dark: hsl(271, 100%, 20%); - --primary-colour: hsl(271, 100%, 30%); - --primary-light: hsl(271, 100%, 40%); - --primary-lighter: hsl(271, 100%, 50%); - - --secondary-colour: hsl(12, 81%, 46%); - --accent-colour: hsl(12, 82%, 32%); - --accent-border: hsl(12, 81%, 25%); - - --accent-shadow: rgba(0, 0, 0, 0.7) 0 0.5em 1em -0.5em; - - color: white; - - } - @media (prefers-color-scheme: light) { - --primary-darker: hsl(272, 100%, 10%); - --primary-dark: hsl(271, 100%, 20%); - --primary-colour: hsl(271, 100%, 30%); - --primary-light: hsl(271, 100%, 40%); - --primary-lighter: hsl(271, 100%, 50%); - - --secondary-colour: hsl(12, 81%, 46%); - --accent-colour: hsl(12, 82%, 32%); - --accent-border: hsl(12, 81%, 25%); - - --accent-shadow: rgba(0, 0, 0, 0.7) 0 0.5em 1em -0.5em; - - color: white; - - - } -} @@ -237,8 +198,14 @@ main { height: 17em; margin: 1.5em; } -/*}*/ +/* LOGIN FORM PAGE */ + + + + + +/* MEDIA TYPE UPDATES*/ @media only screen and (min-device-width: 320px) and (max-device-width: 640px) { @@ -306,4 +273,21 @@ and (min-device-width: 1000px) { .progressionContainer { height: 20svh; } -} \ No newline at end of file +} + +.container { + width: 500px; + margin-block: 5%; + background-color: var(--accent-colour); + border-radius: 10em; + display: flex; + flex-direction: column; + align-items: center; + & form { + margin-inline: auto; + margin-block: 5%; + } +} +.container-active { + background: black; +} diff --git a/src/main/resources/static/scripts/login.js b/src/main/resources/static/scripts/login.js index 38ef327134aa8ae03ddc44ddf796b2df139fc3bb..df3b4ddf21b32933d0b283c60141be8e8eb5197c 100644 --- a/src/main/resources/static/scripts/login.js +++ b/src/main/resources/static/scripts/login.js @@ -1,51 +1,73 @@ -let username = document.forms["loginForm"]["username"]; -let password = document.forms["loginForm"]["password"]; -let pattern = new RegExp("^[a-z0-9_-]{3,15}$"); - -username.addEventListener("input", validateUsername) -password.addEventListener("input", validatePassword) - -function validateUsername() { - if (!(username.value === "") && pattern.test(username.value)){ - username.classList.remove("invalid-field"); - username.classList.add("valid-field"); - document.getElementById(username.name+"Invalid").style.opacity = 0; - username.style.borderColor = "green"; - return true; - } else if( ! (username.classList.contains("invalid-field") ) ){ - username.classList.add("invalid-field"); - username.classList.remove("valid-field"); - document.getElementById(username.name+"Invalid").style.opacity = 1; - username.style.borderColor = "red"; +const container = document.getElementById('container'); +const registerBtn = document.getElementById('register'); +const loginBtn = document.getElementById('login'); + +registerBtn.addEventListener('click', () => { + container.classList.add("active"); +}); + +loginBtn.addEventListener('click', () => { + container.classList.remove("active"); +}); + + +const emailRegEx = new RegExp(/^[A-Za-z0-9.-_]+@[A-Za-z0-9.-]+\.[A-Za-z]+$/m); +const passwordRegEx = new RegExp(/^[A-Za-z0-9_!#$%&'*+\/=?`{|}~^.-]+$/m); +const usernameRegEx = new RegExp(/^[A-Za-z ]+$/m); + +function loginFormValidation(){ + let pass= true; + let email = $("#login-email").val(); + let password = $("#login-password").val(); + if (email === "") { + alert("Email cannot be empty"); + pass = false; + } else if ( !(emailRegEx.test(email)) ) { + pass = false; + alert("Invalid Email address") } - return false; -} -function validatePassword(){ - if (password.value === "") { - password.classList.add("invalid-field"); - password.classList.remove("valid-field"); - document.getElementById(password.name+"Invalid").style.opacity = 1; - password.style.borderColor = "red"; - return false; - } else if( ! (password.classList.contains("valid-field") ) ) { - password.classList.remove("invalid-field"); - password.classList.add("valid-field"); - document.getElementById(password.name+"Invalid").style.opacity = 0; - password.style.borderColor = "green"; + if (password === "") { + alert("Password cannot be empty"); + pass = false; + } else if ( !(passwordRegEx.test(password)) ) { + pass = false; + alert("Password contains invalid characters"); } - return true; + return pass; } -function validateForm(){ - if (validateUsername() & validatePassword()) { //Using just & so it checks both, even if the first is false (it applies the style) - console.log("VALID"); - return false; - } else { - console.log("Invalid"); - document.getElementById("invalidLogin").style.opacity = 1; - return false; +function registerFormValidation(){ + /*WHYTF THIS DONT WORK*/ + let pass=true; + let email = $("#register-email").val(); + let username = $("#register-username").val(); + let password = $("#register-password").val(); + + if (email == "") { + console.log("Email empty bit") + pass = false; + alert("Email cannot be empty"); + } else if ( !(emailRegEx.test(email)) ) { + console.log("Email no match") + pass = false; + alert("Invalid Email address"); } - //TODO SERVER SIDE VALIDATION AND CHECK AGAINST USERS DB TABLE -} + if (username == "") { + pass = false; + alert("Username cannot be empty") + } else if ( !(usernameRegEx.test(username)) ) { + console.log(!usernameRegEx.test(username)); + pass = false; + alert("Invalid username"); + } + if (password == "") { + pass = false; + alert("Password cannot be empty"); + } else if ( !(passwordRegEx.test(password)) ) { + pass = false; + alert("Password contains invalid characters"); + } + return pass; +} \ No newline at end of file diff --git a/src/main/resources/static/scripts/userPage.js b/src/main/resources/static/scripts/userPage.js index 2c1d5069ad652b854abf6b8cb460d7ebf89d696f..a7b036197965493cbdd621b241350c56167bac08 100644 --- a/src/main/resources/static/scripts/userPage.js +++ b/src/main/resources/static/scripts/userPage.js @@ -1,6 +1,6 @@ -function updatePack(userid, packid) { - /* Updates the trail being shown on screen to the one requested by ID */ - $.get("/packInfo/" + userid + "/" + packid).done(function (fragment) { +function updatePack(url) { + /* Updates the trail being shown on screen */ + $.get(url).done(function (fragment) { let packRewardsWrapper = $("#packRewardsWrapper"); packRewardsWrapper.fadeTo("slow", 0, function () { diff --git a/src/main/resources/static/sql/user-data.sql b/src/main/resources/static/sql/user-data.sql new file mode 100644 index 0000000000000000000000000000000000000000..333ec4091f3ab1578e91009cca9fe9e3d9b7872d --- /dev/null +++ b/src/main/resources/static/sql/user-data.sql @@ -0,0 +1,7 @@ +INSERT INTO users (username, password) VALUE ('Admin', 'admin'); +INSERT INTO users (username, password) VALUE ('Hannah', 'root'); +INSERT INTO users (username, password) VALUE ('Nigel', 'root'); +INSERT INTO users (username, password) VALUE ('Oscar', 'root'); + +INSERT INTO authorities (username, authority) VALUE ('Admin', 'ADMIN'); +INSERT INTO authorities (username, authority) VALUE ('Hannah', 'USER'); diff --git a/src/main/resources/static/sql/user-progress-data.sql b/src/main/resources/static/sql/user-progress-data.sql new file mode 100644 index 0000000000000000000000000000000000000000..e9223384658ac8bd55456a60f9b8e52423b3d429 --- /dev/null +++ b/src/main/resources/static/sql/user-progress-data.sql @@ -0,0 +1,7 @@ +DELETE FROM stickerprogress; +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 1); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 2); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 3); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 5); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 2, 1); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 2, 3); \ No newline at end of file diff --git a/src/main/resources/templates/allTrails/allTrails.html b/src/main/resources/templates/allTrails/allTrails.html index d29d1ad91cf459383b4681678742ed0a2cacbcd6..0fefa92c95a928454c100894149bf18340015baf 100644 --- a/src/main/resources/templates/allTrails/allTrails.html +++ b/src/main/resources/templates/allTrails/allTrails.html @@ -11,7 +11,7 @@ <body> <header th:replace="~{/fragments/Templating.html :: header}"></header> - +<div>TEST</div> <main> <section id="allTrailsBar" class="centerFlex"> <img class="trailsImages" diff --git a/src/main/resources/templates/fragments/Templating.html b/src/main/resources/templates/fragments/Templating.html index 55dba5e66042ad586fca111f9432a4c90f50742b..e3afc23854e38d5973a596f44e8d546305b59b83 100644 --- a/src/main/resources/templates/fragments/Templating.html +++ b/src/main/resources/templates/fragments/Templating.html @@ -9,15 +9,15 @@ <li><a id="homeHead" href="/home">Home</a></li> <li>FAQs</li> <li>Contact us</li> + <li th:if="${#authentication.principal}!=anonymousUser"><a href="/logout">Log Out</a></li> + <li th:if="${#authentication.principal}==anonymousUser"><a href="/login">Log In</a></li> </ul> - <label class="work">Who we Work with:</label> - <select> - <ul> - <option value="localauthorities">Local Authorities</option> - <option value="towns">Towns</option> - <option value="businesses"><a href="/businesses">Businesses</a></option> - <option value="consumers">Consumers</option> - </ul> + <label for="stakeholders" class="work">Who we Work with:</label> + <select id="stakeholders"> + <option value="localauthorities">Local Authorities</option> + <option value="towns">Towns</option> + <option value="businesses">Businesses</option> + <option value="consumers">Consumers</option> </select> </nav> </header> diff --git a/src/main/resources/templates/users/login.html b/src/main/resources/templates/users/login.html index 9c7aea8b67c0d18e2910b8c067ea576899fa6340..82f791b9acfff17bada66a547b3f2e3e7d79d29b 100644 --- a/src/main/resources/templates/users/login.html +++ b/src/main/resources/templates/users/login.html @@ -3,41 +3,71 @@ <head> <meta charset="UTF-8"> <title>User Log In</title> - <link rel="stylesheet" th:href="@{/css/userProfile.css}"> + <link rel="stylesheet" th:href="@{/css/style.css}"> + <link rel="stylesheet" th:href="@{/css/login.css}"> + <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <header> - <ul class="footerBar"> - <li class="footerButton"><b>Home</b></li> - <li class="footerButton"><b>About</b></li> - <li class="footerButton"><b>Map</b></li> - <li class="footerButton"><b>Facilities</b></li> - <li class="footerButton"><b>Log In</b></li> - </ul> + <form name="logoutForm" th:action="@{/logout}" method="post" th:hidden="false"> + <input hidden type="submit" value="Sign Out"/> + <button type="submit">CLICK ME OT LOG OUT</button> + </form> </header> -<main class="solidBg"> - <div class="loginWrapper"> - <form action="" onsubmit="return validateForm()" method="post" name="loginForm"> - <h2>Log In</h2> - <div class="label"> - <label for="username"><b>Username</b><br></label> - <div id="usernameInvalid" class="invalid-tooltip">Please fill out this field.</div> - </div> - <input type="text" name="username" id="username" placeholder="Enter Username" title="Username Input"> - <div class="label"> - <label for="password"><b>Password</b><br></label> - <div id="passwordInvalid" class="invalid-tooltip">Please fill out this field.</div> +<main> + <!--CODE MODIFIED FROM: https://github.com/AsmrProg-YT/Modern-Login --> + <div class="container sign-in"> + <div class="container" th:classappend="${status}" id="container"> + <div class="form-container sign-up"> + <form th:object="${user}" action="#" th:action="@{/login/register}" th:method="POST" onsubmit="return registerFormValidation()"> + <h1>Create Account</h1> + <div th:if="${error.equals('User exists')}" class="alert alert-error">User already exist</div> + <label> + <input class="input" th:field="*{name}" id="register-username" type="text" placeholder="Name"> + </label> + <label> + <input class="input" th:field="*{email}" id="register-email" type="email" placeholder="Email"> + </label> + <label> + <input class="input" th:field="*{password}" id="register-password" type="password" placeholder="Password"> + </label> + <button type="submit" >Sign Up</button> + </form> </div> - <input type="password" id="password" name="password"> - <div id="invalidLogin">Username and/or Password incorrect. Please try again.</div> - <button type="submit"><b>Log In</b></button> - </form> - + <div class="form-container sign-in"> + <form name="f" th:action="@{/login}" th:method="POST"> + <h1>Sign In</h1> + <div th:if="${param.error}" class="alert alert-error">Invalid Username or Password</div> + <label> + <input class="input" id="username" type="text" name="username" placeholder="Email"> + </label> + <label> + <input class="input" id="password" type="password" name="password" placeholder="Password"> + </label> + <a href="#" class="text">Forget Your Password?</a> + <button type="submit">Sign In</button> + </form> + </div> + <div class="toggle-container"> + <div class="toggle"> + <div class="toggle-panel toggle-left"> + <h1>Welcome Back!</h1> + <p>Enter your personal details and start tracking your landmarks!</p> + <button class="hidden" id="login">Sign In</button> + </div> + <div class="toggle-panel toggle-right"> + <div th:if="${param.logout}" class="alert alert-success">Successfully Logged out</div> + <h1 th:if="!${param.logout}">Hello, Welcome!</h1> + <p th:if="!${param.logout}">Register with your personal details and start earning stickers!</p> + <button class="hidden" id="register">Sign Up</button> + </div> + </div> + </div> + </div> </div> - </main> diff --git a/src/main/resources/templates/users/logout.html b/src/main/resources/templates/users/logout.html new file mode 100644 index 0000000000000000000000000000000000000000..69107ce66e4b6c4b3ed49219e191e5335de6d9a2 --- /dev/null +++ b/src/main/resources/templates/users/logout.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Logged Out Successfully</title> + <link rel="stylesheet" th:href="@{/css/style.css}"> + <link rel="stylesheet" th:href="@{/css/login.css}"> +</head> +<body> +<main> + <div class="container centerAll"> + <h1>You have successfully logged out</h1> + </div> +</main> + +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/users/userProfile.html b/src/main/resources/templates/users/userProfile.html index 3e865175760bc4b0aa11c9b54aade5d312ac55b5..edbe13918a53d466e7194ba42ff9dcf86bebdc56 100644 --- a/src/main/resources/templates/users/userProfile.html +++ b/src/main/resources/templates/users/userProfile.html @@ -3,6 +3,7 @@ <head> <meta charset="UTF-8"> <title th:text="'VZLA Profile Page of ' + ${user.getName()}"></title> + <link rel="stylesheet" th:href="@{/css/style.css}"> <link rel="stylesheet" th:href="@{/css/userProfile2.css}"> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- <link rel="stylesheet" th:href="@{/css/templatingstyle.css}">--> @@ -38,7 +39,8 @@ <div th:each="pack : ${packs}" class="packContainer"> <img class="packImg" th:src="@{'../' + ${pack.getDisplayImg()}}" th:id="'packImg' + ${pack.getId()}" th:alt="${pack.getName()}" - th:onclick="'updatePack(' + ${user.getId()} +',' + ${pack.getId()} +');'"> + th:data-url="@{/packInfo/{username}/{packID}(username=${user.getName()}, packID=${pack.getId()})}" + onclick="updatePack(this.getAttribute('data-url'))"> <h4 class="packName" th:text="${pack.getName()}"></h4> </div> </div> diff --git a/src/test/java/Team5/SmartTowns/DataSourceConfig.java b/src/test/java/Team5/SmartTowns/DataSourceConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..eb7623abb1831b13d72138f824b600d6ce361fa7 --- /dev/null +++ b/src/test/java/Team5/SmartTowns/DataSourceConfig.java @@ -0,0 +1,20 @@ +package Team5.SmartTowns; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.sql.DataSource; +@Configuration +public class DataSourceConfig { + + @Bean + public DataSource dataSource(){ + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setUrl("jdbc:mariadb://localhost:3306/test_towns"); + dataSource.setUsername("root"); + dataSource.setPassword("comsc"); + return dataSource; + } + +} diff --git a/src/test/java/Team5/SmartTowns/testUsers.java b/src/test/java/Team5/SmartTowns/testUsers.java new file mode 100644 index 0000000000000000000000000000000000000000..2145c47165ed072b8a4c3478774da7410f881414 --- /dev/null +++ b/src/test/java/Team5/SmartTowns/testUsers.java @@ -0,0 +1,79 @@ +package Team5.SmartTowns; + +import Team5.SmartTowns.rewards.RewardsRepository; +import Team5.SmartTowns.users.NewUser; +import Team5.SmartTowns.users.User; +import Team5.SmartTowns.users.UserRepository; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.TestCase.*; + +@SpringBootTest +public class testUsers { + + + @Autowired + UserRepository userRepository; + @Autowired + RewardsRepository rewardsRepository; + + @Test + public void getAllUsersTest(){ // test if we can get all users, admin is sa known user + List<User> users = userRepository.getAllUsers(); + User user = new User("Admin","Admin"); + Assertions.assertEquals("Admin", users.get(0).getName()); + } + + @Test // test if new users can be added + public void addAUserTest(){ + int userNumberBeforeAdd = userRepository.getAllUsers().size(); + NewUser newuser = new NewUser("Meow","Woof","Cat@Dogs.com"); + boolean trueIfAdded= userRepository.addUser(newuser.getName(), newuser.getEmail(), newuser.getPassword()); + int userNumberAfterAdd = userRepository.getAllUsers().size(); + assertTrue(trueIfAdded); + } + + @Test // test if new users and inserted users can be found + public void doesUserExistTest(){ + Boolean insertedUserFoundByEmail = userRepository.doesUserExist("Kevin@Gmail.com"); + NewUser newuser = new NewUser("MeowMeow","WoofMeow","CatMeow@Dogs.com"); + Boolean newUser = userRepository.addUser(newuser.getName(), newuser.getEmail(), newuser.getPassword()); + Boolean newUserFoundByEmail = userRepository.doesUserExist(newuser.getEmail()); + int compareTwoSearches = Boolean.compare(insertedUserFoundByEmail, newUserFoundByEmail); + assertEquals(0,compareTwoSearches); // if 0, both values are the same + + + } + + @Test + public void canUsersUnlockStickersTest(){ + NewUser newuser = new NewUser("MeowMeowMeow","WoofMeowMeow","CatMeowMeow@Dogs.com"); + Boolean newUser = userRepository.addUser(newuser.getName(), newuser.getEmail(), newuser.getPassword()); + Boolean doesStickerUnlock = userRepository.unlockSticker(newuser.getName(),2,2); + System.out.println(doesStickerUnlock); + assertTrue(doesStickerUnlock); + } + @Test + public void canUsersUnlockStickersAndViewThemTest(){ + NewUser newuser = new NewUser("MeowMeowMeowMeow","WoofMeowMeowMeow","CatMeowMeowMeow@Dogs.com"); + NewUser newuserTwo = new NewUser("Jumper","Baa","Sheep@Wool.com"); + Boolean newUser = userRepository.addUser(newuser.getName(), newuser.getEmail(), newuser.getPassword()); + Boolean newUserTwo = userRepository.addUser(newuserTwo.getName(), newuserTwo.getEmail(), newuserTwo.getPassword()); + Boolean doesStickerUnlock = userRepository.unlockSticker(newuser.getName(),1,2); + List<Long> newUserStickerCollection = userRepository.getUserStickersFromPack(newuser.getName(),1); + List<Long> newUserStickerCollectionTwo = userRepository.getUserStickersFromPack(newuserTwo.getName(),1); // compare and see if only new suer that has unlocked a sticker ahs one in their collection for pack 1 + int newUserStickerList = newUserStickerCollection.size(); + int newUserStickerListTwo = newUserStickerCollectionTwo.size(); // should have different sizes + assertNotSame(newUserStickerList,newUserStickerListTwo); + + } + + +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000000000000000000000000000000000000..83f47c5db5a7390e1ab3dadf4882c3ddf1e3b4ea --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,8 @@ +spring.datasource.url=jdbc:mariadb://localhost:3306/ +spring.datasource.username=root +spring.datasource.password=comsc + +spring.sql.init.mode=always +spring.sql.init.platform=test +spring.sql.init.schema-locations=classpath:schema-test.sql +spring.sql.init.data-locations=classpath:test-data.sql \ No newline at end of file diff --git a/src/test/resources/schema-test.sql b/src/test/resources/schema-test.sql new file mode 100644 index 0000000000000000000000000000000000000000..744c10245fa965d81b641fc0cf3231ea612782a7 --- /dev/null +++ b/src/test/resources/schema-test.sql @@ -0,0 +1,110 @@ + + + + +DROP DATABASE IF EXISTS test_towns; +CREATE DATABASE IF NOT EXISTS test_towns; +USE test_towns; +DROP TABLE IF EXISTS trails; +DROP TABLE IF EXISTS locations; +DROP TABLE IF EXISTS users; +DROP TABLE IF EXISTS stickers; +DROP TABLE IF EXISTS packs; +DROP TABLE IF EXISTS stickerProgress; + + +CREATE TABLE IF NOT EXISTS trails ( + trailID bigint auto_increment primary key, + name varchar(128), + tru boolean +) engine=InnoDB; + +drop table if exists locationCoordinates; +drop table if exists locations; +create table if not exists locations +( + locationID bigint auto_increment primary key, + locationName varchar(128), + locationEmail varchar(128), + locationDescription longtext, + locationPlace varchar(255), + locationTrailID varchar(128), + locationApproved boolean +) engine=InnoDB; +CREATE TABLE IF NOT EXISTS users ( + username varchar(30) primary key NOT NULL, + id bigint auto_increment unique, /*DEPRECATED COLUMN, LEFT IN WHILE SOME OTHER FUNCTIONS STILL USE IT*/ + email varchar(128), + password varchar(30) NOT NULL, + enabled boolean default true +); + +CREATE TABLE IF NOT EXISTS authorities ( + id bigint primary key auto_increment NOT NULL, + username varchar(30) NOT NULL , + authority varchar(45) NOT NULL +); + +CREATE TABLE IF NOT EXISTS packs ( + id bigint auto_increment primary key, + name varchar(20) NOT NULL, + description text +); + +CREATE TABLE IF NOT EXISTS stickers ( + id bigint auto_increment primary key, + packID bigint NOT NULL, + FOREIGN KEY (packID) REFERENCES packs(id) + ON DELETE CASCADE + ON UPDATE RESTRICT, + stickerID bigint NOT NULL, /*STICKER ID NUMBER WITHIN ITS OWN PACK*/ + name varchar(30) NOT NULL, + description text NOT NULL, + rarity tinyint +); + +CREATE TABLE IF NOT EXISTS stickerProgress ( + id bigint auto_increment primary key, + username varchar(30) NOT NULL, + FOREIGN KEY (username) REFERENCES users(username) + ON DELETE CASCADE + ON UPDATE RESTRICT, + packID bigint NOT NULL, + FOREIGN KEY (packID) REFERENCES packs(id) + ON DELETE CASCADE + ON UPDATE RESTRICT, + stickerID bigint NOT NULL, + FOREIGN KEY (stickerID) REFERENCES stickers(id) + ON DELETE CASCADE + ON UPDATE RESTRICT +); + +create table if not exists locationCoordinates +( + locationCoordID bigint auto_increment primary key, + locationID bigint, + Foreign Key (locationID) REFERENCES locations(locationID) + ON DELETE CASCADE + ON UPDATE RESTRICT, + locationCoordsLat DECIMAL(8,6), + locationCoordsLong DECIMAL(8,6) + + +)engine=InnoDB; + + +drop table if exists townsWithTrails; +create table if not exists townsWithTrails +( + townID bigint auto_increment primary key, + townName varchar(128), + townCentreCoordsLat varchar(128), + townCentreCoordsLong varchar(128), + townUppermostCoordsLat varchar(128), + townLowermostCoordsLat varchar(128), + townLeftmostCoordsLong varchar(128), + townRightmostCoordsLong varchar(128) + +)engine=InnoDB; + + diff --git a/src/test/resources/test-data.sql b/src/test/resources/test-data.sql new file mode 100644 index 0000000000000000000000000000000000000000..05db12e66b276dace05661710dff01ba513875d7 --- /dev/null +++ b/src/test/resources/test-data.sql @@ -0,0 +1,68 @@ +delete from trails; +insert into trails ( Name,tru) value ( 'Caerphilly Coffee Trail',false); +insert into trails ( Name,tru) value ( 'Penarth Dragon Trail',true); + +delete from locations; +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'St Cenydd','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Castle','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Medieval Trades','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Queen''s War','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Green Lady','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Armoury','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Architecture','','Location description here','Caerphilly',0101, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( '21st Century Landmark','','Location description here','Caerphilly',0101, true); + +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'JD Wetherspoons-Malcolm Uphill','','Location description here','Caerphilly',0102, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Caerphilly Cwtch','','Location description here','Caerphilly',0102, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Caerphilly Conservative Club','','Location description here','Caerphilly',0102, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The King''s Arms','','Location description here','Caerphilly',0102, true); + +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Caerphilly Bus Station','','Location description here','Caerphilly',0103, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Medieval Courthouse','','Location description here','Caerphilly',0103, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ('Caerphilly Castle','','Location description here','Caerphilly',0103, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Ty Vaughan House','','Location description here','Caerphilly',0103, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Risca Colliery','','Location description here','Risca',0201, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'Black Vein Colliery Disaster','','Location description here','Risca',0201, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Esplanade','','Location description here','Penarth',0301, true); +insert into locations ( locationName , locationEmail,locationDescription,locationPlace, locationTrailID, locationApproved) value ( 'The Old Swimming Baths','','Location description here','Penarth',0301, true); + + + + + +DELETE FROM packs; +INSERT INTO packs (name, description) VALUE ('Wales Football Team', 'Pack of Welsh Football Players in the National Team'); +INSERT INTO packs (name, description) VALUE ('Wales Rugby Team', 'Pack of Welsh Rugby Players in the National Team'); +INSERT INTO packs (name, description) VALUE ('Welsh Heritage', 'Pack About Welsh Heritage'); + +DELETE FROM stickers; +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 1, 'wayne_hennessey', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 2, 'neco_williams', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 3, 'joe_morrell', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 4, 'ethan_ampadu', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (1, 5, 'connor_roberts', 'Wales Football Team Player', '2'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 1, 'Taine_Basham', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 2, 'Adam Beard', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 3, 'Elliot Dee', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 4, 'Corey Domachowski', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (2, 5, 'Ryan Elias', 'Wales Rugby Team Player', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (3, 1, 'Welsh Lady', 'Welsh Heritage', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (3, 2, 'Welsh Outline', 'Welsh Heritage', '1'); +INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (3, 3, 'Welsh Spoon', 'Welsh Heritage', '1'); + +INSERT INTO users (username, password) VALUE ('Admin', 'admin'); +INSERT INTO users (username, password) VALUE ('Hannah', 'root'); +INSERT INTO users (username, password) VALUE ('Nigel', 'root'); +INSERT INTO users (username, password) VALUE ('Oscar', 'root'); +INSERT INTO users (username, email, password) VALUE ('kevin','Kevin@Gmail.com' ,'root'); + +INSERT INTO authorities (username, authority) VALUE ('Admin', 'ADMIN'); +INSERT INTO authorities (username, authority) VALUE ('Hannah', 'USER'); + +DELETE FROM stickerprogress; +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 1); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 2); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 3); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 5); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 2, 1); +INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 2, 3);