add personal access token panel #12
This commit is contained in:
parent
21b9d5fa1f
commit
8c9338a537
20 changed files with 311 additions and 62 deletions
|
@ -64,7 +64,7 @@ func checkVersion() {
|
||||||
|
|
||||||
// Check dependency version.
|
// Check dependency version.
|
||||||
macaronVer := git.MustParseVersion(strings.Join(strings.Split(macaron.Version(), ".")[:3], "."))
|
macaronVer := git.MustParseVersion(strings.Join(strings.Split(macaron.Version(), ".")[:3], "."))
|
||||||
if macaronVer.LessThan(git.MustParseVersion("0.4.0")) {
|
if macaronVer.LessThan(git.MustParseVersion("0.4.2")) {
|
||||||
log.Fatal(4, "Package macaron version is too old, did you forget to update?(github.com/Unknwon/macaron)")
|
log.Fatal(4, "Package macaron version is too old, did you forget to update?(github.com/Unknwon/macaron)")
|
||||||
}
|
}
|
||||||
i18nVer := git.MustParseVersion(i18n.Version())
|
i18nVer := git.MustParseVersion(i18n.Version())
|
||||||
|
@ -199,6 +199,7 @@ func runWeb(*cli.Context) {
|
||||||
m.Get("/ssh", user.SettingsSSHKeys)
|
m.Get("/ssh", user.SettingsSSHKeys)
|
||||||
m.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost)
|
m.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost)
|
||||||
m.Get("/social", user.SettingsSocial)
|
m.Get("/social", user.SettingsSocial)
|
||||||
|
m.Combo("/applications").Get(user.SettingsApplications).Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost)
|
||||||
m.Route("/delete", "GET,POST", user.SettingsDelete)
|
m.Route("/delete", "GET,POST", user.SettingsDelete)
|
||||||
}, reqSignIn)
|
}, reqSignIn)
|
||||||
m.Group("/user", func() {
|
m.Group("/user", func() {
|
||||||
|
@ -210,9 +211,6 @@ func runWeb(*cli.Context) {
|
||||||
m.Get("/logout", user.SignOut)
|
m.Get("/logout", user.SignOut)
|
||||||
})
|
})
|
||||||
|
|
||||||
// FIXME: Legacy
|
|
||||||
m.Get("/user/:username", ignSignIn, user.Profile)
|
|
||||||
|
|
||||||
// Gravatar service.
|
// Gravatar service.
|
||||||
avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg")
|
avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg")
|
||||||
os.MkdirAll("public/img/avatar/", os.ModePerm)
|
os.MkdirAll("public/img/avatar/", os.ModePerm)
|
||||||
|
|
|
@ -184,6 +184,7 @@ profile = Profile
|
||||||
password = Password
|
password = Password
|
||||||
ssh_keys = SSH Keys
|
ssh_keys = SSH Keys
|
||||||
social = Social Accounts
|
social = Social Accounts
|
||||||
|
applications = Applications
|
||||||
orgs = Organizations
|
orgs = Organizations
|
||||||
delete = Delete Account
|
delete = Delete Account
|
||||||
uid = Uid
|
uid = Uid
|
||||||
|
@ -224,6 +225,16 @@ social_desc = This is a list of associated social accounts. Remove any binding t
|
||||||
unbind = Unbind
|
unbind = Unbind
|
||||||
unbind_success = Social account has been unbound.
|
unbind_success = Social account has been unbound.
|
||||||
|
|
||||||
|
manage_access_token = Manage Personal Access Tokens
|
||||||
|
generate_new_token = Generate New Token
|
||||||
|
tokens_desc = Tokens you have generated that can be used to access the Gogs API.
|
||||||
|
new_token_desc = As for now, every token will have full access to your account.
|
||||||
|
token_name = Token Name
|
||||||
|
generate_token = Generate Token
|
||||||
|
generate_token_succees = New access token has been generated successfully! Make sure to copy your new personal access token now. You won't be able to see it again!
|
||||||
|
delete_token = Delete
|
||||||
|
delete_token_success = Personal access token has been deleted successfully! Don't forget to update your applications as well.
|
||||||
|
|
||||||
delete_account = Delete Your Account
|
delete_account = Delete Your Account
|
||||||
delete_prompt = The operation will delete your account permanently, and <strong>CANNOT</strong> be undone!
|
delete_prompt = The operation will delete your account permanently, and <strong>CANNOT</strong> be undone!
|
||||||
confirm_delete_account = Confirm Deletion
|
confirm_delete_account = Confirm Deletion
|
||||||
|
|
2
gogs.go
2
gogs.go
|
@ -17,7 +17,7 @@ import (
|
||||||
"github.com/gogits/gogs/modules/setting"
|
"github.com/gogits/gogs/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
const APP_VER = "0.5.7.1110 Beta"
|
const APP_VER = "0.5.8.1112 Beta"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
|
@ -39,7 +39,8 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tables = append(tables, new(User), new(PublicKey), new(Follow), new(Oauth2),
|
tables = append(tables,
|
||||||
|
new(User), new(PublicKey), new(Follow), new(Oauth2), new(AccessToken),
|
||||||
new(Repository), new(Watch), new(Star), new(Action), new(Access),
|
new(Repository), new(Watch), new(Star), new(Action), new(Access),
|
||||||
new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone),
|
new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone),
|
||||||
new(Mirror), new(Release), new(LoginSource), new(Webhook),
|
new(Mirror), new(Release), new(LoginSource), new(Webhook),
|
||||||
|
|
|
@ -236,10 +236,10 @@ func GetPublicKeyById(keyId int64) (*PublicKey, error) {
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListPublicKey returns a list of all public keys that user has.
|
// ListPublicKeys returns a list of public keys belongs to given user.
|
||||||
func ListPublicKey(uid int64) ([]*PublicKey, error) {
|
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
|
||||||
keys := make([]*PublicKey, 0, 5)
|
keys := make([]*PublicKey, 0, 5)
|
||||||
err := x.Find(&keys, &PublicKey{OwnerId: uid})
|
err := x.Where("owner_id=?", uid).Find(&keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
69
models/token.go
Normal file
69
models/token.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogits/gogs/modules/base"
|
||||||
|
"github.com/gogits/gogs/modules/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrAccessTokenNotExist = errors.New("Access token does not exist")
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccessToken represents a personal access token.
|
||||||
|
type AccessToken struct {
|
||||||
|
Id int64
|
||||||
|
Uid int64
|
||||||
|
Name string
|
||||||
|
Sha1 string `xorm:"UNIQUE VARCHAR(40)"`
|
||||||
|
Created time.Time `xorm:"CREATED"`
|
||||||
|
Updated time.Time
|
||||||
|
HasRecentActivity bool `xorm:"-"`
|
||||||
|
HasUsed bool `xorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAccessToken creates new access token.
|
||||||
|
func NewAccessToken(t *AccessToken) error {
|
||||||
|
t.Sha1 = base.EncodeSha1(uuid.NewV4().String())
|
||||||
|
_, err := x.Insert(t)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccessTokenBySha returns access token by given sha1.
|
||||||
|
func GetAccessTokenBySha(sha string) (*AccessToken, error) {
|
||||||
|
t := &AccessToken{Sha1: sha}
|
||||||
|
has, err := x.Get(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, ErrAccessTokenNotExist
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAccessTokens returns a list of access tokens belongs to given user.
|
||||||
|
func ListAccessTokens(uid int64) ([]*AccessToken, error) {
|
||||||
|
tokens := make([]*AccessToken, 0, 5)
|
||||||
|
err := x.Where("uid=?", uid).Desc("id").Find(&tokens)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range tokens {
|
||||||
|
t.HasUsed = t.Updated.After(t.Created)
|
||||||
|
t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||||
|
}
|
||||||
|
return tokens, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAccessTokenById deletes access token by given ID.
|
||||||
|
func DeleteAccessTokenById(id int64) error {
|
||||||
|
_, err := x.Id(id).Delete(new(AccessToken))
|
||||||
|
return err
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignedInId returns the id of signed in user.
|
// SignedInId returns the id of signed in user.
|
||||||
func SignedInId(header http.Header, sess session.Store) int64 {
|
func SignedInId(req *http.Request, sess session.Store) int64 {
|
||||||
if !models.HasEngine {
|
if !models.HasEngine {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -38,20 +38,38 @@ func SignedInId(header http.Header, sess session.Store) int64 {
|
||||||
}
|
}
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API calls also need to check access token.
|
||||||
|
if strings.HasPrefix(req.URL.Path, "/api/") {
|
||||||
|
auHead := req.Header.Get("Authorization")
|
||||||
|
if len(auHead) > 0 {
|
||||||
|
auths := strings.Fields(auHead)
|
||||||
|
if len(auths) == 2 && auths[0] == "token" {
|
||||||
|
t, err := models.GetAccessTokenBySha(auths[1])
|
||||||
|
if err != nil {
|
||||||
|
if err != models.ErrAccessTokenNotExist {
|
||||||
|
log.Error(4, "GetAccessTokenBySha: %v", err)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return t.Uid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignedInUser returns the user object of signed user.
|
// SignedInUser returns the user object of signed user.
|
||||||
func SignedInUser(header http.Header, sess session.Store) *models.User {
|
func SignedInUser(req *http.Request, sess session.Store) *models.User {
|
||||||
if !models.HasEngine {
|
if !models.HasEngine {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
uid := SignedInId(header, sess)
|
uid := SignedInId(req, sess)
|
||||||
|
|
||||||
if uid <= 0 {
|
if uid <= 0 {
|
||||||
if setting.Service.EnableReverseProxyAuth {
|
if setting.Service.EnableReverseProxyAuth {
|
||||||
webAuthUser := header.Get(setting.ReverseProxyAuthUser)
|
webAuthUser := req.Header.Get(setting.ReverseProxyAuthUser)
|
||||||
if len(webAuthUser) > 0 {
|
if len(webAuthUser) > 0 {
|
||||||
u, err := models.GetUserByName(webAuthUser)
|
u, err := models.GetUserByName(webAuthUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -65,7 +83,7 @@ func SignedInUser(header http.Header, sess session.Store) *models.User {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check with basic auth.
|
// Check with basic auth.
|
||||||
baHead := header.Get("Authorization")
|
baHead := req.Header.Get("Authorization")
|
||||||
if len(baHead) > 0 {
|
if len(baHead) > 0 {
|
||||||
auths := strings.Fields(baHead)
|
auths := strings.Fields(baHead)
|
||||||
if len(auths) == 2 && auths[0] == "Basic" {
|
if len(auths) == 2 && auths[0] == "Basic" {
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Unknwon/macaron"
|
|
||||||
"github.com/macaron-contrib/binding"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AddSSHKeyForm struct {
|
|
||||||
SSHTitle string `form:"title" binding:"Required"`
|
|
||||||
Content string `form:"content" binding:"Required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
|
||||||
}
|
|
|
@ -95,3 +95,20 @@ type ChangePasswordForm struct {
|
||||||
func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AddSSHKeyForm struct {
|
||||||
|
SSHTitle string `form:"title" binding:"Required"`
|
||||||
|
Content string `form:"content" binding:"Required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NewAccessTokenForm struct {
|
||||||
|
Name string `form:"name" binding:"Required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *NewAccessTokenForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,13 @@ func EncodeMd5(str string) string {
|
||||||
return hex.EncodeToString(m.Sum(nil))
|
return hex.EncodeToString(m.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encode string to sha1 hex value.
|
||||||
|
func EncodeSha1(str string) string {
|
||||||
|
h := sha1.New()
|
||||||
|
h.Write([]byte(str))
|
||||||
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
func BasicAuthDecode(encoded string) (user string, name string, err error) {
|
func BasicAuthDecode(encoded string) (user string, name string, err error) {
|
||||||
var s []byte
|
var s []byte
|
||||||
s, err = base64.StdEncoding.DecodeString(encoded)
|
s, err = base64.StdEncoding.DecodeString(encoded)
|
||||||
|
|
|
@ -172,7 +172,7 @@ func Contexter() macaron.Handler {
|
||||||
ctx.Data["PageStartTime"] = time.Now()
|
ctx.Data["PageStartTime"] = time.Now()
|
||||||
|
|
||||||
// Get user from session if logined.
|
// Get user from session if logined.
|
||||||
ctx.User = auth.SignedInUser(ctx.Req.Header, ctx.Session)
|
ctx.User = auth.SignedInUser(ctx.Req.Request, ctx.Session)
|
||||||
|
|
||||||
if ctx.User != nil {
|
if ctx.User != nil {
|
||||||
ctx.IsSigned = true
|
ctx.IsSigned = true
|
||||||
|
|
|
@ -1699,18 +1699,21 @@ The register and sign-in page style
|
||||||
#repo-hooks-panel,
|
#repo-hooks-panel,
|
||||||
#repo-hooks-history-panel,
|
#repo-hooks-history-panel,
|
||||||
#user-social-panel,
|
#user-social-panel,
|
||||||
|
#user-applications-panel,
|
||||||
#user-ssh-panel {
|
#user-ssh-panel {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
#repo-hooks-panel .setting-list,
|
#repo-hooks-panel .setting-list,
|
||||||
#repo-hooks-history-panel .setting-list,
|
#repo-hooks-history-panel .setting-list,
|
||||||
#user-social-panel .setting-list,
|
#user-social-panel .setting-list,
|
||||||
|
#user-applications-panel .setting-list,
|
||||||
#user-ssh-panel .setting-list {
|
#user-ssh-panel .setting-list {
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
}
|
}
|
||||||
#repo-hooks-panel .setting-list li,
|
#repo-hooks-panel .setting-list li,
|
||||||
#repo-hooks-history-panel .setting-list li,
|
#repo-hooks-history-panel .setting-list li,
|
||||||
#user-social-panel .setting-list li,
|
#user-social-panel .setting-list li,
|
||||||
|
#user-applications-panel .setting-list li,
|
||||||
#user-ssh-panel .setting-list li {
|
#user-ssh-panel .setting-list li {
|
||||||
padding: 8px 20px;
|
padding: 8px 20px;
|
||||||
border-bottom: 1px solid #eaeaea;
|
border-bottom: 1px solid #eaeaea;
|
||||||
|
@ -1718,18 +1721,21 @@ The register and sign-in page style
|
||||||
#repo-hooks-panel .setting-list li.ssh:hover,
|
#repo-hooks-panel .setting-list li.ssh:hover,
|
||||||
#repo-hooks-history-panel .setting-list li.ssh:hover,
|
#repo-hooks-history-panel .setting-list li.ssh:hover,
|
||||||
#user-social-panel .setting-list li.ssh:hover,
|
#user-social-panel .setting-list li.ssh:hover,
|
||||||
|
#user-applications-panel .setting-list li.ssh:hover,
|
||||||
#user-ssh-panel .setting-list li.ssh:hover {
|
#user-ssh-panel .setting-list li.ssh:hover {
|
||||||
background-color: #ffffEE;
|
background-color: #ffffEE;
|
||||||
}
|
}
|
||||||
#repo-hooks-panel .setting-list li i,
|
#repo-hooks-panel .setting-list li i,
|
||||||
#repo-hooks-history-panel .setting-list li i,
|
#repo-hooks-history-panel .setting-list li i,
|
||||||
#user-social-panel .setting-list li i,
|
#user-social-panel .setting-list li i,
|
||||||
|
#user-applications-panel .setting-list li i,
|
||||||
#user-ssh-panel .setting-list li i {
|
#user-ssh-panel .setting-list li i {
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
#repo-hooks-panel .active-icon,
|
#repo-hooks-panel .active-icon,
|
||||||
#repo-hooks-history-panel .active-icon,
|
#repo-hooks-history-panel .active-icon,
|
||||||
#user-social-panel .active-icon,
|
#user-social-panel .active-icon,
|
||||||
|
#user-applications-panel .active-icon,
|
||||||
#user-ssh-panel .active-icon {
|
#user-ssh-panel .active-icon {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
|
@ -1741,43 +1747,60 @@ The register and sign-in page style
|
||||||
#repo-hooks-panel .ssh-content,
|
#repo-hooks-panel .ssh-content,
|
||||||
#repo-hooks-history-panel .ssh-content,
|
#repo-hooks-history-panel .ssh-content,
|
||||||
#user-social-panel .ssh-content,
|
#user-social-panel .ssh-content,
|
||||||
|
#user-applications-panel .ssh-content,
|
||||||
#user-ssh-panel .ssh-content {
|
#user-ssh-panel .ssh-content {
|
||||||
margin-left: 24px;
|
margin-left: 24px;
|
||||||
}
|
}
|
||||||
#repo-hooks-panel .ssh-content .octicon,
|
#repo-hooks-panel .ssh-content .octicon,
|
||||||
#repo-hooks-history-panel .ssh-content .octicon,
|
#repo-hooks-history-panel .ssh-content .octicon,
|
||||||
#user-social-panel .ssh-content .octicon,
|
#user-social-panel .ssh-content .octicon,
|
||||||
|
#user-applications-panel .ssh-content .octicon,
|
||||||
#user-ssh-panel .ssh-content .octicon {
|
#user-ssh-panel .ssh-content .octicon {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
#repo-hooks-panel .ssh-content .print,
|
#repo-hooks-panel .ssh-content .print,
|
||||||
#repo-hooks-history-panel .ssh-content .print,
|
#repo-hooks-history-panel .ssh-content .print,
|
||||||
#user-social-panel .ssh-content .print,
|
#user-social-panel .ssh-content .print,
|
||||||
|
#user-applications-panel .ssh-content .print,
|
||||||
#user-ssh-panel .ssh-content .print,
|
#user-ssh-panel .ssh-content .print,
|
||||||
|
#repo-hooks-panel .ssh-content .access,
|
||||||
|
#repo-hooks-history-panel .ssh-content .access,
|
||||||
|
#user-social-panel .ssh-content .access,
|
||||||
|
#user-applications-panel .ssh-content .access,
|
||||||
|
#user-ssh-panel .ssh-content .access,
|
||||||
#repo-hooks-panel .ssh-content .activity,
|
#repo-hooks-panel .ssh-content .activity,
|
||||||
#repo-hooks-history-panel .ssh-content .activity,
|
#repo-hooks-history-panel .ssh-content .activity,
|
||||||
#user-social-panel .ssh-content .activity,
|
#user-social-panel .ssh-content .activity,
|
||||||
|
#user-applications-panel .ssh-content .activity,
|
||||||
#user-ssh-panel .ssh-content .activity {
|
#user-ssh-panel .ssh-content .activity {
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
#repo-hooks-panel .ssh-delete-btn,
|
#repo-hooks-panel .ssh-content .access,
|
||||||
#repo-hooks-history-panel .ssh-delete-btn,
|
#repo-hooks-history-panel .ssh-content .access,
|
||||||
#user-social-panel .ssh-delete-btn,
|
#user-social-panel .ssh-content .access,
|
||||||
#user-ssh-panel .ssh-delete-btn {
|
#user-applications-panel .ssh-content .access,
|
||||||
|
#user-ssh-panel .ssh-content .access {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
#repo-hooks-panel .ssh-btn,
|
||||||
|
#repo-hooks-history-panel .ssh-btn,
|
||||||
|
#user-social-panel .ssh-btn,
|
||||||
|
#user-applications-panel .ssh-btn,
|
||||||
|
#user-ssh-panel .ssh-btn {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
#user-ssh-add-form .panel-body {
|
.form-settings-add .panel-body {
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
padding: 30px 0;
|
padding: 30px 0;
|
||||||
}
|
}
|
||||||
#user-ssh-add-form .ipt {
|
.form-settings-add .ipt {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
}
|
}
|
||||||
#user-ssh-add-form textarea {
|
.form-settings-add textarea {
|
||||||
height: 120px;
|
height: 120px;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
#user-ssh-add-form .field {
|
.form-settings-add .field {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
.pr-main {
|
.pr-main {
|
||||||
|
|
|
@ -300,8 +300,11 @@ function initCore() {
|
||||||
$.magnificPopup.close();
|
$.magnificPopup.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Collapse.
|
// Plugins.
|
||||||
$('.collapse').hide();
|
$('.collapse').hide();
|
||||||
|
$('.tipsy-tooltip').tipsy({
|
||||||
|
fade: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initUserSetting() {
|
function initUserSetting() {
|
||||||
|
@ -329,9 +332,9 @@ function initUserSetting() {
|
||||||
$profile_form.submit();
|
$profile_form.submit();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show add SSH key panel.
|
// Show panels.
|
||||||
$('#ssh-add').click(function () {
|
$('.show-form-btn').click(function () {
|
||||||
$('#user-ssh-add-form').removeClass("hide");
|
$($(this).data('target-form')).removeClass("hide");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Confirmation of delete account.
|
// Confirmation of delete account.
|
||||||
|
|
2
public/ng/js/min/gogs-min.js
vendored
2
public/ng/js/min/gogs-min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -68,6 +68,7 @@
|
||||||
#repo-hooks-panel,
|
#repo-hooks-panel,
|
||||||
#repo-hooks-history-panel,
|
#repo-hooks-history-panel,
|
||||||
#user-social-panel,
|
#user-social-panel,
|
||||||
|
#user-applications-panel,
|
||||||
#user-ssh-panel {
|
#user-ssh-panel {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
.setting-list {
|
.setting-list {
|
||||||
|
@ -97,16 +98,20 @@
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
.print,
|
.print,
|
||||||
|
.access,
|
||||||
.activity {
|
.activity {
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
.access {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ssh-delete-btn {
|
.ssh-btn {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#user-ssh-add-form {
|
.form-settings-add {
|
||||||
.panel-body {
|
.panel-body {
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
padding: 30px 0;
|
padding: 30px 0;
|
||||||
|
|
|
@ -18,13 +18,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SETTINGS_PROFILE base.TplName = "user/settings/profile"
|
SETTINGS_PROFILE base.TplName = "user/settings/profile"
|
||||||
SETTINGS_PASSWORD base.TplName = "user/settings/password"
|
SETTINGS_PASSWORD base.TplName = "user/settings/password"
|
||||||
SETTINGS_SSH_KEYS base.TplName = "user/settings/sshkeys"
|
SETTINGS_SSH_KEYS base.TplName = "user/settings/sshkeys"
|
||||||
SETTINGS_SOCIAL base.TplName = "user/settings/social"
|
SETTINGS_SOCIAL base.TplName = "user/settings/social"
|
||||||
SETTINGS_DELETE base.TplName = "user/settings/delete"
|
SETTINGS_APPLICATIONS base.TplName = "user/settings/applications"
|
||||||
NOTIFICATION base.TplName = "user/notification"
|
SETTINGS_DELETE base.TplName = "user/settings/delete"
|
||||||
SECURITY base.TplName = "user/security"
|
NOTIFICATION base.TplName = "user/notification"
|
||||||
|
SECURITY base.TplName = "user/security"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Settings(ctx *middleware.Context) {
|
func Settings(ctx *middleware.Context) {
|
||||||
|
@ -129,7 +130,7 @@ func SettingsSSHKeys(ctx *middleware.Context) {
|
||||||
ctx.Data["PageIsSettingsSSHKeys"] = true
|
ctx.Data["PageIsSettingsSSHKeys"] = true
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
ctx.Data["Keys"], err = models.ListPublicKey(ctx.User.Id)
|
ctx.Data["Keys"], err = models.ListPublicKeys(ctx.User.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "ssh.ListPublicKey", err)
|
ctx.Handle(500, "ssh.ListPublicKey", err)
|
||||||
return
|
return
|
||||||
|
@ -144,7 +145,7 @@ func SettingsSSHKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) {
|
||||||
ctx.Data["PageIsSettingsSSHKeys"] = true
|
ctx.Data["PageIsSettingsSSHKeys"] = true
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
ctx.Data["Keys"], err = models.ListPublicKey(ctx.User.Id)
|
ctx.Data["Keys"], err = models.ListPublicKeys(ctx.User.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "ssh.ListPublicKey", err)
|
ctx.Handle(500, "ssh.ListPublicKey", err)
|
||||||
return
|
return
|
||||||
|
@ -235,6 +236,62 @@ func SettingsSocial(ctx *middleware.Context) {
|
||||||
ctx.HTML(200, SETTINGS_SOCIAL)
|
ctx.HTML(200, SETTINGS_SOCIAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SettingsApplications(ctx *middleware.Context) {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("settings")
|
||||||
|
ctx.Data["PageIsUserSettings"] = true
|
||||||
|
ctx.Data["PageIsSettingsApplications"] = true
|
||||||
|
|
||||||
|
// Delete access token.
|
||||||
|
remove, _ := com.StrTo(ctx.Query("remove")).Int64()
|
||||||
|
if remove > 0 {
|
||||||
|
if err := models.DeleteAccessTokenById(remove); err != nil {
|
||||||
|
ctx.Handle(500, "DeleteAccessTokenById", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Flash.Success(ctx.Tr("settings.delete_token_success"))
|
||||||
|
ctx.Redirect(setting.AppSubUrl + "/user/settings/applications")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens, err := models.ListAccessTokens(ctx.User.Id)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(500, "ListAccessTokens", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Tokens"] = tokens
|
||||||
|
|
||||||
|
ctx.HTML(200, SETTINGS_APPLICATIONS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: split to two different functions and pages to handle access token and oauth2
|
||||||
|
func SettingsApplicationsPost(ctx *middleware.Context, form auth.NewAccessTokenForm) {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("settings")
|
||||||
|
ctx.Data["PageIsUserSettings"] = true
|
||||||
|
ctx.Data["PageIsSettingsApplications"] = true
|
||||||
|
|
||||||
|
switch ctx.Query("type") {
|
||||||
|
case "token":
|
||||||
|
if ctx.HasError() {
|
||||||
|
ctx.HTML(200, SETTINGS_APPLICATIONS)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := &models.AccessToken{
|
||||||
|
Uid: ctx.User.Id,
|
||||||
|
Name: form.Name,
|
||||||
|
}
|
||||||
|
if err := models.NewAccessToken(t); err != nil {
|
||||||
|
ctx.Handle(500, "NewAccessToken", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Flash.Success(ctx.Tr("settings.generate_token_succees"))
|
||||||
|
ctx.Flash.Info(t.Sha1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Redirect(setting.AppSubUrl + "/user/settings/applications")
|
||||||
|
}
|
||||||
|
|
||||||
func SettingsDelete(ctx *middleware.Context) {
|
func SettingsDelete(ctx *middleware.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("settings")
|
ctx.Data["Title"] = ctx.Tr("settings")
|
||||||
ctx.Data["PageIsUserSettings"] = true
|
ctx.Data["PageIsUserSettings"] = true
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.5.7.1110 Beta
|
0.5.8.1112 Beta
|
56
templates/user/settings/applications.tmpl
Normal file
56
templates/user/settings/applications.tmpl
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
{{template "ng/base/head" .}}
|
||||||
|
{{template "ng/base/header" .}}
|
||||||
|
<div id="setting-wrapper" class="main-wrapper">
|
||||||
|
<div id="user-profile-setting" class="container clear">
|
||||||
|
{{template "user/settings/nav" .}}
|
||||||
|
<div class="grid-4-5 left">
|
||||||
|
<div class="setting-content">
|
||||||
|
{{template "ng/base/alert" .}}
|
||||||
|
<div id="setting-content">
|
||||||
|
<div id="user-applications-panel" class="panel panel-radius">
|
||||||
|
<div class="panel-header">
|
||||||
|
<a class="show-form-btn" data-target-form="#access-add-form">
|
||||||
|
<button class="btn btn-medium btn-black btn-radius right">{{.i18n.Tr "settings.generate_new_token"}}</button>
|
||||||
|
</a>
|
||||||
|
<strong>{{.i18n.Tr "settings.manage_access_token"}}</strong>
|
||||||
|
</div>
|
||||||
|
<ul class="panel-body setting-list">
|
||||||
|
<li>{{.i18n.Tr "settings.tokens_desc"}}</li>
|
||||||
|
{{range .Tokens}}
|
||||||
|
<li class="ssh clear">
|
||||||
|
<span class="active-icon left label label-{{if .HasRecentActivity}}green{{else}}gray{{end}} label-radius"></span>
|
||||||
|
<i class="fa fa-send fa-2x left"></i>
|
||||||
|
<div class="ssh-content left">
|
||||||
|
<p><strong>{{.Name}}</strong></p>
|
||||||
|
<p class="activity"><i>{{$.i18n.Tr "settings.add_on"}} {{DateFormat .Created "M d, Y"}} — <i class="octicon octicon-info"></i>{{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{DateFormat .Updated "M d, Y"}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}}</i></p>
|
||||||
|
</div>
|
||||||
|
<a href="{{AppSubUrl}}/user/settings/applications?remove={{.Id}}">
|
||||||
|
<button class="btn btn-small btn-red btn-radius ssh-btn right">{{$.i18n.Tr "settings.delete_token"}}</button>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<form class="panel panel-radius form form-align form-settings-add hide" id="access-add-form" action="{{AppSubUrl}}/user/settings/applications" method="post">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
<p class="panel-header"><strong>{{.i18n.Tr "settings.generate_new_token"}}</strong></p>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="text-center panel-desc">{{.i18n.Tr "settings.new_token_desc"}}</div>
|
||||||
|
<input type="hidden" name="type" value="token">
|
||||||
|
<p class="field">
|
||||||
|
<label class="req" for="token-name">{{.i18n.Tr "settings.token_name"}}</label>
|
||||||
|
<input class="ipt ipt-radius" id="token-name" name="name" required />
|
||||||
|
</p>
|
||||||
|
<p class="field">
|
||||||
|
<label></label>
|
||||||
|
<button class="btn btn-green btn-medium btn-radius" id="ssh-add-btn">{{.i18n.Tr "settings.generate_token"}}</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "ng/base/footer" .}}
|
|
@ -6,6 +6,7 @@
|
||||||
<li {{if .PageIsSettingsPassword}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/password">{{.i18n.Tr "settings.password"}}</a></li>
|
<li {{if .PageIsSettingsPassword}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/password">{{.i18n.Tr "settings.password"}}</a></li>
|
||||||
<li {{if .PageIsSettingsSSHKeys}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/ssh">{{.i18n.Tr "settings.ssh_keys"}}</a></li>
|
<li {{if .PageIsSettingsSSHKeys}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/ssh">{{.i18n.Tr "settings.ssh_keys"}}</a></li>
|
||||||
<li {{if .PageIsSettingsSocial}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/social">{{.i18n.Tr "settings.social"}}</a></li>
|
<li {{if .PageIsSettingsSocial}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/social">{{.i18n.Tr "settings.social"}}</a></li>
|
||||||
|
<li {{if .PageIsSettingsApplications}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/applications">{{.i18n.Tr "settings.applications"}}</a></li>
|
||||||
<li {{if .PageIsSettingsDelete}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/delete">{{.i18n.Tr "settings.delete"}}</a></li>
|
<li {{if .PageIsSettingsDelete}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/delete">{{.i18n.Tr "settings.delete"}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,7 +9,9 @@
|
||||||
<div id="user-ssh-setting-content">
|
<div id="user-ssh-setting-content">
|
||||||
<div id="user-ssh-panel" class="panel panel-radius">
|
<div id="user-ssh-panel" class="panel panel-radius">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<a class="btn btn-small btn-black btn-header btn-radius right" id="ssh-add">{{.i18n.Tr "settings.add_key"}}</a>
|
<a class="show-form-btn" data-target-form="#user-ssh-add-form">
|
||||||
|
<button class="btn btn-medium btn-black btn-radius right">{{.i18n.Tr "settings.add_key"}}</button>
|
||||||
|
</a>
|
||||||
<strong>{{.i18n.Tr "settings.manage_ssh_keys"}}</strong>
|
<strong>{{.i18n.Tr "settings.manage_ssh_keys"}}</strong>
|
||||||
</div>
|
</div>
|
||||||
<ul class="panel-body setting-list">
|
<ul class="panel-body setting-list">
|
||||||
|
@ -27,7 +29,7 @@
|
||||||
{{$.CsrfTokenHtml}}
|
{{$.CsrfTokenHtml}}
|
||||||
<input name="_method" type="hidden" value="DELETE">
|
<input name="_method" type="hidden" value="DELETE">
|
||||||
<input name="id" type="hidden" value="{{.Id}}">
|
<input name="id" type="hidden" value="{{.Id}}">
|
||||||
<button class="right ssh-delete-btn btn btn-red btn-radius btn-small">{{$.i18n.Tr "settings.delete_key"}}</button>
|
<button class="right ssh-btn btn btn-red btn-radius btn-small">{{$.i18n.Tr "settings.delete_key"}}</button>
|
||||||
</form>
|
</form>
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -35,7 +37,7 @@
|
||||||
</div>
|
</div>
|
||||||
<p>{{.i18n.Tr "settings.ssh_helper" "https://help.github.com/articles/generating-ssh-keys" "https://help.github.com/ssh-issues/" | Str2html}}</p>
|
<p>{{.i18n.Tr "settings.ssh_helper" "https://help.github.com/articles/generating-ssh-keys" "https://help.github.com/ssh-issues/" | Str2html}}</p>
|
||||||
<br>
|
<br>
|
||||||
<form class="panel panel-radius form form-align hide" id="user-ssh-add-form" action="{{AppSubUrl}}/user/settings/ssh" method="post">
|
<form class="panel panel-radius form form-align form-settings-add hide" id="user-ssh-add-form" action="{{AppSubUrl}}/user/settings/ssh" method="post">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
<p class="panel-header"><strong>{{.i18n.Tr "settings.add_new_key"}}</strong></p>
|
<p class="panel-header"><strong>{{.i18n.Tr "settings.add_new_key"}}</strong></p>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
|
Reference in a new issue