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 handleRegisterUser(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "リクエストエラー", http.StatusBadRequest) return } c := appengine.NewContext(r) g := goon.FromContext(c) var user User defer r.Body.Close() body, err := ioutil.ReadAll(r.Body) c.Infof("body:%s", string(body)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if err := JsonToStruct(w, body, &user); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } c.Infof("id:%s,name:%s,age:%d,password:%v", user.Id, user.Name, user.Age, user.Password) if !checkUserId(c, user.Id) { http.Error(w, "既に同じユーザーIDが登録されています", http.StatusInternalServerError) return } user.Password = PasswordToHash(user.Password) g.Put(&user) w.Header().Set("Content-Type", "application/json;charset=utf-8") w.Write(StructToJson(w, user)) }
func CFix(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) url := r.FormValue("feed") c.Infof("fix feed %s", url) f := Feed{Url: url} if err := gn.Get(&f); err != nil { c.Criticalf("cfix err: %v", err) serveError(w, err) return } q := datastore.NewQuery("S").Ancestor(gn.Key(&f)) var ss []*Story keys, err := q.GetAll(c, &ss) if err != nil { c.Errorf("getall err: %v", err) serveError(w, err) return } c.Infof("trying to fix %v stories", len(ss)) const putLimit = 500 for i := 0; i <= len(keys)/putLimit; i++ { lo := i * putLimit hi := (i + 1) * putLimit if hi > len(keys) { hi = len(keys) } c.Infof("%v - %v", lo, hi) if _, err := datastore.PutMulti(c, keys[lo:hi], ss[lo:hi]); err != nil { c.Errorf("err: %v, %v, %v", lo, hi, err) } } }
func SubscribeCallback(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) vars := mux.Vars(r) b, _ := base64.URLEncoding.DecodeString(vars["feed"]) f := Feed{Url: string(b)} if err := gn.Get(&f); err != nil { http.Error(w, "", http.StatusNotFound) return } if r.Method == "GET" { if 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.Put(&f) c.Debugf("subscribed: %v - %v", f.Url, f.Subscribed) return } else { c.Infof("push: %v", f.Url) defer r.Body.Close() b, _ := ioutil.ReadAll(r.Body) nf, ss := ParseFeed(c, f.Url, b) err := updateFeed(c, f.Url, nf, ss) if err != nil { c.Errorf("push error: %v", err) } } }
func GetContents(c mpg.Context, w http.ResponseWriter, r *http.Request) { var reqs []struct { Feed string Story string } 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 Update(c mp.Context, w http.ResponseWriter, r *http.Request) error { vars := mux.Vars(r) id, err := strconv.ParseInt(vars["songId"], 10, 64) if err != nil { return err } n := goon.FromContext(c) s := &data.Song{Id: id} if err := n.Get(s); err != nil { return err } cpm := r.FormValue("cpm") songs, _, err := chordpro.Parse(strings.NewReader(cpm)) if err != nil { return err } copySong(&songs[0], s) s.ChordPro = cpm if _, err = n.Put(s); err != nil { return err } if err = putSongDoc(c, s.Id, &songs[0]); err != nil { return err } http.Redirect(w, r, fmt.Sprintf("/songs/%d", s.Id), http.StatusFound) return nil }
func doUncheckout(c mpg.Context) (*UserCharge, error) { 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 { return nil, err } if err := gn.Get(&uc); err != nil || len(uc.Customer) == 0 { return nil, err } resp, err := stripe(c, "DELETE", "customers/"+uc.Customer, "") if err != nil { return nil, err } else if resp.StatusCode != http.StatusOK { c.Errorf("%s", resp.Body) c.Errorf("stripe delete error, but proceeding") } if err := gn.RunInTransaction(func(gn *goon.Goon) error { if err := gn.Get(&u); err != nil && err != datastore.ErrNoSuchEntity { return err } u.Account = AFree u.Until = uc.Next if err := gn.Delete(gn.Key(&uc)); err != nil { return err } _, err := gn.Put(&u) return err }, nil); err != nil { return nil, err } return &uc, 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 UpdateFeeds(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) q := datastore.NewQuery(gn.Key(&Feed{}).Kind()).KeysOnly() q = q.Filter("n <=", time.Now()).Limit(3000) it := gn.Run(q) i := 0 for { k, err := it.Next(nil) if err == datastore.Done { break } else if err != nil { c.Errorf("next error: %v", err.Error()) break } t := taskqueue.NewPOSTTask(routeUrl("update-feed"), url.Values{ "feed": {k.StringID()}, }) if _, err := taskqueue.Add(c, t, "update-feed"); err != nil { c.Errorf("taskqueue error: %v", err.Error()) } i++ } c.Infof("updating %d feeds", i) fmt.Fprintf(w, "updating %d feeds", i) }
func includes(c mpg.Context, r *http.Request) *Includes { i := &Includes{ Angular: Angular, BootstrapCss: BootstrapCss, BootstrapJs: BootstrapJs, Jquery: Jquery, Underscore: Underscore, MiniProfiler: c.Includes(r), GoogleAnalyticsId: GOOGLE_ANALYTICS_ID, GoogleAnalyticsHost: GOOGLE_ANALYTICS_HOST, IsDev: isDevServer, } 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) } } } return i }
func TestBuildPageHandler(t *testing.T) { opt := &aetest.Options{AppID: "unittest", StronglyConsistentDatastore: true} user := &user.User{Email: "*****@*****.**", Admin: true, ID: "1234567890"} inst, err := aetest.NewInstance(opt) if err != nil { t.Fatalf(err.Error()) } defer inst.Close() request, err := inst.NewRequest("POST", "/build", strings.NewReader("Name=ABC&chip=true&fox=true&hour=3&minute=0")) if err != nil { t.Fatalf("Failed to create request: %v", err) } request.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") aetest.Login(user, request) recoder := httptest.NewRecorder() buildPageHandler(recoder, request) if recoder.Code != 302 { b, _ := ioutil.ReadAll(recoder.Body) t.Fatalf("unexpected %d, expected 302, body=%s", recoder.Code, string(b)) } context := appengine.NewContext(request) g := goon.FromContext(context) v := Village{No: 1} if err := g.Get(&v); err != nil { t.Fatalf("unexpected err, expected Get Village{No: 1}") } if v.Name != "ABC" || v.Chip == false || v.IncludeFox == false || v.UpdatetimeHour != 3 || v.UpdatetimeMinute != 0 { t.Fatalf("Failed: Cant't get correct Data from datastore") } }
func (t *testSuite) TestGame() { c, err := aetest.NewContext(nil) if c != nil { defer c.Close() } if err != nil { t.Error("Could not start aetest - %v", err) return } g := goon.FromContext(c) game := NewGame(4) game.Dealer = 0 game.Players[1] = createAI() game.Players[1].SetHand(g, c, game, Hand{KD, QD, JD, JD, ND, TC, KC, QC, KH, NH, QS, NS}, 0, 1) game.Players[2] = createAI() game.Players[2].SetHand(g, c, game, Hand{AD, AD, KD, ND, NC, NC, TH, JH, AS, JS, JS, NS}, 0, 2) game.Players[3] = createAI() game.Players[3].SetHand(g, c, game, Hand{AC, AC, KC, JC, JC, TH, QH, QH, JH, AS, TS, KS}, 0, 3) game.Players[0] = createAI() game.Players[0].SetHand(g, c, game, Hand{TD, TD, QD, TC, QC, AH, AH, KH, NH, TS, KS, QS}, 0, 0) game.Meld = make([]uint8, len(game.Players)/2) game.CountMeld = make([]bool, len(game.Players)/2) game.Counters = make([]uint8, len(game.Players)/2) game.HighBid = 20 game.HighPlayer = game.Dealer game.State = StateBid game.Next = game.Dealer //oright = game.Players[0].(*AI).HT //Log(oright.Owner, "Start of game hands") //oright.Debug() game.inc() // so dealer's not the first to bid game.processAction(g, c, nil, game.Players[game.Next].Tell(nil, nil, game, CreateBid(0, game.Next))) t.True(true) // just getting to the end successfully counts! }
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 } 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 handleUploadImage(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "リクエストエラー", http.StatusBadRequest) return } c := appengine.NewContext(r) g := goon.FromContext(c) key := r.FormValue("key") blobs, _, err := blobstore.ParseUpload(r) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } image := blobs["uploadFile"] if len(image) == 0 { http.Error(w, "画像がありません", http.StatusInternalServerError) return } ibi := ImageBlobInfo{ Id: key, BlobKey: string(image[0].BlobKey), } // keys := datastore.NewIncompleteKey(c, "blobInfo", nil) // _, err = datastore.Put(c, keys, &ibInfo) g.Put(&ibi) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Write([]byte(ibi.BlobKey)) }
func updateHandler(w http.ResponseWriter, r *http.Request) { c := gae.NewContext(r) g := goon.FromContext(c) if r.Header.Get("X-AppEngine-Cron") != "true" { http.NotFound(w, r) return } var ups []UpdateSchedule if cache, err := memcache.Get(c, "UpdateSchedule"); err == memcache.ErrCacheMiss { q := datastore.NewQuery("UpdateSchedule") if _, err := g.GetAll(q, &ups); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if val, err := serialize(&ups); err == nil { item := memcache.Item{Key: "UpdateSchedule", Value: val, Expiration: time.Hour * 12} memcache.Add(c, &item) } } else if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else { deserialize(cache.Value, &ups) } now := time.Now() for _, us := range ups { if us.IsUpdateTime(now) { if err := updateVillage(us, g); err != nil { c.Debugf("%v", err) } memcache.Delete(c, "UpdateSchedule") } } return }
func UploadOpml(c mpg.Context, w http.ResponseWriter, r *http.Request) { opml := Opml{} if err := json.Unmarshal([]byte(r.FormValue("opml")), &opml.Outline); err != nil { serveError(w, err) return } backupOPML(c) cu := user.Current(c) gn := goon.FromContext(c) u := User{Id: cu.ID} ud := UserData{Id: "data", Parent: gn.Key(&u)} if err := gn.Get(&ud); err != nil { serveError(w, err) c.Errorf("get err: %v", err) return } if b, err := json.Marshal(&opml); err != nil { saveError(c, fmt.Sprintf("%v", opml), err) serveError(w, err) c.Errorf("json err: %v", err) return } else { ud.Opml = b } gn.Put(&ud) backupOPML(c) }
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 if r.FormValue("stories") != "" { json.Unmarshal([]byte(r.FormValue("stories")), &stories) } if r.FormValue("feed") != "" { stories = append(stories, readStory{ Feed: r.FormValue("feed"), Story: r.FormValue("story"), }) } gn.RunInTransaction(func(gn *goon.Goon) error { u := &User{Id: cu.ID} ud := &UserData{ Id: "data", Parent: gn.Key(u), } gn.Get(ud) 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 UpdateFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) url := r.FormValue("feed") c.Debugf("update feed %s", url) f := Feed{Url: url} if err := gn.Get(&f); err == datastore.ErrNoSuchEntity { return } else if time.Now().Before(f.NextUpdate) { c.Infof("feed %v already updated", url) return } if feed, stories := fetchFeed(c, url, url); feed != nil { updateFeed(c, url, feed, stories) } else { 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", url, f.Errors, f.NextUpdate) } }
func ExportOpml(c mpg.Context, w http.ResponseWriter, r *http.Request) { cu := user.Current(c) gn := goon.FromContext(c) u := User{Id: cu.ID} ud := UserData{Id: "data", Parent: gn.Key(&User{Id: cu.ID})} if err := gn.Get(&u); err != nil { serveError(w, err) return } gn.Get(&ud) opml := Opml{} json.Unmarshal(ud.Opml, &opml) opml.Version = "1.0" opml.Title = fmt.Sprintf("%s subscriptions in Go Read", u.Email) for _, o := range opml.Outline { o.Text = o.Title if len(o.XmlUrl) > 0 { o.Type = "rss" } for _, so := range o.Outline { so.Text = so.Title so.Type = "rss" } } b, _ := xml.MarshalIndent(&opml, "", "\t") w.Header().Add("Content-Type", "text/xml") w.Header().Add("Content-Disposition", "attachment; filename=subscriptions.opml") fmt.Fprint(w, xml.Header, string(b)) }
func Donate(c mpg.Context, w http.ResponseWriter, r *http.Request) { cu := user.Current(c) gn := goon.FromContext(c) u := User{Id: cu.ID} if err := gn.Get(&u); err != nil { serveError(w, err) return } amount, err := strconv.Atoi(r.FormValue("amount")) if err != nil || amount < 200 { serveError(w, fmt.Errorf("bad amount: %v", r.FormValue("amount"))) return } resp, err := stripe(c, "POST", "charges", url.Values{ "amount": {r.FormValue("amount")}, "description": {fmt.Sprintf("%v - %v", u.Id, u.Email)}, "card": {r.FormValue("stripeToken")}, "currency": {"usd"}, }.Encode()) if err != nil { serveError(w, err) return } else if resp.StatusCode != http.StatusOK { c.Errorf("%s", resp.Body) serveError(w, fmt.Errorf("Error")) return } }
func Create(c mp.Context, w http.ResponseWriter, r *http.Request) error { cpm := r.FormValue("cpm") songs, _, err := chordpro.Parse(strings.NewReader(cpm)) if err != nil { return err } n := goon.FromContext(c) s := &data.Song{ Title: songs[0].Title, Artists: songs[0].Artists, Authors: songs[0].Authors, Compositors: songs[0].Compositors, Album: songs[0].Album, Year: songs[0].Year, ChordPro: cpm, } if _, err = n.Put(s); err != nil { return err } if err = putSongDoc(c, s.Id, &songs[0]); err != nil { return err } http.Redirect(w, r, fmt.Sprintf("/songs/%d", s.Id), http.StatusFound) return nil }
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{ &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.PutMany(&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 AdminUser(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) q := datastore.NewQuery(gn.Key(&User{}).Kind()).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 { q = datastore.NewQuery(gn.Key(&Log{}).Kind()).Ancestor(k) _, err = gn.GetAll(q, &h) ud.Parent = gn.Key(&u) gn.Get(&ud) } if err := templates.ExecuteTemplate(w, "admin-user.html", struct { User User Data UserData Log []Log Error error }{ u, ud, h, err, }); err != nil { serveError(w, err) } }
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 AdminFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) f := Feed{Url: r.FormValue("f")} gn.Get(&f) q := datastore.NewQuery(gn.Key(&Story{}).Kind()).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) f.Subscribe(c) templates.ExecuteTemplate(w, "admin-feed.html", struct { Feed *Feed Stories []*Story Now time.Time }{ &f, stories, time.Now(), }) }
func UploadOpml(c mpg.Context, w http.ResponseWriter, r *http.Request) { opml := Opml{} if err := json.Unmarshal([]byte(r.FormValue("opml")), &opml.Outline); err != nil { serveError(w, err) return } backupOPML(c) cu := user.Current(c) gn := goon.FromContext(c) u := User{Id: cu.ID} ud := UserData{Id: "data", Parent: gn.Key(&u)} if err := gn.Get(&ud); err != nil { serveError(w, err) c.Errorf("get err: %v", err) return } if b, err := json.Marshal(&opml); err != nil { serveError(w, err) c.Errorf("json err: %v", err) return } else { l := Log{ Parent: ud.Parent, Id: time.Now().UnixNano(), Text: fmt.Sprintf("upload opml: %v -> %v", len(ud.Opml), len(b)), } ud.Opml = b if _, err := gn.PutMany(&ud, &l); err != nil { serveError(w, err) return } backupOPML(c) } }
func SubscribeFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) { gn := goon.FromContext(c) f := Feed{Url: r.FormValue("feed")} if err := gn.Get(&f); err != nil { c.Errorf("%v: %v", err, f.Url) serveError(w, err) return } else if f.IsSubscribed() { 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", PUBSUBHUBBUB_HUB, strings.NewReader(u.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") cl := urlfetch.Client(c) resp, err := cl.Do(req) if err != nil { c.Errorf("req error: %v", err) } else if resp.StatusCode != 204 { c.Errorf("resp: %v - %v", f.Url, resp.Status) c.Errorf("%s", resp.Body) } else { c.Infof("subscribed: %v", f.Url) } }
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.Key(&UserOpml{}).Kind()).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 (p *Person) Save(c appengine.Context) error { g := goon.FromContext(c) if _, err := g.Put(p); err != nil { return err } return nil }
func DeleteOldFeed(c mpg.Context, w http.ResponseWriter, r *http.Request) { g := goon.FromContext(c) oldDate := time.Now().Add(-time.Hour * 24 * 90) feed := Feed{Url: r.FormValue("f")} if err := g.Get(&feed); err != nil { c.Criticalf("err: %v", err) return } if feed.LastViewed.After(oldDate) { return } q := datastore.NewQuery(g.Kind(&Story{})).Ancestor(g.Key(&feed)).KeysOnly() keys, err := q.GetAll(c, nil) if err != nil { c.Criticalf("err: %v", err) return } q = datastore.NewQuery(g.Kind(&StoryContent{})).Ancestor(g.Key(&feed)).KeysOnly() sckeys, err := q.GetAll(c, nil) if err != nil { c.Criticalf("err: %v", err) return } keys = append(keys, sckeys...) c.Infof("delete: %v - %v", feed.Url, len(keys)) err = g.DeleteMulti(keys) if err != nil { c.Criticalf("err: %v", err) } }