mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	add personal access token panel #12
This commit is contained in:
		| @@ -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 New Issue
	
	Block a user