@@ -64,7 +64,7 @@ func checkVersion() { | |||
// Check dependency version. | |||
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)") | |||
} | |||
i18nVer := git.MustParseVersion(i18n.Version()) | |||
@@ -199,6 +199,7 @@ func runWeb(*cli.Context) { | |||
m.Get("/ssh", user.SettingsSSHKeys) | |||
m.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost) | |||
m.Get("/social", user.SettingsSocial) | |||
m.Combo("/applications").Get(user.SettingsApplications).Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost) | |||
m.Route("/delete", "GET,POST", user.SettingsDelete) | |||
}, reqSignIn) | |||
m.Group("/user", func() { | |||
@@ -210,9 +211,6 @@ func runWeb(*cli.Context) { | |||
m.Get("/logout", user.SignOut) | |||
}) | |||
// FIXME: Legacy | |||
m.Get("/user/:username", ignSignIn, user.Profile) | |||
// Gravatar service. | |||
avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg") | |||
os.MkdirAll("public/img/avatar/", os.ModePerm) |
@@ -184,6 +184,7 @@ profile = Profile | |||
password = Password | |||
ssh_keys = SSH Keys | |||
social = Social Accounts | |||
applications = Applications | |||
orgs = Organizations | |||
delete = Delete Account | |||
uid = Uid | |||
@@ -224,6 +225,16 @@ social_desc = This is a list of associated social accounts. Remove any binding t | |||
unbind = Unbind | |||
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_prompt = The operation will delete your account permanently, and <strong>CANNOT</strong> be undone! | |||
confirm_delete_account = Confirm Deletion |
@@ -17,7 +17,7 @@ import ( | |||
"github.com/gogits/gogs/modules/setting" | |||
) | |||
const APP_VER = "0.5.7.1110 Beta" | |||
const APP_VER = "0.5.8.1112 Beta" | |||
func init() { | |||
runtime.GOMAXPROCS(runtime.NumCPU()) |
@@ -39,7 +39,8 @@ var ( | |||
) | |||
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(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone), | |||
new(Mirror), new(Release), new(LoginSource), new(Webhook), |
@@ -236,10 +236,10 @@ func GetPublicKeyById(keyId int64) (*PublicKey, error) { | |||
return key, nil | |||
} | |||
// ListPublicKey returns a list of all public keys that user has. | |||
func ListPublicKey(uid int64) ([]*PublicKey, error) { | |||
// ListPublicKeys returns a list of public keys belongs to given user. | |||
func ListPublicKeys(uid int64) ([]*PublicKey, error) { | |||
keys := make([]*PublicKey, 0, 5) | |||
err := x.Find(&keys, &PublicKey{OwnerId: uid}) | |||
err := x.Where("owner_id=?", uid).Find(&keys) | |||
if err != nil { | |||
return nil, err | |||
} |
@@ -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. | |||
func SignedInId(header http.Header, sess session.Store) int64 { | |||
func SignedInId(req *http.Request, sess session.Store) int64 { | |||
if !models.HasEngine { | |||
return 0 | |||
} | |||
@@ -38,20 +38,38 @@ func SignedInId(header http.Header, sess session.Store) int64 { | |||
} | |||
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 | |||
} | |||
// 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 { | |||
return nil | |||
} | |||
uid := SignedInId(header, sess) | |||
uid := SignedInId(req, sess) | |||
if uid <= 0 { | |||
if setting.Service.EnableReverseProxyAuth { | |||
webAuthUser := header.Get(setting.ReverseProxyAuthUser) | |||
webAuthUser := req.Header.Get(setting.ReverseProxyAuthUser) | |||
if len(webAuthUser) > 0 { | |||
u, err := models.GetUserByName(webAuthUser) | |||
if err != nil { | |||
@@ -65,7 +83,7 @@ func SignedInUser(header http.Header, sess session.Store) *models.User { | |||
} | |||
// Check with basic auth. | |||
baHead := header.Get("Authorization") | |||
baHead := req.Header.Get("Authorization") | |||
if len(baHead) > 0 { | |||
auths := strings.Fields(baHead) | |||
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 { | |||
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)) | |||
} | |||
// 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) { | |||
var s []byte | |||
s, err = base64.StdEncoding.DecodeString(encoded) |
@@ -172,7 +172,7 @@ func Contexter() macaron.Handler { | |||
ctx.Data["PageStartTime"] = time.Now() | |||
// 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 { | |||
ctx.IsSigned = true |
@@ -1699,18 +1699,21 @@ The register and sign-in page style | |||
#repo-hooks-panel, | |||
#repo-hooks-history-panel, | |||
#user-social-panel, | |||
#user-applications-panel, | |||
#user-ssh-panel { | |||
margin-bottom: 20px; | |||
} | |||
#repo-hooks-panel .setting-list, | |||
#repo-hooks-history-panel .setting-list, | |||
#user-social-panel .setting-list, | |||
#user-applications-panel .setting-list, | |||
#user-ssh-panel .setting-list { | |||
background-color: #FFF; | |||
} | |||
#repo-hooks-panel .setting-list li, | |||
#repo-hooks-history-panel .setting-list li, | |||
#user-social-panel .setting-list li, | |||
#user-applications-panel .setting-list li, | |||
#user-ssh-panel .setting-list li { | |||
padding: 8px 20px; | |||
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-history-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 { | |||
background-color: #ffffEE; | |||
} | |||
#repo-hooks-panel .setting-list li i, | |||
#repo-hooks-history-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 { | |||
padding-right: 5px; | |||
} | |||
#repo-hooks-panel .active-icon, | |||
#repo-hooks-history-panel .active-icon, | |||
#user-social-panel .active-icon, | |||
#user-applications-panel .active-icon, | |||
#user-ssh-panel .active-icon { | |||
width: 10px; | |||
height: 10px; | |||
@@ -1741,43 +1747,60 @@ The register and sign-in page style | |||
#repo-hooks-panel .ssh-content, | |||
#repo-hooks-history-panel .ssh-content, | |||
#user-social-panel .ssh-content, | |||
#user-applications-panel .ssh-content, | |||
#user-ssh-panel .ssh-content { | |||
margin-left: 24px; | |||
} | |||
#repo-hooks-panel .ssh-content .octicon, | |||
#repo-hooks-history-panel .ssh-content .octicon, | |||
#user-social-panel .ssh-content .octicon, | |||
#user-applications-panel .ssh-content .octicon, | |||
#user-ssh-panel .ssh-content .octicon { | |||
margin-right: 4px; | |||
} | |||
#repo-hooks-panel .ssh-content .print, | |||
#repo-hooks-history-panel .ssh-content .print, | |||
#user-social-panel .ssh-content .print, | |||
#user-applications-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-history-panel .ssh-content .activity, | |||
#user-social-panel .ssh-content .activity, | |||
#user-applications-panel .ssh-content .activity, | |||
#user-ssh-panel .ssh-content .activity { | |||
color: #888; | |||
} | |||
#repo-hooks-panel .ssh-delete-btn, | |||
#repo-hooks-history-panel .ssh-delete-btn, | |||
#user-social-panel .ssh-delete-btn, | |||
#user-ssh-panel .ssh-delete-btn { | |||
#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 { | |||
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; | |||
} | |||
#user-ssh-add-form .panel-body { | |||
.form-settings-add .panel-body { | |||
background-color: #FFF; | |||
padding: 30px 0; | |||
} | |||
#user-ssh-add-form .ipt { | |||
.form-settings-add .ipt { | |||
width: 500px; | |||
} | |||
#user-ssh-add-form textarea { | |||
.form-settings-add textarea { | |||
height: 120px; | |||
margin-left: 3px; | |||
} | |||
#user-ssh-add-form .field { | |||
.form-settings-add .field { | |||
margin-bottom: 24px; | |||
} | |||
.pr-main { |
@@ -300,8 +300,11 @@ function initCore() { | |||
$.magnificPopup.close(); | |||
}); | |||
// Collapse. | |||
// Plugins. | |||
$('.collapse').hide(); | |||
$('.tipsy-tooltip').tipsy({ | |||
fade: true | |||
}); | |||
} | |||
function initUserSetting() { | |||
@@ -329,9 +332,9 @@ function initUserSetting() { | |||
$profile_form.submit(); | |||
}); | |||
// Show add SSH key panel. | |||
$('#ssh-add').click(function () { | |||
$('#user-ssh-add-form').removeClass("hide"); | |||
// Show panels. | |||
$('.show-form-btn').click(function () { | |||
$($(this).data('target-form')).removeClass("hide"); | |||
}); | |||
// Confirmation of delete account. |
@@ -68,6 +68,7 @@ | |||
#repo-hooks-panel, | |||
#repo-hooks-history-panel, | |||
#user-social-panel, | |||
#user-applications-panel, | |||
#user-ssh-panel { | |||
margin-bottom: 20px; | |||
.setting-list { | |||
@@ -97,16 +98,20 @@ | |||
margin-right: 4px; | |||
} | |||
.print, | |||
.access, | |||
.activity { | |||
color: #888; | |||
} | |||
.access { | |||
max-width: 500px; | |||
} | |||
} | |||
.ssh-delete-btn { | |||
.ssh-btn { | |||
margin-top: 6px; | |||
} | |||
} | |||
#user-ssh-add-form { | |||
.form-settings-add { | |||
.panel-body { | |||
background-color: #FFF; | |||
padding: 30px 0; |
@@ -18,13 +18,14 @@ import ( | |||
) | |||
const ( | |||
SETTINGS_PROFILE base.TplName = "user/settings/profile" | |||
SETTINGS_PASSWORD base.TplName = "user/settings/password" | |||
SETTINGS_SSH_KEYS base.TplName = "user/settings/sshkeys" | |||
SETTINGS_SOCIAL base.TplName = "user/settings/social" | |||
SETTINGS_DELETE base.TplName = "user/settings/delete" | |||
NOTIFICATION base.TplName = "user/notification" | |||
SECURITY base.TplName = "user/security" | |||
SETTINGS_PROFILE base.TplName = "user/settings/profile" | |||
SETTINGS_PASSWORD base.TplName = "user/settings/password" | |||
SETTINGS_SSH_KEYS base.TplName = "user/settings/sshkeys" | |||
SETTINGS_SOCIAL base.TplName = "user/settings/social" | |||
SETTINGS_APPLICATIONS base.TplName = "user/settings/applications" | |||
SETTINGS_DELETE base.TplName = "user/settings/delete" | |||
NOTIFICATION base.TplName = "user/notification" | |||
SECURITY base.TplName = "user/security" | |||
) | |||
func Settings(ctx *middleware.Context) { | |||
@@ -129,7 +130,7 @@ func SettingsSSHKeys(ctx *middleware.Context) { | |||
ctx.Data["PageIsSettingsSSHKeys"] = true | |||
var err error | |||
ctx.Data["Keys"], err = models.ListPublicKey(ctx.User.Id) | |||
ctx.Data["Keys"], err = models.ListPublicKeys(ctx.User.Id) | |||
if err != nil { | |||
ctx.Handle(500, "ssh.ListPublicKey", err) | |||
return | |||
@@ -144,7 +145,7 @@ func SettingsSSHKeysPost(ctx *middleware.Context, form auth.AddSSHKeyForm) { | |||
ctx.Data["PageIsSettingsSSHKeys"] = true | |||
var err error | |||
ctx.Data["Keys"], err = models.ListPublicKey(ctx.User.Id) | |||
ctx.Data["Keys"], err = models.ListPublicKeys(ctx.User.Id) | |||
if err != nil { | |||
ctx.Handle(500, "ssh.ListPublicKey", err) | |||
return | |||
@@ -235,6 +236,62 @@ func SettingsSocial(ctx *middleware.Context) { | |||
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) { | |||
ctx.Data["Title"] = ctx.Tr("settings") | |||
ctx.Data["PageIsUserSettings"] = true |
@@ -1 +1 @@ | |||
0.5.7.1110 Beta | |||
0.5.8.1112 Beta |
@@ -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 .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 .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> | |||
</ul> | |||
</div> |
@@ -9,7 +9,9 @@ | |||
<div id="user-ssh-setting-content"> | |||
<div id="user-ssh-panel" class="panel panel-radius"> | |||
<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> | |||
</div> | |||
<ul class="panel-body setting-list"> | |||
@@ -27,7 +29,7 @@ | |||
{{$.CsrfTokenHtml}} | |||
<input name="_method" type="hidden" value="DELETE"> | |||
<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> | |||
</li> | |||
{{end}} | |||
@@ -35,7 +37,7 @@ | |||
</div> | |||
<p>{{.i18n.Tr "settings.ssh_helper" "https://help.github.com/articles/generating-ssh-keys" "https://help.github.com/ssh-issues/" | Str2html}}</p> | |||
<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}} | |||
<p class="panel-header"><strong>{{.i18n.Tr "settings.add_new_key"}}</strong></p> | |||
<div class="panel-body"> |