commit
abdcf912f4
@ -13,6 +13,12 @@ NPM dependencies:
|
|||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### As there is no versioning yet, you need to manually go get all the packages
|
||||||
|
|
||||||
|
```
|
||||||
|
go get ./...
|
||||||
|
```
|
||||||
|
|
||||||
### NOTE: for debian users
|
### NOTE: for debian users
|
||||||
|
|
||||||
If you use debian the node binary is named nodejs you have to symlink it to node:
|
If you use debian the node binary is named nodejs you have to symlink it to node:
|
||||||
@ -24,6 +30,9 @@ If you use debian the node binary is named nodejs you have to symlink it to node
|
|||||||
|
|
||||||
## Dev
|
## Dev
|
||||||
|
|
||||||
|
#### Check your config.yml file
|
||||||
|
|
||||||
|
#### Run
|
||||||
```
|
```
|
||||||
make dev
|
make dev
|
||||||
```
|
```
|
||||||
|
@ -24,6 +24,7 @@ type UserBackend interface {
|
|||||||
|
|
||||||
// User interface for user
|
// User interface for user
|
||||||
type User interface {
|
type User interface {
|
||||||
|
GetName() string
|
||||||
GetHash() string
|
GetHash() string
|
||||||
HasRole(string) bool
|
HasRole(string) bool
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/data"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,7 +30,14 @@ func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.log.Debug("setting user in the context")
|
if user != nil {
|
||||||
|
name := user.GetName()
|
||||||
|
m.log.Debugf("setting user %s in the context", name)
|
||||||
|
data.SetData(r, "user", user)
|
||||||
|
} else {
|
||||||
|
m.log.Debugf("got a nil user in the context")
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.WithValue(r.Context(), "auth.user", user)
|
ctx := context.WithValue(r.Context(), "auth.user", user)
|
||||||
r = r.WithContext(ctx)
|
r = r.WithContext(ctx)
|
||||||
|
|
||||||
|
42
src/internal/data/data.go
Normal file
42
src/internal/data/data.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type key int
|
||||||
|
|
||||||
|
// dKey is key for access to response data in context
|
||||||
|
const dKey key = 0
|
||||||
|
|
||||||
|
// GetAllData return response's data
|
||||||
|
func GetAllData(r *http.Request) map[string]interface{} {
|
||||||
|
data := GetData(r, "data")
|
||||||
|
if data == nil {
|
||||||
|
data = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
mapData, ok := data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
fmt.Printf("something wrong with data")
|
||||||
|
}
|
||||||
|
// log.Printf("got all data %+v", mapData)
|
||||||
|
return mapData
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetData sets some response's data for access in template
|
||||||
|
func SetData(r *http.Request, key string, val interface{}) {
|
||||||
|
allData := GetAllData(r)
|
||||||
|
allData[key] = val
|
||||||
|
|
||||||
|
ctx := context.WithValue(r.Context(), "data", allData)
|
||||||
|
*r = *r.WithContext(ctx)
|
||||||
|
allData = GetAllData(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetData gets some response's data for access in template
|
||||||
|
func GetData(r *http.Request, key string) interface{} {
|
||||||
|
return r.Context().Value(key)
|
||||||
|
}
|
@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/config"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/config"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/data"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
)
|
)
|
||||||
@ -114,14 +115,14 @@ func FromPolochon(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("invalid user type")
|
return fmt.Errorf("invalid user type")
|
||||||
}
|
}
|
||||||
web.SetData(r, "user", user)
|
data.SetData(r, "user", user)
|
||||||
|
|
||||||
movies, err := getPolochonMovies(user)
|
movies, err := getPolochonMovies(user)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Catch network error for accessing specified polochon address
|
// Catch network error for accessing specified polochon address
|
||||||
if err == ErrPolochonUnavailable {
|
if err == ErrPolochonUnavailable {
|
||||||
web.SetData(r, "error", "Invalid address")
|
data.SetData(r, "error", "Invalid address")
|
||||||
return env.Rends(w, r, "movies/library")
|
return env.Rends(w, r, "movies/library")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +168,7 @@ func FromPolochon(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|||||||
|
|
||||||
sort.Sort(smovies)
|
sort.Sort(smovies)
|
||||||
|
|
||||||
web.SetData(r, "movies", movies[params.Start:params.Start+params.Limit])
|
data.SetData(r, "movies", movies[params.Start:params.Start+params.Limit])
|
||||||
return env.Rends(w, r, "movies/library")
|
return env.Rends(w, r, "movies/library")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +206,7 @@ func ExplorePopular(env *web.Env, w http.ResponseWriter, r *http.Request) error
|
|||||||
movies = append(movies, movie)
|
movies = append(movies, movie)
|
||||||
ids = append(ids, m.IDs.ImDB)
|
ids = append(ids, m.IDs.ImDB)
|
||||||
}
|
}
|
||||||
web.SetData(r, "movies", movies)
|
data.SetData(r, "movies", movies)
|
||||||
|
|
||||||
return env.Rends(w, r, "movies/library")
|
return env.Rends(w, r, "movies/library")
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/auth"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/config"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/config"
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/data"
|
||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,20 +38,23 @@ func LoginPOSTHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error
|
|||||||
err = e.Auth.Login(w, r, form.Username, form.Password)
|
err = e.Auth.Login(w, r, form.Username, form.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == auth.ErrInvalidPassword || err == ErrUnknownUser {
|
if err == auth.ErrInvalidPassword || err == ErrUnknownUser {
|
||||||
web.SetData(r, "FormErrors", "Error invalid user or password")
|
data.SetData(r, "FormErrors", "Error invalid user or password")
|
||||||
return e.Rends(w, r, "users/login")
|
return e.Rends(w, r, "users/login")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
e.Log.Debug("logged")
|
||||||
|
|
||||||
path, err := auth.GetPostLoginRedirect(e.Auth, w, r)
|
path, err := auth.GetPostLoginRedirect(e.Auth, w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
e.Log.Debugf("redirecting to %s", path)
|
||||||
if path != "" {
|
if path != "" {
|
||||||
http.Redirect(w, r, path, http.StatusTemporaryRedirect)
|
http.Redirect(w, r, path, http.StatusTemporaryRedirect)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
e.Log.Debugf("got no path, redirecting to /")
|
||||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -58,8 +62,8 @@ func LoginPOSTHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error
|
|||||||
// LogoutHandler just logout
|
// LogoutHandler just logout
|
||||||
func LogoutHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error {
|
func LogoutHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||||
e.Auth.Logout(w, r)
|
e.Auth.Logout(w, r)
|
||||||
route := e.GetLoginRouteGetter()()
|
|
||||||
http.Redirect(w, r, route, http.StatusTemporaryRedirect)
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +81,7 @@ func DetailsHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
web.SetData(r, "user", user)
|
data.SetData(r, "polochon", polochonConfig)
|
||||||
web.SetData(r, "polochon", polochonConfig)
|
|
||||||
return e.Rends(w, r, "users/details")
|
return e.Rends(w, r, "users/details")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,8 +99,7 @@ func EditHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
web.SetData(r, "user", user)
|
data.SetData(r, "polochon", polochonConfig)
|
||||||
web.SetData(r, "polochon", polochonConfig)
|
|
||||||
return e.Rends(w, r, "users/edit")
|
return e.Rends(w, r, "users/edit")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +182,11 @@ func (u *User) GetHash() string {
|
|||||||
return u.Hash
|
return u.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetName implements auth.User interface
|
||||||
|
func (u *User) GetName() string {
|
||||||
|
return u.Name
|
||||||
|
}
|
||||||
|
|
||||||
func (u *User) HasRole(role string) bool {
|
func (u *User) HasRole(role string) bool {
|
||||||
if role == AdminRole && !u.Admin {
|
if role == AdminRole && !u.Admin {
|
||||||
return false
|
return false
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
package web
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gorilla/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
type key int
|
|
||||||
|
|
||||||
// dKey is key for access to response data in context
|
|
||||||
const dKey key = 0
|
|
||||||
|
|
||||||
// GetAllData return response's data
|
|
||||||
func GetAllData(r *http.Request) map[string]interface{} {
|
|
||||||
data, ok := context.GetOk(r, dKey)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
d, ok := data.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetData sets some response's data for access in template
|
|
||||||
func SetData(r *http.Request, key string, val interface{}) {
|
|
||||||
data, ok := context.GetOk(r, dKey)
|
|
||||||
if !ok {
|
|
||||||
context.Set(r, dKey, make(map[string]interface{}))
|
|
||||||
data = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
data.(map[string]interface{})[key] = val
|
|
||||||
context.Set(r, dKey, data)
|
|
||||||
}
|
|
@ -4,6 +4,8 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/data"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,12 +37,12 @@ func (e *Env) Rends(w http.ResponseWriter, r *http.Request, template string) err
|
|||||||
if r.Header.Get("Accept") == "application/json" {
|
if r.Header.Get("Accept") == "application/json" {
|
||||||
return e.Render.JSON(w, http.StatusOK, TemplateData{
|
return e.Render.JSON(w, http.StatusOK, TemplateData{
|
||||||
Route: mux.CurrentRoute(r).GetName(),
|
Route: mux.CurrentRoute(r).GetName(),
|
||||||
Data: GetAllData(r),
|
Data: data.GetAllData(r),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.Render.HTML(w, http.StatusOK, template, TemplateData{
|
return e.Render.HTML(w, http.StatusOK, template, TemplateData{
|
||||||
Route: mux.CurrentRoute(r).GetName(),
|
Route: mux.CurrentRoute(r).GetName(),
|
||||||
Data: GetAllData(r),
|
Data: data.GetAllData(r),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,10 @@ import (
|
|||||||
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
"gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/negroni"
|
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
|
"github.com/urfave/negroni"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserBackend represents the data backend to get the user
|
// UserBackend represents the data backend to get the user
|
||||||
@ -69,6 +69,7 @@ func main() {
|
|||||||
|
|
||||||
authMiddleware := auth.NewMiddleware(env.Auth, log)
|
authMiddleware := auth.NewMiddleware(env.Auth, log)
|
||||||
|
|
||||||
|
env.Handle("/", movies.ExplorePopular).Name("movies.home")
|
||||||
env.Handle("/users/login", users.LoginGETHandler).Name("users.login").Methods("GET")
|
env.Handle("/users/login", users.LoginGETHandler).Name("users.login").Methods("GET")
|
||||||
env.Handle("/users/login", users.LoginPOSTHandler).Name("users.login").Methods("POST")
|
env.Handle("/users/login", users.LoginPOSTHandler).Name("users.login").Methods("POST")
|
||||||
env.Handle("/users/logout", users.LogoutHandler).Name("users.logout")
|
env.Handle("/users/logout", users.LogoutHandler).Name("users.logout")
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
<body>
|
<body>
|
||||||
{{ template "navbar" $ }}
|
{{ template "navbar" $ }}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
{{ yield }}
|
<div class="container">
|
||||||
|
{{ yield }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/js/app.js"></script>
|
<script src="/js/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -32,12 +32,25 @@
|
|||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{$.Data.user.Name}} <span class="caret"></span></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{$.Data.user.Name}} <span class="caret"></span></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="{{ URL "users.edit"}}">Edit</a></li>
|
<li>
|
||||||
|
<a href="{{ URL "users.details"}}">My account</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{{ URL "users.edit"}}">Edit</a>
|
||||||
|
</li>
|
||||||
<li role="separator" class="divider"></li>
|
<li role="separator" class="divider"></li>
|
||||||
<li><a href="{{ URL "users.logout"}}">Logout</a></li>
|
<li>
|
||||||
|
<a href="{{ URL "users.logout"}}">Logout</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{{ end}}
|
{{ else }}
|
||||||
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
<li>
|
||||||
|
<a href="{{ URL "users.login"}}">Sign in</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div><!-- /.navbar-collapse -->
|
</div><!-- /.navbar-collapse -->
|
||||||
|
@ -1,43 +1,40 @@
|
|||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="content-fluid">
|
<div class="content-fluid">
|
||||||
<div class="col-md-6 col-md-offset-3 col-xs-12">
|
<div class="col-md-6 col-md-offset-3 col-xs-12">
|
||||||
<h2>Edit user</h2>
|
<h2>Edit user</h2>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<form accept-charset="UTF-8" action="{{ URL "users.edit" }}" method="POST" class="form-horizontal" id="user">
|
<form accept-charset="UTF-8" action="{{ URL "users.edit" }}" method="POST" class="form-horizontal" id="user">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label" for="PolochonURL">Polochon URL</label>
|
<label class="control-label" for="PolochonURL">Polochon URL</label>
|
||||||
<input autofocus="autofocus" class="form-control" name="PolochonURL" type="text" value="{{ $.Data.polochon.URL }}">
|
<input autofocus="autofocus" class="form-control" name="PolochonURL" type="text" value="{{ $.Data.polochon.URL }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label" for="PolochonToken">Polochon token</label>
|
||||||
|
<input autofocus="autofocus" class="form-control" name="PolochonToken" type="text" value="{{ $.Data.polochon.Token }}" >
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label" for="Password">Password</label>
|
||||||
|
<input autocomplete="off" class="form-control" name="Password" type="password" value="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label" for="PasswordVerify">Confirm Password</label>
|
||||||
|
<input autocomplete="off" class="form-control" name="PasswordVerify" type="password" value="">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input class="btn btn-primary pull-right" type="submit" value="Update">
|
||||||
|
<a class="btn btn-default pull-left" href="{{ URL "users.details" }}">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label" for="PolochonToken">Polochon token</label>
|
|
||||||
<input autofocus="autofocus" class="form-control" name="PolochonToken" type="text" value="{{ $.Data.polochon.Token }}" >
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label" for="Password">Password</label>
|
|
||||||
<input autocomplete="off" class="form-control" name="Password" type="password" value="">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label" for="PasswordVerify">Confirm Password</label>
|
|
||||||
<input autocomplete="off" class="form-control" name="PasswordVerify" type="password" value="">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input class="btn btn-primary pull-right" type="submit" value="Update">
|
|
||||||
<a class="btn btn-default pull-left" href="{{ URL "users.details" }}">Cancel</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,27 @@
|
|||||||
<form action="/users/login" method="post">
|
<div class="container">
|
||||||
<input type="text" name="Username">
|
<div class="content-fluid">
|
||||||
<input type="text" name="Password">
|
<div class="col-md-6 col-md-offset-3 col-xs-12">
|
||||||
<input type="submit" value="Submit">
|
<h2>Log in</h2>
|
||||||
</form>
|
<hr>
|
||||||
|
<form accept-charset="UTF-8" action="/users/login" method="POST" class="form-horizontal">
|
||||||
|
<div>
|
||||||
|
<label for="user_email">Username</label>
|
||||||
|
<br>
|
||||||
|
<input autofocus="autofocus" class="form-control" id="username" name="Username" type="username" value="">
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="user_password">Password</label>
|
||||||
|
<br>
|
||||||
|
<input autocomplete="off" class="form-control" id="password" name="Password" type="password">
|
||||||
|
<p></p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input class="btn btn-primary pull-right" type="submit" value="Log in">
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-default btn-sm pull-left" href="/movies/%3cnil%3e">Cancel</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user