• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

Golang appengine.NewContext函数代码示例

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

本文整理汇总了Golang中google/golang.org/appengine.NewContext函数的典型用法代码示例。如果您正苦于以下问题:Golang NewContext函数的具体用法?Golang NewContext怎么用?Golang NewContext使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。



在下文中一共展示了NewContext函数的20个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于我们的系统推荐出更棒的Golang代码示例。

示例1: handlePut

func handlePut(res http.ResponseWriter, req *http.Request) {
	ctx := appengine.NewContext(req)

	hc := &http.Client{
		Transport: &oauth2.Transport{
			Source: google.AppEngineTokenSource(ctx, storage.ScopeFullControl),
			Base:   &urlfetch.Transport{Context: ctx},
		},
	}

	cctx := cloud.NewContext(appengine.AppID(ctx), hc)

	rdr, hdr, err := req.FormFile("form-upload-file")
	if err != nil {
		http.Error(res, "ERROR RECEIVING FILE: "+err.Error(), 500)
		return
	}

	writer := storage.NewWriter(cctx, bucketName, hdr.Filename)
	io.Copy(writer, rdr)

	err = writer.Close()
	if err != nil {
		http.Error(res, "ERROR WRITING TO BUCKET: "+err.Error(), 500)
		return
	}
}
开发者ID:kaveenherath,项目名称:SummerBootCamp,代码行数:27,代码来源:storage.go


示例2: index

func index(res http.ResponseWriter, req *http.Request) {
	html := ``
	//Build Cookie
	id, _ := uuid.NewV4()
	cookie := &http.Cookie{
		Name:     "my-cookie",
		Value:    id.String(),
		HttpOnly: true,
	}
	http.SetCookie(res, cookie)

	//Store memcache
	ctx := appengine.NewContext(req)
	item := memcache.Item{
		Key:   id.String(),
		Value: []byte("Matthew"),
	}
	memcache.Set(ctx, &item)

	//Get uuid from cookie
	cookieGet, _ := req.Cookie("my-cookie")
	if cookieGet != nil {
		html += `UUID from cookie: ` + cookieGet.Value + `<br>`
	}

	//Get uuid and value from memcache
	ctx = appengine.NewContext(req)
	item0, _ := memcache.Get(ctx, id.String())
	if item0 != nil {
		html += `Value from Memcache using uuid: ` + string(item0.Value) + `<br>`
	}
	res.Header().Set("Content-Type", "text/html; charset=utf-8")
	fmt.Fprint(res, html)

}
开发者ID:shadon09,项目名称:CSCI130,代码行数:35,代码来源:hello.go


示例3: APIHandler

// APIHandler for addrshrt API
func APIHandler(w http.ResponseWriter, r *http.Request) {
	apiRequest := models.ParseAPIRequestURL(*r.URL)

	// method router
	switch apiRequest.Method {
	case "create":
		// create our short URL
		base64encoded, _ := url.QueryUnescape(apiRequest.Query)
		shortURL := models.DecodeAndShortenURL(string(base64encoded))

		// store it
		c := appengine.NewContext(r)
		err := storeShortURL(c, shortURL)
		if err != nil {
			fmt.Fprint(w, err.Error())
		}

		// respond with the UUID
		w.Header().Add("Content-Type", "text/plain")
		fmt.Fprintf(w, shortURL.ShortURL)

		return
	case "read":
		c := appengine.NewContext(r)
		surl, err := retrieveShortURL(c, apiRequest.Query)
		if err != nil {
			fmt.Fprint(w, err.Error())
		}
		w.Header().Add("Content-Type", "text/plain")
		fmt.Fprintf(w, surl.OriginalURL)
		return
	}

}
开发者ID:bytetrip,项目名称:addrshrt,代码行数:35,代码来源:ApiHandler.go


示例4: FetchRatesJob

