Compare commits

..

2 commits

24 changed files with 221 additions and 208 deletions

View file

@ -2,7 +2,6 @@ package controllers
import ( import (
"fmt" "fmt"
"log"
"net/http" "net/http"
"net/mail" "net/mail"
"officeHours/config" "officeHours/config"
@ -149,12 +148,12 @@ func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Requ
id, err := b.officeHourRepo.Add(officeHour) id, err := b.officeHourRepo.Add(officeHour)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
Templates.ExecuteTemplate(w, "addFailure.html", err) b.serveTemplate(w, "addFailure", err)
return return
} }
officeHour, _ = b.officeHourRepo.FindById(id) officeHour, _ = b.officeHourRepo.FindById(id)
b.requestRepo.Add(officeHour, models.RequestActivate) b.requestRepo.Add(officeHour, models.RequestActivate)
Templates.ExecuteTemplate(w, "addSuccess.html", struct{}{}) b.serveTemplate(w, "addSuccess", nil)
} }
} }
@ -165,10 +164,5 @@ func (b *BaseHandler) writeAddOfficeHourMask(w http.ResponseWriter, req *http.Re
if len(data.Errors) != 0 { if len(data.Errors) != 0 {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
} }
err := Templates.ExecuteTemplate(w, "addMask.html", data) b.serveTemplate(w, "addMask", data)
if err != nil {
log.Printf("Template addMask.html could not be parsed: %s", err.Error())
w.Write([]byte(fmt.Sprintf("Template konnte nicht geparst werden : %s", err.Error())))
return
}
} }

View file

@ -9,17 +9,15 @@ func (b *BaseHandler) ConfirmRequestHandler(w http.ResponseWriter, req *http.Req
request, err := b.requestRepo.FindBySecret(secret) request, err := b.requestRepo.FindBySecret(secret)
if err != nil { if err != nil {
w.WriteHeader(http.StatusNotFound) b.serveTemplate(w, "requestNotFound", nil)
Templates.ExecuteTemplate(w, "requestNotFound.html", struct{}{})
return return
} }
err = b.requestRepo.Execute(request) err = b.requestRepo.Execute(request)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) b.serveTemplate(w, "executeFailure", err.Error())
Templates.ExecuteTemplate(w, "executeFailure.html", err.Error())
return return
} }
Templates.ExecuteTemplate(w, "executeSuccess.html", struct{}{}) b.serveTemplate(w, "executeSuccess", nil)
} }

View file

