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
}
Beispiel #4
0
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
}
Beispiel #5
0
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
}
Beispiel #6
0
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
	}
}
Beispiel #7
0
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
}
Beispiel #8
0
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
}
Beispiel #9
0
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

}