// FetchRatesJob - method get rates data from open suorce
func FetchRatesJob(w http.ResponseWriter, r *http.Request) {
	_, success, err := updateFromSource2ForAppEngine(r)
	if err != nil {
		if r != nil {
			ctx := appengine.NewContext(r)
			logAE.Errorf(ctx, "FetchRatesJob Fatal: %v", err)
			w.Header().Set("Cache-Control", "no-cache")
			http.Error(w, fmt.Sprintf("FetchRatesJob Fatal: %v", err), http.StatusInternalServerError)
		} else {
			log.Fatal(err)
		}
		return
	}

	if success == false {
		if r != nil {
			ctx := appengine.NewContext(r)
			logAE.Errorf(ctx, "FetchRatesJob Error")
			w.Header().Set("Cache-Control", "no-cache")
			http.Error(w, "FetchRatesJob Error", http.StatusInternalServerError)
		}
		return
	}
	executeDomainLogic(w, r)
}
开发者ID:rutmir,项目名称:pr.optima,代码行数:26,代码来源:fetchRatesJob.go


示例5: init

// init is called before the application starts.
func init() {

	m := martini.Classic()
	m.Use(render.Renderer())

	m.Use(func(res http.ResponseWriter, req *http.Request) {
		authorization := &spark.Authorization{AccessToken: os.Getenv("SPARK_TOKEN")}
		spark.InitClient(authorization)
		ctx := appengine.NewContext(req)
		spark.SetHttpClient(urlfetch.Client(ctx), ctx)
	})

	m.Post("/spark", binding.Json(SparkEvent{}), func(sparkEvent SparkEvent, res http.ResponseWriter, req *http.Request, r render.Render) {
		ctx := appengine.NewContext(req)
		client := urlfetch.Client(ctx)

		message := spark.Message{ID: sparkEvent.Id}
		message.Get()
		log.Infof(ctx, message.Text)

		if strings.HasPrefix(message.Text, "/") {
			s := strings.Split(sparkEvent.Text, " ")

			command := s[0]
			log.Infof(ctx, "command = %s", command)
			if command == "/routes" {

				resp, _ := client.Get("http://galwaybus.herokuapp.com/routes.json")
				defer resp.Body.Close()
				contents, _ := ioutil.ReadAll(resp.Body)
				log.Infof(ctx, "body = %s\n", contents)

				var routeMap map[string]BusRoute
				json.Unmarshal([]byte(contents), &routeMap)

				text := "Routes:\n\n"
				for _, route := range routeMap {
					text = text + strconv.Itoa(route.Id) + " " + route.LongName + "\n"
				}

				message := spark.Message{
					RoomID: sparkEvent.RoomId,
					Text:   text,
				}
				message.Post()
			}
		}

	})

	http.Handle("/", m)
}
开发者ID:joreilly,项目名称:GalwayBusSparkBot,代码行数:53,代码来源:main.go


示例6: deleteAll

func deleteAll(w http.ResponseWriter, r *http.Request, m map[string]interface{}) {

	lg, _ := loghttp.BuffLoggerUniversal(w, r)

	err := r.ParseForm()
	lg(err)

	wpf(w, tplx.ExecTplHelper(tplx.Head, map[string]interface{}{"HtmlTitle": "Delete all filesystem data"}))
	defer wpf(w, tplx.Foot)

	confirm := r.FormValue("confirm")
	if confirm != "yes" {
		wpf(w, "All dsfs contents are deletes. All memfs contents are deleted<br>\n")
		wpf(w, "Put a get param into the URL ?confirm - and set it to 'yes'<br>\n")
		wpf(w, "Put a get param 'mountname' into url; i.e. mountname=mntftch<br>\n")
		return
	}

	wpf(w, "<pre>\n")
	defer wpf(w, "\n</pre>")

	//
	//
	fs := dsfs.New(
		dsfs.AeContext(appengine.NewContext(r)),
	)

	mountName := r.FormValue("mountname")
	if mountName != "" {
		wpf(w, "mountame = "+mountName+"\n")
		fs = dsfs.New(
			dsfs.AeContext(appengine.NewContext(r)),
			dsfs.MountName(mountName),
		)
	}

	wpf(w, "dsfs:\n")
	msg, err := fs.DeleteAll()
	if err != nil {
		wpf(w, "err during delete %v\n", err)
	}
	wpf(w, msg)

	memMapFileSys = memfs.New()
	wpf(w, "\n")
	wpf(w, "memMapFs new")

	// cleanup must be manual
	osFileSys = osfs.New()

}
开发者ID:aarzilli,项目名称:tools,代码行数:51,代码来源:spec_ops.go