@ -18,7 +18,7 @@ func (b *BaseHandler) DeleteOfficeHourHandler(w http.ResponseWriter, req *http.R
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
} }
_, err = b.requestRepo.Add(officeHour, models.RequestDelete) _, err = b.requestRepo.Add(officeHour, models.RequestDelete)
Templates.ExecuteTemplate(w, "deleteSuccess.html", struct{}{}) b.serveTemplate(w, "deleteSuccess", nil)
} else { } else {
officeHours, _ := b.officeHourRepo.GetAll(true) officeHours, _ := b.officeHourRepo.GetAll(true)
timetable, slots := b.GetTimetable(officeHours) timetable, slots := b.GetTimetable(officeHours)

View file

@ -1,7 +1,6 @@
package controllers package controllers
import ( import (
"fmt"
"html/template" "html/template"
"net/http" "net/http"
"officeHours/models" "officeHours/models"
@ -28,6 +27,7 @@ func (b *BaseHandler) GetByCourseHandler(w http.ResponseWriter, req *http.Reques
courseid, err := strconv.Atoi(req.FormValue("veranstaltung")) courseid, err := strconv.Atoi(req.FormValue("veranstaltung"))
if err != nil { if err != nil {
b.RootHandler(w, req) b.RootHandler(w, req)
return
} }
course, err := b.courseRepo.FindById(courseid) course, err := b.courseRepo.FindById(courseid)
if err != nil { if err != nil {
@ -51,9 +51,5 @@ func (b *BaseHandler) writeTimetablePage(w http.ResponseWriter, req *http.Reques
SelectedRoom int SelectedRoom int
SelectedCourse int SelectedCourse int
}{courses, rooms, timetable, selectedRoom, selectedCourse} }{courses, rooms, timetable, selectedRoom, selectedCourse}
err := Templates.ExecuteTemplate(w, "index.html", data) b.serveTemplate(w, "index", data)
if err != nil {
w.Write([]byte(fmt.Sprintf("Template konnte nicht geparst werden : %s", err.Error())))
return
}
} }

View file

@ -1,21 +1,47 @@
package controllers package controllers
import ( import (
"fmt"
"html/template" "html/template"
"net/http"
"officeHours/models" "officeHours/models"
"os"
) )
var Templates, _ = template.Must(template.ParseFiles( var funcs = template.FuncMap{
"templates/addFailure.html", "DayName": models.DayName,
"templates/addMask.html", "divide": func(i int, j int) int { return i / j },
"templates/addSuccess.html", }
"templates/deleteSuccess.html", var baseTemplate = template.Must(template.ParseFiles("templates/base.html")).New("base.html").Funcs(funcs)
"templates/executeFailure.html",
"templates/executeSuccess.html", func (b *BaseHandler) serveTemplate(w http.ResponseWriter, name string, data any) {
"templates/footer.html", full_name := "templates/" + name + ".html"
"templates/head.html", // check that template exists
"templates/index.html", info, err := os.Stat(full_name)
"templates/officeHourTable.html", if (err != nil && os.IsNotExist(err)) || info.IsDir() {
"templates/requestNotFound.html")). w.WriteHeader(http.StatusNotFound)
New("").Funcs(template.FuncMap{"DayName": models.DayName, w.Write([]byte("404: Template nicht gefunden."))
"divide": func(i int, j int) int { return i / j }}).ParseFiles("templates/confirmationMail", "templates/td.html") return
}
tmpl, err := baseTemplate.ParseFiles(full_name)
if err != nil {
// TODO: log.Printf
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("500: Template "+full_name+" konnte nicht geparst werden : %s", err.Error())))
return
}
// TODO: cache templates in parsed state, but not executed
err = template.Must(tmpl.Clone()).Execute(w, data)
if err != nil {
// TODO: log.Printf
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("500: Template "+full_name+" konnte nicht ausgeführt werden : %s", err.Error())))
return
}
}
var RawTemplates = template.Must(template.New("").Funcs(funcs).ParseFiles(
"templates/confirmationMail",
"templates/td.html",
"templates/officeHourTable.html"))

View file

@ -84,7 +84,7 @@ func (b *BaseHandler) printTimetable(timetable map[models.Date]map[int]models.Of
MinuteGranularity int MinuteGranularity int
DeleteIcons bool DeleteIcons bool
}{OfficeHour: current, MinuteGranularity: b.config.Date.MinuteGranularity, DeleteIcons: deleteIcons} }{OfficeHour: current, MinuteGranularity: b.config.Date.MinuteGranularity, DeleteIcons: deleteIcons}
Templates.ExecuteTemplate(&celldata, "td.html", data) RawTemplates.ExecuteTemplate(&celldata, "td.html", data)
tableBody += celldata.String() tableBody += celldata.String()
} }
} else { } else {
@ -116,6 +116,6 @@ func (b *BaseHandler) printTimetable(timetable map[models.Date]map[int]models.Of
slots[4], slots[4],
template.HTML(tableBody), template.HTML(tableBody),
} }
Templates.ExecuteTemplate(&table, "officeHourTable.html", tableData) RawTemplates.ExecuteTemplate(&table, "officeHourTable.html", tableData)
return template.HTML(table.String()) return template.HTML(table.String())
} }

View file

