func defaultPage(params martini.Params, u *user.User, c context.Context, w http.ResponseWriter, r *http.Request) { rpkey := model.NewRootPageKey(c) var rp model.RootPage if err := ds.Get(c, rpkey, &rp); err != nil { if errors.Root(err) == datastore.ErrNoSuchEntity { login(c, w, r) } else { handleError(c, w, err, http.StatusInternalServerError) } return } renderPage(strconv.FormatInt(rp.PageID, 10), u, c, w, r, false) }
func getPageAsync(c context.Context, key *datastore.Key) <-chan func() (*model.Page, error) { ch := make(chan func() (*model.Page, error)) go func() { var p model.Page if err := ds.Get(c, key, &p); err != nil { ch <- func() (*model.Page, error) { return nil, err } return } ch <- func() (*model.Page, error) { return &p, nil } }() return ch }
func getHTMLBlockAsync(c context.Context, key *datastore.Key) <-chan func() (*model.HTMLBlock, error) { ch := make(chan func() (*model.HTMLBlock, error)) go func() { var block model.HTMLBlock if err := ds.Get(c, key, &block); err != nil { ch <- func() (*model.HTMLBlock, error) { return nil, err } return } ch <- func() (*model.HTMLBlock, error) { return &block, nil } }() return ch }
func queryPages(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) rpkey := model.NewRootPageKey(c) var rp model.RootPage err := ds.Get(c, rpkey, &rp) if err != nil && errors.Root(err) != datastore.ErrNoSuchEntity { handleError(c, w, err, http.StatusInternalServerError) return } pages, err := getPages(c) if err != nil { log.Errorf(c, err.Error()) handleError(c, w, err, http.StatusInternalServerError) return } jsonPages := make([]*model.JsonPage, len(pages)) for i, p := range pages { jsonPages[i] = model.NewJsonPage(p, nil, p.Key.IntID() == rp.PageID) } b, err := json.Marshal(jsonPages) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") _, err = w.Write(b) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } return }
func queryProperty(params martini.Params, w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) pn := params["name"] if pn != "" { // get property from datastore c := appengine.NewContext(r) gpageKey := model.NewGlobalPageKey(c) gpropKey := model.NewPagePropertyKey(c, pn, gpageKey) var gpp model.PageProperty err := ds.Get(c, gpropKey, &gpp) if err != nil && errors.Root(err) != datastore.ErrNoSuchEntity { handleError(c, w, err, http.StatusInternalServerError) return } b, err := json.Marshal(gpp) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } responseJson := string(b) w.Header().Set("Content-Type", "application/json; charset=utf-8") _, err = w.Write([]byte(responseJson)) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } } else { handleError(c, w, errors.New("error: property name not found."), http.StatusInternalServerError) return } return }
func putRootPage(c context.Context, w http.ResponseWriter, r *http.Request) { var rp model.RootPage if err := getRequestJson(w, r, &rp); err != nil { handleError(c, w, err, http.StatusBadRequest) return } if rp.PageID == 0 { handleError(c, w, errors.New("pageID not found."), http.StatusBadRequest) return } rootPageKey := model.NewRootPageKey(c) err := datastore.RunInTransaction(c, func(c context.Context) error { var rp2put model.RootPage if err := ds.Get(c, rootPageKey, &rp2put); err != nil { if errors.Root(err) != datastore.ErrNoSuchEntity { return err } else { rp2put = model.RootPage{} rp2put.Key = rootPageKey } } if rp2put.PageID != rp.PageID { rp2put.PageID = rp.PageID ds.Put(c, &rp2put) } return nil }, nil) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } }
func getPage(params martini.Params, w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) if r.Method != "GET" { handleError(c, w, errors.New("error: illegal access."), http.StatusInternalServerError) return } keyIDStr := params["key"] if keyIDStr != "" { // get page from datastore intID, err := strconv.ParseInt(keyIDStr, 10, 64) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } key := model.NewPageKey(c, intID) var p model.Page if err := ds.Get(c, key, &p); err != nil { handleError(c, w, err, http.StatusInternalServerError) return } props, err := getPageProperties(c, key) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } rpkey := model.NewRootPageKey(c) var rp model.RootPage err = ds.Get(c, rpkey, &rp) if err != nil && errors.Root(err) != datastore.ErrNoSuchEntity { handleError(c, w, err, http.StatusInternalServerError) return } jsonPage := model.NewJsonPage(&p, props, intID == rp.PageID) b, err := json.Marshal(jsonPage) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } pageJson := string(b) w.Header().Set("Content-Type", "application/json; charset=utf-8") _, err = w.Write([]byte(pageJson)) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } } else { handleError(c, w, errors.New("error: key string not found."), http.StatusInternalServerError) return } return }
func savePage(c context.Context, pageKey *datastore.Key, p *model.Page, props map[string]string) error { err := datastore.RunInTransaction(c, func(c context.Context) error { var p2 model.Page if !pageKey.Incomplete() { if err := ds.Get(c, pageKey, &p2); err != nil { if errors.Root(err) != datastore.ErrNoSuchEntity { return errors.WrapOr(err) } else { p2 = model.Page{} p2.Key = pageKey } } } oldAlias := p2.Alias p2.Name = p.Name p2.Alias = p.Alias p2.Leaf = p.Leaf aliasChanged := oldAlias != p2.Alias if err := ds.Put(c, &p2); err != nil { return errors.WrapOr(err) } if aliasChanged && p2.Alias != "" { newAliasKey := model.NewPageAliasKey(c, p2.Alias) var pa model.PageAlias if err := ds.Get(c, newAliasKey, &pa); err == nil { return ErrPageAliasAlreadyExists } else { newAlias := model.PageAlias{} newAlias.Key = newAliasKey newAlias.PageKey = pageKey if err := ds.Put(c, &newAlias); err != nil { return errors.WrapOr(err) } } } if aliasChanged && oldAlias != "" { oldAliasKey := model.NewPageAliasKey(c, oldAlias) err := ds.Delete(c, oldAliasKey) if err != nil { return err } } // TODO: put multi for name, value := range props { initial, _ := utf8.DecodeRune([]byte(name)) global := unicode.IsUpper(initial) var pkey *datastore.Key if global { pkey = model.NewGlobalPageKey(c) } else { pkey = pageKey } propKey := model.NewPagePropertyKey(c, name, pkey) var prop model.PageProperty err := ds.Get(c, propKey, &prop) if err == nil { prop.Key = propKey prop.Value = value } if err != nil { if errors.Root(err) != datastore.ErrNoSuchEntity { return errors.WrapOr(err) } else { prop = model.PageProperty{} prop.Key = propKey prop.Value = value } } if err = ds.Put(c, &prop); err != nil { return errors.WrapOr(err) } } return nil }, &datastore.TransactionOptions{XG: true}) if err != nil { return err } return nil }
func saveBlocks(params martini.Params, w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) keyIDStr := params["key"] if keyIDStr == "" { handleError(c, w, errors.New("the pageID not found."), http.StatusInternalServerError) return } var b map[string]interface{} if err := getRequestJson(w, r, &b); err != nil { handleError(c, w, err, http.StatusBadRequest) return } intID, err := strconv.ParseInt(keyIDStr, 10, 64) if err != nil { handleError(c, w, err, http.StatusBadRequest) return } pageKey := model.NewPageKey(c, intID) var p model.Page if err := ds.Get(c, pageKey, &p); err != nil { handleError(c, w, err, http.StatusBadRequest) return } err = datastore.RunInTransaction(c, func(c context.Context) error { // TODO make async for id, value := range b { log.Infof(c, "saving block. id:%s, value:%s", id, value) r := regexp.MustCompile(`^ctpl_block:(true|false):(\w+):(\d+)$`) gr := r.FindStringSubmatch(id) if gr == nil { return errors.New("illegal block id:" + id) } global, err := strconv.ParseBool(gr[1]) if err != nil { return err } areaID := gr[2] strBlockID := gr[3] blockID, err := strconv.ParseInt(strBlockID, 10, 64) if err != nil { return err } var pkey *datastore.Key if global { pkey = model.NewGlobalPageKey(c) } else { pkey = pageKey } akey := model.NewAreaKey(c, areaID, pkey) bkey := model.NewHTMLBlockKey(c, blockID, akey) var block model.HTMLBlock if err := ds.Get(c, bkey, &block); err != nil { return errors.WrapOr(err) } var ok bool block.Value, ok = value.(string) if !ok { return errors.New( fmt.Sprintf("illegal block value type :%T", value)) } if err = ds.Put(c, &block); err != nil { return errors.WrapOr(err) } // save backup entity when HTMLBlock saved. blocks := []*model.HTMLBlock{&block} backupHTMLBlockFunc.Call(c, uuid.New(), blocks) } return nil }, &datastore.TransactionOptions{XG: true}) if err != nil { handleError(c, w, err, http.StatusInternalServerError) return } }
func getAreaAndBlocksAsync(c context.Context, pkey *datastore.Key, areaName string) <-chan func() (*model.Area, []model.Block, error) { ch := make(chan func() (*model.Area, []model.Block, error)) go func() { var area model.Area area.Key = model.NewAreaKey(c, areaName, pkey) err := datastore.RunInTransaction(c, func(c context.Context) error { err := ds.Get(c, area.Key, &area) if err != nil && errors.Root(err) == datastore.ErrNoSuchEntity { // create if not exist // TODO make blocks extendable & initially zero blocks var block model.HTMLBlock block.Key = model.NewHTMLBlockIncompleteKey(c, area.Key) block.Value = areaName + ": edit here." err = ds.Put(c, &block) if err != nil { return err } area.Name = areaName area.Blocks = []*datastore.Key{block.Key} err = ds.Put(c, &area) if err != nil { return err } } return err }, nil) if err != nil { ch <- func() (*model.Area, []model.Block, error) { return nil, nil, err } return } blockChans := []<-chan func() (*model.HTMLBlock, error){} for _, blockKey := range area.Blocks { blockCh := getHTMLBlockAsync(c, blockKey) blockChans = append(blockChans, blockCh) } blocks := []model.Block{} for _, blockCh := range blockChans { b, err := (<-blockCh)() if err != nil { // TODO block not found blocks = append(blocks, &model.HTMLBlock{Value: "block not found."}) } else { blocks = append(blocks, b) } } ch <- func() (*model.Area, []model.Block, error) { return &area, blocks, nil } }() return ch }
func renderPage(id string, u *user.User, c context.Context, w http.ResponseWriter, r *http.Request, edit bool) { var pageKey *datastore.Key pID, err := strconv.ParseInt(id, 10, 64) if err != nil { // check alias paKey := model.NewPageAliasKey(c, id) var pa model.PageAlias if err := ds.Get(c, paKey, &pa); err != nil { handleError(c, w, err, http.StatusNotFound) return } pID = pa.PageKey.IntID() pageKey = pa.PageKey } else { pageKey = model.NewPageKey(c, pID) } var p model.Page if err := ds.Get(c, pageKey, &p); err != nil { handleError(c, w, err, http.StatusNotFound) return } if edit { if u == nil { loginURL, _ := user.LoginURL(c, p.URLBase()+common.EDIT_PAGE_EXT) http.Redirect(w, r, loginURL, http.StatusFound) return } else if !u.Admin { // TODO: prepare error page http.Redirect(w, r, "/caterpillar/error/invalidUser", http.StatusFound) return } } leaf := leaves[p.Leaf] if leaf == nil { errmsg := fmt.Sprintf("leaf not found:" + p.Leaf) handleError(c, w, errors.New(errmsg), http.StatusNotFound) return } tparam := struct { Properties map[string]interface{} Areas map[string]interface{} User *user.User Edit bool PageID int64 ViewURL string EditURL string PagesURL string LogoutURL string }{ make(map[string]interface{}), make(map[string]interface{}), u, edit, pID, "", "", "", "", } if u != nil { tparam.PagesURL = "/caterpillar/static/mng/#/queryPage" purl := p.URLBase() tparam.ViewURL = purl + common.VIEW_PAGE_EXT tparam.EditURL = purl + common.EDIT_PAGE_EXT logoutURL, err := user.LogoutURL(c, tparam.ViewURL) if err != nil { // only log applog.Warningf(c, "cannot get logoutURL. err:%v", err) } tparam.LogoutURL = logoutURL } futureProps := make(map[string]<-chan func() (*model.PageProperty, error)) futureAreas := make(map[string]<-chan func() (*model.Area, []model.Block, error)) for _, hole := range leaf.Wormholes { var pkey *datastore.Key if hole.Global { pkey = model.NewGlobalPageKey(c) } else { pkey = pageKey } switch hole.Type { case PROPERTY: propkey := model.NewPagePropertyKey(c, hole.Name, pkey) ch := getPagePropertyAsync(c, propkey) futureProps[hole.Name] = ch case AREA: ch := getAreaAndBlocksAsync(c, pkey, hole.Name) futureAreas[hole.Name] = ch } } pageURLs := make(map[string]string) for _, hole := range leaf.Wormholes { switch hole.Type { case PROPERTY: prop, err := (<-futureProps[hole.Name])() if err == nil { tparam.Properties[hole.Name] = prop.Value } else { // TODO handle error applog.Errorf(c, "%s", err) } case AREA: area, blocks, err := (<-futureAreas[hole.Name])() if err == nil { areasrc := renderArea(hole.Global, area, blocks, edit) if !edit { futurePages := make(map[string]<-chan func() (*model.Page, error)) urls := pageUrlRegex.FindAllStringSubmatch(areasrc, -1) for _, url := range urls { purl := url[0] if _, exists := pageURLs[purl]; exists { continue } if _, exists := futurePages[purl]; exists { continue } pageID, err := strconv.ParseInt(url[1], 10, 64) if err != nil { // TODO handle error applog.Errorf(c, "%s", err) continue } pkey := model.NewPageKey(c, pageID) futurePages[purl] = getPageAsync(c, pkey) } for purl, ch := range futurePages { page, err := (<-ch)() if err != nil { // TODO handle error applog.Errorf(c, "%s", err) continue } pageURLs[purl] = page.URLBase() + common.VIEW_PAGE_EXT } areasrc = pageUrlRegex.ReplaceAllStringFunc(areasrc, func(s string) string { if r, true := pageURLs[s]; true { return r } return s }) } tparam.Areas[hole.Name] = template.HTML(areasrc) } else { // TODO handle error applog.Errorf(c, "%s", err) } } } // TODO: validate reserved page name property // or put some prefix? tparam.Properties["name"] = p.Name // TODO: resolve charset from somewhere w.Header().Set("Content-Type", "text/html; charset=utf-8") if err = leaf.Template.Execute(w, tparam); err != nil { handleError(c, w, err, http.StatusInternalServerError) return } return }