| // Copyright 2011 Google Inc. All rights reserved. |
| // Use of this source code is governed by the Apache 2.0 |
| // license that can be found in the LICENSE file. |
| |
| // This example only works on App Engine "flexible environment". |
| // +build !appengine |
| |
| package main |
| |
| import ( |
| "html/template" |
| "net/http" |
| "time" |
| |
| "golang.org/x/net/context" |
| |
| "google.golang.org/appengine" |
| "google.golang.org/appengine/datastore" |
| "google.golang.org/appengine/log" |
| "google.golang.org/appengine/user" |
| ) |
| |
| var initTime time.Time |
| |
| type Greeting struct { |
| Author string |
| Content string |
| Date time.Time |
| } |
| |
| func main() { |
| http.HandleFunc("/", handleMainPage) |
| http.HandleFunc("/sign", handleSign) |
| appengine.Main() |
| } |
| |
| // guestbookKey returns the key used for all guestbook entries. |
| func guestbookKey(ctx context.Context) *datastore.Key { |
| // The string "default_guestbook" here could be varied to have multiple guestbooks. |
| return datastore.NewKey(ctx, "Guestbook", "default_guestbook", 0, nil) |
| } |
| |
| var tpl = template.Must(template.ParseGlob("templates/*.html")) |
| |
| func handleMainPage(w http.ResponseWriter, r *http.Request) { |
| if r.Method != "GET" { |
| http.Error(w, "GET requests only", http.StatusMethodNotAllowed) |
| return |
| } |
| if r.URL.Path != "/" { |
| http.NotFound(w, r) |
| return |
| } |
| |
| ctx := appengine.NewContext(r) |
| tic := time.Now() |
| q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(ctx)).Order("-Date").Limit(10) |
| var gg []*Greeting |
| if _, err := q.GetAll(ctx, &gg); err != nil { |
| http.Error(w, err.Error(), http.StatusInternalServerError) |
| log.Errorf(ctx, "GetAll: %v", err) |
| return |
| } |
| log.Infof(ctx, "Datastore lookup took %s", time.Since(tic).String()) |
| log.Infof(ctx, "Rendering %d greetings", len(gg)) |
| |
| var email, logout, login string |
| if u := user.Current(ctx); u != nil { |
| logout, _ = user.LogoutURL(ctx, "/") |
| email = u.Email |
| } else { |
| login, _ = user.LoginURL(ctx, "/") |
| } |
| data := struct { |
| Greetings []*Greeting |
| Login, Logout, Email string |
| }{ |
| Greetings: gg, |
| Login: login, |
| Logout: logout, |
| Email: email, |
| } |
| w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| if err := tpl.ExecuteTemplate(w, "guestbook.html", data); err != nil { |
| log.Errorf(ctx, "%v", err) |
| } |
| } |
| |
| func handleSign(w http.ResponseWriter, r *http.Request) { |
| if r.Method != "POST" { |
| http.Error(w, "POST requests only", http.StatusMethodNotAllowed) |
| return |
| } |
| ctx := appengine.NewContext(r) |
| g := &Greeting{ |
| Content: r.FormValue("content"), |
| Date: time.Now(), |
| } |
| if u := user.Current(ctx); u != nil { |
| g.Author = u.String() |
| } |
| key := datastore.NewIncompleteKey(ctx, "Greeting", guestbookKey(ctx)) |
| if _, err := datastore.Put(ctx, key, g); err != nil { |
| http.Error(w, err.Error(), http.StatusInternalServerError) |
| return |
| } |
| // Redirect with 303 which causes the subsequent request to use GET. |
| http.Redirect(w, r, "/", http.StatusSeeOther) |
| } |