m.Post("/email", bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost) | m.Post("/email", bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost) | ||||
m.Get("/password", user.SettingsPassword) | m.Get("/password", user.SettingsPassword) | ||||
m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost) | m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost) | ||||
m.Get("/ssh", user.SettingsSSHKeys) | |||||
m.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost) | |||||
m.Combo("/ssh").Get(user.SettingsSSHKeys). | |||||
Post(bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost) | |||||
m.Post("/ssh/delete", user.DeleteSSHKey) | |||||
m.Get("/social", user.SettingsSocial) | m.Get("/social", user.SettingsSocial) | ||||
m.Combo("/applications").Get(user.SettingsApplications). | m.Combo("/applications").Get(user.SettingsApplications). | ||||
Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost) | Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost) |
key_content = Content | key_content = Content | ||||
add_key_success = New SSH key '%s' has been added successfully! | add_key_success = New SSH key '%s' has been added successfully! | ||||
delete_key = Delete | delete_key = Delete | ||||
ssh_key_deletion = SSH Key Deletion | |||||
ssh_key_deletion_desc = Delete this SSH key will remove all related accesses for your account. Do you want to continue? | |||||
ssh_key_deletion_success = SSH key has been deleted successfully! | |||||
add_on = Added on | add_on = Added on | ||||
last_used = Last used on | last_used = Last used on | ||||
no_activity = No recent activity | no_activity = No recent activity |
} | } | ||||
// DeletePublicKey deletes SSH key information both in database and authorized_keys file. | // DeletePublicKey deletes SSH key information both in database and authorized_keys file. | ||||
func DeletePublicKey(key *PublicKey) (err error) { | |||||
func DeletePublicKey(id int64) (err error) { | |||||
key := &PublicKey{ID: id} | |||||
has, err := x.Id(key.ID).Get(key) | |||||
if err != nil { | |||||
return err | |||||
} else if !has { | |||||
return nil | |||||
} | |||||
sess := x.NewSession() | sess := x.NewSession() | ||||
defer sessionRelease(sess) | defer sessionRelease(sess) | ||||
if err = sess.Begin(); err != nil { | if err = sess.Begin(); err != nil { |
ctx.Data["Title"] = ctx.Tr("repo.settings") | ctx.Data["Title"] = ctx.Tr("repo.settings") | ||||
ctx.Data["PageIsSettingsKeys"] = true | ctx.Data["PageIsSettingsKeys"] = true | ||||
keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID) | |||||
if err != nil { | |||||
ctx.Handle(500, "ListDeployKeys", err) | |||||
return | |||||
} | |||||
ctx.Data["Deploykeys"] = keys | |||||
if ctx.HasError() { | if ctx.HasError() { | ||||
ctx.HTML(200, DEPLOY_KEYS) | ctx.HTML(200, DEPLOY_KEYS) | ||||
return | return |
ctx.Data["Title"] = ctx.Tr("settings") | ctx.Data["Title"] = ctx.Tr("settings") | ||||
ctx.Data["PageIsSettingsSSHKeys"] = true | ctx.Data["PageIsSettingsSSHKeys"] = true | ||||
var err error | |||||
ctx.Data["Keys"], err = models.ListPublicKeys(ctx.User.Id) | |||||
keys, err := models.ListPublicKeys(ctx.User.Id) | |||||
if err != nil { | if err != nil { | ||||
ctx.Handle(500, "ssh.ListPublicKey", err) | |||||
ctx.Handle(500, "ListPublicKeys", err) | |||||
return | return | ||||
} | } | ||||
ctx.Data["Keys"] = keys | |||||
ctx.HTML(200, SETTINGS_SSH_KEYS) | ctx.HTML(200, SETTINGS_SSH_KEYS) | ||||
} | } | ||||
ctx.Data["Title"] = ctx.Tr("settings") | ctx.Data["Title"] = ctx.Tr("settings") | ||||
ctx.Data["PageIsSettingsSSHKeys"] = true | ctx.Data["PageIsSettingsSSHKeys"] = true | ||||
var err error | |||||
ctx.Data["Keys"], err = models.ListPublicKeys(ctx.User.Id) | |||||
keys, err := models.ListPublicKeys(ctx.User.Id) | |||||
if err != nil { | if err != nil { | ||||
ctx.Handle(500, "ssh.ListPublicKey", err) | |||||
ctx.Handle(500, "ListPublicKeys", err) | |||||
return | return | ||||
} | } | ||||
ctx.Data["Keys"] = keys | |||||
// Delete SSH key. | |||||
if ctx.Query("_method") == "DELETE" { | |||||
id := com.StrTo(ctx.Query("id")).MustInt64() | |||||
if id <= 0 { | |||||
return | |||||
} | |||||
if ctx.HasError() { | |||||
ctx.HTML(200, SETTINGS_SSH_KEYS) | |||||
return | |||||
} | |||||
if err = models.DeletePublicKey(&models.PublicKey{ID: id}); err != nil { | |||||
ctx.Handle(500, "DeletePublicKey", err) | |||||
content, err := models.CheckPublicKeyString(form.Content) | |||||
if err != nil { | |||||
if err == models.ErrKeyUnableVerify { | |||||
ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key")) | |||||
} else { | } else { | ||||
log.Trace("SSH key deleted: %s", ctx.User.Name) | |||||
ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error())) | |||||
ctx.Redirect(setting.AppSubUrl + "/user/settings/ssh") | ctx.Redirect(setting.AppSubUrl + "/user/settings/ssh") | ||||
return | |||||
} | } | ||||
return | |||||
} | } | ||||
// Add new SSH key. | |||||
if ctx.Req.Method == "POST" { | |||||
if ctx.HasError() { | |||||
ctx.HTML(200, SETTINGS_SSH_KEYS) | |||||
return | |||||
if err = models.AddPublicKey(ctx.User.Id, form.Title, content); err != nil { | |||||
ctx.Data["HasError"] = true | |||||
switch { | |||||
case models.IsErrKeyAlreadyExist(err): | |||||
ctx.Data["Err_Content"] = true | |||||
ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), SETTINGS_SSH_KEYS, &form) | |||||
case models.IsErrKeyNameAlreadyUsed(err): | |||||
ctx.Data["Err_Title"] = true | |||||
ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), SETTINGS_SSH_KEYS, &form) | |||||
default: | |||||
ctx.Handle(500, "AddPublicKey", err) | |||||
} | } | ||||
return | |||||
} | |||||
content, err := models.CheckPublicKeyString(form.Content) | |||||
if err != nil { | |||||
if err == models.ErrKeyUnableVerify { | |||||
ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key")) | |||||
} else { | |||||
ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error())) | |||||
ctx.Redirect(setting.AppSubUrl + "/user/settings/ssh") | |||||
return | |||||
} | |||||
} | |||||
ctx.Flash.Success(ctx.Tr("settings.add_key_success", form.Title)) | |||||
ctx.Redirect(setting.AppSubUrl + "/user/settings/ssh") | |||||
} | |||||
if err = models.AddPublicKey(ctx.User.Id, form.Title, content); err != nil { | |||||
switch { | |||||
case models.IsErrKeyAlreadyExist(err): | |||||
ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), SETTINGS_SSH_KEYS, &form) | |||||
case models.IsErrKeyNameAlreadyUsed(err): | |||||
ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), SETTINGS_SSH_KEYS, &form) | |||||
default: | |||||
ctx.Handle(500, "AddPublicKey", err) | |||||
} | |||||
return | |||||
} else { | |||||
log.Trace("SSH key added: %s", ctx.User.Name) | |||||
ctx.Flash.Success(ctx.Tr("settings.add_key_success", form.Title)) | |||||
ctx.Redirect(setting.AppSubUrl + "/user/settings/ssh") | |||||
return | |||||
} | |||||
func DeleteSSHKey(ctx *middleware.Context) { | |||||
if err := models.DeletePublicKey(ctx.QueryInt64("id")); err != nil { | |||||
ctx.Flash.Error("DeletePublicKey: " + err.Error()) | |||||
} else { | |||||
ctx.Flash.Success(ctx.Tr("settings.ssh_key_deletion_success")) | |||||
} | } | ||||
ctx.HTML(200, SETTINGS_SSH_KEYS) | |||||
ctx.JSON(200, map[string]interface{}{ | |||||
"redirect": setting.AppSubUrl + "/user/settings/ssh", | |||||
}) | |||||
} | } | ||||
func SettingsSocial(ctx *middleware.Context) { | func SettingsSocial(ctx *middleware.Context) { | ||||
ctx.Data["PageIsSettingsApplications"] = true | ctx.Data["PageIsSettingsApplications"] = true | ||||
if ctx.HasError() { | if ctx.HasError() { | ||||
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) | ctx.HTML(200, SETTINGS_APPLICATIONS) | ||||
return | return | ||||
} | } |
<form class="ui form" action="{{.Link}}" method="post"> | <form class="ui form" action="{{.Link}}" method="post"> | ||||
{{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
<div class="field {{if .Err_Title}}error{{end}}"> | <div class="field {{if .Err_Title}}error{{end}}"> | ||||
<label>{{.i18n.Tr "repo.settings.title"}}</label> | |||||
<input name="title" value="{{.title}}" autofocus required> | |||||
<label for="title">{{.i18n.Tr "repo.settings.title"}}</label> | |||||
<input id="title" name="title" value="{{.title}}" autofocus required> | |||||
</div> | </div> | ||||
<div class="field {{if .Err_Content}}error{{end}}"> | <div class="field {{if .Err_Content}}error{{end}}"> | ||||
<label>{{.i18n.Tr "repo.settings.deploy_key_content"}}</label> | |||||
<textarea name="content" required>{{.content}}</textarea> | |||||
<label for="content">{{.i18n.Tr "repo.settings.deploy_key_content"}}</label> | |||||
<textarea id="content" name="content" required>{{.content}}</textarea> | |||||
</div> | </div> | ||||
<button class="ui green button"> | <button class="ui green button"> | ||||
{{.i18n.Tr "repo.settings.add_deploy_key"}} | {{.i18n.Tr "repo.settings.add_deploy_key"}} |
<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 .PageIsSettingsEmails}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/email">{{.i18n.Tr "settings.emails"}}</a></li> | <li {{if .PageIsSettingsEmails}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/email">{{.i18n.Tr "settings.emails"}}</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> | ||||
{{if .HasOAuthService}} | |||||
<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> | ||||
{{end}} | |||||
<li {{if .PageIsSettingsApplications}}class="current"{{end}}><a href="{{AppSubUrl}}/user/settings/applications">{{.i18n.Tr "settings.applications"}}</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> |
{{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="user-ssh-setting-content"> | |||||
<div id="user-ssh-panel" class="panel panel-radius"> | |||||
<div class="panel-header"> | |||||
<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"> | |||||
<li>{{.i18n.Tr "settings.ssh_desc"}}</li> | |||||
{{range .Keys}} | |||||
<li class="ssh clear"> | |||||
<span class="active-icon left label label-{{if .HasRecentActivity}}green{{else}}gray{{end}} label-radius"></span> | |||||
<i class="mega-octicon octicon-key left"></i> | |||||
<div class="ssh-content left"> | |||||
<p><strong>{{.Name}}</strong></p> | |||||
<p class="print">{{.Fingerprint}}</p> | |||||
<p class="activity"><i>{{$.i18n.Tr "settings.add_on"}} <span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span> — <i class="octicon octicon-info"></i>{{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} <span title="{{DateFmtLong .Updated}}">{{DateFmtShort .Updated}}</span>{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}}</i></p> | |||||
</div> | |||||
<form action="{{AppSubUrl}}/user/settings/ssh" method="post"> | |||||
{{$.CsrfTokenHtml}} | |||||
<input name="_method" type="hidden" value="DELETE"> | |||||
<input name="id" type="hidden" value="{{.ID}}"> | |||||
<button class="right ssh-btn btn btn-red btn-radius btn-small">{{$.i18n.Tr "settings.delete_key"}}</button> | |||||
</form> | |||||
</li> | |||||
{{end}} | |||||
</ul> | |||||
</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 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"> | |||||
<p class="field"> | |||||
<label class="req" for="ssh-title">{{.i18n.Tr "settings.key_name"}}</label> | |||||
<input class="ipt ipt-radius" id="ssh-title" name="title" type="text" required /> | |||||
</p> | |||||
<p class="field clear"> | |||||
<label class="left req" for="ssh-key">{{.i18n.Tr "settings.key_content"}}</label> | |||||
<textarea class="ipt ipt-radius left" name="content" id="ssh-key" required></textarea> | |||||
</p> | |||||
<p class="field"> | |||||
<label></label> | |||||
<button class="btn btn-green btn-radius" id="ssh-add-btn">{{.i18n.Tr "settings.add_key"}}</button> | |||||
</p> | |||||
</div> | |||||
</form> | |||||
{{template "base/head" .}} | |||||
<div class="user settings"> | |||||
<div class="ui container"> | |||||
<div class="ui grid"> | |||||
{{template "user/settings/navbar" .}} | |||||
<div class="twelve wide column content"> | |||||
{{template "base/alert" .}} | |||||
<h4 class="ui top attached header"> | |||||
{{.i18n.Tr "settings.manage_ssh_keys"}} | |||||
<div class="ui right"> | |||||
<div class="ui blue tiny show-panel button" data-panel="#add-ssh-key-panel">{{.i18n.Tr "settings.add_key"}}</div> | |||||
</div> | |||||
</h4> | |||||
<div class="ui attached segment"> | |||||
<div class="ui key list"> | |||||
<div class="item"> | |||||
{{.i18n.Tr "settings.ssh_desc"}} | |||||
</div> | |||||
{{range .Keys}} | |||||
<div class="item ui grid"> | |||||
<div class="one wide column"> | |||||
<i class="ssh-key-state-indicator fa fa-circle{{if .HasRecentActivity}} active invert poping up{{else}}-o{{end}}" {{if .HasRecentActivity}}data-content="{{$.i18n.Tr "settings.key_state_desc"}}" data-variation="inverted"{{end}}></i> | |||||
</div> | |||||
<div class="one wide column"> | |||||
<i class="mega-octicon octicon-key left"></i> | |||||
</div> | |||||
<div class="eleven wide column"> | |||||
<strong>{{.Name}}</strong> | |||||
<div class="print meta"> | |||||
{{.Fingerprint}} | |||||
</div> | |||||
<div class="activity meta"> | |||||
<i>{{$.i18n.Tr "settings.add_on"}} <span>{{DateFmtShort .Created}}</span> — <i class="octicon octicon-info"></i> {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} <span>{{DateFmtShort .Updated}}</span>{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}}</i> | |||||
</div> | </div> | ||||
</div> | |||||
<div class="two wide column"> | |||||
<button class="ui red tiny button delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}"> | |||||
{{$.i18n.Tr "settings.delete_key"}} | |||||
</button> | |||||
</div> | |||||
</div> | </div> | ||||
{{end}} | |||||
</div> | |||||
</div> | |||||
<br> | |||||
<p>{{.i18n.Tr "settings.ssh_helper" "https://help.github.com/articles/generating-ssh-keys" "https://help.github.com/ssh-issues/" | Str2html}}</p> | |||||
<div {{if not .HasError}}class="hide"{{end}} id="add-ssh-key-panel"> | |||||
<h4 class="ui top attached header"> | |||||
{{.i18n.Tr "settings.add_new_key"}} | |||||
</h4> | |||||
<div class="ui attached segment"> | |||||
<form class="ui form" action="{{.Link}}" method="post"> | |||||
{{.CsrfTokenHtml}} | |||||
<div class="field {{if .Err_Title}}error{{end}}"> | |||||
<label for="title">{{.i18n.Tr "settings.key_name"}}</label> | |||||
<input id="title" name="title" value="{{.title}}" autofocus required> | |||||
</div> | |||||
<div class="field {{if .Err_Content}}error{{end}}"> | |||||
<label for="content">{{.i18n.Tr "settings.key_content"}}</label> | |||||
<textarea id="content" name="content" required>{{.content}}</textarea> | |||||
</div> | |||||
<button class="ui green button"> | |||||
{{.i18n.Tr "settings.add_key"}} | |||||
</button> | |||||
</form> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="ui small basic delete modal"> | |||||
<div class="ui icon header"> | |||||
<i class="trash icon"></i> | |||||
{{.i18n.Tr "settings.ssh_key_deletion"}} | |||||
</div> | |||||
<div class="content"> | |||||
<p>{{.i18n.Tr "settings.ssh_key_deletion_desc"}}</p> | |||||
</div> | |||||
<div class="actions"> | |||||
<div class="ui red basic inverted cancel button"> | |||||
<i class="remove icon"></i> | |||||
{{.i18n.Tr "modal.no"}} | |||||
</div> | |||||
<div class="ui green basic inverted ok button"> | |||||
<i class="checkmark icon"></i> | |||||
{{.i18n.Tr "modal.yes"}} | |||||
</div> | </div> | ||||
</div> | |||||
</div> | </div> | ||||
{{template "ng/base/footer" .}} | |||||
{{template "base/footer" .}} |