diff --git a/pom.xml b/pom.xml
index 97f867f..f293ecf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,18 +18,10 @@
1.4.31
-
- org.springframework.boot
- spring-boot-starter-data-jdbc
- org.springframework.bootspring-boot-starter-data-jpa
-
- org.springframework.boot
- spring-boot-starter-jdbc
- org.springframework.bootspring-boot-starter-security
diff --git a/src/main/kotlin/uk/co/neviyn/projectplanner/Entities.kt b/src/main/kotlin/uk/co/neviyn/projectplanner/Entities.kt
index 3a3a9e1..8b87534 100644
--- a/src/main/kotlin/uk/co/neviyn/projectplanner/Entities.kt
+++ b/src/main/kotlin/uk/co/neviyn/projectplanner/Entities.kt
@@ -1,11 +1,11 @@
package uk.co.neviyn.projectplanner
-import com.fasterxml.jackson.annotation.JsonBackReference
-import com.fasterxml.jackson.annotation.JsonManagedReference
+import com.fasterxml.jackson.annotation.JsonIgnore
import java.time.LocalDateTime
import javax.persistence.CascadeType
import javax.persistence.Entity
import javax.persistence.GeneratedValue
+import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.JoinColumn
import javax.persistence.JoinTable
@@ -14,42 +14,47 @@ import javax.persistence.ManyToOne
import javax.persistence.OneToMany
@Entity
-class User(
+open class User(
var username: String = "INVALID",
var email: String = "INVALID",
var password: String = "INVALID",
@ManyToMany(cascade = [CascadeType.ALL])
- @JsonManagedReference
+ @JsonIgnore
@JoinTable(
name = "Team",
joinColumns = [JoinColumn(name = "user_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "project_id", referencedColumnName = "id")]
)
- var projects: Set = mutableSetOf(),
- @Id @GeneratedValue var id: Long? = null
-)
+ var projects: MutableSet = mutableSetOf(),
+ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null
+) {}
@Entity
class Project(
var title: String = "INVALID",
@ManyToMany(mappedBy = "projects")
- @JsonBackReference
- var members: Set = mutableSetOf(),
+ @JsonIgnore
+ var members: MutableSet = mutableSetOf(),
@OneToMany(mappedBy = "project")
- var events: Set = mutableSetOf(),
- @Id @GeneratedValue var id: Long? = null
+ @JsonIgnore
+ var events: MutableSet = mutableSetOf(),
+ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null
)
@Entity
class Event(
var title: String = "INVALID",
var description: String = "INVALID",
+ var start: LocalDateTime = LocalDateTime.MIN,
+ var end: LocalDateTime = LocalDateTime.MIN,
@ManyToOne
@JoinColumn(name = "project_id")
+ @JsonIgnore
var project: Project? = null,
@ManyToMany(mappedBy = "events")
- var tags: Set = mutableSetOf(),
- @Id @GeneratedValue var id: Long? = null
+ @JsonIgnore
+ var tags: MutableSet = mutableSetOf(),
+ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null
)
@Entity
@@ -59,12 +64,14 @@ class Comment(
var user: User? = null,
@ManyToOne
@JoinColumn(name = "event_id")
+ @JsonIgnore
var event: Event? = null,
@ManyToMany(mappedBy = "comments")
- var tags: Set = mutableSetOf(),
+ @JsonIgnore
+ var tags: MutableSet = mutableSetOf(),
var created: LocalDateTime = LocalDateTime.MIN,
var comment: String = "INVALID",
- @Id @GeneratedValue var id: Long? = null
+ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null
)
@Entity
@@ -76,13 +83,15 @@ class Tag(
joinColumns = [JoinColumn(name = "tag_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "comment_id", referencedColumnName = "id")]
)
- var comments: Set = mutableSetOf(),
+ @JsonIgnore
+ var comments: MutableSet = mutableSetOf(),
@ManyToMany
@JoinTable(
name = "event_tags",
joinColumns = [JoinColumn(name = "tag_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "event_id", referencedColumnName = "id")]
)
- var events: Set = mutableSetOf(),
- @Id @GeneratedValue var id: Long? = null
+ @JsonIgnore
+ var events: MutableSet = mutableSetOf(),
+ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null
)
\ No newline at end of file
diff --git a/src/main/kotlin/uk/co/neviyn/projectplanner/HtmlController.kt b/src/main/kotlin/uk/co/neviyn/projectplanner/HtmlController.kt
index 54c104d..97f584d 100644
--- a/src/main/kotlin/uk/co/neviyn/projectplanner/HtmlController.kt
+++ b/src/main/kotlin/uk/co/neviyn/projectplanner/HtmlController.kt
@@ -6,14 +6,37 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
-import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.ui.Model
-
-
+import org.springframework.web.bind.annotation.ModelAttribute
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.RequestBody
+import org.springframework.web.bind.annotation.RequestMapping
+import javax.persistence.EntityManager
+import javax.transaction.Transactional
@Controller
-class HtmlController @Autowired constructor(val userRepository: UserRepository, val projectRepository: ProjectRepository){
+class HtmlController @Autowired constructor(val userRepository: UserRepository, val projectRepository: ProjectRepository, val entityManager: EntityManager){
+
+ @GetMapping("/")
+ fun landingPage(@AuthenticationPrincipal userDetails: CustomUserDetails?) : String {
+ return if(userDetails == null) "landing"
+ else "redirect:/projects"
+ }
+
+ @GetMapping("/register")
+ fun register(model: Model) : String {
+ val user = User(username = "", email = "", password = "")
+ model.addAttribute("user_details", user)
+ return "register"
+ }
+
+ @PostMapping("/register")
+ fun register(@ModelAttribute newUser: User) : String {
+ newUser.password = passwordEncoder().encode(newUser.password)
+ userRepository.save(newUser)
+ return "login"
+ }
@GetMapping("/login")
fun login(model: Model, error: String?, logout: String?): String? {
@@ -22,24 +45,96 @@ class HtmlController @Autowired constructor(val userRepository: UserRepository,
return "login"
}
- @GetMapping("/user/{id}")
- @ResponseBody
- fun getUser(@PathVariable id: Long) : User {
- return userRepository.findById(id).get()
+ @GetMapping("/profile")
+ fun getLoggedInUser(@AuthenticationPrincipal userDetails: CustomUserDetails, model: Model) : String {
+ val user = DisplayUser(userDetails.user.id!!, userDetails.user.username, userDetails.user.email, "", "")
+ model.addAttribute("userData", user)
+ return "profile"
}
- @PreAuthorize("hasPermission(#projectID, 'Long', '')")
- @GetMapping("/project/{projectID}")
- @ResponseBody
- fun getProject(@PathVariable projectID: Long) : Project{
- return projectRepository.findById(projectID).get()
+ @PostMapping("/profile")
+ @Transactional
+ fun updateLoggedInUser(@ModelAttribute userData: DisplayUser, @AuthenticationPrincipal userDetails: CustomUserDetails, model: Model) : String {
+ if(userData.id == userDetails.user.id!! && passwordEncoder().matches(userData.oldPassword, userDetails.password)) {
+ val user = userDetails.user
+ user.email = userData.email
+ if(userData.password.isNotEmpty()) user.password = passwordEncoder().encode(userData.password)
+ userRepository.save(user)
+ model.addAttribute("message", "Your profile has been updated")
+ }
+ else{
+ model.addAttribute("error", "Incorrect existing password")
+ }
+ model.addAttribute("userData", DisplayUser(userData.id, userData.username, userData.email, userData.password, ""))
+ return "profile"
}
- @GetMapping("/me")
- @ResponseBody
- fun getLoggedInUser(@AuthenticationPrincipal userDetails: CustomUserDetails) : Long {
- return userDetails.user.id!!
+ @PostMapping("/newproject")
+ fun createNewProject(@RequestBody newProject: NewProject){
+ val project = Project(title = newProject.name)
+ projectRepository.save(project)
}
+ @GetMapping("/projects")
+ @Transactional
+ fun listUserProjects(model: Model, @AuthenticationPrincipal userDetails: CustomUserDetails) : String {
+ val user = entityManager.merge(userDetails.user) // Reattach User entity
+ model.addAttribute("projects", user.projects)
+ return "projectlist"
+ }
+}
+
+@Controller
+@RequestMapping("/project/{id}")
+class ProjectController @Autowired constructor(val projectRepository: ProjectRepository, val userRepository: UserRepository, val eventRepository: EventRepository) {
+
+ @GetMapping("")
+ @PreAuthorize("hasPermission(#id, 'Long', '')")
+ fun getProject(@PathVariable id: Long, model: Model) : String {
+ val project = projectRepository.findById(id).get()
+ model.addAttribute("project", project)
+ return "project"
+ }
+
+ @GetMapping("/events")
+ @PreAuthorize("hasPermission(#id, 'Long', '')")
+ fun getProjectEvents(@PathVariable id: Long) : Set {
+ return projectRepository.findById(id).get().events
+ }
+
+ @GetMapping("/adduser")
+ @PreAuthorize("hasPermission(#id, 'Long', '')")
+ fun addUserToProjectForm(@PathVariable id: Long, model: Model) : String{
+ val project = projectRepository.findById(id).get()
+ val users = userRepository.findByIdNotIn(project.members.map { it.id!! }).map { SimpleUser(it.id!!, it.username) }
+ model.addAttribute("available_users", users)
+ return "addprojectuser"
+ }
+
+ @PostMapping("/adduser")
+ @PreAuthorize("hasPermission(#id, 'Long', '')")
+ fun addUserToProject(@PathVariable id: Long, @RequestBody u: UserID) {
+ val user = userRepository.findById(u.id).get()
+ val project = projectRepository.findById(id).get()
+ project.members.add(user)
+ projectRepository.save(project)
+ }
+
+ @PostMapping("/removeuser")
+ @PreAuthorize("hasPermission(#id, 'Long', '')")
+ fun removeUserFromProject(@PathVariable id: Long, @RequestBody u: UserID) {
+ val user = userRepository.findById(u.id).get()
+ val project = projectRepository.findById(id).get()
+ project.members.remove(user)
+ projectRepository.save(project)
+ }
+
+ @PostMapping("/addevent")
+ @PreAuthorize("hasPermission(#id, 'Long', '')")
+ fun addEventToProject(@PathVariable id: Long, @RequestBody e: NewEvent) {
+ val project = projectRepository.findById(id).get()
+ val event = Event(title = e.title, description = e.description, start = e.start, end = e.end, project = project)
+ eventRepository.save(event)
+ }
}
\ No newline at end of file
diff --git a/src/main/kotlin/uk/co/neviyn/projectplanner/Repositories.kt b/src/main/kotlin/uk/co/neviyn/projectplanner/Repositories.kt
index 0aee66f..1313341 100644
--- a/src/main/kotlin/uk/co/neviyn/projectplanner/Repositories.kt
+++ b/src/main/kotlin/uk/co/neviyn/projectplanner/Repositories.kt
@@ -4,6 +4,8 @@ import org.springframework.data.repository.CrudRepository
interface UserRepository : CrudRepository{
fun findByUsername(username: String): User?
+ fun findByUsernameIsLike(partialUsername: String) : List
+ fun findByIdNotIn(ids: List) : List
}
interface ProjectRepository : CrudRepository{}
diff --git a/src/main/kotlin/uk/co/neviyn/projectplanner/Requests.kt b/src/main/kotlin/uk/co/neviyn/projectplanner/Requests.kt
new file mode 100644
index 0000000..3ac90a9
--- /dev/null
+++ b/src/main/kotlin/uk/co/neviyn/projectplanner/Requests.kt
@@ -0,0 +1,13 @@
+package uk.co.neviyn.projectplanner
+
+import java.time.LocalDateTime
+
+data class UserID(val id: Long)
+
+data class SimpleUser(val id: Long, val username: String)
+
+data class DisplayUser(val id: Long, val username: String, val email: String, val password: String, val oldPassword: String)
+
+data class NewProject(val name: String)
+
+data class NewEvent(val title: String, val description: String, val start: LocalDateTime, val end: LocalDateTime)
\ No newline at end of file
diff --git a/src/main/kotlin/uk/co/neviyn/projectplanner/Security.kt b/src/main/kotlin/uk/co/neviyn/projectplanner/Security.kt
index ebcbee0..34294be 100644
--- a/src/main/kotlin/uk/co/neviyn/projectplanner/Security.kt
+++ b/src/main/kotlin/uk/co/neviyn/projectplanner/Security.kt
@@ -25,10 +25,10 @@ import java.io.Serializable
@Configuration
class SecurityConfig @Autowired constructor(val userDetailsService: UserDetailsServiceImpl) : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
- http.authorizeRequests().antMatchers("/").permitAll()
+ http.authorizeRequests().antMatchers("/", "/*.jpg", "/*.svg", "/register").permitAll()
.anyRequest().authenticated().and()
.formLogin().loginPage("/login").permitAll().and()
- .logout().permitAll().and()
+ .logout().logoutSuccessUrl("/").permitAll().and()
.httpBasic()
}
diff --git a/src/main/resources/static/landing_1.jpg b/src/main/resources/static/landing_1.jpg
new file mode 100644
index 0000000..ac4c166
Binary files /dev/null and b/src/main/resources/static/landing_1.jpg differ
diff --git a/src/main/resources/templates/addprojectuser.html b/src/main/resources/templates/addprojectuser.html
new file mode 100644
index 0000000..edea7b6
--- /dev/null
+++ b/src/main/resources/templates/addprojectuser.html
@@ -0,0 +1,26 @@
+
+
+
+
+ Add user to project | Project Planner
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/fragments.html b/src/main/resources/templates/fragments.html
index b7b72e3..1bb2097 100644
--- a/src/main/resources/templates/fragments.html
+++ b/src/main/resources/templates/fragments.html
@@ -1,15 +1,40 @@
-
+
Base Title
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/landing.html b/src/main/resources/templates/landing.html
new file mode 100644
index 0000000..f32468f
--- /dev/null
+++ b/src/main/resources/templates/landing.html
@@ -0,0 +1,33 @@
+
+
+
+ Project Planner
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html
index 16cf1ef..7fdca85 100644
--- a/src/main/resources/templates/login.html
+++ b/src/main/resources/templates/login.html
@@ -8,24 +8,36 @@