diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 000000000..bdb1e7d06
Binary files /dev/null and b/.DS_Store differ
diff --git a/.gitignore b/.gitignore
index c64b53754..1b7d0efb3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,5 @@ dependency-reduced-pom.xml
.factorypath
.project
.settings/
+application.yaml
+application.yml
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 32aeb8fc4..93463f585 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,13 @@
21
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ 5.11.1
+
org.springframework.boot
spring-boot-starter-data-jpa
@@ -42,6 +48,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
org.flywaydb
flyway-core
@@ -66,6 +76,11 @@
lombok
provided
+
+ org.junit.vintage
+ junit-vintage-engine
+ 5.11.1
+
junit
junit
@@ -95,6 +110,11 @@
org.thymeleaf.extras
thymeleaf-extras-springsecurity6
+
+ org.junit.vintage
+ junit-vintage-engine
+ 5.11.1
+
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 000000000..c2fe07baf
Binary files /dev/null and b/src/.DS_Store differ
diff --git a/src/main/.DS_Store b/src/main/.DS_Store
new file mode 100644
index 000000000..8594c23dc
Binary files /dev/null and b/src/main/.DS_Store differ
diff --git a/src/main/java/.DS_Store b/src/main/java/.DS_Store
new file mode 100644
index 000000000..9279a7e35
Binary files /dev/null and b/src/main/java/.DS_Store differ
diff --git a/src/main/java/com/.DS_Store b/src/main/java/com/.DS_Store
new file mode 100644
index 000000000..56edf86c1
Binary files /dev/null and b/src/main/java/com/.DS_Store differ
diff --git a/src/main/java/com/makersacademy/.DS_Store b/src/main/java/com/makersacademy/.DS_Store
new file mode 100644
index 000000000..d146cc031
Binary files /dev/null and b/src/main/java/com/makersacademy/.DS_Store differ
diff --git a/src/main/java/com/makersacademy/acebook/config/SecurityConfiguration.java b/src/main/java/com/makersacademy/acebook/config/SecurityConfiguration.java
index 542bce54c..fa9d2205b 100644
--- a/src/main/java/com/makersacademy/acebook/config/SecurityConfiguration.java
+++ b/src/main/java/com/makersacademy/acebook/config/SecurityConfiguration.java
@@ -1,21 +1,28 @@
package com.makersacademy.acebook.config;
+import com.makersacademy.acebook.model.User;
+import com.makersacademy.acebook.repository.UserRepository;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.io.IOException;
+import java.util.Optional;
import static org.springframework.security.config.Customizer.withDefaults;
@@ -23,6 +30,9 @@
@EnableWebSecurity
public class SecurityConfiguration {
+ @Autowired
+ private UserRepository userRepository;
+
@Value("${okta.oauth2.issuer}")
private String issuer;
@Value("${okta.oauth2.client-id}")
@@ -31,7 +41,7 @@ public class SecurityConfiguration {
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
- .csrf(csrf -> csrf.disable())
+ .csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/", "/images/**").permitAll()
.anyRequest().authenticated()
@@ -40,7 +50,15 @@ public SecurityFilterChain configure(HttpSecurity http) throws Exception {
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
- response.sendRedirect("/users/after-login");
+ DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal();
+ String username = (String) principal.getAttributes().get("email");
+ Optional user = userRepository.findUserByUsername(username);
+ if (user.isEmpty()) {
+ response.sendRedirect("/users/newUser"); // Redirect if user is missing
+ } else {
+ response.sendRedirect("/users/after-login");
+ //^^ if user is not in OUR database, they get redirected
+ }
}
})
)
diff --git a/src/main/java/com/makersacademy/acebook/controller/HomeController.java b/src/main/java/com/makersacademy/acebook/controller/HomeController.java
index 2036ec7e0..79337e122 100644
--- a/src/main/java/com/makersacademy/acebook/controller/HomeController.java
+++ b/src/main/java/com/makersacademy/acebook/controller/HomeController.java
@@ -1,13 +1,37 @@
package com.makersacademy.acebook.controller;
+import com.makersacademy.acebook.model.User;
+import com.makersacademy.acebook.repository.UserRepository;
+import org.springframework.security.core.Authentication;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.view.RedirectView;
+import java.util.Optional;
+
@Controller
public class HomeController {
- @RequestMapping(value = "/")
- public RedirectView index() {
- return new RedirectView("/posts");
+
+ @Autowired
+ UserRepository userRepository;
+
+ @ModelAttribute("user")
+ Optional findUser(Authentication authentication) {
+ DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal();
+ String username = (String) principal.getAttributes().get("email");
+ return userRepository.findUserByUsername(username);
+ }
+
+ // Routes ------
+
+ @GetMapping(value = "/")
+ public String index(@ModelAttribute("user") Optional user) {
+ return user.
+ map(_user -> "redirect:/users/" + _user.getUsername())
+ .orElse("redirect:/users/newUser");
}
}
diff --git a/src/main/java/com/makersacademy/acebook/controller/NavigationController.java b/src/main/java/com/makersacademy/acebook/controller/NavigationController.java
new file mode 100644
index 000000000..661f4b60d
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/controller/NavigationController.java
@@ -0,0 +1,70 @@
+// STOP PEOPLE BEING SNEAKY IF NOT REGISTERED
+/*
+ import com.makersacademy.acebook.repository.UserRepository;
+ import org.springframework.security.core.Authentication; <- replaces the tomcat one
+ @Autowired
+ UserRepository userRepository;
+
+ public String exampleMethod(Authentication authentication)
+ DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal();
+ String username = (String) principal.getAttributes().get("email");
+ Optional user = userRepository.findUserByUsername(username);
+ if (user.isEmpty()) {
+ return "redirect:/users/newUser";
+ }
+ return "whateverhtmlyouwant"
+*/
+
+package com.makersacademy.acebook.controller;
+
+import com.makersacademy.acebook.model.User;
+import com.makersacademy.acebook.repository.UserRepository;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.stereotype.Controller;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+
+import java.util.Optional;
+
+
+@Controller
+public class NavigationController {
+
+ @Autowired
+ UserRepository userRepository;
+
+ @ModelAttribute("user")
+ public Optional getUser(Authentication authentication) {
+ DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal();
+ String username = (String) principal.getAttributes().get("email");
+ return userRepository.findUserByUsername(username);
+ }
+
+
+ // Routes ------
+
+ @GetMapping("/profile")
+ public String profile(@ModelAttribute("user") Optional user) {
+ return user.isEmpty() ? "redirect:/users/newUser" : "profile";
+ }
+
+ @GetMapping("/settings")
+ public String settings(@ModelAttribute("user") Optional user) {
+ return user.isEmpty() ? "redirect:users/newUser" : "settings";
+ }
+
+ @PostMapping("/settings")
+ public String settings(@Valid User user, BindingResult bindingResult) {
+ if (bindingResult.hasErrors()) {
+ return "settings";
+ } else {
+ userRepository.save(user);
+ return "redirect:/posts";
+ }
+ }
+}
diff --git a/src/main/java/com/makersacademy/acebook/controller/PostsController.java b/src/main/java/com/makersacademy/acebook/controller/PostsController.java
index 57a7e5f4d..b22a17a74 100644
--- a/src/main/java/com/makersacademy/acebook/controller/PostsController.java
+++ b/src/main/java/com/makersacademy/acebook/controller/PostsController.java
@@ -1,14 +1,22 @@
package com.makersacademy.acebook.controller;
import com.makersacademy.acebook.model.Post;
+import com.makersacademy.acebook.model.User;
+
+import com.makersacademy.acebook.repository.UserRepository;
import com.makersacademy.acebook.repository.PostRepository;
+import org.springframework.security.core.Authentication;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.view.RedirectView;
+import java.security.Principal;
import java.util.List;
+import java.util.Optional;
@Controller
public class PostsController {
@@ -16,12 +24,41 @@ public class PostsController {
@Autowired
PostRepository repository;
+ @Autowired
+ UserRepository userRepository;
+
+
+
@GetMapping("/posts")
- public String index(Model model) {
+ public String index(Model model, Authentication authentication) {
+ DefaultOidcUser principal = (DefaultOidcUser) authentication.getPrincipal();
+ String username = (String) principal.getAttributes().get("email");
+ // code above to get email from the authenticator
+ Optional user = userRepository.findUserByUsername(username);
+ // ^^ optional user, theoretical
+
+ if (user.isEmpty()) {
+ return "redirect:/users/newUser"; // Redirect if not registered
+ }
+ // ^^ if the user is not saved in our database, they get redirected to the registration page
+
+ Long userId = user.get().getId(); // getting id from database - checking that id is connected
+ String email = user.get().getUsername(); // getting email in same way
+// public String index(Model model, @AuthenticationPrincipal OAuth2User principal) {
+ // String userName = principal.getAttribute("email");
+// Optional user = userRepository.findUserByUsername(userName);
+// long id = user.map(User::getId).orElse(999999999999L);
+// model.addAttribute("userID",id);
+
Iterable posts = repository.findAll();
model.addAttribute("posts", posts);
model.addAttribute("post", new Post());
- return "posts/index";
+
+ // code below to get userId and email from database
+ model.addAttribute("userId", userId);
+ model.addAttribute("email", email);
+
+ return "index";
}
@PostMapping("/posts")
diff --git a/src/main/java/com/makersacademy/acebook/controller/UsersController.java b/src/main/java/com/makersacademy/acebook/controller/UsersController.java
index a7c9db1d8..2194a83ea 100644
--- a/src/main/java/com/makersacademy/acebook/controller/UsersController.java
+++ b/src/main/java/com/makersacademy/acebook/controller/UsersController.java
@@ -2,13 +2,20 @@
import com.makersacademy.acebook.model.User;
import com.makersacademy.acebook.repository.UserRepository;
+import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.stereotype.Controller;
+import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
-@RestController
+import java.util.Optional;
+
+@Controller
+@RequestMapping
public class UsersController {
@Autowired
UserRepository userRepository;
@@ -21,10 +28,33 @@ public RedirectView afterLogin() {
.getPrincipal();
String username = (String) principal.getAttributes().get("email");
- userRepository
- .findUserByUsername(username)
- .orElseGet(() -> userRepository.save(new User(username)));
+ Optional user = userRepository.findUserByUsername(username);
+ if (user.isPresent()){
+ return new RedirectView("/posts"); // redirect to posts if registered
+ } else {
+ return new RedirectView("/users/newUser"); // redirect to registration if missing
+ }
+
+ }
+ @GetMapping("/users/newUser")
+ public String afterSignUp(@ModelAttribute("our_user") User user) {
+ DefaultOidcUser principal = (DefaultOidcUser) SecurityContextHolder
+ .getContext()
+ .getAuthentication()
+ .getPrincipal();
+
+ String username = (String) principal.getAttributes().get("email");
+ user.setUsername(username); // automatically fills in username on new user field, perhaps make this not allowed to change? once we add FN and LN
+ return "newUser";
- return new RedirectView("/posts");
+ }
+ @PostMapping("/users/newUser")
+ public String saveNewUser(@Valid @ModelAttribute("our_user") User user, BindingResult result) {
+ if (result.hasErrors()) { // error messages come from class constraints (needs dependency)
+ return "newUser"; //stays on page
+ } else {
+ userRepository.save(user); // this saves user to database, eventually fill out parameters as database changes to include first name, last name, dob etc
+ return "redirect:/posts"; // redirects to posts
+ }
}
}
diff --git a/src/main/java/com/makersacademy/acebook/model/Comment.java b/src/main/java/com/makersacademy/acebook/model/Comment.java
new file mode 100644
index 000000000..4dd4d2b93
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/model/Comment.java
@@ -0,0 +1,35 @@
+package com.makersacademy.acebook.model;
+
+
+import jakarta.persistence.*;
+import lombok.Data;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+
+@Data
+@Entity
+@Table(name = "comments")
+public class Comment {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ private String content;
+
+ @Column(name = "user_id")
+ private int userId;
+
+ @Column(name = "post_id")
+ private int postId;
+
+ @Column(name = "comment_timestamp")
+ private Timestamp commentTimestamp;
+
+ public Comment(String content, int userId, int postId){
+ this.content = content;
+ this.userId = userId;
+ this.postId = postId;
+ this.commentTimestamp = Timestamp.from(Instant.now());
+ }
+}
diff --git a/src/main/java/com/makersacademy/acebook/model/CommentLike.java b/src/main/java/com/makersacademy/acebook/model/CommentLike.java
new file mode 100644
index 000000000..c87db57f8
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/model/CommentLike.java
@@ -0,0 +1,32 @@
+package com.makersacademy.acebook.model;
+
+import jakarta.persistence.*;
+import lombok.Data;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+
+@Data
+@Entity
+@Table(name = "comment_likes")
+public class CommentLike {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "user_id")
+ private int userId;
+
+ @Column(name = "comment_id")
+ private int commentId;
+
+ @Column(name = "like_timestamp")
+ private Timestamp timestamp;
+
+ public CommentLike(int userId, int commentId){
+ this.userId = userId;
+ this.commentId = commentId;
+ this.timestamp = Timestamp.from(Instant.now());
+ }
+}
diff --git a/src/main/java/com/makersacademy/acebook/model/Friendship.java b/src/main/java/com/makersacademy/acebook/model/Friendship.java
new file mode 100644
index 000000000..cc0b55b81
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/model/Friendship.java
@@ -0,0 +1,31 @@
+package com.makersacademy.acebook.model;
+
+
+import jakarta.persistence.*;
+import lombok.Data;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+
+@Data
+@Entity
+@Table(name = "friendships")
+public class Friendship {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "lower_user_id")
+ private int lowerUserId;
+
+ @Column(name = "higher_user_id")
+ private int higherUserId;
+ private Timestamp friendshipTimestamp;
+
+ public Friendship(int lowerUserId, int higherUserId){
+ this.lowerUserId = lowerUserId;
+ this.higherUserId = higherUserId;
+ this.friendshipTimestamp = Timestamp.from(Instant.now());
+ }
+}
diff --git a/src/main/java/com/makersacademy/acebook/model/Post.java b/src/main/java/com/makersacademy/acebook/model/Post.java
index 33492c6b1..565b44ed0 100644
--- a/src/main/java/com/makersacademy/acebook/model/Post.java
+++ b/src/main/java/com/makersacademy/acebook/model/Post.java
@@ -4,19 +4,46 @@
import lombok.Data;
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDate;
+
@Data
@Entity
-@Table(name = "POSTS")
+@Table(name = "posts")
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
+ private String photo;
+
+ @Column(name = "user_id")
+ private int userId;
+
+ @Column(name = "post_timestamp")
+ private Timestamp timestamp;
public Post() {}
+ // used in the legacy code
public Post(String content) {
this.content = content;
}
+
+ // used when posting without a picture
+ public Post(String content,int userId) {
+ this.content = content;
+ this.userId = userId;
+ this.timestamp = Timestamp.from(Instant.now());
+ }
+
+ // posting with a picture
+ public Post(String content,int userId, String photo) {
+ this.content = content;
+ this.userId = userId;
+ this.timestamp = Timestamp.from(Instant.now());
+ this.photo = photo;
+ }
}
diff --git a/src/main/java/com/makersacademy/acebook/model/PostLike.java b/src/main/java/com/makersacademy/acebook/model/PostLike.java
new file mode 100644
index 000000000..b36318d10
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/model/PostLike.java
@@ -0,0 +1,36 @@
+package com.makersacademy.acebook.model;
+
+import jakarta.persistence.*;
+import lombok.Data;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+
+@Data
+@Entity
+@Table(name = "post_likes")
+public class PostLike {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "user_id")
+ private int userId;
+
+ @Column(name = "post_id")
+ private int postId;
+
+ @Column(name = "like_timestamp")
+ private Timestamp timestamp;
+
+ public PostLike(int user_id, int post_id){
+ this.userId = user_id;
+ this.postId = post_id;
+ this.timestamp = Timestamp.from(Instant.now());
+ }
+
+ public PostLike() {
+
+ }
+}
diff --git a/src/main/java/com/makersacademy/acebook/model/User.java b/src/main/java/com/makersacademy/acebook/model/User.java
index 6013fbe23..729267bc4 100644
--- a/src/main/java/com/makersacademy/acebook/model/User.java
+++ b/src/main/java/com/makersacademy/acebook/model/User.java
@@ -1,31 +1,70 @@
package com.makersacademy.acebook.model;
+import com.makersacademy.acebook.model.validation.DateOfBirthConstraint;
import jakarta.persistence.*;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.util.Date;
import static java.lang.Boolean.TRUE;
@Data
@Entity
-@Table(name = "USERS")
+@Getter @Setter
+
+@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
+
+
+ @NotBlank(message = "Username cannot be blank.") // error message: means we don't need to specify elsewhere that it can't be blank
private String username;
private boolean enabled;
+ private String avatar;
+
+ @NotNull(message = "Please set your date of birth")
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ @DateOfBirthConstraint
+ private LocalDate dob;
+
+ @NotBlank(message = "Please enter your first name")
+ @Column(name = "first_name")
+ private String firstName;
+
+ @NotBlank(message = "Please enter your last name")
+ @Column(name = "last_name")
+ private String lastName;
public User() {
this.enabled = TRUE;
}
+ //used in legacy code
public User(String username) {
this.username = username;
this.enabled = TRUE;
}
- public User(String username, boolean enabled) {
+ // used - assumes there will be an avatar
+ public User(String username,String firstName, String lastName, LocalDate dob, String avatar) {
this.username = username;
- this.enabled = enabled;
+ this.avatar = avatar;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.dob = dob;
+ this.avatar = avatar;
+ this.enabled = TRUE;
}
+
+
}
diff --git a/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthConstraint.java b/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthConstraint.java
new file mode 100644
index 000000000..a5f6a3864
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthConstraint.java
@@ -0,0 +1,16 @@
+package com.makersacademy.acebook.model.validation;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.*;
+
+@Documented
+@Constraint(validatedBy = DateOfBirthValidator.class)
+@Target( {ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DateOfBirthConstraint {
+ String message() default "Users must be 14 or older";
+ Class>[] groups() default {};
+ Class extends Payload>[] payload() default {};
+}
diff --git a/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthValidator.java b/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthValidator.java
new file mode 100644
index 000000000..7cd16d3a2
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/model/validation/DateOfBirthValidator.java
@@ -0,0 +1,22 @@
+package com.makersacademy.acebook.model.validation;
+
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+import java.time.LocalDate;
+
+
+
+public class DateOfBirthValidator implements ConstraintValidator {
+
+ @Override
+ public void initialize(DateOfBirthConstraint dob) { }
+
+ @Override
+ public boolean isValid(LocalDate dob, ConstraintValidatorContext cxt) {
+ LocalDate tomorrow = LocalDate.now().plusDays(1);
+ return dob.isBefore(tomorrow.minusYears(14));
+ }
+}
+
+
diff --git a/src/main/java/com/makersacademy/acebook/repository/CommentLikeRepository.java b/src/main/java/com/makersacademy/acebook/repository/CommentLikeRepository.java
new file mode 100644
index 000000000..1e37fcec5
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/repository/CommentLikeRepository.java
@@ -0,0 +1,7 @@
+package com.makersacademy.acebook.repository;
+
+import com.makersacademy.acebook.model.CommentLike;
+import org.springframework.data.repository.CrudRepository;
+
+public interface CommentLikeRepository extends CrudRepository {
+}
diff --git a/src/main/java/com/makersacademy/acebook/repository/CommentRepository.java b/src/main/java/com/makersacademy/acebook/repository/CommentRepository.java
new file mode 100644
index 000000000..a9dfc802f
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/repository/CommentRepository.java
@@ -0,0 +1,7 @@
+package com.makersacademy.acebook.repository;
+
+import com.makersacademy.acebook.model.Comment;
+import org.springframework.data.repository.CrudRepository;
+
+public interface CommentRepository extends CrudRepository {
+}
diff --git a/src/main/java/com/makersacademy/acebook/repository/FriendshipRepository.java b/src/main/java/com/makersacademy/acebook/repository/FriendshipRepository.java
new file mode 100644
index 000000000..eaef59e5e
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/repository/FriendshipRepository.java
@@ -0,0 +1,7 @@
+package com.makersacademy.acebook.repository;
+
+import com.makersacademy.acebook.model.Friendship;
+import org.springframework.data.repository.CrudRepository;
+
+public interface FriendshipRepository extends CrudRepository {
+}
diff --git a/src/main/java/com/makersacademy/acebook/repository/PostLikeRepository.java b/src/main/java/com/makersacademy/acebook/repository/PostLikeRepository.java
new file mode 100644
index 000000000..3e3c43565
--- /dev/null
+++ b/src/main/java/com/makersacademy/acebook/repository/PostLikeRepository.java
@@ -0,0 +1,7 @@
+package com.makersacademy.acebook.repository;
+
+import com.makersacademy.acebook.model.PostLike;
+import org.springframework.data.repository.CrudRepository;
+
+public interface PostLikeRepository extends CrudRepository {
+}
diff --git a/src/main/resources/.DS_Store b/src/main/resources/.DS_Store
new file mode 100644
index 000000000..f5a5d21ac
Binary files /dev/null and b/src/main/resources/.DS_Store differ
diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties
index 865b41e1c..926b9edf4 100644
--- a/src/main/resources/application-test.properties
+++ b/src/main/resources/application-test.properties
@@ -3,4 +3,4 @@ spring.datasource.username=
spring.datasource.password=
flyway.baseline-on-migrate=true
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false
-spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
+spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 699e8575a..39af65fd8 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,5 +1,5 @@
okta:
oauth2:
- issuer: https://dev-edward-andress.uk.auth0.com/
- client-id: ${OKTA_CLIENT_ID}
- client-secret: ${OKTA_CLIENT_SECRET}
+ issuer: ${ISSUER_ACEBOOK}
+ client-id: ${CLIENT_ID_ACEBOOK}
+ client-secret: ${CLIENT_SECRET_ACEBOOK}
diff --git a/src/main/resources/db/migration/V3__starting_table_design.sql b/src/main/resources/db/migration/V3__starting_table_design.sql
new file mode 100644
index 000000000..95bac7a10
--- /dev/null
+++ b/src/main/resources/db/migration/V3__starting_table_design.sql
@@ -0,0 +1,38 @@
+ALTER table users
+add email varchar(50),
+add avatar varchar(100);
+
+ALTER table posts
+add photo varchar(100),
+add user_id int;
+
+ALTER TABLE posts
+ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id);
+
+
+CREATE table comments (
+ id bigserial PRIMARY KEY,
+ content varchar(250),
+ user_id int,
+ post_id int,
+ FOREIGN KEY (user_id) REFERENCES users(id),
+ FOREIGN KEY (post_id) REFERENCES posts(id)
+);
+
+CREATE table friendships (
+id bigserial PRIMARY KEY,
+lower_user_id int,
+higher_user_id int,
+ FOREIGN KEY (lower_user_id) REFERENCES users(id),
+ FOREIGN KEY (higher_user_id) REFERENCES users(id)
+);
+
+CREATE table likes (
+ id bigserial PRIMARY KEY,
+ user_id int,
+ post_id int,
+ comment_id int,
+ FOREIGN KEY (user_id) REFERENCES users(id),
+ FOREIGN KEY (post_id) REFERENCES posts(id),
+ FOREIGN KEY (comment_id) REFERENCES comments(id)
+)
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V4__changes_after_meeting.sql b/src/main/resources/db/migration/V4__changes_after_meeting.sql
new file mode 100644
index 000000000..b8af886ff
--- /dev/null
+++ b/src/main/resources/db/migration/V4__changes_after_meeting.sql
@@ -0,0 +1,18 @@
+ALTER table users
+DROP column username,
+add first_name varchar(50),
+add last_name varchar(50),
+add dob date;
+
+ALTER table posts
+add post_timestamp TIMESTAMP;
+
+
+alter table comments
+add comment_timestamp TIMESTAMP;
+
+alter table friendships
+add friendship_timestamp TIMESTAMP;
+
+alter table likes
+add like_timestamp TIMESTAMP;
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V5__add_username_back.sql b/src/main/resources/db/migration/V5__add_username_back.sql
new file mode 100644
index 000000000..a3f2aad84
--- /dev/null
+++ b/src/main/resources/db/migration/V5__add_username_back.sql
@@ -0,0 +1,3 @@
+alter table users
+add username varchar(50) NOT NULL UNIQUE,
+drop column email;
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V6__separate_likes.sql b/src/main/resources/db/migration/V6__separate_likes.sql
new file mode 100644
index 000000000..a99ef656a
--- /dev/null
+++ b/src/main/resources/db/migration/V6__separate_likes.sql
@@ -0,0 +1,19 @@
+drop table likes;
+
+create table comment_likes (
+ id bigserial PRIMARY KEY,
+ user_id int,
+ comment_id int,
+ FOREIGN KEY (user_id) REFERENCES users(id),
+ FOREIGN KEY (comment_id) REFERENCES comments(id),
+ like_timestamp TIMESTAMP
+);
+
+create table post_likes (
+ id bigserial PRIMARY KEY,
+ user_id int,
+ post_id int,
+ FOREIGN KEY (user_id) REFERENCES users(id),
+ FOREIGN KEY (post_id) REFERENCES posts(id),
+ like_timestamp TIMESTAMP
+);
\ No newline at end of file
diff --git a/src/main/resources/db/migration/V7__seed_data.sql b/src/main/resources/db/migration/V7__seed_data.sql
new file mode 100644
index 000000000..97843754f
--- /dev/null
+++ b/src/main/resources/db/migration/V7__seed_data.sql
@@ -0,0 +1,39 @@
+ALTER SEQUENCE users_id_seq RESTART WITH 1;
+ALTER SEQUENCE posts_id_seq RESTART WITH 1;
+ALTER SEQUENCE comments_id_seq RESTART WITH 1;
+ALTER SEQUENCE friendships_id_seq RESTART WITH 1;
+ALTER SEQUENCE post_likes_id_seq RESTART WITH 1;
+ALTER SEQUENCE comment_likes_id_seq RESTART WITH 1;
+
+INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('jimmyd@gmail.com','1990-11-01','James','Dickinson','TRUE','https://imgur.com/g0Lfp8N');
+INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('sashab@gmail.com','1991-06-01','Sansha','Ballom','TRUE','https://imgur.com/LZE6sj2');
+INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('walterwhite@gmail.com','1972-07-10','Walter','White','TRUE','https://imgur.com/Fm5tAKH');
+INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('Mandymayhem@gmail.com','1956-03-01','Mandy','Tucker','TRUE','https://imgur.com/jTQ7gAu');
+INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('vicky123@gmail.com','2015-01-01','Victoria','Ballom','TRUE','https://imgur.com/2IprHCN');
+INSERT INTO users (username,dob,first_name,last_name,enabled,avatar) VALUES ('yoshiboy@gmail.com','2024-03-10','Yoshi','Hunter','TRUE','https://imgur.com/a/jWApU9E');
+
+
+INSERT INTO posts (content,user_id,post_timestamp,photo) VALUES ('Saw a dog today!',1,'2024-01-01 12:00:00','https://imgur.com/a/tSWP7DS');
+INSERT INTO posts (content,user_id,post_timestamp) VALUES ('Going out to cook, noone follow me',3,'2024-01-03 18:00:00');
+INSERT INTO posts (content,user_id,post_timestamp) VALUES ('How do I use this?',4,'2024-01-04 12:00:00');
+INSERT INTO posts (content,user_id,post_timestamp) VALUES ('How to use',4,'2024-01-04 12:04:00');
+INSERT INTO posts (content,user_id,post_timestamp) VALUES ('Roast beef casserole recipe',4,'2024-01-04 15:04:00');
+
+INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('It''s so cute!',2,1,'2024-01-01 12:30:00');
+INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('It is! <3 <3',1,1,'2024-01-01 12:45:00');
+INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('Where are you going?',5,2,'2024-01-03 18:03:00');
+INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('None of your business',3,2,'2024-01-03 18:05:00');
+INSERT INTO comments(content,user_id,post_id, comment_timestamp) VALUES ('Don''t talk to my daughter like that!',2,2,'2024-01-03 18:07:00');
+
+INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (1,2,'2023-09-01 08:00:00');
+INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (2,5,'2023-06-01 10:00:00');
+INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (3,4,'2022-01-01 12:00:00');
+INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (1,6,'2024-03-11 12:00:00');
+INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (2,6,'2024-03-11 12:00:00');
+INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (3,6,'2024-03-11 12:00:00');
+INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (4,6,'2024-03-11 12:00:00');
+INSERT INTO friendships(lower_user_id,higher_user_id,friendship_timestamp) values (5,6,'2024-03-11 12:00:00');
+
+INSERT INTO post_likes(user_id,post_id,like_timestamp) values (2,1,'2024-01-01 12:27:00');
+
+INSERT INTO comment_likes(user_id,comment_id,like_timestamp) values (1,1,'2024-01-01 12:42:00');
\ No newline at end of file
diff --git a/src/main/resources/static/images/placeholders/alan_partridge.jpg b/src/main/resources/static/images/placeholders/alan_partridge.jpg
new file mode 100644
index 000000000..e818b7d9a
Binary files /dev/null and b/src/main/resources/static/images/placeholders/alan_partridge.jpg differ
diff --git a/src/main/resources/static/images/userAvatars/9.jpg b/src/main/resources/static/images/userAvatars/9.jpg
new file mode 100644
index 000000000..e818b7d9a
Binary files /dev/null and b/src/main/resources/static/images/userAvatars/9.jpg differ
diff --git a/src/main/resources/static/main.css b/src/main/resources/static/main.css
index d0260873c..ed95dc396 100644
--- a/src/main/resources/static/main.css
+++ b/src/main/resources/static/main.css
@@ -8,3 +8,17 @@
text-align: left;
margin-bottom: 0.5rem;;
}
+
+.user-initials {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background-color: #1877f2;
+ color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: bold;
+ font-size: 14px;
+ margin-left: 12px;
+}
\ No newline at end of file
diff --git a/src/main/resources/templates/.DS_Store b/src/main/resources/templates/.DS_Store
new file mode 100644
index 000000000..06be266d5
Binary files /dev/null and b/src/main/resources/templates/.DS_Store differ
diff --git a/src/main/resources/templates/friends.html b/src/main/resources/templates/friends.html
new file mode 100644
index 000000000..c51c3857c
--- /dev/null
+++ b/src/main/resources/templates/friends.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+ Account Settings
+
+
+
+
+
+
+
+
+
+
+Friends List
+
+
+
+