func deleteClass(w http.ResponseWriter, r *http.Request) *webapp.Error { idString := r.FormValue("class") if idString == "" { return missingFields(w) } id, err := strconv.ParseInt(idString, 10, 64) if err != nil { return invalidData(w, fmt.Sprintf("Invalid class ID")) } c := appengine.NewContext(r) class, err := classes.ClassWithID(c, id) switch err { case nil: break case classes.ErrClassNotFound: return invalidData(w, "No such class.") default: return webapp.InternalError(fmt.Errorf("failed to look up class %d: %s", id, err)) } staffAccount, ok := staffContext(r) if !ok { return webapp.UnauthorizedError(fmt.Errorf("only staff may delete classes")) } if r.Method == "POST" { c.Infof("updating class %d", class.ID) token, err := auth.TokenForRequest(c, staffAccount.ID, r.URL.Path) if err != nil { return webapp.UnauthorizedError(fmt.Errorf("didn't find an auth token")) } if !token.IsValid(r.FormValue(auth.TokenFieldName), time.Now()) { return webapp.UnauthorizedError(fmt.Errorf("invalid auth token")) } if err := class.Delete(c); err != nil { return webapp.InternalError(fmt.Errorf("failed to delete class %d: %s", class.ID, err)) } token.Delete(c) http.Redirect(w, r, "/staff", http.StatusSeeOther) return nil } token, err := auth.NewToken(staffAccount.ID, r.URL.Path, time.Now()) if err != nil { return webapp.InternalError(err) } if err := token.Store(c); err != nil { return webapp.InternalError(err) } data := map[string]interface{}{ "Token": token.Encode(), "Class": class, "Teacher": class.TeacherEntity(c), } if err := deleteClassPage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }
func addSession(w http.ResponseWriter, r *http.Request) *webapp.Error { c := appengine.NewContext(r) staffAccount, ok := staffContext(r) if !ok { return webapp.UnauthorizedError(fmt.Errorf("only staff may add sessions")) } if r.Method == "POST" { token, err := auth.TokenForRequest(c, staffAccount.ID, r.URL.Path) if err != nil { return webapp.UnauthorizedError(fmt.Errorf("didn't find an auth token")) } if !token.IsValid(r.FormValue(auth.TokenFieldName), time.Now()) { return webapp.UnauthorizedError(fmt.Errorf("invalid auth token")) } fields, err := webapp.ParseRequiredValues(r, "name", "startdate", "enddate") if err != nil { return missingFields(w) } start, err := parseLocalDate(fields["startdate"]) if err != nil { return invalidData(w, "Invalid start date; please use mm/dd/yyyy format.") } end, err := parseLocalDate(fields["enddate"]) if err != nil { return invalidData(w, "Invalid end date; please use mm/dd/yyyy format.") } session := classes.NewSession(fields["name"], start, end) if err := session.Insert(c); err != nil { return webapp.InternalError(fmt.Errorf("failed to put session: %s", err)) } token.Delete(c) http.Redirect(w, r, "/staff", http.StatusSeeOther) return nil } token, err := auth.NewToken(staffAccount.ID, r.URL.Path, time.Now()) if err != nil { return webapp.InternalError(err) } if err := token.Store(c); err != nil { return webapp.InternalError(err) } data := map[string]interface{}{ "Token": token.Encode(), } if err := addSessionPage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }
func deleteYinYogassage(w http.ResponseWriter, r *http.Request) *webapp.Error { fields, err := webapp.ParseRequiredValues(r, "id") if err != nil { return webapp.InternalError(err) } id, err := strconv.ParseInt(fields["id"], 10, 64) if err != nil { return invalidData(w, fmt.Sprintf("Invalid yogassage ID")) } c := appengine.NewContext(r) yin, err := yogassage.WithID(c, id) if err != nil { return webapp.InternalError(fmt.Errorf("failed to find yogassage %d: %s", id, err)) } staffAccount, ok := staffContext(r) if !ok { return webapp.UnauthorizedError(fmt.Errorf("only staff may delete yogassage classes")) } if r.Method == "POST" { token, err := auth.TokenForRequest(c, staffAccount.ID, r.URL.Path) if err != nil { return webapp.UnauthorizedError(fmt.Errorf("didn't find an auth token")) } if !token.IsValid(r.FormValue(auth.TokenFieldName), time.Now()) { return webapp.UnauthorizedError(fmt.Errorf("invalid auth token")) } if err := yin.Delete(c); err != nil { return webapp.InternalError(fmt.Errorf("failed to delete yogassage %d: %s", yin.ID, err)) } token.Delete(c) http.Redirect(w, r, "/staff", http.StatusSeeOther) return nil } token, err := auth.NewToken(staffAccount.ID, r.URL.Path, time.Now()) if err != nil { return webapp.InternalError(err) } if err := token.Store(c); err != nil { return webapp.InternalError(err) } data := map[string]interface{}{ "Token": token.Encode(), "Class": yin, } if err := deleteYinYogassagePage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }
func deleteAnnouncement(w http.ResponseWriter, r *http.Request) *webapp.Error { fields, err := webapp.ParseRequiredValues(r, "id") if err != nil { return webapp.InternalError(err) } id, err := strconv.ParseInt(fields["id"], 10, 64) if err != nil { return webapp.InternalError(fmt.Errorf("failed to parse %q as announcement ID: %s", fields["id"], err)) } c := appengine.NewContext(r) announce, err := staff.AnnouncementWithID(c, id) if err != nil { return missingFields(w) } staffAccount, ok := staffContext(r) if !ok { return webapp.UnauthorizedError(fmt.Errorf("only staff may delete announcements")) } if r.Method == "POST" { token, err := auth.TokenForRequest(c, staffAccount.ID, r.URL.Path) if err != nil { return webapp.UnauthorizedError(fmt.Errorf("didn't find an auth token")) } if !token.IsValid(r.FormValue(auth.TokenFieldName), time.Now()) { return webapp.UnauthorizedError(fmt.Errorf("invalid auth token")) } if err := announce.Delete(c); err != nil { return webapp.InternalError(fmt.Errorf("failed to delete announcement %d: %s", announce.ID, err)) } token.Delete(c) http.Redirect(w, r, "/staff", http.StatusSeeOther) return nil } token, err := auth.NewToken(staffAccount.ID, r.URL.Path, time.Now()) if err != nil { return webapp.InternalError(err) } if err := token.Store(c); err != nil { return webapp.InternalError(err) } data := map[string]interface{}{ "Token": token.Encode(), "Announcement": announce, } if err := deleteAnnouncementPage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }
func registerForSession(w http.ResponseWriter, r *http.Request) *webapp.Error { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "Method not allowed") return nil } c := appengine.NewContext(r) user, class, err := classAndUser(w, r) if err != nil { return err } token, ok := checkToken(c, user.ID, r.URL.Path, r.FormValue(auth.TokenFieldName)) if !ok { return webapp.UnauthorizedError(fmt.Errorf("Invalid auth token")) } student := students.New(user, class) switch err := student.Add(c, time.Now()); err { case nil: break case students.ErrClassIsFull: if err := classFullPage.Execute(w, class); err != nil { return webapp.InternalError(err) } default: return webapp.InternalError(fmt.Errorf("failed to write student: %s", err)) } token.Delete(c) http.Redirect(w, r, "/", http.StatusSeeOther) return nil }
func roster(w http.ResponseWriter, r *http.Request) *webapp.Error { id, err := strconv.ParseInt(r.FormValue("class"), 10, 64) if err != nil { return invalidData(w, "Invalid class ID") } c := appengine.NewContext(r) class, err := classes.ClassWithID(c, id) if err != nil { return invalidData(w, "No such class.") } acct, ok := userContext(r) if !ok { return badRequest(w, "Must be logged in.") } staff, _ := staff.WithID(c, acct.ID) if !canViewRoster(staff, acct, class.TeacherEntity(c)) { return webapp.UnauthorizedError(fmt.Errorf("only staff or teachers can view rosters")) } classStudents := students.In(c, class, time.Now()) sort.Sort(students.ByName(classStudents)) token, err := storeNewToken(c, acct.ID, "/register/paper") if err != nil { return webapp.InternalError(fmt.Errorf("Failed to store token: %s", err)) } data := map[string]interface{}{ "Class": class, "Students": classStudents, "Token": token.Encode(), } if err := rosterPage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }
func addTeacher(w http.ResponseWriter, r *http.Request) *webapp.Error { c := appengine.NewContext(r) vals, err := webapp.ParseRequiredValues(r, "email") if err != nil { return webapp.InternalError(err) } staffAccount, ok := staffContext(r) if !ok { return webapp.UnauthorizedError(fmt.Errorf("only staff may add teachers")) } account, err := account.WithEmail(c, vals["email"]) if err != nil { return webapp.InternalError(fmt.Errorf("Couldn't find account for '%s'", vals["email"])) } if r.Method == "POST" { token, err := auth.TokenForRequest(c, staffAccount.ID, r.URL.Path) if err != nil { return webapp.UnauthorizedError(fmt.Errorf("didn't find an auth token")) } if !token.IsValid(r.FormValue(auth.TokenFieldName), time.Now()) { return webapp.UnauthorizedError(fmt.Errorf("invalid auth token")) } teacher := classes.NewTeacher(account) if err := teacher.Put(c); err != nil { return webapp.InternalError(fmt.Errorf("Couldn't store teacher for %q: %s", account.Email, err)) } token.Delete(c) http.Redirect(w, r, "/staff", http.StatusSeeOther) return nil } token, err := auth.NewToken(staffAccount.ID, r.URL.Path, time.Now()) if err != nil { return webapp.InternalError(err) } if err := token.Store(c); err != nil { return webapp.InternalError(err) } data := map[string]interface{}{ "Token": token.Encode(), "User": account, } if err := addTeacherPage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }
func addAnnouncement(w http.ResponseWriter, r *http.Request) *webapp.Error { c := appengine.NewContext(r) staffAccount, ok := staffContext(r) if !ok { return webapp.UnauthorizedError(fmt.Errorf("only staff may add announcements")) } if r.Method == "POST" { token, err := auth.TokenForRequest(c, staffAccount.ID, r.URL.Path) if err != nil { return webapp.UnauthorizedError(fmt.Errorf("didn't find an auth token")) } if !token.IsValid(r.FormValue(auth.TokenFieldName), time.Now()) { return webapp.UnauthorizedError(fmt.Errorf("invalid auth token")) } fields, err := webapp.ParseRequiredValues(r, "text", "expiration") if err != nil { return missingFields(w) } expiration, err := parseLocalDate(fields["expiration"]) if err != nil { return invalidData(w, "Invalid date entry; please use mm/dd/yyyy format.") } c.Infof("expiration: %s", expiration) announce := staff.NewAnnouncement(fields["text"], expiration) if err := staffAccount.AddAnnouncement(c, announce); err != nil { return webapp.InternalError(fmt.Errorf("staff: failed to add announcement: %s", err)) } token.Delete(c) http.Redirect(w, r, "/staff", http.StatusSeeOther) return nil } token, err := auth.NewToken(staffAccount.ID, r.URL.Path, time.Now()) if err != nil { return webapp.InternalError(err) } if err := token.Store(c); err != nil { return webapp.InternalError(err) } data := map[string]interface{}{ "Token": token.Encode(), } if err := addAnnouncementPage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }
func addStaff(w http.ResponseWriter, r *http.Request) *webapp.Error { c := appengine.NewContext(r) adminAccount, ok := userContext(r) if !ok { return webapp.InternalError(fmt.Errorf("user not logged in")) } account, err := account.WithEmail(c, r.FormValue("email")) if err != nil { return webapp.InternalError(fmt.Errorf("Couldn't find user for email %s", r.FormValue("email"))) } if r.Method == "POST" { token, err := auth.TokenForRequest(c, adminAccount.ID, r.URL.Path) if err != nil { return webapp.UnauthorizedError(fmt.Errorf("didn't find an auth token")) } if !token.IsValid(r.FormValue("xsrf_token"), time.Now()) { return webapp.UnauthorizedError(fmt.Errorf("Invalid XSRF token provided")) } staff := staff.New(account) if err := staff.Store(c); err != nil { return webapp.InternalError(fmt.Errorf("Couldn't add %s as staff", account.Email)) } http.Redirect(w, r, "/admin", http.StatusSeeOther) return nil } token, err := auth.NewToken(adminAccount.ID, r.URL.Path, time.Now()) if err != nil { return webapp.InternalError(err) } if err := token.Store(c); err != nil { return webapp.InternalError(err) } data := map[string]interface{}{ "Token": token.Encode(), "User": account, } if err := addStaffPage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }
func staffContextHandler(handler webapp.Handler) webapp.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) *webapp.Error { c := appengine.NewContext(r) account, ok := userContext(r) if !ok { return webapp.InternalError(fmt.Errorf("staff context requires user context")) } u := user.Current(c) switch staffer, err := maybeOldStaff(c, account, u); err { case nil: setStaffContext(r, staffer) return handler.Serve(w, r) case staff.ErrUserIsNotStaff: return webapp.UnauthorizedError(fmt.Errorf("%s is not staff", account.Email)) default: return webapp.InternalError(fmt.Errorf("failed to look up staff for %q: %s", account.ID, err)) } } }
func registerForOneDay(w http.ResponseWriter, r *http.Request) *webapp.Error { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "Method not allowed") return nil } c := appengine.NewContext(r) user, class, err := classAndUser(w, r) if err != nil { return err } token, ok := checkToken(c, user.ID, r.URL.Path, r.FormValue(auth.TokenFieldName)) if !ok { return webapp.UnauthorizedError(fmt.Errorf("Invalid auth token")) } date, dateErr := parseLocalDate(r.FormValue("date")) if dateErr != nil { return invalidData(w, "Invalid date; please use mm/dd/yyyy format") } // TODO(rwsims): The date here should really be the end time of the // class on the given day. student := students.NewDropIn(user, class, date) switch err := student.Add(c, time.Now()); err { case nil: break case students.ErrClassIsFull: if err := classFullPage.Execute(w, class); err != nil { return webapp.InternalError(err) } default: return webapp.InternalError(fmt.Errorf("failed to write student: %s", err)) } token.Delete(c) http.Redirect(w, r, "/", http.StatusSeeOther) return nil }
func registerPaperStudent(w http.ResponseWriter, r *http.Request) *webapp.Error { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintf(w, "Method not allowed") return nil } c := appengine.NewContext(r) user, class, werr := classAndUser(w, r) if werr != nil { return werr } token, ok := checkToken(c, user.ID, r.URL.Path, r.FormValue(auth.TokenFieldName)) if !ok { return webapp.UnauthorizedError(fmt.Errorf("Invalid auth token")) } fields, err := webapp.ParseRequiredValues(r, "firstname", "lastname", "email", "type") if err != nil { return missingFields(w) } acct, err := account.WithEmail(c, fields["email"]) switch err { case nil: // Register with existing account break case account.ErrUserNotFound: // Need to create a paper account for this registration. This account will not be stored. info := account.Info{ FirstName: fields["firstname"], LastName: fields["lastname"], Email: fields["email"], } if phone := fields["phone"]; phone != "" { info.Phone = phone } acct = account.Paper(info, class.ID) break default: return webapp.InternalError(fmt.Errorf("failed to look up account for %q: %s", fields["email"], err)) } var student *students.Student if fields["type"] == "dropin" { // TODO(rwsims): The date here should really be the end time of the // class on the given day. date, err := parseLocalDate(r.FormValue("date")) if err != nil { return invalidData(w, "Invalid date; please use mm/dd/yyyy format") } student = students.NewDropIn(acct, class, date) } else { student = students.New(acct, class) } switch err := student.Add(c, time.Now()); err { case nil: break case students.ErrClassIsFull: if err := classFullPage.Execute(w, class); err != nil { return webapp.InternalError(err) } default: return webapp.InternalError(fmt.Errorf("failed to write student: %s", err)) } token.Delete(c) http.Redirect(w, r, fmt.Sprintf("/roster?class=%d", class.ID), http.StatusSeeOther) return nil }
func editClass(w http.ResponseWriter, r *http.Request) *webapp.Error { idString := r.FormValue("class") if idString == "" { return missingFields(w) } id, err := strconv.ParseInt(idString, 10, 64) if err != nil { return invalidData(w, fmt.Sprintf("Invalid class ID")) } c := appengine.NewContext(r) class, err := classes.ClassWithID(c, id) switch err { case nil: break case classes.ErrClassNotFound: return invalidData(w, "No such class.") default: return webapp.InternalError(fmt.Errorf("failed to look up class %d: %s", id, err)) } staffAccount, ok := staffContext(r) if !ok { return webapp.UnauthorizedError(fmt.Errorf("only staff may edit classes")) } if r.Method == "POST" { c.Infof("updating class %d", class.ID) token, err := auth.TokenForRequest(c, staffAccount.ID, r.URL.Path) if err != nil { return webapp.UnauthorizedError(fmt.Errorf("didn't find an auth token")) } if !token.IsValid(r.FormValue(auth.TokenFieldName), time.Now()) { return webapp.UnauthorizedError(fmt.Errorf("invalid auth token")) } fields, err := webapp.ParseRequiredValues(r, "name", "description", "maxstudents", "dayofweek", "starttime", "length", "dropinonly") if err != nil { return missingFields(w) } class.Title = fields["name"] class.LongDescription = []byte(fields["description"]) class.DropInOnly = fields["dropinonly"] == "yes" weekday, err := parseWeekday(fields["dayofweek"]) if err != nil { return invalidData(w, "Invalid weekday") } class.Weekday = weekday maxStudents, err := strconv.ParseInt(fields["maxstudents"], 10, 32) if err != nil || maxStudents <= 0 { return invalidData(w, "Invalid student capacity") } class.Capacity = int32(maxStudents) length, err := parseMinutes(fields["length"]) if err != nil { return invalidData(w, "Invalid length") } class.Length = length start, err := parseLocalTime(fields["starttime"]) if err != nil { return invalidData(w, "Invalid start time; please use HH:MMpm format (e.g., 3:04pm)") } class.StartTime = start if email := r.FormValue("teacher"); email == "" { class.Teacher = nil } else { teacher, err := classes.TeacherWithEmail(c, email) if err != nil { return invalidData(w, "Invalid teacher selected") } class.Teacher = teacher.Key(c) } if err := class.Update(c); err != nil { return webapp.InternalError(fmt.Errorf("failed to update class %d: %s", class.ID, err)) } token.Delete(c) http.Redirect(w, r, "/staff", http.StatusSeeOther) return nil } token, err := auth.NewToken(staffAccount.ID, r.URL.Path, time.Now()) if err != nil { return webapp.InternalError(err) } if err := token.Store(c); err != nil { return webapp.InternalError(err) } data := map[string]interface{}{ "Token": token.Encode(), "Class": class, "Teacher": class.TeacherEntity(c), "Teachers": classes.Teachers(c), "DaysInOrder": daysInOrder, } if err := editClassPage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }
func newAccount(w http.ResponseWriter, r *http.Request) *webapp.Error { target := continueTarget(r) c := appengine.NewContext(r) u := user.Current(c) if u == nil { webapp.RedirectToLogin(w, r, "/") return nil } if _, err := account.ForUser(c, u); err != account.ErrUserNotFound { if err != nil { return webapp.InternalError(fmt.Errorf("failed to account for current user: %s", err)) } http.Redirect(w, r, "/", http.StatusFound) return nil } id, err := account.ID(u) if err != nil { return webapp.InternalError(err) } if r.Method == "POST" { token, err := auth.TokenForRequest(c, id, r.URL.Path) if err != nil { return webapp.UnauthorizedError(fmt.Errorf("no stored token for request")) } if !token.IsValid(r.FormValue(auth.TokenFieldName), time.Now()) { return webapp.UnauthorizedError(fmt.Errorf("invalid XSRF token")) } fields, err := webapp.ParseRequiredValues(r, "email", "firstname", "lastname") if err != nil { // TODO(rwsims): Clean up this error reporting. w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "Please go back and enter all required data.") return nil } claim := account.NewClaimedEmail(c, id, fields["email"]) switch err := claim.Claim(c); { case err == nil: break case err == account.ErrEmailAlreadyClaimed: // TODO(rwsims): Clean up this error reporting. w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "That email is already in use; please use a different email") return nil default: return webapp.InternalError(fmt.Errorf("failed to claim email %q: %s", claim.Email, err)) } info := account.Info{ FirstName: fields["firstname"], LastName: fields["lastname"], Email: fields["email"], } if phone := r.FormValue("phone"); phone != "" { info.Phone = phone } acct, err := account.New(u, info) if err != nil { return webapp.InternalError(fmt.Errorf("failed to create user account: %s", err)) } if err := acct.Put(c); err != nil { return webapp.InternalError(fmt.Errorf("failed to write new user account: %s", err)) } if err := acct.SendConfirmation(c); err != nil { c.Errorf("Failed to send confirmation email to %q: %s", acct.Email, err) } http.Redirect(w, r, target, http.StatusSeeOther) token.Delete(c) return nil } token, err := auth.NewToken(id, r.URL.Path, time.Now()) if err != nil { return webapp.InternalError(fmt.Errorf("failed to create auth token: %s", err)) } if err := token.Store(c); err != nil { return webapp.InternalError(fmt.Errorf("failed to store token: %s", err)) } data := map[string]interface{}{ "Target": target, "Token": token.Encode(), } if err := newAccountPage.Execute(w, data); err != nil { return webapp.InternalError(err) } return nil }