示例7: DeleteSubtree

func DeleteSubtree(w http.ResponseWriter, r *http.Request, m map[string]interface{}) {

	lg, lge := loghttp.Logger(w, r)

	err := r.ParseForm()
	lge(err)

	wpf(w, tplx.ExecTplHelper(tplx.Head, map[string]interface{}{"HtmlTitle": "Delete Subtree for curr FS"}))
	defer wpf(w, tplx.Foot)

	if r.Method == "POST" {
		wpf(w, "<pre>\n")
		defer wpf(w, "\n</pre>")

		mountPoint := dsfs.MountPointLast()
		if len(r.FormValue("mountname")) > 0 {
			mountPoint = r.FormValue("mountname")
		}
		lg("mount point is %v", mountPoint)

		pathPrefix := "impossible-value"
		if len(r.FormValue("pathprefix")) > 0 {
			pathPrefix = r.FormValue("pathprefix")
		}
		lg("pathprefix is %v", pathPrefix)

		fs := getFS(appengine.NewContext(r), mountPoint)
		lg("created fs %v-%v ", fs.Name(), fs.String())

		lg("removing %q - and its subtree  ...", pathPrefix)
		err := fs.RemoveAll(pathPrefix)
		lge(err)

		errMc := memcache.Flush(appengine.NewContext(r))
		lge(errMc)

		if err == nil && errMc == nil {
			lg("success")
		}

	} else {
		tData := map[string]string{"Url": UriDeleteSubtree}
		err := tplBase.ExecuteTemplate(w, "tplName01", tData)
		lge(err)

	}

}
开发者ID:aarzilli,项目名称:tools,代码行数:48,代码来源:spec_ops.go


示例8: addHandler

func addHandler(w http.ResponseWriter, r *http.Request) {
	t := template.Must(template.ParseGlob("template/*")) // add sub-templates in /template
	if r.Method == "GET" {                               // get method display form
		t.ParseFiles("add.html")                                // parse add.html if 'method' = 'get'
		ctx := appengine.NewContext(r)                          // get ctx
		uploadURL, err := blobstore.UploadURL(ctx, "/add", nil) // get uploadurl for blobstore
		if err != nil {
			return
		}
		actionURL := map[string]string{"BlobActionURL": uploadURL.String()} // provide blobuploadurl to action field of the form
		t.ExecuteTemplate(w, "base", actionURL)
	}
	if r.Method == "POST" {
		ctx := appengine.NewContext(r)
		r.ParseForm()
		blobs, others, err := blobstore.ParseUpload(r) // get upload blob info
		if err != nil {
			w.Write([]byte(err.Error())) // error
			return
		}
		files := blobs["img"]                    // name="img" in the html form
		title := others.Get("title")             // '' title
		password := others.Get("password")       // '' password
		description := others.Get("description") // '' description

		var blobKeys []string
		var imgSrcs []string
		for _, file := range files {
			imgSrc := "/serve/?blobKey=" + string(file.BlobKey) // create imgsrc url from blobkey
			imgSrcs = append(imgSrcs, imgSrc)                   //multiple images in singe post
			blobKeys = append(blobKeys, string(file.BlobKey))   // also save blobkey in case for use.
		}

		var post m.Post // creating post and fill the fields.
		post.Title = title
		post.Password = password
		post.Description = description
		post.BlobKeys = blobKeys
		post.Time = time.Now()
		post.ImageSrc = imgSrcs

		posts := []m.DataModel{post}            // uploading posts
		keys, _ := m.SaveDataModels(ctx, posts) // success?
		i := strconv.Itoa(int(keys[0].IntID()))
		http.Redirect(w, r, "/main/?id="+i, http.StatusFound) // redirect to /main/?uuid= with uuid
		// http.Redirect(w, r, "/error", http.StatusNotFound)
	}
}
开发者ID:baek0429,项目名称:todaylist,代码行数:48,代码来源:todaylist.go


