OLD | NEW |
1 // Copyright 2016 The LUCI Authors. | 1 // Copyright 2017 The LUCI Authors. All rights reserved. |
2 // | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // that can be found in the LICENSE file. |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | 4 |
15 package common | 5 package frontend |
16 | 6 |
17 import ( | 7 import ( |
18 "bytes" | 8 "bytes" |
19 "fmt" | 9 "fmt" |
20 "html/template" | 10 "html/template" |
21 "net/http" | 11 "net/http" |
| 12 "strconv" |
22 "strings" | 13 "strings" |
23 "time" | 14 "time" |
24 | 15 |
| 16 "golang.org/x/net/context" |
| 17 |
| 18 "github.com/luci/gae/service/info" |
| 19 |
| 20 "github.com/luci/luci-go/appengine/gaeauth/server" |
| 21 "github.com/luci/luci-go/appengine/gaemiddleware" |
| 22 "github.com/luci/luci-go/common/clock" |
| 23 "github.com/luci/luci-go/server/analytics" |
| 24 "github.com/luci/luci-go/server/auth" |
| 25 "github.com/luci/luci-go/server/auth/identity" |
| 26 "github.com/luci/luci-go/server/router" |
| 27 "github.com/luci/luci-go/server/templates" |
| 28 |
25 "github.com/luci/luci-go/milo/api/resp" | 29 "github.com/luci/luci-go/milo/api/resp" |
26 ) | 30 ) |
27 | 31 |
28 // A collection of useful templating functions | 32 // A collection of useful templating functions |
29 | 33 |
30 // funcMap is what gets fed into the template bundle. | 34 // funcMap is what gets fed into the template bundle. |
31 var funcMap = template.FuncMap{ | 35 var funcMap = template.FuncMap{ |
32 "humanDuration": humanDuration, | 36 "humanDuration": humanDuration, |
33 "parseRFC3339": parseRFC3339, | 37 "parseRFC3339": parseRFC3339, |
34 "linkify": linkify, | 38 "linkify": linkify, |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 } | 217 } |
214 | 218 |
215 // shortHash abbriviates a git hash into 6 characters. | 219 // shortHash abbriviates a git hash into 6 characters. |
216 func shortHash(s string) string { | 220 func shortHash(s string) string { |
217 if len(s) > 6 { | 221 if len(s) > 6 { |
218 return s[0:6] | 222 return s[0:6] |
219 } | 223 } |
220 return s | 224 return s |
221 } | 225 } |
222 | 226 |
| 227 // GetLimit extracts the "limit", "numbuilds", or "num_builds" http param from |
| 228 // the request, or returns "-1" implying no limit was specified. |
| 229 func GetLimit(r *http.Request) (int, error) { |
| 230 sLimit := r.FormValue("limit") |
| 231 if sLimit == "" { |
| 232 sLimit = r.FormValue("numbuilds") |
| 233 if sLimit == "" { |
| 234 sLimit = r.FormValue("num_builds") |
| 235 if sLimit == "" { |
| 236 return -1, nil |
| 237 } |
| 238 } |
| 239 } |
| 240 limit, err := strconv.Atoi(sLimit) |
| 241 if err != nil { |
| 242 return -1, fmt.Errorf("limit parameter value %q is not a number:
%s", sLimit, err) |
| 243 } |
| 244 if limit < 0 { |
| 245 return -1, fmt.Errorf("limit parameter value %q is less than 0",
sLimit) |
| 246 } |
| 247 return limit, nil |
| 248 } |
| 249 |
223 // pagedURL returns a self URL with the given cursor and limit paging options. | 250 // pagedURL returns a self URL with the given cursor and limit paging options. |
224 // if limit is set to 0, then inherit whatever limit is set in request. If | 251 // if limit is set to 0, then inherit whatever limit is set in request. If |
225 // both are unspecified, then limit is omitted. | 252 // both are unspecified, then limit is omitted. |
226 func pagedURL(r *http.Request, limit int, cursor string) string { | 253 func pagedURL(r *http.Request, limit int, cursor string) string { |
227 if limit == 0 { | 254 if limit == 0 { |
228 var err error | 255 var err error |
229 limit, err = GetLimit(r) | 256 limit, err = GetLimit(r) |
230 if err != nil { | 257 if err != nil { |
231 // This should not happen because the handler should've
already validated the | 258 // This should not happen because the handler should've
already validated the |
232 // limit earlier in the process. | 259 // limit earlier in the process. |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 | 302 |
276 linkifyTemplate = template.Must( | 303 linkifyTemplate = template.Must( |
277 template.New("linkify"). | 304 template.New("linkify"). |
278 Parse( | 305 Parse( |
279 `<a href="{{.URL}}">` + | 306 `<a href="{{.URL}}">` + |
280 `{{if .Img}}<img src="{{.Img}}"{{if .Alt
}} alt="{{.Alt}}"{{end}}>` + | 307 `{{if .Img}}<img src="{{.Img}}"{{if .Alt
}} alt="{{.Alt}}"{{end}}>` + |
281 `{{else if .Alias}}[{{.Label}}]` + | 308 `{{else if .Alias}}[{{.Label}}]` + |
282 `{{else}}{{.Label}}{{end}}` + | 309 `{{else}}{{.Label}}{{end}}` + |
283 `</a>`)) | 310 `</a>`)) |
284 } | 311 } |
| 312 |
| 313 var authconfig *auth.Config |
| 314 |
| 315 // getTemplateBundles is used to render HTML templates. It provides base args |
| 316 // passed to all templates. It takes a path to the template folder, relative |
| 317 // to the path of the binary during runtime. |
| 318 func getTemplateBundle(templatePath string) *templates.Bundle { |
| 319 return &templates.Bundle{ |
| 320 Loader: templates.FileSystemLoader(templatePath), |
| 321 DebugMode: info.IsDevAppServer, |
| 322 DefaultTemplate: "base", |
| 323 DefaultArgs: func(c context.Context) (templates.Args, error) { |
| 324 r := getRequest(c) |
| 325 path := r.URL.Path |
| 326 loginURL, err := auth.LoginURL(c, path) |
| 327 if err != nil { |
| 328 return nil, err |
| 329 } |
| 330 logoutURL, err := auth.LogoutURL(c, path) |
| 331 if err != nil { |
| 332 return nil, err |
| 333 } |
| 334 return templates.Args{ |
| 335 "AppVersion": strings.Split(info.VersionID(c),
".")[0], |
| 336 "IsAnonymous": auth.CurrentIdentity(c) == identi
ty.AnonymousIdentity, |
| 337 "User": auth.CurrentUser(c), |
| 338 "LoginURL": loginURL, |
| 339 "LogoutURL": logoutURL, |
| 340 "CurrentTime": clock.Now(c), |
| 341 "Analytics": analytics.Snippet(c), |
| 342 "RequestID": info.RequestID(c), |
| 343 "Request": r, |
| 344 }, nil |
| 345 }, |
| 346 FuncMap: funcMap, |
| 347 } |
| 348 } |
| 349 |
| 350 // base returns the basic LUCI appengine middlewares. |
| 351 func base(templatePath string) router.MiddlewareChain { |
| 352 return gaemiddleware.BaseProd().Extend( |
| 353 auth.Authenticate(server.CookieAuth), |
| 354 withRequestMiddleware, |
| 355 templates.WithTemplates(getTemplateBundle(templatePath)), |
| 356 ) |
| 357 } |
| 358 |
| 359 // The context key, so that we can embed the http.Request object into |
| 360 // the context. |
| 361 var requestKey = "http.request" |
| 362 |
| 363 // withRequest returns a context with the http.Request object |
| 364 // in it. |
| 365 func withRequest(c context.Context, r *http.Request) context.Context { |
| 366 return context.WithValue(c, &requestKey, r) |
| 367 } |
| 368 |
| 369 // withRequestMiddleware is a middleware that installs a request into the contex
t. |
| 370 // This is used for various things in the default template. |
| 371 func withRequestMiddleware(c *router.Context, next router.Handler) { |
| 372 c.Context = withRequest(c.Context, c.Request) |
| 373 next(c) |
| 374 } |
| 375 |
| 376 func getRequest(c context.Context) *http.Request { |
| 377 if req, ok := c.Value(&requestKey).(*http.Request); ok { |
| 378 return req |
| 379 } |
| 380 panic("No http.request found in context") |
| 381 } |
OLD | NEW |