guestbook.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // Copyright 2011 Google Inc. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. // This example only works on Managed VMs.
  5. // +build !appengine
  6. package main
  7. import (
  8. "html/template"
  9. "net/http"
  10. "time"
  11. "golang.org/x/net/context"
  12. "google.golang.org/appengine"
  13. "google.golang.org/appengine/datastore"
  14. "google.golang.org/appengine/log"
  15. "google.golang.org/appengine/user"
  16. )
  17. var initTime time.Time
  18. type Greeting struct {
  19. Author string
  20. Content string
  21. Date time.Time
  22. }
  23. func main() {
  24. http.HandleFunc("/", handleMainPage)
  25. http.HandleFunc("/sign", handleSign)
  26. appengine.Main()
  27. }
  28. // guestbookKey returns the key used for all guestbook entries.
  29. func guestbookKey(ctx context.Context) *datastore.Key {
  30. // The string "default_guestbook" here could be varied to have multiple guestbooks.
  31. return datastore.NewKey(ctx, "Guestbook", "default_guestbook", 0, nil)
  32. }
  33. var tpl = template.Must(template.ParseGlob("templates/*.html"))
  34. func handleMainPage(w http.ResponseWriter, r *http.Request) {
  35. if r.Method != "GET" {
  36. http.Error(w, "GET requests only", http.StatusMethodNotAllowed)
  37. return
  38. }
  39. if r.URL.Path != "/" {
  40. http.NotFound(w, r)
  41. return
  42. }
  43. ctx := appengine.NewContext(r)
  44. tic := time.Now()
  45. q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(ctx)).Order("-Date").Limit(10)
  46. var gg []*Greeting
  47. if _, err := q.GetAll(ctx, &gg); err != nil {
  48. http.Error(w, err.Error(), http.StatusInternalServerError)
  49. log.Errorf(ctx, "GetAll: %v", err)
  50. return
  51. }
  52. log.Infof(ctx, "Datastore lookup took %s", time.Since(tic).String())
  53. log.Infof(ctx, "Rendering %d greetings", len(gg))
  54. var email, logout, login string
  55. if u := user.Current(ctx); u != nil {
  56. logout, _ = user.LogoutURL(ctx, "/")
  57. email = u.Email
  58. } else {
  59. login, _ = user.LoginURL(ctx, "/")
  60. }
  61. data := struct {
  62. Greetings []*Greeting
  63. Login, Logout, Email string
  64. }{
  65. Greetings: gg,
  66. Login: login,
  67. Logout: logout,
  68. Email: email,
  69. }
  70. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  71. if err := tpl.ExecuteTemplate(w, "guestbook.html", data); err != nil {
  72. log.Errorf(ctx, "%v", err)
  73. }
  74. }
  75. func handleSign(w http.ResponseWriter, r *http.Request) {
  76. if r.Method != "POST" {
  77. http.Error(w, "POST requests only", http.StatusMethodNotAllowed)
  78. return
  79. }
  80. ctx := appengine.NewContext(r)
  81. g := &Greeting{
  82. Content: r.FormValue("content"),
  83. Date: time.Now(),
  84. }
  85. if u := user.Current(ctx); u != nil {
  86. g.Author = u.String()
  87. }
  88. key := datastore.NewIncompleteKey(ctx, "Greeting", guestbookKey(ctx))
  89. if _, err := datastore.Put(ctx, key, g); err != nil {
  90. http.Error(w, err.Error(), http.StatusInternalServerError)
  91. return
  92. }
  93. // Redirect with 303 which causes the subsequent request to use GET.
  94. http.Redirect(w, r, "/", http.StatusSeeOther)
  95. }