示例9: logout

func logout(res http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	ctx := appengine.NewContext(req)

	cookie, err := req.Cookie("session")
	// cookie is not set
	if err != nil {
		http.Redirect(res, req, "/", 302)
		return
	}

	// clear memcache
	sd := memcache.Item{
		Key:        cookie.Value,
		Value:      []byte(""),
		Expiration: time.Duration(1 * time.Microsecond),
	}
	memcache.Set(ctx, &sd)

	// clear the cookie
	cookie.MaxAge = -1
	http.SetCookie(res, cookie)

	// redirect
	http.Redirect(res, req, "/", 302)
}
开发者ID:RaviTezu,项目名称:GolangTraining,代码行数:25,代码来源:api.go


示例10: TestCreateSpot

func TestCreateSpot(t *testing.T) {
	opt := aetest.Options{AppID: "t2jp-2015", StronglyConsistentDatastore: true}
	inst, err := aetest.NewInstance(&opt)
	defer inst.Close()
	input, err := json.Marshal(Spot{SpotName: "foo", Body: "bar"})
	req, err := inst.NewRequest("POST", "/edit/v1/spots", bytes.NewBuffer(input))
	if err != nil {
		t.Fatalf("Failed to create req: %v", err)
	}
	loginUser := user.User{Email: "[email protected]", Admin: false, ID: "111111"}
	aetest.Login(&loginUser, req)
	ctx := appengine.NewContext(req)
	res := httptest.NewRecorder()
	c := web.C{}
	spotCreateHandler(c, res, req)
	if res.Code != http.StatusCreated {
		t.Fatalf("Fail to request spots create, status code: %v", res.Code)
	}
	spots := []Spot{}
	_, err = datastore.NewQuery("Spot").Order("-UpdatedAt").GetAll(ctx, &spots)
	for i := 0; i < len(spots); i++ {
		t.Logf("SpotCode:%v", spots[i].SpotCode)
		t.Logf("SpotName:%v", spots[i].SpotName)
	}
	if spots[0].SpotName != "foo" {
		t.Fatalf("not expected value! :%v", spots[0].SpotName)
	}

}
开发者ID:yosukesuzuki,项目名称:t2tapp,代码行数:29,代码来源:main_test.go


示例11: deleteData

func deleteData(res http.ResponseWriter, req *http.Request) {
	ctx := appengine.NewContext(req)
	u := user.Current(ctx)
	keyVal := req.FormValue("keyVal")
	key, err := datastore.DecodeKey(keyVal)
	if err != nil {
		http.Error(res, "Invalid data", http.StatusBadRequest)
		log.Warningf(ctx, err.Error())
		return
	}
	var l list
	err = datastore.Get(ctx, key, &l)
	if err != nil {
		http.Error(res, "Invalid data", http.StatusBadRequest)
		log.Warningf(ctx, err.Error())
		return
	}
	if l.Owner != u.Email {
		http.Error(res, "Not authorized to delete this entry", http.StatusUnauthorized)
		log.Warningf(ctx, err.Error())
		return
	}
	err = datastore.Delete(ctx, key)
	if err != nil {
		http.Error(res, "Server Error", http.StatusInternalServerError)
		log.Errorf(ctx, err.Error())
		return
	}
}
开发者ID:CodingDance,项目名称:GolangTraining,代码行数:29,代码来源:main.go


