Merge pull request #3 from andrewlalis/data_import
Merge current working branch to master
This commit is contained in:
commit
2a205b5dc0
|
@ -1,3 +1,4 @@
|
|||
.idea/*
|
||||
*.iml
|
||||
target/*
|
||||
javadoc/*
|
|
@ -0,0 +1,105 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64.000001 64.000001"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="TAA.svg"
|
||||
inkscape:export-filename="/home/andrew/Programming/Projects/Teaching_Assistant_Assistant/src/main/resources/static/images/logo.png"
|
||||
inkscape:export-xdpi="360"
|
||||
inkscape:export-ydpi="360">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.2"
|
||||
inkscape:cx="24.365199"
|
||||
inkscape:cy="32.206088"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1030"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-988.36216)">
|
||||
<rect
|
||||
style="opacity:1;fill:#008000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="rect4136"
|
||||
width="60"
|
||||
height="60"
|
||||
x="1"
|
||||
y="991.36218"
|
||||
ry="16" />
|
||||
<flowRoot
|
||||
xml:space="preserve"
|
||||
id="flowRoot4138"
|
||||
style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
transform="matrix(2.0532963,0,0,2.0532963,-37.008,965.44742)"><flowRegion
|
||||
id="flowRegion4140"><rect
|
||||
id="rect4142"
|
||||
width="18.125"
|
||||
height="26.249994"
|
||||
x="27.5"
|
||||
y="9.8928566" /></flowRegion><flowPara
|
||||
id="flowPara4144"
|
||||
style="fill:#ffffff;fill-opacity:1">T</flowPara></flowRoot> <flowRoot
|
||||
xml:space="preserve"
|
||||
id="flowRoot4154"
|
||||
style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
transform="matrix(1.3900147,0,0,1.3900147,-2.1586653,998.49896)"><flowRegion
|
||||
id="flowRegion4156"><rect
|
||||
id="rect4158"
|
||||
width="18.125"
|
||||
height="26.249994"
|
||||
x="27.5"
|
||||
y="9.8928566" /></flowRegion><flowPara
|
||||
id="flowPara4160"
|
||||
style="fill:#ffffff;fill-opacity:1">A</flowPara></flowRoot> <flowRoot
|
||||
transform="matrix(1.3900147,0,0,1.3900147,-29.448313,998.49896)"
|
||||
style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="flowRoot4162"
|
||||
xml:space="preserve"><flowRegion
|
||||
id="flowRegion4164"><rect
|
||||
y="9.8928566"
|
||||
x="27.5"
|
||||
height="26.249994"
|
||||
width="18.125"
|
||||
id="rect4166" /></flowRegion><flowPara
|
||||
style="fill:#ffffff;fill-opacity:1"
|
||||
id="flowPara4168">A</flowPara></flowRoot> </g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.1 KiB |
|
@ -1,25 +1,13 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.PersonRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.TeamRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
@SpringBootApplication
|
||||
public class TeachingAssistantAssistantApplication implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
CourseRepository courseRepository;
|
||||
|
||||
@Autowired
|
||||
TeamRepository teamRepository;
|
||||
|
||||
@Autowired
|
||||
PersonRepository personRepository;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TeachingAssistantAssistantApplication.class, args);
|
||||
}
|
||||
|
@ -27,5 +15,6 @@ public class TeachingAssistantAssistantApplication implements CommandLineRunner
|
|||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
System.out.println("Running startup...");
|
||||
System.out.println(new BCryptPasswordEncoder().encode("test"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.config;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.security.UserDetailsService;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* Security configuration for the TAA application.
|
||||
*
|
||||
* This configuration makes use of the custom user details service provided by the application for database-persistent
|
||||
* user accounts.
|
||||
*
|
||||
* Login, logout, and registration pages are set so that all users, authenticated and unauthenticated, may access them,
|
||||
* while actual site content is only visible to authenticated users.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
protected WebSecurityConfig(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(this.userDetailsService)
|
||||
.passwordEncoder(passwordEncoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(final HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf().disable() // So that we can GET the logout page.
|
||||
|
||||
.authorizeRequests() // Let anyone view the login and logout pages, as well as various registration pages.
|
||||
.antMatchers("/login*", "/logout*", "/register*", "/register/**")
|
||||
.permitAll()
|
||||
.and()
|
||||
|
||||
.authorizeRequests()
|
||||
.antMatchers("/css/**", "/images/**", "/javascript/**")
|
||||
.permitAll()
|
||||
.and()
|
||||
|
||||
.authorizeRequests() // Only logged in users should be able to see site content.
|
||||
.antMatchers("/**").hasRole("user")
|
||||
.anyRequest().hasRole("user")
|
||||
.and()
|
||||
|
||||
.formLogin()
|
||||
.loginPage("/login")
|
||||
.permitAll()
|
||||
.loginProcessingUrl("/login")
|
||||
.defaultSuccessUrl("/", true)
|
||||
.failureUrl("/login?error")
|
||||
.and()
|
||||
|
||||
.logout()
|
||||
.permitAll()
|
||||
.clearAuthentication(true)
|
||||
.invalidateHttpSession(true)
|
||||
.logoutUrl("/logout")
|
||||
.logoutSuccessUrl("/login")
|
||||
.deleteCookies("JSESSIONID");
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Spring Security to use a specific password encoder.
|
||||
* @return The password encoder to use.
|
||||
*/
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
|
@ -12,11 +12,11 @@ import org.springframework.web.bind.annotation.PostMapping;
|
|||
* Controller for the list of courses in the system.
|
||||
*/
|
||||
@Controller
|
||||
public class Courses {
|
||||
public class CoursesController extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
protected Courses(CourseRepository courseRepository) {
|
||||
protected CoursesController(CourseRepository courseRepository) {
|
||||
this.courseRepository = courseRepository;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
/**
|
||||
* Controller for the login page, visible to all users.
|
||||
*/
|
||||
@Controller
|
||||
public class LoginController {
|
||||
|
||||
private final Logger logger = LogManager.getLogger(LoginController.class);
|
||||
|
||||
@GetMapping("/login")
|
||||
public String get() {
|
||||
return "login";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.dto.NewUserRegistrationDTO;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Controller for the registration page which is used for the creation of new accounts.
|
||||
*/
|
||||
@Controller
|
||||
public class RegisterController {
|
||||
|
||||
private final Logger logger = LogManager.getLogger(RegisterController.class);
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
private static final String[] personTypes = {
|
||||
"Student",
|
||||
"Teaching Assistant",
|
||||
"Professor",
|
||||
"Administrator"
|
||||
};
|
||||
|
||||
protected RegisterController(CourseRepository courseRepository) {
|
||||
this.courseRepository = courseRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/register")
|
||||
public String get(Model model) {
|
||||
model.addAttribute("newUserRegistration", new NewUserRegistrationDTO(Arrays.asList(personTypes), this.courseRepository.findAll()));
|
||||
return "register";
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public String post(@ModelAttribute NewUserRegistrationDTO newUserRegistration) {
|
||||
logger.info("Received new registration: " + newUserRegistration);
|
||||
|
||||
return "redirect:/login";
|
||||
}
|
||||
}
|
|
@ -2,18 +2,13 @@ package nl.andrewlalis.teaching_assistant_assistant.controllers;
|
|||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
public class RootController {
|
||||
public class RootController extends UserPageController {
|
||||
|
||||
@RequestMapping(
|
||||
path = "/",
|
||||
produces = "text/html"
|
||||
)
|
||||
@GetMapping("/")
|
||||
public String index(Model model) {
|
||||
model.addAttribute("name", "JOHN");
|
||||
return "index.html";
|
||||
return "index";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
/**
|
||||
* Controller for operations dealing with the global collection of students, not particular to one course.
|
||||
*/
|
||||
@Controller
|
||||
public class StudentsController extends UserPageController {
|
||||
|
||||
private StudentRepository studentRepository;
|
||||
|
||||
protected StudentsController(StudentRepository studentRepository) {
|
||||
this.studentRepository = studentRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all students.
|
||||
* @param model The view model.
|
||||
* @return The template for displaying a list of students.
|
||||
*/
|
||||
@GetMapping("/students")
|
||||
public String get(Model model) {
|
||||
model.addAttribute("students", this.studentRepository.findAll());
|
||||
return "students";
|
||||
}
|
||||
}
|
|
@ -13,13 +13,16 @@ import org.springframework.web.bind.annotation.PostMapping;
|
|||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Controller for the list of teaching assistants in the system.
|
||||
*/
|
||||
@Controller
|
||||
public class TeachingAssistants {
|
||||
public class TeachingAssistantsController extends UserPageController {
|
||||
|
||||
private TeachingAssistantRepository teachingAssistantRepository;
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
protected TeachingAssistants(TeachingAssistantRepository teachingAssistantRepository, CourseRepository courseRepository) {
|
||||
protected TeachingAssistantsController(TeachingAssistantRepository teachingAssistantRepository, CourseRepository courseRepository) {
|
||||
this.teachingAssistantRepository = teachingAssistantRepository;
|
||||
this.courseRepository = courseRepository;
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Controller
|
||||
public class TestController {
|
||||
|
||||
@GetMapping("/test")
|
||||
public String test (@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
|
||||
model.addAttribute("name", name);
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.security.User;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.security.UserDetails;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
/**
|
||||
* An abstract controller which simplifies the definition of controllers for pages that require a user to be signed in.
|
||||
*/
|
||||
public abstract class UserPageController {
|
||||
|
||||
/**
|
||||
* A shortcut to get the current authenticated user.
|
||||
* @param auth The spring authentication.
|
||||
* @return The user that's logged in.
|
||||
*/
|
||||
@ModelAttribute("user")
|
||||
protected User getUser(Authentication auth) {
|
||||
UserDetails userDetails = (UserDetails) auth.getPrincipal();
|
||||
return userDetails.getUser();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
@ -13,7 +14,7 @@ import java.util.Optional;
|
|||
* Controller for the course entity, that is, one individual course.
|
||||
*/
|
||||
@Controller
|
||||
public class CourseEntity {
|
||||
public class CourseEntity extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
@ -7,7 +8,7 @@ import org.springframework.ui.Model;
|
|||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
public class CreateCourse {
|
||||
public class CreateCourse extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
|
@ -17,7 +18,7 @@ public class CreateCourse {
|
|||
|
||||
@GetMapping("/courses/create")
|
||||
public String get(Model model) {
|
||||
Course course = new Course("no name", "no code");
|
||||
Course course = new Course(null, null);
|
||||
model.addAttribute("course", course);
|
||||
return "courses/create";
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.util.sample_data.CourseGenerator;
|
||||
|
@ -10,7 +11,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
public class Generate {
|
||||
public class Generate extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
|
@ -22,7 +23,7 @@ import java.util.Optional;
|
|||
* Controller for importing students from a CSV sheet.
|
||||
*/
|
||||
@Controller
|
||||
public class ImportStudents {
|
||||
public class ImportStudents extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.student_teams;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Student;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam;
|
||||
|
@ -18,7 +19,7 @@ import java.util.Optional;
|
|||
* Controller for exporting team information into readable files.
|
||||
*/
|
||||
@Controller
|
||||
public class ExportStudentTeams {
|
||||
public class ExportStudentTeams extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
|
@ -79,10 +80,17 @@ public class ExportStudentTeams {
|
|||
|
||||
private byte[] getContactInfo(Course course) {
|
||||
StringBuilder sb = new StringBuilder("Student Team Contact Details\n");
|
||||
sb.append("--------------------------------------------\n");
|
||||
sb.append("Student Number, Name, Email, Github Username\n");
|
||||
sb.append("--------------------------------------------\n");
|
||||
for (StudentTeam team : course.getStudentTeams()) {
|
||||
sb.append("2019_Team_").append(team.getId()).append(": ");
|
||||
sb.append("Team ").append(team.getId()).append(":\n");
|
||||
for (Student student : team.getStudents()) {
|
||||
sb.append(student.getFullName()).append(" (").append(student.getEmailAddress()).append("), ");
|
||||
sb.append("\t - s").append(student.getStudentNumber())
|
||||
.append(", ").append(student.getFullName())
|
||||
.append(", ").append(student.getEmailAddress())
|
||||
.append(", ").append(student.getGithubUsername())
|
||||
.append('\n');
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.student_teams;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
|
@ -15,7 +16,7 @@ import java.io.IOException;
|
|||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
public class GenerateRepositories {
|
||||
public class GenerateRepositories extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
private StudentTeamRepository studentTeamRepository;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.student_teams;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
|
@ -20,7 +21,7 @@ import java.util.Optional;
|
|||
* TODO: Implement this functionality automatically.
|
||||
*/
|
||||
@Controller
|
||||
public class MergeSingleTeams {
|
||||
public class MergeSingleTeams extends UserPageController {
|
||||
|
||||
private Logger logger = LogManager.getLogger(MergeSingleTeams.class);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.student_teams;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Student;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam;
|
||||
|
@ -20,7 +21,7 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
public class StudentTeamEntity {
|
||||
public class StudentTeamEntity extends UserPageController {
|
||||
|
||||
private StudentTeamRepository studentTeamRepository;
|
||||
private CourseRepository courseRepository;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.student_teams;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
|
@ -15,7 +16,7 @@ import java.util.Optional;
|
|||
* Updates branch protection for all student repositories in a given course.
|
||||
*/
|
||||
@Controller
|
||||
public class UpdateBranchProtection {
|
||||
public class UpdateBranchProtection extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.students;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Student;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
|
@ -17,7 +18,7 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
public class InviteAllToRepository {
|
||||
public class InviteAllToRepository extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.teaching_assistant_teams;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.TeachingAssistantTeam;
|
||||
|
@ -14,7 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
import java.util.*;
|
||||
|
||||
@Controller
|
||||
public class AssignToStudentTeams {
|
||||
public class AssignToStudentTeams extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.teaching_assistant_teams;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistant;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.TeachingAssistantTeam;
|
||||
|
@ -15,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
public class CreateTeachingAssistantTeam {
|
||||
public class CreateTeachingAssistantTeam extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
private TeachingAssistantRepository teachingAssistantRepository;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.teaching_assistant_teams;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.TeachingAssistantTeam;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
|
@ -12,7 +13,7 @@ import org.springframework.web.bind.annotation.PathVariable;
|
|||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
public class TeachingAssistantTeamEntity {
|
||||
public class TeachingAssistantTeamEntity extends UserPageController {
|
||||
|
||||
private CourseRepository courseRepository;
|
||||
private TeachingAssistantTeamRepository teachingAssistantTeamRepository;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* Contains all page and action controllers which respond to requests on the website.
|
||||
*
|
||||
* <p>
|
||||
* Each class defined here should therefore be annotated with Spring's <code>@Controller</code> annotation, and
|
||||
* define one or more methods annotated with a <code>@RequestMapping</code> (or any convenience shortcut) to respond
|
||||
* to requests to the controller.
|
||||
* </p>
|
||||
*/
|
||||
package nl.andrewlalis.teaching_assistant_assistant.controllers;
|
|
@ -0,0 +1,19 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.register;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
/**
|
||||
* Controller for the registration form for new students.
|
||||
*/
|
||||
@Controller
|
||||
public class StudentRegisterController {
|
||||
|
||||
@GetMapping("/register/student")
|
||||
public String get() {
|
||||
|
||||
|
||||
return "/register/student";
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers;
|
||||
package nl.andrewlalis.teaching_assistant_assistant.controllers.students;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Student;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.services.StudentService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
@ -14,30 +15,28 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Controller for operations dealing with the global collection of students, not particular to one course.
|
||||
* Controller for creating a new student.
|
||||
*/
|
||||
@Controller
|
||||
public class Students {
|
||||
public class StudentCreateController extends UserPageController {
|
||||
|
||||
/**
|
||||
* A constant which defines what value is returned if the user says that the newly created student should not be
|
||||
* part of a course.
|
||||
*/
|
||||
private static final String NO_COURSE = "NO_COURSE_SELECTED";
|
||||
|
||||
private StudentRepository studentRepository;
|
||||
private CourseRepository courseRepository;
|
||||
private StudentService studentService;
|
||||
|
||||
protected Students(StudentRepository studentRepository, CourseRepository courseRepository) {
|
||||
this.studentRepository = studentRepository;
|
||||
protected StudentCreateController(CourseRepository courseRepository, StudentService studentService) {
|
||||
this.courseRepository = courseRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/students")
|
||||
public String get(Model model) {
|
||||
model.addAttribute("students", this.studentRepository.findAll());
|
||||
return "students";
|
||||
this.studentService = studentService;
|
||||
}
|
||||
|
||||
@GetMapping("/students/create")
|
||||
public String getCreate(Model model) {
|
||||
model.addAttribute("student", new Student("First Name", "Last Name", "Email Address", "Github Username", 1234567));
|
||||
model.addAttribute("student", new Student(null, null, null, null, 1234567));
|
||||
model.addAttribute("courses", this.courseRepository.findAll());
|
||||
|
||||
return "students/create";
|
||||
|
@ -49,20 +48,16 @@ public class Students {
|
|||
)
|
||||
public String postCreate(
|
||||
@ModelAttribute Student newStudent,
|
||||
@RequestParam(value = "course_code", required = false) String courseCode
|
||||
@RequestParam(value = "course_code", required = false, defaultValue = NO_COURSE) String courseCode
|
||||
) {
|
||||
this.studentRepository.save(newStudent);
|
||||
|
||||
if (courseCode != null && !courseCode.equals(NO_COURSE)) {
|
||||
Optional<Course> optionalCourse = this.courseRepository.findByCode(courseCode);
|
||||
optionalCourse.ifPresent(course -> {
|
||||
course.addParticipant(newStudent);
|
||||
newStudent.assignToCourse(course);
|
||||
this.courseRepository.save(course);
|
||||
this.studentRepository.save(newStudent);
|
||||
});
|
||||
Optional<Course> optionalCourse = this.courseRepository.findByCode(courseCode);
|
||||
Course course = null;
|
||||
if (optionalCourse.isPresent()) {
|
||||
course = optionalCourse.get();
|
||||
}
|
||||
|
||||
this.studentService.createStudent(newStudent, course);
|
||||
|
||||
return "redirect:/students";
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.students;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Student;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.Team;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.TeamRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Controller
|
||||
public class StudentEntity {
|
||||
|
||||
private StudentRepository studentRepository;
|
||||
private TeamRepository teamRepository;
|
||||
private CourseRepository courseRepository;
|
||||
|
||||
protected StudentEntity(StudentRepository studentRepository, TeamRepository teamRepository, CourseRepository courseRepository) {
|
||||
this.studentRepository = studentRepository;
|
||||
this.teamRepository = teamRepository;
|
||||
this.courseRepository = courseRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/students/{id}")
|
||||
public String get(@PathVariable long id, Model model) {
|
||||
Optional<Student> optionalStudent = this.studentRepository.findById(id);
|
||||
optionalStudent.ifPresent(student -> model.addAttribute("student", student));
|
||||
return "students/entity";
|
||||
}
|
||||
|
||||
@GetMapping("/students/{id}/edit")
|
||||
public String getEdit(@PathVariable long id, Model model) {
|
||||
Optional<Student> optionalStudent = this.studentRepository.findById(id);
|
||||
optionalStudent.ifPresent(student -> model.addAttribute("student", student));
|
||||
return "students/entity/edit";
|
||||
}
|
||||
|
||||
@PostMapping(
|
||||
value = "/students/{id}/edit",
|
||||
consumes = "application/x-www-form-urlencoded"
|
||||
)
|
||||
public String post(@ModelAttribute Student editedStudent, @PathVariable long id) {
|
||||
Optional<Student> optionalStudent = this.studentRepository.findById(id);
|
||||
optionalStudent.ifPresent(student -> {
|
||||
student.setFirstName(editedStudent.getFirstName());
|
||||
student.setLastName(editedStudent.getLastName());
|
||||
student.setEmailAddress(editedStudent.getEmailAddress());
|
||||
student.setGithubUsername(editedStudent.getGithubUsername());
|
||||
student.setStudentNumber(editedStudent.getStudentNumber());
|
||||
this.studentRepository.save(student);
|
||||
});
|
||||
|
||||
return "redirect:/students/{id}";
|
||||
}
|
||||
|
||||
@GetMapping("/students/{id}/remove")
|
||||
public String getRemove(@PathVariable long id) {
|
||||
Optional<Student> optionalStudent = this.studentRepository.findById(id);
|
||||
optionalStudent.ifPresent(student -> {
|
||||
|
||||
for (Team team : student.getTeams()) {
|
||||
team.removeMember(student);
|
||||
student.removeFromAssignedTeam(team);
|
||||
this.teamRepository.save(team);
|
||||
}
|
||||
|
||||
for (Course course : student.getCourses()) {
|
||||
course.removeParticipant(student);
|
||||
student.removeFromAssignedCourse(course);
|
||||
this.courseRepository.save(course);
|
||||
}
|
||||
|
||||
this.studentRepository.delete(student);
|
||||
});
|
||||
|
||||
return "redirect:/students";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.students;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Student;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Controller for a single student entity.
|
||||
*/
|
||||
@Controller
|
||||
public class StudentEntityController extends UserPageController {
|
||||
|
||||
private StudentRepository studentRepository;
|
||||
|
||||
protected StudentEntityController(StudentRepository studentRepository) {
|
||||
this.studentRepository = studentRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/students/{id}")
|
||||
public String get(@PathVariable long id, Model model) {
|
||||
Optional<Student> optionalStudent = this.studentRepository.findById(id);
|
||||
optionalStudent.ifPresent(student -> model.addAttribute("student", student));
|
||||
return "students/entity";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.students.entity;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Student;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.services.StudentService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Controller for editing a student entity.
|
||||
*/
|
||||
@Controller("/students/{id}/edit")
|
||||
public class StudentEntityEditController extends UserPageController {
|
||||
|
||||
private StudentRepository studentRepository;
|
||||
private StudentService studentService;
|
||||
|
||||
protected StudentEntityEditController(StudentRepository studentRepository, StudentService studentService) {
|
||||
this.studentRepository = studentRepository;
|
||||
this.studentService = studentService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data of the student whose information is going to be edited, and add that to the model to be rendered.
|
||||
* @param id The id of the student to edit.
|
||||
* @param model The view model.
|
||||
* @return The edit template which will be populated with the student's data.
|
||||
*/
|
||||
@GetMapping("/students/{id}/edit")
|
||||
public String getEdit(@PathVariable long id, Model model) {
|
||||
Optional<Student> optionalStudent = this.studentRepository.findById(id);
|
||||
optionalStudent.ifPresent(student -> model.addAttribute("student", student));
|
||||
return "students/entity/edit";
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives edited data about a student and saves it.
|
||||
* @param editedStudent A temporary <code>Student</code> object containing the edited information.
|
||||
* @param id The id of the student to edit the information of.
|
||||
* @return A redirect to the entity page for the student whose information was just edited.
|
||||
*/
|
||||
@PostMapping(
|
||||
value = "/students/{id}/edit",
|
||||
consumes = "application/x-www-form-urlencoded"
|
||||
)
|
||||
public String post(@ModelAttribute Student editedStudent, @PathVariable long id) {
|
||||
Optional<Student> optionalStudent = this.studentRepository.findById(id);
|
||||
optionalStudent.ifPresent(student -> this.studentService.editStudent(student, editedStudent));
|
||||
|
||||
return "redirect:/students/{id}";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.students.entity;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Student;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.services.StudentService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Controller for removing a student from the application.
|
||||
*/
|
||||
@Controller
|
||||
public class StudentEntityRemoveController {
|
||||
|
||||
private StudentService studentService;
|
||||
private StudentRepository studentRepository;
|
||||
|
||||
protected StudentEntityRemoveController(StudentService studentService, StudentRepository studentRepository) {
|
||||
this.studentService = studentService;
|
||||
this.studentRepository = studentRepository;
|
||||
}
|
||||
|
||||
@GetMapping("/students/{id}/remove")
|
||||
public String getRemove(@PathVariable long id) {
|
||||
Optional<Student> optionalStudent = this.studentRepository.findById(id);
|
||||
optionalStudent.ifPresent(student -> this.studentService.removeStudent(student));
|
||||
|
||||
return "redirect:/students";
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.controllers.teaching_assistants;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.controllers.UserPageController;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistant;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.TeachingAssistantRepository;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
@ -9,12 +10,15 @@ import org.springframework.web.bind.annotation.PathVariable;
|
|||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Controller for a single teaching assistant entity.
|
||||
*/
|
||||
@Controller
|
||||
public class TeachingAssistantEntity {
|
||||
public class TeachingAssistantEntityController extends UserPageController {
|
||||
|
||||
private TeachingAssistantRepository teachingAssistantRepository;
|
||||
|
||||
protected TeachingAssistantEntity(TeachingAssistantRepository teachingAssistantRepository) {
|
||||
protected TeachingAssistantEntityController(TeachingAssistantRepository teachingAssistantRepository) {
|
||||
this.teachingAssistantRepository = teachingAssistantRepository;
|
||||
}
|
||||
|
|
@ -6,23 +6,39 @@ import javax.persistence.*;
|
|||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* The basic entity properties which any entity in this system should have: an id and a creation timestamp. Every entity
|
||||
* in this system should extend from BasicEntity.
|
||||
* An abstract object from which all persistent objects in this application should extend.
|
||||
* <p>
|
||||
* The basic entity properties which any entity in this system should have: an {@code id} and a creation timestamp.
|
||||
* Every entity in this system should extend from BasicEntity.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Every single entity in this system therefore is identified uniquely by a Long primary key, although it may also
|
||||
* be identified by other codes or attributes defined in such an entity. For example, the {@link Course} object has
|
||||
* a unique code, which can also be used to select a course.
|
||||
* </p>
|
||||
*
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class BasicEntity {
|
||||
|
||||
/**
|
||||
* The primary key for any basic entity.
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(
|
||||
strategy = GenerationType.IDENTITY
|
||||
)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* The date at which this basic entity was created.
|
||||
*/
|
||||
@Temporal(
|
||||
value = TemporalType.TIMESTAMP
|
||||
)
|
||||
@CreationTimestamp
|
||||
@Column
|
||||
@Column(nullable = false)
|
||||
private Date createdOn;
|
||||
|
||||
protected BasicEntity() {}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.model.dto;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A data transfer object to aid in the registration of a new authenticated user. This object therefore contains all the
|
||||
* data that potential users enter prior to the actual creation of a new user.
|
||||
*/
|
||||
public class NewUserRegistrationDTO {
|
||||
|
||||
private String firstName;
|
||||
|
||||
private String lastName;
|
||||
|
||||
private String emailAddress;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String passwordConfirm;
|
||||
|
||||
private String selectedPersonType;
|
||||
|
||||
private List<String> availablePersonTypes;
|
||||
|
||||
private Iterable<Course> availableCourses;
|
||||
|
||||
public NewUserRegistrationDTO(List<String> availablePersonTypes, Iterable<Course> availableCourses) {
|
||||
this.availablePersonTypes = availablePersonTypes;
|
||||
this.availableCourses = availableCourses;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
public void setPasswordConfirm(String passwordConfirm) {
|
||||
this.passwordConfirm = passwordConfirm;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getEmailAddress() {
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
public void setEmailAddress(String emailAddress) {
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
|
||||
public List<String> getAvailablePersonTypes() {
|
||||
return availablePersonTypes;
|
||||
}
|
||||
|
||||
public Iterable<Course> getAvailableCourses() {
|
||||
return availableCourses;
|
||||
}
|
||||
|
||||
public String getSelectedPersonType() {
|
||||
return this.selectedPersonType;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Contains all persistent entities that make up the core data model for the application.
|
||||
*
|
||||
* <p>
|
||||
* All model objects should ultimately extend from {@link nl.andrewlalis.teaching_assistant_assistant.model.BasicEntity}
|
||||
* and define new attributes as necessary for use.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* From BasicEntity it is implied that all entities contained herein can be identified uniquely by at least a
|
||||
* <code>Long id</code>.
|
||||
* </p>
|
||||
*/
|
||||
package nl.andrewlalis.teaching_assistant_assistant.model;
|
|
@ -3,6 +3,7 @@ package nl.andrewlalis.teaching_assistant_assistant.model.people;
|
|||
import nl.andrewlalis.teaching_assistant_assistant.model.BasicEntity;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.Team;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.security.User;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
|
@ -10,6 +11,11 @@ import java.util.List;
|
|||
|
||||
/**
|
||||
* Represents any person (teaching assistant, student, or other) that exists in this application.
|
||||
*
|
||||
* <p>
|
||||
* A Person may belong to many {@link Team}s and may also belong to many {@link Course}s, irrespective of Team
|
||||
* involvement.
|
||||
* </p>
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "people")
|
||||
|
@ -18,15 +24,27 @@ import java.util.List;
|
|||
)
|
||||
public abstract class Person extends BasicEntity {
|
||||
|
||||
/**
|
||||
* The person's first name.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private String firstName;
|
||||
|
||||
/**
|
||||
* The person's last name.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private String lastName;
|
||||
|
||||
/**
|
||||
* The person's email address.
|
||||
*/
|
||||
@Column
|
||||
private String emailAddress;
|
||||
|
||||
/**
|
||||
* The person's github username.
|
||||
*/
|
||||
@Column
|
||||
private String githubUsername;
|
||||
|
||||
|
@ -51,6 +69,16 @@ public abstract class Person extends BasicEntity {
|
|||
)
|
||||
private List<Course> courses;
|
||||
|
||||
/**
|
||||
* The authenticated user belonging to this person.
|
||||
*/
|
||||
@OneToOne(
|
||||
fetch = FetchType.LAZY,
|
||||
optional = true,
|
||||
mappedBy = "person"
|
||||
)
|
||||
private User user;
|
||||
|
||||
/**
|
||||
* Default constructor for JPA.
|
||||
*/
|
||||
|
@ -74,12 +102,20 @@ public abstract class Person extends BasicEntity {
|
|||
this.githubUsername = githubUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns this person to a team.
|
||||
* @param team The team to assign to.
|
||||
*/
|
||||
public void assignToTeam(Team team) {
|
||||
if (!this.teams.contains(team)) {
|
||||
this.teams.add(team);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this person from a team.
|
||||
* @param team The team to remove from.
|
||||
*/
|
||||
public void removeFromAssignedTeam(Team team) {
|
||||
this.teams.remove(team);
|
||||
}
|
||||
|
@ -142,6 +178,14 @@ public abstract class Person extends BasicEntity {
|
|||
return this.teams;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if two Persons are equal. They are considered equal when all of the basic identifying information
|
||||
* about the person is the same, regardless of case.
|
||||
|
|
|
@ -9,9 +9,11 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A group consisting of one or more members. Child classes should define P as a sub class of Person to define custom
|
||||
* behavior if needed.
|
||||
* @param <P> The type of members this group contains.
|
||||
* A group consisting of one or more members that belongs to a {@link Course}.
|
||||
*
|
||||
* <p>
|
||||
* Any Team contains a collection of {@link Person} objects, each representing a member of the team.
|
||||
* </p>
|
||||
*/
|
||||
@Entity
|
||||
@Inheritance(
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.model.repositories;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.security.Role;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The repository for all roles to which users may be assigned.
|
||||
*/
|
||||
public interface RoleRepository extends CrudRepository<Role, Long> {
|
||||
|
||||
/**
|
||||
* Try to find a role by the given name.
|
||||
* @return An optional Role that has the given name.
|
||||
*/
|
||||
public Optional<Role> findByName(String name);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.model.repositories;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.security.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByUsername(String username);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* A collection of {@link org.springframework.data.repository.CrudRepository} repositories that are used to access key
|
||||
* components of the {@code nl.andrewlalis.teaching_assistant_assistant.model} package.
|
||||
*/
|
||||
package nl.andrewlalis.teaching_assistant_assistant.model.repositories;
|
|
@ -0,0 +1,43 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.model.security;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.BasicEntity;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToMany;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a role that a user has, which gives the user access to certain resources.
|
||||
*/
|
||||
@Entity
|
||||
public class Role extends BasicEntity {
|
||||
|
||||
/**
|
||||
* The name of this role.
|
||||
*/
|
||||
@Column(nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The list of users with this role.
|
||||
*/
|
||||
@ManyToMany(mappedBy = "roles")
|
||||
private List<User> users;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<User> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public void setUsers(List<User> users) {
|
||||
this.users = users;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.model.security;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.BasicEntity;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Person;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a user of the website with some credentials.
|
||||
*/
|
||||
@Entity
|
||||
public class User extends BasicEntity {
|
||||
|
||||
/**
|
||||
* A unique username for the user.
|
||||
*/
|
||||
@Column(nullable = false, unique = true)
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* The password for this user.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* Whether or not this user has been activated.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private boolean activated = false;
|
||||
|
||||
/**
|
||||
* Whether or not this user has been locked (no more access).
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private boolean locked = false;
|
||||
|
||||
@ManyToMany(
|
||||
fetch = FetchType.EAGER
|
||||
)
|
||||
@JoinTable(
|
||||
name = "user_roles",
|
||||
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")
|
||||
)
|
||||
private List<Role> roles;
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(
|
||||
name = "person_id",
|
||||
nullable = true,
|
||||
referencedColumnName = "id"
|
||||
)
|
||||
private Person person;
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
public Person getPerson() {
|
||||
return this.person;
|
||||
}
|
||||
|
||||
public boolean isActivated() {
|
||||
return activated;
|
||||
}
|
||||
|
||||
public void setActivated(boolean activated) {
|
||||
this.activated = activated;
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return locked;
|
||||
}
|
||||
|
||||
public void setLocked(boolean locked) {
|
||||
this.locked = locked;
|
||||
}
|
||||
|
||||
public List<Role> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(List<Role> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.model.security;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TAA-specific implementation of Spring's UserDetails interface to supply user authentication data.
|
||||
*/
|
||||
public class UserDetails implements org.springframework.security.core.userdetails.UserDetails {
|
||||
|
||||
private User user;
|
||||
|
||||
protected UserDetails(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
List<Role> roles = this.user.getRoles();
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
for (Role role : roles) {
|
||||
authorities.add(new SimpleGrantedAuthority(role.getName()));
|
||||
}
|
||||
return authorities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.user.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.user.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return !this.user.isLocked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.user.isActivated();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.model.security;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.UserRepository;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A custom user details service to supply database persistent user information to Spring Security.
|
||||
*/
|
||||
@Service
|
||||
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
|
||||
|
||||
private UserRepository userRepository;
|
||||
|
||||
protected UserDetailsService(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
Optional<User> optionalUser = this.userRepository.findByUsername(username);
|
||||
optionalUser.orElseThrow(() -> new UsernameNotFoundException("Username not found."));
|
||||
|
||||
return new nl.andrewlalis.teaching_assistant_assistant.model.security.UserDetails(optionalUser.get());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package nl.andrewlalis.teaching_assistant_assistant.services;
|
||||
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.Course;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.Student;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.Team;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository;
|
||||
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.TeamRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helps with manipulation and various operations on individual students or groups of students (not teams).
|
||||
*/
|
||||
@Service
|
||||
public class StudentService {
|
||||
|
||||
private StudentRepository studentRepository;
|
||||
private CourseRepository courseRepository;
|
||||
private TeamRepository teamRepository;
|
||||
|
||||
protected StudentService (StudentRepository studentRepository, CourseRepository courseRepository, TeamRepository teamRepository) {
|
||||
this.studentRepository = studentRepository;
|
||||
this.courseRepository = courseRepository;
|
||||
this.teamRepository = teamRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new student and assigns them to a course, if provided.
|
||||
* @param student An unsaved student model.
|
||||
* @param course The course to assign the student to. This may be null.
|
||||
*/
|
||||
public void createStudent(Student student, Course course) {
|
||||
if (course != null) {
|
||||
course.addParticipant(student);
|
||||
student.assignToCourse(course);
|
||||
this.courseRepository.save(course); // This cascades to save the student as well.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits a students' data. More specifically, updates the provided <code>student</code> with the attributes found
|
||||
* in the provided <code>editedStudent</code> object.
|
||||
* @param student The student to update.
|
||||
* @param editedStudent A model containing updated attributes to assign to the given student.
|
||||
*/
|
||||
public void editStudent(Student student, Student editedStudent) {
|
||||
student.setFirstName(editedStudent.getFirstName());
|
||||
student.setLastName(editedStudent.getLastName());
|
||||
student.setStudentNumber(editedStudent.getStudentNumber());
|
||||
student.setEmailAddress(editedStudent.getEmailAddress());
|
||||
student.setGithubUsername(editedStudent.getGithubUsername());
|
||||
this.studentRepository.save(student);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a student from the application completely.
|
||||
* @param student The student to remove.
|
||||
*/
|
||||
public void removeStudent(Student student) {
|
||||
List<Team> teams = student.getTeams();
|
||||
for (Team team : teams) {
|
||||
team.removeMember(student);
|
||||
student.removeFromAssignedTeam(team);
|
||||
this.teamRepository.save(team);
|
||||
}
|
||||
|
||||
List<Course> courses = student.getCourses();
|
||||
for (Course course : courses) {
|
||||
course.removeParticipant(student);
|
||||
student.removeFromAssignedCourse(course);
|
||||
this.courseRepository.save(course);
|
||||
}
|
||||
|
||||
this.studentRepository.delete(student);
|
||||
}
|
||||
|
||||
}
|
|
@ -50,7 +50,7 @@ public class StudentTeamService {
|
|||
StudentTeam newTeam = new StudentTeam(course);
|
||||
course.addStudentTeam(newTeam);
|
||||
this.courseRepository.save(course);
|
||||
logger.info("Created new team: " + newTeam.getId());
|
||||
logger.info("Created new team.");
|
||||
|
||||
return newTeam;
|
||||
}
|
||||
|
@ -185,13 +185,19 @@ public class StudentTeamService {
|
|||
s.removeFromAssignedTeam(team);
|
||||
team.removeMember(s);
|
||||
this.studentRepository.save(s);
|
||||
|
||||
logger.debug("Removed student " + s.getFullName() + " from team " + team.getId());
|
||||
}
|
||||
|
||||
// Remove the TA team assignment.
|
||||
TeachingAssistantTeam teachingAssistantTeam = team.getAssignedTeachingAssistantTeam();
|
||||
teachingAssistantTeam.removeAssignedStudentTeam(team);
|
||||
if (teachingAssistantTeam != null) {
|
||||
teachingAssistantTeam.removeAssignedStudentTeam(team);
|
||||
this.teachingAssistantTeamRepository.save(teachingAssistantTeam);
|
||||
logger.debug("Removed team " + team.getId() + " from Teaching Assistant Team " + teachingAssistantTeam.getId() + " assigned teams list.");
|
||||
}
|
||||
|
||||
team.setAssignedTeachingAssistantTeam(null);
|
||||
this.teachingAssistantTeamRepository.save(teachingAssistantTeam);
|
||||
|
||||
// Remove the repository from the course and delete it.
|
||||
course.removeStudentTeam(team);
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,17 +1,9 @@
|
|||
.footer_bar {
|
||||
background-color: darkslategray;
|
||||
color: whitesmoke;
|
||||
height: 30px;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
z-index: -1; /* Make this footer appear above all other things. */
|
||||
body {
|
||||
padding-bottom: 110px;
|
||||
}
|
||||
|
||||
.third {
|
||||
width: 33%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
.footer {
|
||||
background-color: darkslategray;
|
||||
color: whitesmoke;
|
||||
height: 100px;
|
||||
}
|
|
@ -1,45 +1,13 @@
|
|||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.header_bar {
|
||||
.header {
|
||||
background-color: green;
|
||||
width: 100%;
|
||||
color: whitesmoke;
|
||||
padding-bottom: 20px;
|
||||
padding-top: 20px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.header_title {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
width: 25%;
|
||||
display: inline;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.header_link_list {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 75%;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.header_link_list li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.header_link_list a {
|
||||
.header a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
background-color: darkgreen;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header_link_list a:hover {
|
||||
background-color: darkgray;
|
||||
.header a:hover {
|
||||
color: lightgray;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page_row {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login_form {
|
||||
background-color: green;
|
||||
color: white;
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.login_form_row {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.login_form label {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.login_form input {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.login_form input[type=submit]:hover {
|
||||
background-color: lightgray;
|
||||
}
|
|
@ -1,52 +1,7 @@
|
|||
/* Set the font for the whole website here. */
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
/*.btn-primary {*/
|
||||
/* background-color: green;*/
|
||||
/*}*/
|
||||
|
||||
.content_container {
|
||||
width: 50%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.sidebar_container {
|
||||
width: 20%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.sidebar_block {
|
||||
margin: 20px 0 20px 0;
|
||||
padding: 20px;
|
||||
background-color: green;
|
||||
color: whitesmoke;
|
||||
}
|
||||
|
||||
.sidebar_block a {
|
||||
text-decoration: underline;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.sidebar_block a:hover {
|
||||
color: lightgreen;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.page_row {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
/*.btn-primary:hover {*/
|
||||
/* background-color: darkgreen;*/
|
||||
/*}*/
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.3 KiB |
File diff suppressed because one or more lines are too long
|
@ -1,39 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Courses</title>
|
||||
</head>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Courses', content=~{::#content}, actions=~{::#actions})}" lang="en">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Courses</h1>
|
||||
<hr>
|
||||
|
||||
<ul>
|
||||
<li th:each="course: ${courses}">
|
||||
<h2><a th:href="@{/courses/{code}(code=${course.getCode()})}" th:text="${course.getName()}"></a></h2>
|
||||
<ul>
|
||||
<li>Code: <span th:text="${course.getCode()}"></span></li>
|
||||
<li>Created on: <span th:text="${course.getCreatedOn()}"></span></li>
|
||||
<li>Students: <span th:text="${course.getStudents().size()}"></span></li>
|
||||
<li>Teaching Assistants: <span th:text="${course.getTeachingAssistants().size()}"></span></li>
|
||||
<li>Student Teams: <span th:text="${course.getStudentTeams().size()}"></span></li>
|
||||
<li>Teaching Assistant Teams: <span th:text="${course.getTeachingAssistantTeams().size()}"></span></li>
|
||||
<li>Number of Active Students (in a team): <span th:text="${course.getNumberOfStudentsInTeams()}"></span></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div id="actions">
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/create}">Create New Course</a>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block">
|
||||
<a href="/courses/create">Create new course</a>
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
Do something else
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
Click this link!
|
||||
<div id="content" class="container-fluid">
|
||||
<div class="col-sm-12 col-md-6 offset-3">
|
||||
<div class="list-group">
|
||||
<a th:each="course: ${courses}" class="list-group-item list-group-item-action flex-column align-items-start" th:href="@{/courses/{code}(code=${course.getCode()})}">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h4 class="mb-1" th:text="${course.getName()}"></h4>
|
||||
<small th:text="${course.getCode()}"></small>
|
||||
</div>
|
||||
<ul>
|
||||
<li><span class="badge badge-primary" th:text="${course.getStudentTeams().size()}"></span> Student Teams</li>
|
||||
<li><span class="badge badge-primary" th:text="${course.getNumberOfStudentsInTeams()}"></span> Active Students</li>
|
||||
<li><span class="badge badge-primary" th:text="${course.getTeachingAssistants().size()}"></span> Teaching Assistants</li>
|
||||
</ul>
|
||||
<small>Created on <span th:text="${course.getCreatedOn()}"></span></small>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,43 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Create a Course</title>
|
||||
</head>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Create New Course', content=~{::#content})}" lang="en">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Create New Course</h1>
|
||||
|
||||
<form action="#" th:action="@{/courses}" th:object="${course}" method="post">
|
||||
<div class="page_row">
|
||||
<label for="course_name_input">Name:</label>
|
||||
<input id="course_name_input" type="text" th:field="*{name}" required/>
|
||||
<div id="content" class="row justify-content-center">
|
||||
<form th:action="@{/courses}" th:object="${course}" method="post">
|
||||
<div class="form-group">
|
||||
<label for="course_name_input">Name</label>
|
||||
<input id="course_name_input" type="text" class="form-control" th:field="*{name}" required/>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="course_code_input">Code:</label>
|
||||
<input id="course_code_input" type="text" th:field="*{code}" required/>
|
||||
<div class="form-group">
|
||||
<label for="course_code_input">Code</label>
|
||||
<input id="course_code_input" type="text" class="form-control" th:field="*{code}" required/>
|
||||
<small class="form-text text-muted">Unique identifying code for this course.</small>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="course_github_organization_name_input">Github Organization:</label>
|
||||
<input id="course_github_organization_name_input" type="text" th:field="*{githubOrganizationName}" required/>
|
||||
<div class="form-group">
|
||||
<label for="course_github_organization_name_input">Github Organization</label>
|
||||
<input id="course_github_organization_name_input" type="text" class="form-control" th:field="*{githubOrganizationName}" required/>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="course_github_api_key_input">Github API Key:</label>
|
||||
<input id="course_github_api_key_input" type="text" th:field="*{apiKey}" required/>
|
||||
<div class="form-group">
|
||||
<label for="course_github_api_key_input">Github API Key</label>
|
||||
<input id="course_github_api_key_input" type="text" class="form-control" th:field="*{apiKey}" required/>
|
||||
<small class="form-text text-muted">Needed to manage student repositories.</small>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<button type="submit">Submit</button>
|
||||
</div>
|
||||
<input class="btn btn-primary" type="submit" value="Create"/>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,40 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title th:text="${course.getName()}"></title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title=${course.getName()}, content=~{::#content}, actions=~{::#actions})}">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1><span th:text="${course.getName()}"></span> (Code: <span th:text="${course.getCode()}"></span>)</h1>
|
||||
<hr>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Github Organization: <a th:href="@{${'https://www.github.com/' + course.getGithubOrganizationName()}}" th:text="${course.getGithubOrganizationName()}"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/courses/{code}/teaching_assistant_teams(code=${course.getCode()})}">Teaching Assistant Teams</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/courses/{code}/student_teams(code=${course.getCode()})}">Student Teams</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/courses/{code}/students(code=${course.getCode()})}">All Students</a>
|
||||
</li>
|
||||
<li>
|
||||
<a th:href="@{/courses/{code}/teaching_assistants(code=${course.getCode()})}">All Teaching Assistants</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div id="actions">
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/import_students(code=${course.getCode()})}">Import Students From CSV</a>
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/import_teaching_assistants(code=${course.getCode()})}">Import Teaching Assistants From CSV</a>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/import_students(code=${course.getCode()})}">Import students from CSV</a>
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
<a th:disabled="true" th:href="@{/courses/{code}/import_teaching_assistants(code=${course.getCode()})}">Import teaching assistants from CSV</a>
|
||||
<div id="content" class="row justify-content-center">
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<div class="list-group">
|
||||
<a class="list-group-item list-group-item-action" th:href="@{${'https://www.github.com/' + course.getGithubOrganizationName()}}">
|
||||
Github Organization: <span th:text="${course.getGithubOrganizationName()}"></span>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" th:href="@{/courses/{code}/teaching_assistant_teams(code=${course.getCode()})}">
|
||||
Teaching Assistant Teams
|
||||
<span class="badge badge-primary" th:text="${course.getTeachingAssistantTeams().size()}"></span>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" th:href="@{/courses/{code}/student_teams(code=${course.getCode()})}">
|
||||
Student Teams
|
||||
<span class="badge badge-primary" th:text="${course.getStudentTeams().size()}"></span>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" th:href="@{/courses/{code}/students(code=${course.getCode()})}">
|
||||
Students
|
||||
<span class="badge badge-primary" th:text="${course.getStudents().size()}"></span>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" th:href="@{/courses/{code}/teaching_assistants(code=${course.getCode()})}">
|
||||
Teaching Assistants
|
||||
<span class="badge badge-primary" th:text="${course.getTeachingAssistants().size()}"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar_content})}">
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Import Students From CSV', content=~{::#content})}">
|
||||
<head>
|
||||
<title>Import Students via CSV</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Import Students from CSV File</h1>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<p>
|
||||
Use this form to import student data from a CSV which has been generated by a Google form. As of right now, the column headers are hard-coded, but in the future it will be possible to dynamically import data from any CSV file by custom header format definitions.
|
||||
</p>
|
||||
|
||||
<form method="post" action="#" enctype="multipart/form-data" th:action="@{/courses/{code}/import_students(code=${course.getCode()})}">
|
||||
<div class="page_row">
|
||||
<form method="post" enctype="multipart/form-data" th:action="@{/courses/{code}/import_students(code=${course.getCode()})}">
|
||||
<div class="form-row">
|
||||
<label for="file_input">File:</label>
|
||||
<input id="file_input" type="file" name="file" accept="text/csv"/>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<div class="form-row">
|
||||
<button type="submit">Submit</button>
|
||||
</div>
|
||||
|
||||
|
@ -26,9 +24,5 @@
|
|||
|
||||
</div>
|
||||
|
||||
<div id="sidebar_content">
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,18 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Student Teams</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Student Teams for ' + ${course.getCode()}, content=~{::#content}, actions=~{::#actions})}">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Student Teams for <span th:text="${course.getName()}"></span></h1>
|
||||
<div id="actions">
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/student_teams/generate_repositories(code=${course.getCode()})}">
|
||||
Generate Repositories
|
||||
</a>
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/student_teams/branch_protection_update(code=${course.getCode()})}">
|
||||
Update Branch Protection
|
||||
</a>
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/student_teams/export(code=${course.getCode()})}">
|
||||
Export
|
||||
</a>
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/student_teams/export_contact_info(code=${course.getCode()})}">
|
||||
Export Team Contact Details
|
||||
</a>
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/student_teams/create(code=${course.getCode()})}">
|
||||
Create New Student Team
|
||||
</a>
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/student_teams/merge_single_teams(code=${course.getCode()})}">
|
||||
Merge Single Teams
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<div th:if="${course.getStudentTeams().isEmpty()}">
|
||||
<p>No student teams.</p>
|
||||
</div>
|
||||
<table>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Team</th>
|
||||
<th>Students</th>
|
||||
|
@ -53,26 +68,5 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/student_teams/generate_repositories(code=${course.getCode()})}">Generate Repositories</a>
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/student_teams/branch_protection_update(code=${course.getCode()})}">Update Branch Protection</a>
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/student_teams/export(code=${course.getCode()})}">Export</a>
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/student_teams/export_contact_info(code=${course.getCode()})}">Export Team Contact Details</a>
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/student_teams/create(code=${course.getCode()})}">Create New Student Team</a>
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/student_teams/merge_single_teams(code=${course.getCode()})}">Merge Single Teams</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,28 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Create Student Team</title>
|
||||
</head>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Create Student Team', content=~{::#content})}">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Create a New Student Team</h1>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<p>
|
||||
Creates a new student team for the <span th:text="${course.getName()}"></span> course, without any assigned TA team or student members.
|
||||
</p>
|
||||
|
||||
<form action="#" th:action="@{/courses/{code}/student_teams/create(code=${course.getCode()})}" enctype="application/x-www-form-urlencoded" method="post">
|
||||
<form th:action="@{/courses/{code}/student_teams/create(code=${course.getCode()})}" enctype="application/x-www-form-urlencoded" method="post">
|
||||
|
||||
<div class="page_row">
|
||||
<button type="submit">Submit</button>
|
||||
<div class="form-row">
|
||||
<input type="submit" value="Submit" class="btn btn-primary"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,14 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Student Team</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Student Team ' + ${student_team.getId()}, content=~{::#content}, actions=~{::#actions})}">
|
||||
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Student Team <span th:text="${student_team.getId()}"></span></h1>
|
||||
<div id="actions">
|
||||
<th:block th:if="${student_team.getGithubRepositoryName() == null && student_team.getAssignedTeachingAssistantTeam() != null}">
|
||||
<a
|
||||
class="btn btn-primary m-1"
|
||||
th:href="@{/courses/{code}/student_teams/{id}/generate_repository
|
||||
(code=${student_team.getCourse().getCode()}, id=${student_team.getId()})}"
|
||||
>Generate Repository</a>
|
||||
</th:block>
|
||||
<th:block th:if="${student_team.getGithubRepositoryName() != null}">
|
||||
<a
|
||||
class="btn btn-primary m-1"
|
||||
th:href="@{/courses/{code}/student_teams/{id}/delete_repository
|
||||
(code=${student_team.getCourse().getCode()}, id=${student_team.getAssignedTeachingAssistantTeam().getId()})}"
|
||||
>Archive Repository</a>
|
||||
</th:block>
|
||||
<a
|
||||
class="btn btn-primary m-1"
|
||||
th:href="@{/courses/{code}/student_teams/{id}/add_student(code=${student_team.getCourse().getCode()}, id=${student_team.getId()})}">
|
||||
Add Student To Team
|
||||
</a>
|
||||
<a
|
||||
class="btn btn-primary m-1"
|
||||
th:href="@{/courses/{code}/student_teams/{id}/assign_teaching_assistant_team(code=${student_team.getCourse().getCode()}, id=${student_team.getId()})}">
|
||||
Assign Teaching Assistant Team
|
||||
</a>
|
||||
<a
|
||||
class="btn btn-primary m-1"
|
||||
th:href="@{/courses/{code}/student_teams/{id}/remove(code=${student_team.getCourse().getCode()}, id=${student_team.getId()})}">Remove This Team</a>
|
||||
</div>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<ul>
|
||||
<li>
|
||||
Github Repository:
|
||||
|
@ -43,39 +68,13 @@
|
|||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block" th:if="${student_team.getGithubRepositoryName() == null && student_team.getAssignedTeachingAssistantTeam() != null}">
|
||||
<a
|
||||
th:href="@{/courses/{code}/student_teams/{id}/generate_repository
|
||||
(code=${student_team.getCourse().getCode()}, id=${student_team.getId()})}"
|
||||
>Generate Repository</a>
|
||||
<div class="sidebar_block" >
|
||||
|
||||
</div>
|
||||
|
||||
<div class="sidebar_block" th:if="${student_team.getGithubRepositoryName() != null}">
|
||||
<a
|
||||
th:href="@{/courses/{code}/student_teams/{id}/delete_repository
|
||||
(code=${student_team.getCourse().getCode()}, id=${student_team.getAssignedTeachingAssistantTeam().getId()})}"
|
||||
>Archive Repository</a>
|
||||
</div>
|
||||
<div class="sidebar_block" >
|
||||
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/student_teams/{id}/add_student(code=${student_team.getCourse().getCode()}, id=${student_team.getId()})}">
|
||||
Add Student To Team
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/student_teams/{id}/assign_teaching_assistant_team(code=${student_team.getCourse().getCode()}, id=${student_team.getId()})}">
|
||||
Assign Teaching Assistant Team
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/student_teams/{id}/remove(code=${student_team.getCourse().getCode()}, id=${student_team.getId()})}">Remove This Team</a>
|
||||
<p>
|
||||
Removes this team permanently, and archives the repository.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,28 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Add Student To Team</title>
|
||||
</head>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Add Student To Team', content=~{::#content})}" lang="en">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Add a Student to Team <span th:text="${student_team.getId()}"></span></h1>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<form
|
||||
action="#"
|
||||
th:action="@{/courses/{code}/student_teams/{id}/add_student(code=${course.getCode()}, id=${student_team.getId()})}"
|
||||
enctype="application/x-www-form-urlencoded"
|
||||
method="post"
|
||||
>
|
||||
<div class="page_row">
|
||||
<div class="form-row">
|
||||
<label for="student_select">Select a Student to Add:</label>
|
||||
<select id="student_select" name="student_id" required>
|
||||
<select id="student_select" name="student_id" class="form-control" required>
|
||||
<option th:each="student: ${eligible_students}" th:value="${student.getId()}" th:text="${student.getFullName()}"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<button type="submit">Submit</button>
|
||||
<div class="form-row">
|
||||
<input type="submit" class="btn btn-primary" value="Submit"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Assign Teaching Assistant Team</title>
|
||||
</head>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Assign Teaching Assistant Team', content=~{::#content})}">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Assign a Teaching Assistant Team to Student Team <span th:text="${student_team.getId()}"></span></h1>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<form
|
||||
action="#"
|
||||
th:action="@{/courses/{code}/student_teams/{id}/assign_teaching_assistant_team(code=${course.getCode()}, id=${student_team.getId()})}"
|
||||
enctype="application/x-www-form-urlencoded"
|
||||
method="post"
|
||||
>
|
||||
<div class="page_row">
|
||||
<div class="form-row">
|
||||
<label for="ta_team_select">Select a Team:</label>
|
||||
<select id="ta_team_select" name="teaching_assistant_team_id">
|
||||
<select id="ta_team_select" name="teaching_assistant_team_id" class="form-control">
|
||||
<option value="-1">None</option>
|
||||
<option
|
||||
th:each="team: ${course.getTeachingAssistantTeams()}" th:value="${team.getId()}"
|
||||
|
@ -25,8 +19,8 @@
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<button type="submit">Submit</button>
|
||||
<div class="form-row">
|
||||
<input type="submit" value="Submit" class="btn btn-primary"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Generate Repositories</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Generate Repositories', content=~{::#content})}">
|
||||
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Generate Repositories for all Student Teams in <span th:text="${course.getName()}"></span></h1>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<p>
|
||||
Be careful, this may take a little while...
|
||||
Generate Repositories for all Student Teams in <span th:text="${course.getName()}"></span>. Be careful, this may take a little while...
|
||||
</p>
|
||||
|
||||
<form
|
||||
|
@ -23,9 +18,5 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,15 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Students</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Students in ' + ${course.getCode()}, content=~{::#content}, actions=~{::#actions})}">
|
||||
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Students in <span th:text="${course.getName()}"></span></h1>
|
||||
<div id="actions">
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/students/invite_all(code=${course.getCode()})}">
|
||||
Invite All Students To Repository
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<div id="content">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Student</th>
|
||||
<th>Student Number</th>
|
||||
|
@ -25,11 +26,5 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/students/invite_all(code=${course.getCode()})}">Invite All Students To Repository</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<div id="content">
|
||||
<h1>Invite All Students in <span th:text="${course.getName()}"></span> to Repository</h1>
|
||||
|
||||
<!-- TODO: Upgrade the basic method by which students gain access to the repository. Follow instructions from Github. -->
|
||||
<p>
|
||||
Be careful, this may take a little while...
|
||||
</p>
|
||||
|
|
|
@ -1,30 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Teaching Assistant Teams</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Teaching Assistants for ' + ${course.getCode()}, content=~{::#content}, actions=~{::#actions})}">
|
||||
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Teaching Assistant Teams for <span th:text="${course.getName()}"></span></h1>
|
||||
<div id="actions">
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/teaching_assistant_teams/create(code=${course.getCode()})}">
|
||||
Create Teaching Assistant Team
|
||||
</a>
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/teaching_assistant_teams/assign_to_student_teams(code=${course.getCode()})}">
|
||||
Assign Teams to Student Teams
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<div th:if="${course.getTeachingAssistantTeams().isEmpty()}">
|
||||
<p>No teaching assistant teams.</p>
|
||||
</div>
|
||||
<table>
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Team</th>
|
||||
<th>Teaching Assistants</th>
|
||||
<th>Github Team</th>
|
||||
<th>Assigned Student Teams</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
<tr th:each="taTeam: ${course.getTeachingAssistantTeams()}">
|
||||
<td>
|
||||
<a
|
||||
th:href="@{/courses/{code}/teaching_assistant_teams/{team_id}
|
||||
<a th:href="@{/courses/{code}/teaching_assistant_teams/{team_id}
|
||||
(code=${course.getCode()}, team_id=${taTeam.getId()})}">
|
||||
Teaching Assistant Team <span th:text="${taTeam.getId()}"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td th:each="ta: ${taTeam.getTeachingAssistants()}">
|
||||
<span th:text="${ta.getFullName()}"></span>
|
||||
<td>
|
||||
<div th:each="ta: ${taTeam.getTeachingAssistants()}">
|
||||
<a th:href="@{/teaching_assistants/{id}(id=${ta.getId()})}" th:text="${ta.getFullName()}"></a><br>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a
|
||||
th:href="'https://www.github.com/orgs/' + ${course.getGithubOrganizationName()} + '/teams/' + ${taTeam.getGithubTeamName().toLowerCase()}"
|
||||
th:text="${taTeam.getGithubTeamName()}"
|
||||
target="_blank"
|
||||
></a>
|
||||
</td>
|
||||
<td>
|
||||
<div th:each="studentTeam: ${taTeam.getAssignedStudentTeams()}">
|
||||
<a th:href="@{/courses/{code}/student_teams/{id}(code=${course.getCode()}, id=${studentTeam.getId()})}" th:text="'Student Team ' + ${studentTeam.getId()}"></a>
|
||||
</div>
|
||||
</td>
|
||||
<td th:text="${taTeam.getGithubTeamName()}"></td>
|
||||
<td>
|
||||
<a th:href="@{/courses/{code}/teaching_assistant_teams/{team_id}/delete
|
||||
(code=${course.getCode()}, team_id=${taTeam.getId()})}">
|
||||
|
@ -35,14 +58,5 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/teaching_assistant_teams/create(code=${course.getCode()})}">Create Teaching Assistant Team</a>
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/teaching_assistant_teams/assign_to_student_teams(code=${course.getCode()})}">Assign Teams to Student Teams</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,11 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Assign To Student Teams</title>
|
||||
</head>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Assign To Student Teams', content=~{::#content})}">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<div id="content" class="row justify-content-center">
|
||||
<form action="#" th:action="@{/courses/{code}/teaching_assistant_teams/assign_to_student_teams(code=${course.getCode()})}" enctype="application/x-www-form-urlencoded" method="post">
|
||||
<label for="seed_input">Random Seed:</label>
|
||||
<input id="seed_input" type="number" name="seed" value="0" required/>
|
||||
|
@ -13,9 +10,5 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,25 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Create Teaching Assistant Team</title>
|
||||
</head>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Create Teaching Assistant Team', content=~{::#content})}">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<form action="#" th:action="@{/courses/{code}/teaching_assistant_teams(code=${course.getCode()})}" enctype="application/x-www-form-urlencoded" method="post">
|
||||
<label for="ta_team_github_team_name">Github team name:</label>
|
||||
<input id="ta_team_github_team_name" type="text" name="github_team_name"/>
|
||||
<div id="content" class="row justify-content-center">
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<form action="#" th:action="@{/courses/{code}/teaching_assistant_teams(code=${course.getCode()})}" enctype="application/x-www-form-urlencoded" method="post">
|
||||
<div class="form-row">
|
||||
<label for="ta_team_github_team_name">Github team name:</label>
|
||||
<input id="ta_team_github_team_name" type="text" name="github_team_name" class="form-control"/>
|
||||
</div>
|
||||
|
||||
<label for="ta_team_member_1">Select the first Team Member:</label>
|
||||
<select id="ta_team_member_1" name="id_1">
|
||||
<option th:each="ta: ${course.getTeachingAssistants()}" th:value="${ta.getId()}" th:text="${ta.getFullName()}"></option>
|
||||
</select>
|
||||
<div class="form-row">
|
||||
<label for="ta_team_member_1">Select the first Team Member:</label>
|
||||
<select id="ta_team_member_1" name="id_1" class="form-control">
|
||||
<option th:each="ta: ${course.getTeachingAssistants()}" th:value="${ta.getId()}" th:text="${ta.getFullName()}"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<input type="submit" value="Submit" class="btn btn-primary"/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Teaching Assistant Team ' + ${teachingAssistantTeam.getId()}, content=~{::#content})}">
|
||||
<head>
|
||||
<title>Teaching Assistant Team</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Teaching Assistant Team <span th:text="${teachingAssistantTeam.getId()}"></span> for <span th:text="${course.getName()}"></span></h1>
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<ul>
|
||||
<li>
|
||||
Github Team Name: <code th:text="${teachingAssistantTeam.getGithubTeamName()}"></code>
|
||||
|
@ -35,9 +33,5 @@
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,14 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Teaching Assistants</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Teaching Assistants for ' + ${course.getCode()}, content=~{::#content}, actions=~{::#actions})}">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Teaching Assistants for <span th:text="${course.getName()}"></span></h1>
|
||||
<div id="actions">
|
||||
<a class="btn btn-primary m-1" th:href="@{/courses/{code}/teaching_assistants/create(code=${course.getCode()})}">Add Teaching Assistant</a>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<div id="content">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Teaching Assistant</th>
|
||||
<th>Email</th>
|
||||
<th>Id</th>
|
||||
</tr>
|
||||
<tr th:each="teachingAssistant: ${course.getTeachingAssistants()}">
|
||||
<td>
|
||||
<a th:href="@{/teaching_assistants/{id}(id=${teachingAssistant.getId()})}"><span th:text="${teachingAssistant.getFullName()}"></span></a>
|
||||
|
@ -19,11 +23,5 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/courses/{code}/teaching_assistants/create(code=${course.getCode()})}">Add Teaching Assistant</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -5,19 +5,32 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
<footer th:fragment="footer" class="footer_bar">
|
||||
<!-- <link rel="stylesheet" href="../../../resources/static/css/footer.css" th:href="@{/css/footer.css}"/>-->
|
||||
|
||||
<!-- <div class="third">-->
|
||||
<!-- First third of footer-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="third">-->
|
||||
<!-- Middle of footer-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="third">-->
|
||||
<!-- Right side of footer.-->
|
||||
<!-- </div>-->
|
||||
<footer th:fragment="footer" class="container-fluid footer fixed-bottom">
|
||||
<link rel="stylesheet" th:href="@{/css/footer.css}" type="text/css"/>
|
||||
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-12 col-md-4">
|
||||
<h5>Quick Links</h5>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-4">
|
||||
<h5>More links</h5>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-4">
|
||||
<h5>Yet more links</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 text-center">
|
||||
Social media icons go here.
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 text-center">
|
||||
<p class="h6">
|
||||
© 2019 Andrew Lalis, all rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -7,15 +7,36 @@
|
|||
<body>
|
||||
|
||||
|
||||
<nav th:fragment="header" class="header_bar">
|
||||
<link rel="stylesheet" href="../../../resources/static/css/header.css" th:href="@{/css/header.css}"/>
|
||||
<h1 class="header_title">Teaching Assistant Assistant</h1>
|
||||
<ul class="header_link_list">
|
||||
<li><a href="/" th:href="@{/}">Home</a>
|
||||
<li><a href="/courses" th:href="@{/courses}">Courses</a>
|
||||
<li><a href="/students" th:href="@{/students}">Students</a></li>
|
||||
<li><a href="/teaching_assistants" th:href="@{/teaching_assistants}">Teaching Assistants</a></li>
|
||||
</ul>
|
||||
<nav th:fragment="header" class="navbar navbar-expand navbar- header">
|
||||
<link rel="stylesheet" th:href="@{/css/header.css}"/>
|
||||
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="/../resources/static/images/logo.png" th:src="@{/images/logo.png}" class="d-inline-block align-top" alt="" width="30" height="30"/>
|
||||
Teaching Assistant Assistant
|
||||
</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" th:href="@{/}">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" th:href="@{/courses}">Courses</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" th:href="@{/students}">Students</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" th:href="@{/teaching_assistants}">
|
||||
Teaching Assistants
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" th:href="@{/logout}">
|
||||
Log out (Logged in as <span th:text="${user.getPerson().getFullName()}"></span>)
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Homepage</title>
|
||||
</head>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Home', content=~{::#content})}" lang="en">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<p>
|
||||
Welcome to the home page. To find the courses in this application please follow the link to <a th:href="@{/courses}">courses</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div id="content" class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-6">
|
||||
<h3>Welcome to the Teaching Assistant Assistant, <span th:text="${user.getPerson().getFullName()}"></span>.</h3>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block">
|
||||
This is an example sidebar block.
|
||||
</div>
|
||||
<div id="generate_courses_block" class="sidebar_block">
|
||||
<a href="/courses/generate">Click here to generate test data.</a>
|
||||
</div>
|
||||
<div class="sidebar_block">
|
||||
Another sidebar block with a lot more text in it, so that the text should properly wrap to the next lines if needed.
|
||||
<p>
|
||||
To find the courses in this application please follow the link to <a th:href="@{/courses}">courses</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This application makes it easy to sift through lots of student groups that are usually handled manually in some
|
||||
spreadsheet. By integrating all data into one web application it is possible to access the information you need
|
||||
faster, and make coordinated changes without fear of introducing inconsistencies or errors.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,30 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" th:fragment="layout (title, content, sidebar_content)" xmlns:th="http://www.thymeleaf.org">
|
||||
<html lang="en" th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title th:replace="${title}">basic_page</title>
|
||||
<title th:text="${title}">basic_page</title>
|
||||
|
||||
<link rel="icon" th:href="@{/images/favicon.ico}"/>
|
||||
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"/>
|
||||
<script th:src="@{/javascript/bootstrap.bundle.min.js}"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||||
|
||||
<link rel="stylesheet" href="../../../resources/static/css/style.css" th:href="@{/css/style.css}" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div th:replace="~{fragments/header :: header}"></div>
|
||||
<!-- Only show the header if the user is authenticated. -->
|
||||
<header th:if="${user != null}">
|
||||
<div th:replace="~{fragments/header :: header}"></div>
|
||||
</header>
|
||||
|
||||
<div class="content_container">
|
||||
<div th:replace="${content}">
|
||||
<div class="content_container container-fluid">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1 class="text-center" th:text="${title}"></h1>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- If the page controller specifies some actions, add them here as cards. -->
|
||||
<th:block th:if="${actions != null}">
|
||||
<div th:include="${actions}" class="row justify-content-center mb-2">
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<div th:replace="${content}" class="row">
|
||||
<p>
|
||||
Basic page content block.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar_container">
|
||||
<div th:replace="${sidebar_content}">
|
||||
<p>
|
||||
Basic page sidebar block.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div th:replace="~{fragments/footer :: footer}"></div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Login', content=~{::#content})}" lang="en">
|
||||
|
||||
<body>
|
||||
|
||||
<section id="content" class="container">
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="alert alert-danger" th:if="${param.error}">
|
||||
Invalid username or password!
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success" th:if="${param.logout}">
|
||||
You have been logged out!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<p>
|
||||
Please log in to access to the world's most advanced course organization tool ever created. With the ability to manipulate large groups of students with ease, you might actually enjoy teaching a course.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<form th:action="@{/login}" method="post">
|
||||
<div class="form-group">
|
||||
<label for="username_input">Username</label>
|
||||
<input id="username_input" class="form-control" type="text" name="username"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password_input">Password</label>
|
||||
<input id="password_input" class="form-control" type="password" name="password"/>
|
||||
</div>
|
||||
|
||||
<input class="btn btn-primary" type="submit" value="Login"/>
|
||||
|
||||
<a class="btn btn-secondary btn-sm" th:href="@{/register}">Don't have an account? Sign up here!</a>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Register', content=~{::#content})}" lang="en">
|
||||
<body>
|
||||
|
||||
<section id="content" class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p>
|
||||
Here you can register to create a new account to access the information provided by this application, whether you are a professor, teaching assistant, or student. After filling out this submission form, you'll receive an email with a link to verify that you are who you say you are, after which your account will be activated.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<h3>I am a ...</h3>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col text-center">
|
||||
<a class="btn btn-primary" th:href="@{/register/student}">Student</a>
|
||||
</div>
|
||||
<div class="col text-center">
|
||||
<a class="btn btn-primary" th:href="@{/register/teaching_assistant}">Teaching Assistant</a>
|
||||
</div>
|
||||
<div class="col text-center">
|
||||
<a class="btn btn-primary" th:href="@{/register/administrator}">Administrator</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Register as a New Student', content=~{::#content})}" lang="en">
|
||||
<body>
|
||||
|
||||
<section id="content" class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p>
|
||||
To register as a student, please fill out the form below. Make sure to double-check that all information is entered is accurate.
|
||||
</p>
|
||||
<p>
|
||||
Where the form asks for a <em>Registration Code</em>, please provide the code that has been given to you by your lecturers. This code ensures that you'll join the right course.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col">
|
||||
<form action="/register/student" method="post" enctype="application/x-www-form-urlencoded">
|
||||
<div class="form-group">
|
||||
<label>
|
||||
Registration Code:
|
||||
<input type="text" name="registration_code" class="form-control">
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,15 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Students', content=~{::#content}, actions=~{::#actions})}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Students</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>All Students</h1>
|
||||
<div id="actions">
|
||||
<a class="btn btn-primary m-1" th:href="@{/students/create}">Create Student</a>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<div id="content" class="row">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Student</th>
|
||||
<th>Student Number</th>
|
||||
|
@ -44,11 +46,5 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/students/create}">Create Student</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,59 +1,66 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Create a Student</title>
|
||||
</head>
|
||||
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Create New Student', content=~{::#content})}">
|
||||
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Create New Student</h1>
|
||||
<div id="content" class="row justify-content-center">
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<form th:action="@{/students/create}" th:object="${student}" method="post" enctype="application/x-www-form-urlencoded">
|
||||
<div class="form-row">
|
||||
<div class="col">
|
||||
<label for="student_first_name_input">First Name</label>
|
||||
<input id="student_first_name_input" class="form-control" type="text" th:field="*{firstName}" placeholder="Elon" required />
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="student_last_name_input">Last Name</label>
|
||||
<input id="student_last_name_input" class="form-control" type="text" th:field="*{lastName}" placeholder="Musk" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="#" th:action="@{/students/create}" th:object="${student}" method="post" enctype="application/x-www-form-urlencoded">
|
||||
<div class="page_row">
|
||||
<label for="student_first_name_input">First Name:</label>
|
||||
<input id="student_first_name_input" type="text" th:field="*{firstName}" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col">
|
||||
<label for="student_email_input">Email Address</label>
|
||||
<input id="student_email_input" class="form-control" type="email" th:field="*{emailAddress}" placeholder="me@example.com" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="student_last_name_input">Last Name:</label>
|
||||
<input id="student_last_name_input" type="text" th:field="*{lastName}" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col">
|
||||
<label for="student_github_username_input">Github Username</label>
|
||||
<input id="student_github_username_input" class="form-control" type="text" th:field="*{githubUsername}" placeholder="username" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="student_email_input">Email Address:</label>
|
||||
<input id="student_email_input" type="email" th:field="*{emailAddress}" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col">
|
||||
<label for="student_number_input">Student Number</label>
|
||||
<input id="student_number_input" class="form-control" type="number" th:field="*{studentNumber}" value="" placeholder="1234567" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="student_github_username_input">Github Username:</label>
|
||||
<input id="student_github_username_input" type="text" th:field="*{githubUsername}" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col">
|
||||
<label for="student_course_select">Add this student to a course?</label>
|
||||
<select id="student_course_select" class="form-control" name="course_code">
|
||||
<option value="NO_COURSE_SELECTED">None</option>
|
||||
<option
|
||||
th:each="course: ${courses}"
|
||||
th:value="${course.getCode()}"
|
||||
th:text="${course.getName()}"
|
||||
></option>
|
||||
</select>
|
||||
<small class="form-text text-muted">This can also be done later at this student's page.</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="student_number_input">Student Number:</label>
|
||||
<input id="student_number_input" type="number" th:field="*{studentNumber}" required />
|
||||
</div>
|
||||
<div class="form-row mt-1">
|
||||
<div class="col">
|
||||
<input class="btn btn-primary" type="submit" value="Submit" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="student_course_select">Select a course to add the student to:</label>
|
||||
<select id="student_course_select" name="course_code">
|
||||
<option value="NO_COURSE_SELECTED">None</option>
|
||||
<option
|
||||
th:each="course: ${courses}"
|
||||
th:value="${course.getCode()}"
|
||||
th:text="${course.getName()}"
|
||||
></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<button type="submit">Submit</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Student</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title=${student.getFullName()}, content=~{::#content}, actions=~{::#actions})}">
|
||||
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Student: <span th:text="${student.getFullName()}"></span></h1>
|
||||
<div id="actions">
|
||||
<a class="btn btn-primary m-1" th:href="@{/students/{id}/edit(id=${student.getId()})}">Edit</a>
|
||||
</div>
|
||||
|
||||
<div id="content" class="row">
|
||||
<ul>
|
||||
<li>
|
||||
Student Number: <code th:text="${student.getStudentNumber()}"></code>
|
||||
|
@ -29,11 +29,5 @@
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="sidebar_block">
|
||||
<a th:href="@{/students/{id}/edit(id=${student.getId()})}">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,53 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Edit Student</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Edit Student', content=~{::#content})}">
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Edit Student: <span th:text="${student.getFullName()}"></span></h1>
|
||||
<div id="content" class="row justify-content-center">
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<form
|
||||
th:action="@{/students/{id}/edit(id=${student.getId()})}" th:object="${student}"
|
||||
method="post"
|
||||
>
|
||||
|
||||
<form
|
||||
action="#"
|
||||
th:action="@{/students/{id}/edit(id=${student.getId()})}" th:object="${student}"
|
||||
method="post"
|
||||
>
|
||||
<div class="form-row">
|
||||
<label for="first_name_input">First Name:</label>
|
||||
<input id="first_name_input" type="text" th:field="*{firstName}" required />
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="first_name_input">First Name:</label>
|
||||
<input id="first_name_input" type="text" th:field="*{firstName}" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="last_name_input">Last Name:</label>
|
||||
<input id="last_name_input" type="text" th:field="*{lastName}" required />
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="last_name_input">Last Name:</label>
|
||||
<input id="last_name_input" type="text" th:field="*{lastName}" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="email_input">Email Address:</label>
|
||||
<input id="email_input" type="email" th:field="*{emailAddress}" required />
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="email_input">Email Address:</label>
|
||||
<input id="email_input" type="email" th:field="*{emailAddress}" required />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="github_username_input">Github Username:</label>
|
||||
<input id="github_username_input" type="text" th:field="*{githubUsername}" required />
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="github_username_input">Github Username:</label>
|
||||
<input id="github_username_input" type="text" th:field="*{githubUsername}" required />
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<label for="student_number_input">Student Number:</label>
|
||||
<input id="student_number_input" type="number" th:field="*{studentNumber}" required />
|
||||
</div>
|
||||
|
||||
<div class="page_row">
|
||||
<button type="submit">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div class="form-row">
|
||||
<label for="student_number_input">Student Number:</label>
|
||||
<input id="student_number_input" type="number" th:field="*{studentNumber}" required />
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<button type="submit">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -1,22 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Teaching Assistants</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title='Teaching Assistants', content=~{::#content})}">
|
||||
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>All Teaching Assistants</h1>
|
||||
|
||||
<table>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Teaching Assistant</th>
|
||||
<th>Github Username</th>
|
||||
<th>Email</th>
|
||||
<th>Courses</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
<tr th:each="teachingAssistant: ${teaching_assistants}">
|
||||
<td>
|
||||
<a th:href="@{/teaching_assistants/{id}(id=${teachingAssistant.getId()})}"><span th:text="${teachingAssistant.getFullName()}"></span></a>
|
||||
</td>
|
||||
<td th:text="${teachingAssistant.getGithubUsername()}"></td>
|
||||
<td th:text="${teachingAssistant.getEmailAddress()}"></td>
|
||||
<td th:text="${teachingAssistant.getId()}"></td>
|
||||
<td>
|
||||
<div th:each="course: ${teachingAssistant.getCourses()}">
|
||||
<a th:href="@{/courses/{code}(code=${course.getCode()})}" th:text="${course.getName()}"></a><br>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a th:href="@{/teaching_assistants/{id}/delete(id=${teachingAssistant.getId()})}">Delete</a>
|
||||
</td>
|
||||
|
@ -24,9 +30,5 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,33 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (~{::title}, ~{::#content}, ~{::#sidebar})}">
|
||||
<head>
|
||||
<title>Teaching Assistant</title>
|
||||
</head>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="~{layouts/basic_page :: layout (title=${teachingAssistant.getFullName()}, content=~{::#content})}">
|
||||
|
||||
<body>
|
||||
|
||||
<div id="content">
|
||||
<h1>Teaching Assistant: <span th:text="${teachingAssistant.getFullName()}"></span></h1>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
Email: <code th:text="${teachingAssistant.getEmailAddress()}"></code>
|
||||
</li>
|
||||
<li>
|
||||
Github Username: <code th:text="${teachingAssistant.getGithubUsername()}"></code>
|
||||
</li>
|
||||
<li>
|
||||
Courses:
|
||||
<ul>
|
||||
<li th:each="course: ${teachingAssistant.getCourses()}">
|
||||
<a th:href="@{/courses/{code}(code=${course.getCode()})}"><span th:text="${course.getName()}"></span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
|
||||
<div id="content" class="row justify-content-center">
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<div class="list-group">
|
||||
<a class="list-group-item list-group-item-action" th:text="'Email: ' + ${teachingAssistant.getEmailAddress()}" th:href="'mailto: ' + ${teachingAssistant.getEmailAddress()}"></a>
|
||||
<a class="list-group-item list-group-item-action" th:text="'Github Username: ' + ${teachingAssistant.getGithubUsername()}" th:href="'https://www.github.com/' + ${teachingAssistant.getGithubUsername()}" target="_blank"></a>
|
||||
<div class="list-group-item">
|
||||
<h5>Courses</h5>
|
||||
<div th:each="course: ${teachingAssistant.getCourses()}">
|
||||
<a class="list-group-item-action" th:text="${course.getName()}" th:href="@{/courses/{code}(code=${course.getCode()})}"></a><br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>Test Controller Page</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p th:text="'Hello, ' + ${name} + '!'"></p>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue