func GetFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) f := Feed{Url: r.FormValue("f")} var stars []string wg := sync.WaitGroup{} fk := gn.Key(&f) q := datastore.NewQuery(gn.Kind(&Story{})).Ancestor(fk).KeysOnly() q = q.Order("-" + IDX_COL) if cur := r.FormValue("c"); cur != "" { if dc, err := datastore.DecodeCursor(cur); err == nil { q = q.Start(dc) } } else { // grab the stars list on the first run wg.Add(1) go c.Step("stars", func(c mpg.Context) { gn := goon.FromContext(c) usk := starKey(c, f.Url, "") q := datastore.NewQuery(gn.Kind(&UserStar{})).Ancestor(gn.Key(usk).Parent()).KeysOnly() keys, _ := gn.GetAll(q, nil) stars = make([]string, len(keys)) for i, key := range keys { stars[i] = starID(key) } wg.Done() }) } iter := gn.Run(q) var stories []*Story for i := 0; i < 20; i++ { if k, err := iter.Next(nil); err == nil { stories = append(stories, &Story{ Id: k.StringID(), Parent: k.Parent(), }) } else if err == datastore.Done { break } else { serveError(w, err) return } } cursor := "" if ic, err := iter.Cursor(); err == nil { cursor = ic.String() } gn.GetMulti(&stories) wg.Wait() b, _ := json.Marshal(struct { Cursor string Stories []*Story Stars []string `json:",omitempty"` }{ Cursor: cursor, Stories: stories, Stars: stars, }) w.Write(b) }
func CreateChannel(c mpg.Context, w http.ResponseWriter, r *http.Request) { cn := appengine.NewContext(r) urlfetchClient := urlfetch.Client(cn) client := pusher.Client{ AppId: "184465", Key: "e2aac50ec511259792df", Secret: "72f723c5a34c6d3efa0d", HttpClient: urlfetchClient, } gn := goon.FromContext(c) ch := Channel{Id: r.FormValue("id")} if err := gn.Get(&ch); err != nil { ch = Channel{Id: r.FormValue("id"), FeedLinks: r.FormValue("feedLinks"), DisplayName: r.FormValue("displayName"), Tags: r.FormValue("tags")} gn.Put(&ch) client.Trigger("test_channel", "create_channel", ch) return } if strings.Contains(ch.FeedLinks, r.FormValue("feedLinks")) { //fmt.Printf("Found subStr in str \n") } else { feedLinks := ch.FeedLinks + ";" + r.FormValue("feedLinks") ch.FeedLinks = feedLinks gn.Put(&ch) } client.Trigger("test_channel", "create_channel", ch) }
func MarkRead(c mpg.Context, w http.ResponseWriter, r *http.Request) { cu := user.Current(c) gn := goon.FromContext(c) read := make(Read) var stories []readStory defer r.Body.Close() b, _ := ioutil.ReadAll(r.Body) if err := json.Unmarshal(b, &stories); err != nil { serveError(w, err) return } gn.RunInTransaction(func(gn *goon.Goon) error { u := &User{Id: cu.ID} ud := &UserData{ Id: "data", Parent: gn.Key(u), } if err := gn.Get(ud); err != nil { return err } gob.NewDecoder(bytes.NewReader(ud.Read)).Decode(&read) for _, s := range stories { read[s] = true } var b bytes.Buffer gob.NewEncoder(&b).Encode(&read) ud.Read = b.Bytes() _, err := gn.Put(ud) return err }, nil) }
func MarkUnread(c mpg.Context, w http.ResponseWriter, r *http.Request) { cu := user.Current(c) gn := goon.FromContext(c) read := make(Read) f := r.FormValue("feed") s := r.FormValue("story") rs := readStory{Feed: f, Story: s} u := &User{Id: cu.ID} ud := &UserData{ Id: "data", Parent: gn.Key(u), } gn.RunInTransaction(func(gn *goon.Goon) error { if err := gn.Get(ud); err != nil { return err } gob.NewDecoder(bytes.NewReader(ud.Read)).Decode(&read) delete(read, rs) b := bytes.Buffer{} gob.NewEncoder(&b).Encode(&read) ud.Read = b.Bytes() _, err := gn.Put(ud) return err }, nil) }
func GetContents(c mpg.Context, w http.ResponseWriter, r *http.Request) { var reqs []struct { Feed string Story string } defer r.Body.Close() b, _ := ioutil.ReadAll(r.Body) if err := json.Unmarshal(b, &reqs); err != nil { serveError(w, err) return } scs := make([]*StoryContent, len(reqs)) gn := goon.FromContext(c) for i, r := range reqs { f := &Feed{Url: r.Feed} s := &Story{Id: r.Story, Parent: gn.Key(f)} scs[i] = &StoryContent{Id: 1, Parent: gn.Key(s)} } gn.GetMulti(scs) ret := make([]string, len(reqs)) for i, sc := range scs { ret[i] = sc.content() } b, _ = json.Marshal(&ret) w.Write(b) }
func AddSubscription(c mpg.Context, w http.ResponseWriter, r *http.Request) { backupOPML(c) cu := user.Current(c) url := r.FormValue("url") o := &OpmlOutline{ Outline: []*OpmlOutline{ {XmlUrl: url}, }, } if err := addFeed(c, cu.ID, o); err != nil { c.Errorf("add sub error (%s): %s", url, err.Error()) serveError(w, err) return } gn := goon.FromContext(c) ud := UserData{Id: "data", Parent: gn.Key(&User{Id: cu.ID})} gn.Get(&ud) if err := mergeUserOpml(c, &ud, o); err != nil { c.Errorf("add sub error opml (%v): %v", url, err) serveError(w, err) return } gn.PutMulti([]interface{}{&ud, &Log{ Parent: ud.Parent, Id: time.Now().UnixNano(), Text: fmt.Sprintf("add sub: %v", url), }}) if r.Method == "GET" { http.Redirect(w, r, routeUrl("main"), http.StatusFound) } backupOPML(c) }
func FeedHistory(c mpg.Context, w http.ResponseWriter, r *http.Request) { cu := user.Current(c) gn := goon.FromContext(c) u := User{Id: cu.ID} uk := gn.Key(&u) if v := r.FormValue("v"); len(v) == 0 { q := datastore.NewQuery(gn.Kind(&UserOpml{})).Ancestor(uk).KeysOnly() keys, err := gn.GetAll(q, nil) if err != nil { serveError(w, err) return } times := make([]string, len(keys)) for i, k := range keys { times[i] = strconv.FormatInt(k.IntID(), 10) } b, _ := json.Marshal(×) w.Write(b) } else { a, _ := strconv.ParseInt(v, 10, 64) uo := UserOpml{Id: a, Parent: uk} if err := gn.Get(&uo); err != nil { serveError(w, err) return } downloadOpml(w, uo.opml(), cu.Email) } }
func UpdateFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(appengine.Timeout(c, time.Minute)) url := r.FormValue("feed") if url == "" { c.Errorf("empty update feed") return } c.Debugf("update feed %s", url) last := len(r.FormValue("last")) > 0 f := Feed{Url: url} s := "" defer func() { gn.Put(&Log{ Parent: gn.Key(&f), Id: time.Now().UnixNano(), Text: "UpdateFeed - " + s, }) }() if err := gn.Get(&f); err == datastore.ErrNoSuchEntity { c.Errorf("no such entity - " + url) s += "NSE" return } else if err != nil { s += "err - " + err.Error() return } else if last { // noop } else if time.Now().Before(f.NextUpdate) { c.Errorf("feed %v already updated: %v", url, f.NextUpdate) s += "already updated" return } feedError := func(err error) { s += "feed err - " + err.Error() f.Errors++ v := f.Errors + 1 const max = 24 * 7 if v > max { v = max } else if f.Errors == 1 { v = 0 } f.NextUpdate = time.Now().Add(time.Hour * time.Duration(v)) gn.Put(&f) c.Warningf("error with %v (%v), bump next update to %v, %v", url, f.Errors, f.NextUpdate, err) } if feed, stories, err := fetchFeed(c, f.Url, f.Url); err == nil { if err := updateFeed(c, f.Url, feed, stories, false, false, last); err != nil { feedError(err) } else { s += "success" } } else { feedError(err) } f.Subscribe(c) }
func AdminStats(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) uc, _ := datastore.NewQuery(gn.Kind(&User{})).Count(c) templates.ExecuteTemplate(w, "admin-stats.html", struct { Users int }{ uc, }) }
func starKey(c appengine.Context, feed, story string) *UserStar { cu := user.Current(c) gn := goon.FromContext(c) u := User{Id: cu.ID} uk := gn.Key(&u) return &UserStar{ Parent: datastore.NewKey(c, "USF", feed, 0, uk), Id: story, } }
func ClearFeeds(c mpg.Context, w http.ResponseWriter, r *http.Request) { if !isDevServer { return } cu := user.Current(c) gn := goon.FromContext(c) done := make(chan bool) go func() { u := &User{Id: cu.ID} defer func() { done <- true }() ud := &UserData{Id: "data", Parent: gn.Key(u)} if err := gn.Get(u); err != nil { c.Errorf("user del err: %v", err.Error()) return } gn.Get(ud) u.Read = time.Time{} ud.Read = nil ud.Opml = nil gn.PutMulti([]interface{}{u, ud}) c.Infof("%v cleared", u.Email) }() del := func(kind string) { defer func() { done <- true }() q := datastore.NewQuery(kind).KeysOnly() keys, err := gn.GetAll(q, nil) if err != nil { c.Errorf("err: %v", err.Error()) return } if err := gn.DeleteMulti(keys); err != nil { c.Errorf("err: %v", err.Error()) return } c.Infof("%v deleted", kind) } types := []interface{}{ &Feed{}, &Story{}, &StoryContent{}, &Log{}, &UserOpml{}, } for _, i := range types { k := gn.Kind(i) go del(k) } for i := 0; i < len(types); i++ { <-done } http.Redirect(w, r, fmt.Sprintf("%s?url=http://localhost:8080%s", routeUrl("add-subscription"), routeUrl("test-atom")), http.StatusFound) }
func AdminSubHub(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) f := Feed{Url: r.FormValue("f")} if err := gn.Get(&f); err != nil { serveError(w, err) return } f.Subscribed = time.Time{} f.Subscribe(c) fmt.Fprintf(w, "subscribed") }
func SetStatus(c mpg.Context, w http.ResponseWriter, r *http.Request) { cu := user.Current(c) status := r.FormValue("status") readChats := r.FormValue("readChats") gn := goon.FromContext(c) q := datastore.NewQuery(gn.Kind(&User{})) var users []User _, err1 := gn.GetAll(q, &users) if err1 != nil { http.Error(w, err1.Error(), http.StatusInternalServerError) return } for i := 0; i < len(users); i++ { if strings.ToLower(users[i].Email) == strings.ToLower(cu.Email) { users[i].Status = status gn.Put(&users[i]) } } if status == "f" { q = datastore.NewQuery(gn.Kind(&Chat{})) var chats []Chat _, err2 := gn.GetAll(q, &chats) if err2 != nil { return } lastChat := chats[len(chats)-1] cc := ChatCursor{ Id: cu.Email, Cursor: lastChat.Id, ReadChats: readChats, } gn.Put(&cc) } cn := appengine.NewContext(r) urlfetchClient := urlfetch.Client(cn) client := pusher.Client{ AppId: "184465", Key: "e2aac50ec511259792df", Secret: "72f723c5a34c6d3efa0d", HttpClient: urlfetchClient, } client.Trigger("test_channel", "user_status_changed", "") }
func UpdateFeedLast(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) url := r.FormValue("feed") c.Debugf("update feed last %s", url) f := Feed{Url: url} if err := gn.Get(&f); err != nil { return } f.LastViewed = time.Now() gn.Put(&f) }
// Task used to subscribe a feed to push. func SubscribeFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) { start := time.Now() gn := goon.FromContext(c) f := Feed{Url: r.FormValue("feed")} fk := gn.Key(&f) s := "" defer func() { gn.Put(&Log{ Parent: fk, Id: time.Now().UnixNano(), Text: "SubscribeFeed - start " + start.String() + " - f.sub " + f.Subscribed.String() + " - " + s, }) }() if err := gn.Get(&f); err != nil { c.Errorf("%v: %v", err, f.Url) serveError(w, err) s += "err" return } else if f.IsSubscribed() { s += "is subscribed" return } u := url.Values{} u.Add("hub.callback", f.PubSubURL()) u.Add("hub.mode", "subscribe") u.Add("hub.verify", "sync") fu, _ := url.Parse(f.Url) fu.Fragment = "" u.Add("hub.topic", fu.String()) req, err := http.NewRequest("POST", f.Hub, strings.NewReader(u.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") cl := &http.Client{ Transport: &urlfetch.Transport{ Context: c, Deadline: time.Minute, }, } resp, err := cl.Do(req) if err != nil { c.Errorf("req error: %v", err) } else if resp.StatusCode != http.StatusNoContent { f.Subscribed = time.Now().Add(time.Hour * 48) gn.Put(&f) if resp.StatusCode != http.StatusConflict { c.Errorf("resp: %v - %v", f.Url, resp.Status) c.Errorf("%s", resp.Body) } s += "resp err" } else { c.Infof("subscribed: %v", f.Url) s += "success" } }
func LoginGoogle(c mpg.Context, w http.ResponseWriter, r *http.Request) { if cu := user.Current(c); cu != nil { gn := goon.FromContext(c) u := &User{Id: cu.ID} if err := gn.Get(u); err == datastore.ErrNoSuchEntity { u.Email = cu.Email u.Read = time.Now().Add(-time.Hour * 24) gn.Put(u) } } http.Redirect(w, r, routeUrl("main"), http.StatusFound) }
func CreateMessage(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) msg := Message{ Title: r.FormValue("title"), CreatedBy: r.FormValue("createdBy"), DateCreated: r.FormValue("dateCreated"), Channel: r.FormValue("channel"), DocId: r.FormValue("docId"), Content: r.FormValue("content"), } gn.Put(&msg) }
func AllFeedsOpml(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) q := datastore.NewQuery(gn.Kind(&Feed{})).KeysOnly() keys, _ := gn.GetAll(q, nil) fs := make([]*Feed, len(keys)) for i, k := range keys { fs[i] = &Feed{Url: k.StringID()} } b := feedsToOpml(fs) w.Header().Add("Content-Type", "text/xml") w.Header().Add("Content-Disposition", "attachment; filename=all.opml") w.Write(b) }
func includes(c mpg.Context, w http.ResponseWriter, r *http.Request) *Includes { i := &Includes{ Angular: Angular, BootstrapCss: BootstrapCss, BootstrapJs: BootstrapJs, FontAwesome: FontAwesome, Jquery: Jquery, JqueryUI: JqueryUI, Underscore: Underscore, MiniProfiler: c.Includes(), GoogleAnalyticsId: GOOGLE_ANALYTICS_ID, GoogleAnalyticsHost: GOOGLE_ANALYTICS_HOST, SubURL: subURL, IsDev: isDevServer, /* StripeKey: STRIPE_KEY, StripePlans: STRIPE_PLANS,*/ } session, _ := Store.Get(r, "pretlist-session") i.Session = session if cu := user.Current(c); cu != nil { gn := goon.FromContext(c) user := &User{Id: cu.ID} if err := gn.Get(user); err == nil { i.User = user i.IsAdmin = cu.Admin if len(user.Messages) > 0 { i.Messages = user.Messages user.Messages = nil gn.Put(user) } /* if _, err := r.Cookie("update-bug"); err != nil { i.Messages = append(i.Messages, "Go Read had some problems updating feeds. It may take a while for new stories to appear again. Sorry about that.") http.SetCookie(w, &http.Cookie{ Name: "update-bug", Value: "done", Expires: time.Now().Add(time.Hour * 24 * 7), }) } */ } } return i }
func Charge(c mpg.Context, w http.ResponseWriter, r *http.Request) { cu := user.Current(c) gn := goon.FromContext(c) u := User{Id: cu.ID} uc := &UserCharge{Id: 1, Parent: gn.Key(&u)} if err := gn.Get(&u); err != nil { serveError(w, err) return } else if u.Account != AFree { serveError(w, fmt.Errorf("You're already subscribed.")) return } if err := gn.Get(uc); err == nil && len(uc.Customer) > 0 { serveError(w, fmt.Errorf("You're already subscribed.")) return } else if err != datastore.ErrNoSuchEntity { serveError(w, err) return } resp, err := stripe(c, "POST", "customers", url.Values{ "email": {u.Email}, "description": {u.Id}, "card": {r.FormValue("token")}, "plan": {r.FormValue("plan")}, }.Encode()) if err != nil { serveError(w, err) return } else if resp.StatusCode != http.StatusOK { var se StripeError defer resp.Body.Close() b, _ := ioutil.ReadAll(resp.Body) if err := json.Unmarshal(b, &se); err == nil { serveError(w, fmt.Errorf(se.Error.Message)) } else { serveError(w, fmt.Errorf("Error")) } c.Errorf("status: %v, %s", resp.StatusCode, b) return } uc, err = setCharge(c, resp) if err != nil { serveError(w, err) return } b, _ := json.Marshal(&uc) w.Write(b) }
func GetMessages(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) q := datastore.NewQuery(gn.Kind(&Message{})) var msgs []Message _, err1 := gn.GetAll(q, &msgs) if err1 != nil { return } b, err2 := json.Marshal(msgs) if err2 != nil { return } w.Header().Set("Access-Control-Allow-Origin", "*") w.Write(b) }
func AdminFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) f := Feed{Url: r.FormValue("f")} if err := gn.Get(&f); err != nil { serveError(w, err) return } q := datastore.NewQuery(gn.Kind(&Story{})).KeysOnly() fk := gn.Key(&f) q = q.Ancestor(fk) q = q.Limit(100) q = q.Order("-" + IDX_COL) keys, _ := gn.GetAll(q, nil) stories := make([]*Story, len(keys)) for j, key := range keys { stories[j] = &Story{ Id: key.StringID(), Parent: fk, } } gn.GetMulti(stories) lk := gn.Key(&Log{Parent: fk, Id: time.Now().Add(-time.Hour * 6).UnixNano()}) q = datastore.NewQuery(lk.Kind()).KeysOnly() q = q.Ancestor(fk) q = q.Filter("__key__ >", lk) keys, _ = gn.GetAll(q, nil) logs := make([]*Log, len(keys)) for j, key := range keys { logs[j] = &Log{ Id: key.IntID(), Parent: fk, } } gn.GetMulti(logs) templates.ExecuteTemplate(w, "admin-feed.html", struct { Feed *Feed Logs []*Log Stories []*Story Now time.Time }{ &f, logs, stories, time.Now(), }) }
func AdminUser(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) q := datastore.NewQuery(gn.Kind(&User{})).Limit(1) q = q.Filter("e =", r.FormValue("u")) it := gn.Run(q) var u User ud := UserData{Id: "data"} var h []Log k, err := it.Next(&u) if err != nil { serveError(w, err) return } ud.Parent = gn.Key(&u) gn.Get(&ud) until := r.FormValue("until") if d, err := time.Parse("2006-01-02", until); err == nil { u.Until = d gn.Put(&u) } if o := []byte(r.FormValue("opml")); len(o) > 0 { opml := Opml{} if err := json.Unmarshal(o, &opml); err != nil { serveError(w, err) return } ud.Opml = o if _, err := gn.Put(&ud); err != nil { serveError(w, err) return } c.Infof("opml updated") } q = datastore.NewQuery(gn.Kind(&Log{})).Ancestor(k) _, err = gn.GetAll(q, &h) if err := templates.ExecuteTemplate(w, "admin-user.html", struct { User User Data UserData Log []Log }{ u, ud, h, }); err != nil { serveError(w, err) } }
func addFeed(c mpg.Context, userid string, outline *OpmlOutline) error { gn := goon.FromContext(c) o := outline.Outline[0] c.Infof("adding feed %v to user %s", o.XmlUrl, userid) fu, ferr := url.Parse(o.XmlUrl) if ferr != nil { return ferr } fu.Fragment = "" o.XmlUrl = fu.String() f := Feed{Url: o.XmlUrl} if err := gn.Get(&f); err == datastore.ErrNoSuchEntity { if feed, stories, err := fetchFeed(c, o.XmlUrl, o.XmlUrl); err != nil { return fmt.Errorf("could not add feed %s: %v", o.XmlUrl, err) } else { f = *feed f.Updated = time.Time{} f.Checked = f.Updated f.NextUpdate = f.Updated f.LastViewed = time.Now() gn.Put(&f) for _, s := range stories { s.Created = s.Published } if err := updateFeed(c, f.Url, feed, stories, false, false, false); err != nil { return err } o.XmlUrl = feed.Url o.HtmlUrl = feed.Link if o.Title == "" { o.Title = feed.Title } } } else if err != nil { return err } else { o.HtmlUrl = f.Link if o.Title == "" { o.Title = f.Title } } o.Text = "" return nil }
func GetChannels(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) q := datastore.NewQuery(gn.Kind(&Channel{})) var ch []Channel _, err1 := gn.GetAll(q, &ch) if err1 != nil { return } b, err2 := json.Marshal(ch) if err2 != nil { return } w.Write(b) }
func SubscribeCallback(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) furl := r.FormValue("feed") b, _ := base64.URLEncoding.DecodeString(furl) f := Feed{Url: string(b)} c.Infof("url: %v", f.Url) if err := gn.Get(&f); err != nil { http.Error(w, "", http.StatusNotFound) return } if r.Method == "GET" { if f.NotViewed() || r.FormValue("hub.mode") != "subscribe" || r.FormValue("hub.topic") != f.Url { http.Error(w, "", http.StatusNotFound) return } w.Write([]byte(r.FormValue("hub.challenge"))) i, _ := strconv.Atoi(r.FormValue("hub.lease_seconds")) f.Subscribed = time.Now().Add(time.Second * time.Duration(i)) gn.PutMulti([]interface{}{&f, &Log{ Parent: gn.Key(&f), Id: time.Now().UnixNano(), Text: "SubscribeCallback - subscribed - " + f.Subscribed.String(), }}) c.Debugf("subscribed: %v - %v", f.Url, f.Subscribed) return } else if !f.NotViewed() { c.Infof("push: %v", f.Url) gn.Put(&Log{ Parent: gn.Key(&f), Id: time.Now().UnixNano(), Text: "SubscribeCallback - push update", }) defer r.Body.Close() b, _ := ioutil.ReadAll(r.Body) nf, ss, err := ParseFeed(c, r.Header.Get("Content-Type"), f.Url, f.Url, b) if err != nil { c.Errorf("parse error: %v", err) return } if err := updateFeed(c, f.Url, nf, ss, false, true, false); err != nil { c.Errorf("push error: %v", err) } } else { c.Infof("not viewed") } }
func ExportOpml(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) var u User if uid := r.FormValue("u"); len(uid) != 0 && user.IsAdmin(c) { u = User{Id: uid} } else { cu := user.Current(c) u = User{Id: cu.ID} } ud := UserData{Id: "data", Parent: gn.Key(&u)} if err := gn.Get(&u); err != nil { serveError(w, err) return } gn.Get(&ud) downloadOpml(w, ud.Opml, u.Email) }
func UsersList(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) q := datastore.NewQuery(gn.Kind(&User{})) var us []User _, err1 := gn.GetAll(q, &us) if err1 != nil { return } b, err2 := json.Marshal(us) if err2 != nil { return } w.Header().Set("Access-Control-Allow-Origin", "*") w.Write(b) }
func (f *Feed) Subscribe(c appengine.Context) { if !f.IsSubscribed() { gn := goon.FromContext(c) gn.Put(&Log{ Parent: gn.Key(&f), Id: time.Now().UnixNano(), Text: fmt.Sprintf("Subscribe %v", f.Subscribed.String()), }) t := taskqueue.NewPOSTTask(routeUrl("subscribe-feed"), url.Values{ "feed": {f.Url}, }) if _, err := taskqueue.Add(c, t, "update-manual"); err != nil { c.Errorf("taskqueue error: %v", err.Error()) } else { c.Warningf("subscribe feed: %v", f.Url) } } }
func SaveOptions(c mpg.Context, w http.ResponseWriter, r *http.Request) { cu := user.Current(c) gn := goon.FromContext(c) gn.RunInTransaction(func(gn *goon.Goon) error { u := User{Id: cu.ID} if err := gn.Get(&u); err != nil { serveError(w, err) return nil } u.Options = r.FormValue("options") _, err := gn.PutMulti([]interface{}{&u, &Log{ Parent: gn.Key(&u), Id: time.Now().UnixNano(), Text: fmt.Sprintf("save options: %v", len(u.Options)), }}) return err }, nil) }