示例12: submitAddHandler

func submitAddHandler(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	if err := r.ParseForm(); err != nil {
		serveErr(ctx, err, w)
		return
	}

	success, err := recaptchaCheck(ctx, r.FormValue("g-recaptcha-response"), r.RemoteAddr)
	if err != nil {
		serveErr(ctx, err, w)
		return
	}

	if !success {
		log.Warningf(ctx, "reCAPTCHA check failed")
		failedTmpl.ExecuteTemplate(w, "base", nil)
		return
	}

	sub := Submission{
		URL:       template.URL(strings.TrimSpace(r.FormValue("url"))),
		Submitted: time.Now(),
	}

	if _, err := datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Submission", nil), &sub); err != nil {
		serveErr(ctx, err, w)
		return
	}

	thanksTmpl.ExecuteTemplate(w, "base", nil)
}
开发者ID:rojters,项目名称:gopherpods,代码行数:31,代码来源:gopherpods.go


示例13: createSession

func createSession(res http.ResponseWriter, req *http.Request, user User) {
	ctx := appengine.NewContext(req)
	// SET COOKIE
	id, _ := uuid.NewV4()
	cookie := &http.Cookie{
		Name:  "session",
		Value: id.String(),
		Path:  "/",
		// twenty minute session:
		MaxAge: 60 * 20,
		//		UNCOMMENT WHEN DEPLOYED:
		//		Secure: true,
		//		HttpOnly: true,
	}
	http.SetCookie(res, cookie)

	// SET MEMCACHE session data (sd)
	json, err := json.Marshal(user)
	if err != nil {
		log.Errorf(ctx, "error marshalling during user creation: %v", err)
		http.Error(res, err.Error(), 500)
		return
	}
	sd := memcache.Item{
		Key:   id.String(),
		Value: json,
	}
	memcache.Set(ctx, &sd)
}
开发者ID:RaviTezu,项目名称:GolangTraining,代码行数:29,代码来源:api.go


示例14: HandleGoalDelete

func HandleGoalDelete(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	params := mux.Vars(r)

	goalName, exists := params["goal"]
	if !exists {
		w.WriteHeader(http.StatusInternalServerError)
		// add error notesmessage that "goal parameter is missing"
		return
	}

	goal := model.Goal{}
	goal.Name = goalName

	err := goal.Delete(c)
	if err == types.ErrorNoMatch {
		http.Error(w, err.Error(), http.StatusOK)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)

}
开发者ID:muly,项目名称:lifelog,代码行数:27,代码来源:goal.go


示例15: HandleGoalGet