@ -7,11 +7,11 @@ import (
"log" "log"
"log/syslog" "log/syslog"
"net/http" "net/http"
"os"
"officeHours/config" "officeHours/config"
"officeHours/controllers" "officeHours/controllers"
"officeHours/repositories" "officeHours/repositories"
"officeHours/sqldb" "officeHours/sqldb"
"os"
) )
func main() { func main() {
@ -35,6 +35,9 @@ func main() {
log.Fatalf("%s: %s", "Reading JSON config file into config structure", err) log.Fatalf("%s: %s", "Reading JSON config file into config structure", err)
} }
// serve static files
staticHandler := http.FileServer(http.Dir("./static"))
db := sqldb.Connect(conf) db := sqldb.Connect(conf)
// Create repos // Create repos
roomRepo := repositories.NewRoomRepo(db) roomRepo := repositories.NewRoomRepo(db)
@ -50,6 +53,7 @@ func main() {
http.HandleFunc("/confirmRequest", h.ConfirmRequestHandler) http.HandleFunc("/confirmRequest", h.ConfirmRequestHandler)
http.HandleFunc("/deleteOfficeHour", h.DeleteOfficeHourHandler) http.HandleFunc("/deleteOfficeHour", h.DeleteOfficeHourHandler)
http.HandleFunc("/", h.RootHandler) http.HandleFunc("/", h.RootHandler)
http.Handle("/static/", http.StripPrefix("/static/", staticHandler))
err = http.ListenAndServe(fmt.Sprintf("%s:%d", conf.Server.ListenAddress, conf.Server.ListenPort), nil) err = http.ListenAndServe(fmt.Sprintf("%s:%d", conf.Server.ListenAddress, conf.Server.ListenPort), nil)
log.Println(err.Error()) log.Println(err.Error())

View file

@ -128,7 +128,7 @@ func (r *RequestRepo) sendConfirmationMail(request models.Request) error {
Config config.Config Config config.Config
Request models.Request Request models.Request
}{r.config, request} }{r.config, request}
err := controllers.Templates.ExecuteTemplate(&message, "confirmationMail", data) err := controllers.RawTemplates.ExecuteTemplate(&message, "confirmationMail", data)
if err != nil { if err != nil {
log.Printf("Error parsing confirmation Mail: %s", err.Error()) log.Printf("Error parsing confirmation Mail: %s", err.Error())
return err return err

1
static/bootstrap Symbolic link
View file

@ -0,0 +1 @@
bootstrap-5.2.1-dist/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,13 +1,7 @@
<!DOCTYPE html> {{define "title"}}Fehler{{end}}
<html lang="de">
<head> {{define "content"}}
<title>Sprechstunde anlegen</title> Irgendetwas ist schief gegangen. Bitte sende folgende Daten an <a href="mailto:sprechstundentool@mathebau.de">sprechstundentool@mathebau.de</a> mit einer Beschreibung, was du tun wolltest.
{{template "head.html" .}} <br />
</head> {{.}}
<body> {{end}}
Irgendetwas ist schief gegangen. Bitte sende folgende Daten an <a href="mailto:sprechstundentool@mathebau.de">sprechstundentool@mathebau.de</a> mit einer Beschreibung, was du tun wolltest.
<br />
{{.}}
{{template "footer.html" .}}
</body>
</html>

View file

@ -1,17 +1,15 @@
<!DOCTYPE html> <{{define "title"}}Sprechstunde anlegen{{end}}
<html lang="de">
<head> {{define "content"}}
<title>Sprechstunde anlegen</title> <p>
{{template "head.html" .}}
</head>
<body>
<p>
{{range .Errors}}{{.}}<br />{{end}} {{range .Errors}}{{.}}<br />{{end}}
</p> </p>
<form method="POST" action="addOfficeHour"> <form method="POST" action="addOfficeHour">
<label for="veranstaltung">Veranstaltung</label>: <label for="veranstaltung">Veranstaltung</label>:
<select name="veranstaltung" id="veranstaltung">{{range $course := .Courses}} <select name="veranstaltung" id="veranstaltung">
<option value="{{$course.Id}}"{{if eq $course.Id $.SelectedCourse}} selected{{end}}>{{$course.Name}}</option>{{end}} {{range $course := .Courses}}
<option value="{{$course.Id}}"{{if eq $course.Id $.SelectedCourse}} selected{{end}}>{{$course.Name}}</option>
{{end}}
</select><br /> </select><br />
<label for="woche">Woche</label>: <label for="woche">Woche</label>:
<select name="woche" id="woche"> <select name="woche" id="woche">
@ -29,8 +27,10 @@
<label for="startzeit">Startzeit</label>: <input type="time" name="startzeit" id="startzeit" min="08:00" max="17:30" {{if gt $.Date.Hour 7}}value="{{printf "%02d" $.Date.Hour}}:{{printf "%02d" $.Date.Minute}}"{{end}} required/><br /> <label for="startzeit">Startzeit</label>: <input type="time" name="startzeit" id="startzeit" min="08:00" max="17:30" {{if gt $.Date.Hour 7}}value="{{printf "%02d" $.Date.Hour}}:{{printf "%02d" $.Date.Minute}}"{{end}} required/><br />
<label for="dauer">Dauer in Minuten</label>: <input name="dauer" id="dauer" type="number" min="{{.MinuteGranularity}}" max="120" step="{{.MinuteGranularity}}" value="{{.Duration}}" required/><br /> <label for="dauer">Dauer in Minuten</label>: <input name="dauer" id="dauer" type="number" min="{{.MinuteGranularity}}" max="120" step="{{.MinuteGranularity}}" value="{{.Duration}}" required/><br />
<label for="raum">Raum</label>: <label for="raum">Raum</label>:
<select name="raum" id="raum">{{range $room := .Rooms}} <select name="raum" id="raum">
<option value="{{$room.Id}}"{{if eq $room.Id $.SelectedRoom}} selected{{end}}>{{$room.Name}}</option>{{end}} {{range $room := .Rooms}}
<option value="{{$room.Id}}"{{if eq $room.Id $.SelectedRoom}} selected{{end}}>{{$room.Name}}</option>
{{end}}
</select><br /> </select><br />
<label for="raumname">Raumname (für Sonderräume)</label>: <input type="text" name="raumname" id="raumname" value="{{.Roomname}}"/><br /> <label for="raumname">Raumname (für Sonderräume)</label>: <input type="text" name="raumname" id="raumname" value="{{.Roomname}}"/><br />
<label for="name">Name</label>: <input name="name" id="name" type="text" size="50" value="{{.Name}}" required/><br /> <label for="name">Name</label>: <input name="name" id="name" type="text" size="50" value="{{.Name}}" required/><br />
@ -38,13 +38,15 @@
<input name="email" id="email" type="email" size="50" value="{{.Email}}" required/><br /> <input name="email" id="email" type="email" size="50" value="{{.Email}}" required/><br />
<label for="info">Info</label>: <input name="info" id="info" type="text" size="50" value="{{.Info}}"/><br /> <label for="info">Info</label>: <input name="info" id="info" type="text" size="50" value="{{.Info}}"/><br />
<input type="submit"> <input type="submit">
</form> </form>
{{if ne .Config.Tutor.MailSuffix ""}}Du musst hier eine Email-Adresse angeben, die auf „{{.Config.Tutor.MailSuffix}}“ endet.<br />{{end}} {{if ne .Config.Tutor.MailSuffix ""}}
Außerdem dürfen in Räumen nur begrenzt viele Sprechstunden gleichzeitig stattfinden, nämlich Du musst hier eine Email-Adresse angeben, die auf „{{.Config.Tutor.MailSuffix}}“ endet.<br />
<dl> {{end}}
Außerdem dürfen in Räumen nur begrenzt viele Sprechstunden gleichzeitig stattfinden, nämlich
<dl>
{{range $room := .Rooms}} {{range $room := .Rooms}}
<dt>{{$room.Name}}</dt><dd>{{$room.MaxOccupy}} Sprechstunde{{if gt $room.MaxOccupy 1}}n{{end}}</dd>{{end}} <dt>{{$room.Name}}</dt>
</dl> <dd>{{$room.MaxOccupy}} Sprechstunde{{if gt $room.MaxOccupy 1}}n{{end}}</dd>
{{template "footer.html" .}} {{end}}
</body> </dl>
</html> {{end}}

View file

@ -1,12 +1,5 @@
<!DOCTYPE html> {{define "title"}}Sprechstunde anlegen{{end}}
<html lang="de">
<head> {{define "content"}}
<title>Sprechstunde anlegen</title> Die Sprechstunde wurde angelegt. Du solltest eine Mail mit einem Aktivierungslink erhalten haben.
{{template "head.html" .}} {{end}}
</head>
<body>
Die Sprechstunde wurde angelegt. Du solltest eine Mail mit einem Aktivierungslink erhalten haben.
<br />
{{template "footer.html" .}}
</body>
</html>

27
templates/base.html Normal file
View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="keywords" content="Mathebau, Sprechstunde, Sprechstunden, Mathe, Mathematik, technische, Universität, Darmstadt, TU, Fachschaft">
<meta name="description" content="Eine Übersicht der Sprechstunden, die in den offenen Arbeitsräumen der Fachschaft Mathematik, TU Darmstadt, angeboten werden">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<title>{{block "title" .}}Start{{end}} Sprechstunden</title>
</head>
<body>
<div class="container">
{{block "content" .}}Du solltest dies nicht sehen.{{end}}
</div>
<footer class="container">
<a href="/">Startseite</a><br />
<a href="/addOfficeHour">Sprechstunde anlegen</a><br />
<a href="/deleteOfficeHour">Sprechstunde löschen</a><br />
Technische Fragen an <a href="mailto:sprechstundentool@mathebau.de">sprechstundentool@mathebau.de</a>
</footer>
<script src="/static/bootstrap/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View file

@ -1,13 +1,7 @@
<!DOCTYPE html> {{define "title"}}Sprechstunde löschen{{end}}
<html lang="de">
<head> {{define "content"}}
<title>Sprechstunde löschen</title> Du solltest eine Mail mit einem Bestätigungslink erhalten haben. <br />
{{template "head.html" .}} Sie wurde an die Adresse geschickt, mit der die Sprechstunde angelegt wurde.
</head> <br />
<body> {{end}}
Du solltest eine Mail mit einem Bestätigungslink erhalten haben. <br />
Sie wurde an die Adresse geschickt, mit der die Sprechstunde angelegt wurde.
<br />
{{template "footer.html" .}}
</body>
</html>

View file

@ -1,13 +1,7 @@
<!DOCTYPE html> {{define "title"}}Anfrage ausführen fehlgeschlagen{{end}}
<html lang="de">
<head> {{define "content"}}
<title>Anfrage ausführen fehlgeschlagen</title> Irgendetwas ist schief gegangen. Bitte sende folgende Daten an <a href="mailto:sprechstundentool@mathebau.de">sprechstundentool@mathebau.de</a> mit einer Beschreibung, was du tun wolltest.
{{template "head.html" .}} <br />
</head> {{.}}
<body> {{end}}
Irgendetwas ist schief gegangen. Bitte sende folgende Daten an <a href="mailto:sprechstundentool@mathebau.de">sprechstundentool@mathebau.de</a> mit einer Beschreibung, was du tun wolltest.
<br />
{{.}}
{{template "footer.html" .}}
</body>
</html>

View file

@ -1,11 +1,5 @@
<!DOCTYPE html> {{define "title"}}Anfrage ausgeführt{{end}}
<html lang="de">
<head> {{define "content"}}
<title>Anfrage ausgeführt</title> Deine Anfrage wurde ausgeführt.
{{template "head.html" .}} {{end}}
</head>
<body>
Deine Anfrage wurde ausgeführt. <br />
{{template "footer.html" .}}
</body>
</html>

View file

@ -1,6 +0,0 @@
<footer>
<a href="/">Startseite</a><br />
<a href="/addOfficeHour">Sprechstunde anlegen</a><br />
<a href="/deleteOfficeHour">Sprechstunde löschen</a><br />
Technische Fragen an <a href="mailto:sprechstundentool@mathebau.de">sprechstundentool@mathebau.de</a>
</footer>

View file

@ -1,4 +0,0 @@
<meta charset="UTF-8">
<meta name="keywords" content="Mathebau, Sprechstunde, Sprechstunden, Mathe, Mathematik, technische, Universität, Darmstadt, TU, Fachschaft">
<meta name="description" content="Eine Übersicht der Sprechstunden, die in den offenen Arbeitsräumen der Fachschaft Mathematik, TU Darmstadt, angeboten werden">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View file

@ -1,29 +1,25 @@
<!DOCTYPE html> {{define "title"}}Übersicht{{end}}
<html lang="de">
<head> {{define "content"}}
<title>Sprechstunden</title> <form method="GET" action="/getByCourse">
{{template "head.html" .}}
</head>
<body>
<form method="GET" action="/getByCourse">
<label for="veranstaltung">Veranstaltung: </label> <label for="veranstaltung">Veranstaltung: </label>
<select name="veranstaltung" id="veranstaltung" size="1" onchange="document.forms[0].submit()"> <select name="veranstaltung" id="veranstaltung" size="1" onchange="document.forms[0].submit()">
<option value="">Alle</option> <option value="">Alle</option>
{{range $course := .Courses}} {{range $course := .Courses}}
<option value="{{$course.Id}}"{{if eq $course.Id $.SelectedCourse}} selected{{end}}>{{$course.Name}}</option>{{end}} <option value="{{$course.Id}}"{{if eq $course.Id $.SelectedCourse}} selected{{end}}>{{$course.Name}}</option>
{{end}}
</select> </select>
<input type="submit" value="Auswählen" /> <input type="submit" value="Auswählen" />
</form> </form>
<form method="GET" action="/getByRoom"> <form method="GET" action="/getByRoom">
<label for="raum">Raum: </label> <label for="raum">Raum: </label>
<select name="raum" id="raum" size="1" onchange="document.forms[1].submit()"> <select name="raum" id="raum" size="1" onchange="document.forms[1].submit()">
<option value="">Alle</option> <option value="">Alle</option>
{{range $room := .Rooms}} {{range $room := .Rooms}}
<option value="{{$room.Id}}"{{if eq $room.Id $.SelectedRoom}} selected{{end}}>{{$room.Name}}</option>{{end}} <option value="{{$room.Id}}"{{if eq $room.Id $.SelectedRoom}} selected{{end}}>{{$room.Name}}</option>
{{end}}
</select> </select>
<input type="submit" value="Auswählen" /> <input type="submit" value="Auswählen" />
</form> </form>
{{.Timetable}} {{.Timetable}}
{{template "footer.html" .}} {{end}}
</body>
</html>

View file

@ -1,18 +1,12 @@
<!DOCTYPE html> {{define "title"}}Anfrage bestätigen fehlgeschlagen{{end}}
<html lang="de">
<head> {{define "content"}}
<title>Anfrage bestätigen fehlgeschlagen</title> <p>
{{template "head.html" .}}
</head>
<body>
<p>
Dieser Bestätigungscode ist nicht verfügbar. <br /> Dieser Bestätigungscode ist nicht verfügbar. <br />
Bitte gib deinen Bestätigungscode hier ein. Bitte gib deinen Bestätigungscode hier ein.
</p> </p>
<form action="/confirmRequest"> <form action="/confirmRequest">
<label for="code">Bestätigungscode</label>: <input type="text" name="code" id="code"/> <label for="code">Bestätigungscode</label>: <input type="text" name="code" id="code"/>
<input type="submit" /> <input type="submit" />
</form> </form>
{{template "footer.html" .}} {{end}}
</body>
</html>