| OLD | NEW |
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package settings | 5 package settings |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | 8 "fmt" |
| 9 "io" | 9 "io" |
| 10 "net/http" | 10 "net/http" |
| 11 "path" | 11 "path" |
| 12 "sort" | 12 "sort" |
| 13 "strings" | 13 "strings" |
| 14 | 14 |
| 15 "google.golang.org/appengine" | 15 "google.golang.org/appengine" |
| 16 | 16 |
| 17 "github.com/julienschmidt/httprouter" | 17 "github.com/julienschmidt/httprouter" |
| 18 "github.com/luci/gae/service/info" | 18 "github.com/luci/gae/service/info" |
| 19 "github.com/luci/luci-go/appengine/cmd/milo/miloerror" | 19 "github.com/luci/luci-go/appengine/cmd/milo/miloerror" |
| 20 "github.com/luci/luci-go/appengine/gaeauth/server" | 20 "github.com/luci/luci-go/appengine/gaeauth/server" |
| 21 "github.com/luci/luci-go/appengine/gaemiddleware" | 21 "github.com/luci/luci-go/appengine/gaemiddleware" |
| 22 "github.com/luci/luci-go/common/clock" | 22 "github.com/luci/luci-go/common/clock" |
| 23 "github.com/luci/luci-go/server/auth" | 23 "github.com/luci/luci-go/server/auth" |
| 24 » "github.com/luci/luci-go/server/middleware" | 24 » "github.com/luci/luci-go/server/router" |
| 25 "github.com/luci/luci-go/server/templates" | 25 "github.com/luci/luci-go/server/templates" |
| 26 "golang.org/x/net/context" | 26 "golang.org/x/net/context" |
| 27 ) | 27 ) |
| 28 | 28 |
| 29 type themeContextKey string | 29 type themeContextKey string |
| 30 | 30 |
| 31 // NamedBundle is a tuple of a name (That matches it's corresponding theme) | 31 // NamedBundle is a tuple of a name (That matches it's corresponding theme) |
| 32 // and a template bundle. | 32 // and a template bundle. |
| 33 type NamedBundle struct { | 33 type NamedBundle struct { |
| 34 Name string | 34 Name string |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 return result | 112 return result |
| 113 } | 113 } |
| 114 | 114 |
| 115 // UseNamedBundle is like templates.Use, but with the choice of one of many bund
les (themes) | 115 // UseNamedBundle is like templates.Use, but with the choice of one of many bund
les (themes) |
| 116 func UseNamedBundle(c context.Context, nb NamedBundle) (context.Context, error)
{ | 116 func UseNamedBundle(c context.Context, nb NamedBundle) (context.Context, error)
{ |
| 117 err := nb.Bundle.EnsureLoaded(c) | 117 err := nb.Bundle.EnsureLoaded(c) |
| 118 return context.WithValue(c, themeContextKey(nb.Name), nb.Bundle), err | 118 return context.WithValue(c, themeContextKey(nb.Name), nb.Bundle), err |
| 119 } | 119 } |
| 120 | 120 |
| 121 // withNamedBundle is like templates.WithTemplates, but with the choice of one o
f many bundles (themes) | 121 // withNamedBundle is like templates.WithTemplates, but with the choice of one o
f many bundles (themes) |
| 122 func withNamedBundle(h middleware.Handler, nb NamedBundle) middleware.Handler { | 122 func withNamedBundle(nb NamedBundle) router.Middleware { |
| 123 » return func(c context.Context, rw http.ResponseWriter, r *http.Request,
p httprouter.Params) { | 123 » return func(c *router.Context, next router.Handler) { |
| 124 » » c, err := UseNamedBundle(c, nb) // calls EnsureLoaded and initia
lizes b.err inside | 124 » » var err error |
| 125 » » c.Context, err = UseNamedBundle(c.Context, nb) // calls EnsureLo
aded and initializes b.err inside |
| 125 if err != nil { | 126 if err != nil { |
| 126 » » » http.Error(rw, fmt.Sprintf("Can't load HTML templates.\n
%s", err), http.StatusInternalServerError) | 127 » » » http.Error(c.Writer, fmt.Sprintf("Can't load HTML templa
tes.\n%s", err), http.StatusInternalServerError) |
| 127 return | 128 return |
| 128 } | 129 } |
| 129 » » h(c, rw, r, p) | 130 » » next(c) |
| 130 } | 131 } |
| 131 } | 132 } |
| 132 | 133 |
| 133 // themedMustRender renders theme and panics if it can't be rendered. This shou
ld never fail in | 134 // themedMustRender renders theme and panics if it can't be rendered. This shou
ld never fail in |
| 134 // production. | 135 // production. |
| 135 func themedMustRender(c context.Context, out io.Writer, theme, name string, args
templates.Args) { | 136 func themedMustRender(c context.Context, out io.Writer, theme, name string, args
templates.Args) { |
| 136 if b, _ := c.Value(themeContextKey(theme)).(*templates.Bundle); b != nil
{ | 137 if b, _ := c.Value(themeContextKey(theme)).(*templates.Bundle); b != nil
{ |
| 137 blob, err := b.Render(c, name, args) | 138 blob, err := b.Render(c, name, args) |
| 138 if err != nil { | 139 if err != nil { |
| 139 panic(fmt.Errorf("Could not render template %s from them
e %s:\n%s", name, theme, err)) | 140 panic(fmt.Errorf("Could not render template %s from them
e %s:\n%s", name, theme, err)) |
| 140 } | 141 } |
| 141 _, err = out.Write(blob) | 142 _, err = out.Write(blob) |
| 142 if err != nil { | 143 if err != nil { |
| 143 panic(fmt.Errorf("Could not write out template %s from t
heme %s:\n%s", name, theme, err)) | 144 panic(fmt.Errorf("Could not write out template %s from t
heme %s:\n%s", name, theme, err)) |
| 144 } | 145 } |
| 145 return | 146 return |
| 146 } | 147 } |
| 147 panic(fmt.Errorf("Error: Could not load template %s from theme %s", name
, theme)) | 148 panic(fmt.Errorf("Error: Could not load template %s from theme %s", name
, theme)) |
| 148 } | 149 } |
| 149 | 150 |
| 150 // Base adds the basic luci appengine middlewares. | 151 // Base returns the basic luci appengine middlewares. |
| 151 func Base(h middleware.Handler) httprouter.Handle { | 152 func Base() router.MiddlewareChain { |
| 152 methods := auth.Authenticator{ | 153 methods := auth.Authenticator{ |
| 153 &server.OAuth2Method{Scopes: []string{server.EmailScope}}, | 154 &server.OAuth2Method{Scopes: []string{server.EmailScope}}, |
| 154 server.CookieAuth, | 155 server.CookieAuth, |
| 155 &server.InboundAppIDAuthMethod{}, | 156 &server.InboundAppIDAuthMethod{}, |
| 156 } | 157 } |
| 158 m := append(gaemiddleware.BaseProd(), auth.Use(methods)) |
| 157 for _, nb := range GetTemplateBundles() { | 159 for _, nb := range GetTemplateBundles() { |
| 158 » » h = withNamedBundle(h, nb) | 160 » » m = append(m, withNamedBundle(nb)) |
| 159 } | 161 } |
| 160 » return gaemiddleware.BaseProd(auth.Use(h, methods)) | 162 » return m |
| 161 } | 163 } |
| 162 | 164 |
| 163 // Wrap wraps Milo "Render" functions and emits a middleware.Handler function.
Of note | 165 // Wrap adapts a ThemedHandler into a router.Handler. Of note, the |
| 164 // is that Render functions' interface into rendering is purely through a single | 166 // Render functions' interface into rendering is purely through a single |
| 165 // templates.Args value which gets rendered here, while the http.ResponseWriter | 167 // templates.Args value which gets rendered here, while the http.ResponseWriter |
| 166 // is stripped out. | 168 // is stripped out. |
| 167 func Wrap(h ThemedHandler) func(http.ResponseWriter, *http.Request, httprouter.P
arams) { | 169 func Wrap(h ThemedHandler) router.Handler { |
| 168 » hx := func(c context.Context, w http.ResponseWriter, r *http.Request, p
httprouter.Params) { | 170 » return func(c *router.Context) { |
| 169 // Figure out if we need to do the things. | 171 // Figure out if we need to do the things. |
| 170 » » theme := GetTheme(c, r) | 172 » » theme := GetTheme(c.Context, c.Request) |
| 171 template := h.GetTemplateName(theme) | 173 template := h.GetTemplateName(theme) |
| 172 | 174 |
| 173 // Do the things. | 175 // Do the things. |
| 174 » » args, err := h.Render(c, r, p) | 176 » » args, err := h.Render(c.Context, c.Request, c.Params) |
| 175 | 177 |
| 176 // Throw errors. | 178 // Throw errors. |
| 177 // TODO(hinoka): Add themes and templates for errors so they loo
k better. | 179 // TODO(hinoka): Add themes and templates for errors so they loo
k better. |
| 178 if err != nil { | 180 if err != nil { |
| 179 if merr, ok := err.(*miloerror.Error); ok { | 181 if merr, ok := err.(*miloerror.Error); ok { |
| 180 » » » » http.Error(w, merr.Message, merr.Code) | 182 » » » » http.Error(c.Writer, merr.Message, merr.Code) |
| 181 } else { | 183 } else { |
| 182 » » » » http.Error(w, err.Error(), http.StatusInternalSe
rverError) | 184 » » » » http.Error(c.Writer, err.Error(), http.StatusInt
ernalServerError) |
| 183 } | 185 } |
| 184 return | 186 return |
| 185 } | 187 } |
| 186 | 188 |
| 187 // Render the stuff. | 189 // Render the stuff. |
| 188 name := fmt.Sprintf("pages/%s", template) | 190 name := fmt.Sprintf("pages/%s", template) |
| 189 » » themedMustRender(c, w, theme.Name, name, *args) | 191 » » themedMustRender(c.Context, c.Writer, theme.Name, name, *args) |
| 190 } | 192 } |
| 191 return Base(hx) | |
| 192 } | 193 } |
| OLD | NEW |