func HandleGoalGet(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	params := mux.Vars(r)

	goalName, exists := params["goal"]
	if !exists {
		http.Error(w, "Goal parameter is missing in URI", http.StatusBadRequest)
		return
	}

	goal := model.Goal{}
	goal.Name = goalName

	// if given goal is not found, return appropriate error
	if err := goal.Get(c); err == types.ErrorNoMatch {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if err := json.NewEncoder(w).Encode(goal); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	//w.WriteHeader(http.StatusOK) // removed because of "multiple response.WriteHeader calls" error

}
开发者ID:muly,项目名称:lifelog,代码行数:31,代码来源:goal.go


示例16: getWord

func getWord(res http.ResponseWriter, req *http.Request) {
	ctx := appengine.NewContext(req)
	term := req.FormValue("term")

	q := datastore.NewQuery("Word").Order("Term").Filter("Term =", term)

	html := " "

	iterator := q.Run(ctx)

	for {
		var entity Word
		_, err := iterator.Next(&entity)
		if err == datastore.Done {
			break
		} else if err != nil {
			http.Error(res, err.Error(), 500)
			return
		}
		html += `<dt>` + entity.Term + `</dt>
				 <dd>` + entity.Def + `</dd>`
	}

	res.Header().Set("Content-Type", "text/html")
	fmt.Fprintln(res, `
	<dl>`+html+`<dl>
	<form method="POST" action="">
		Enter a term to see its definition <input type="text" name="term">
		<input type="submit">
	</form>
		`)
}
开发者ID:andybenavides,项目名称:CSCI-191T,代码行数:32,代码来源:datastorePut.go


示例17: handleIndex

func handleIndex(res http.ResponseWriter, req *http.Request) {
	if req.Method == "POST" {
		term := req.FormValue("term")
		def := req.FormValue("def")

		ctx := appengine.NewContext(req)
		key := datastore.NewKey(ctx, "Word", term, 0, nil)

		entity := &Word{
			Term: term,
			Def:  def,
		}

		_, err := datastore.Put(ctx, key, entity)
		if err != nil {
			http.Error(res, err.Error(), 500)
			return
		}
	}

	res.Header().Set("Content-Type", "text/html")
	fmt.Fprintln(res, `
	<form method="POST" action="/words/">
		Enter a term <input type="text" name="term">
		Enter its definition <textarea name="def"></textarea>
		<input type="submit">
	</form>`)
}
开发者ID:andybenavides,项目名称:CSCI-191T,代码行数:28,代码来源:datastorePut.go


示例18: tweetProcess

func tweetProcess(res http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	ctx := appengine.NewContext(req)
	memItem, err := getSession(req)
	if err != nil {
		log.Infof(ctx, "Attempt to post tweet from logged out user")
		http.Error(res, "You must be logged in", http.StatusForbidden)
		return
	}
	// declare a variable of type user
	// initialize user with values from memcache item
	var user User
	json.Unmarshal(memItem.Value, &user)
	// declare a variable of type tweet
	// initialize it with values
	log.Infof(ctx, user.UserName)
	tweet := Tweet{
		Msg:      req.FormValue("tweet"),
		Time:     time.Now(),
		UserName: user.UserName,
	}
	// put in datastore
	err = putTweet(req, &user, &tweet)
	if err != nil {
		log.Errorf(ctx, "error adding todo: %v", err)
		http.Error(res, err.Error(), 500)
		return
	}
	// redirect
	time.Sleep(time.Millisecond * 500) // This is not the best code, probably. Thoughts?
	http.Redirect(res, req, "/", 302)
}
开发者ID:RaviTezu,项目名称:GolangTraining,代码行数:31,代码来源:api.go


示例19: createUser

func createUser(res http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	ctx := appengine.NewContext(req)
	hashedPass, err := bcrypt.GenerateFromPassword([]byte(req.FormValue("password")), bcrypt.DefaultCost)
	if err != nil {
		log.Errorf(ctx, "error creating password: %v", err)
		http.Error(res, err.Error(), 500)
		return
	}
	user := User{
		Email:    req.FormValue("email"),
		UserName: req.FormValue("userName"),
		Password: string(hashedPass),
	}
	key := datastore.NewKey(ctx, "Users", user.UserName, 0, nil)
	key, err = datastore.Put(ctx, key, &user)
	if err != nil {
		log.Errorf(ctx, "error adding todo: %v", err)
		http.Error(res, err.Error(), 500)
		return
	}

	createSession(res, req, user)
	// redirect
	http.Redirect(res, req, "/", 302)
}
开发者ID:RaviTezu,项目名称:GolangTraining,代码行数:25,代码来源:api.go


示例20: handler

func handler(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	module := appengine.ModuleName(ctx)
	instance := appengine.InstanceID()

	log.Infof(ctx, "Received on module %s; instance %s", module, instance)
}
开发者ID:GoogleCloudPlatform,项目名称:golang-samples,代码行数:7,代码来源:module.go



注:本文中的google/golang.org/appengine.NewContext函数示例由纯净天空整理自Github/MSDocs等源码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Golang appengine.VersionID函数代码示例发布时间:2022-05-28
下一篇:
Golang appengine.Namespace函数代码示例发布时间:2022-05-28
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap