@@ -217,6 +217,7 @@ func runWeb(ctx *cli.Context) { | |||
m.Get("", user.Settings) | |||
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost) | |||
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar) | |||
m.Post("/avatar/delete", user.SettingsDeleteAvatar) | |||
m.Combo("/email").Get(user.SettingsEmails). | |||
Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost) | |||
m.Post("/email/delete", user.DeleteEmail) | |||
@@ -264,11 +264,10 @@ continue = Continue | |||
cancel = Cancel | |||
enable_custom_avatar = Enable Custom Avatar | |||
enable_custom_avatar_helper = Disable fetch from Gravatar | |||
choose_new_avatar = Choose new avatar | |||
update_avatar = Update Avatar Setting | |||
delete_current_avatar = Delete Current Avatar | |||
uploaded_avatar_not_a_image = Uploaded file is not a image. | |||
no_custom_avatar_available = No custom avatar available, cannot enable it. | |||
update_avatar_success = Your avatar setting has been updated successfully. | |||
change_password = Change Password | |||
@@ -17,7 +17,7 @@ import ( | |||
"github.com/gogits/gogs/modules/setting" | |||
) | |||
const APP_VER = "0.8.57.0304" | |||
const APP_VER = "0.8.57.0305" | |||
func init() { | |||
runtime.GOMAXPROCS(runtime.NumCPU()) | |||
@@ -617,7 +617,7 @@ func ChangeUserName(u *User, newUserName string) (err error) { | |||
} | |||
func updateUser(e Engine, u *User) error { | |||
// Organization does not need e-mail. | |||
// Organization does not need email | |||
if !u.IsOrganization() { | |||
u.Email = strings.ToLower(u.Email) | |||
has, err := e.Where("id!=?", u.Id).And("type=?", u.Type).And("email=?", u.Email).Get(new(User)) | |||
@@ -634,16 +634,9 @@ func updateUser(e Engine, u *User) error { | |||
} | |||
u.LowerName = strings.ToLower(u.Name) | |||
if len(u.Location) > 255 { | |||
u.Location = u.Location[:255] | |||
} | |||
if len(u.Website) > 255 { | |||
u.Website = u.Website[:255] | |||
} | |||
if len(u.Description) > 255 { | |||
u.Description = u.Description[:255] | |||
} | |||
u.Location = base.TruncateString(u.Location, 255) | |||
u.Website = base.TruncateString(u.Website, 255) | |||
u.Description = base.TruncateString(u.Description, 255) | |||
u.FullName = markdown.Sanitizer.Sanitize(u.FullName) | |||
_, err := e.Id(u.Id).AllCols().Update(u) | |||
@@ -464,6 +464,15 @@ func EllipsisString(str string, length int) string { | |||
return str[:length-3] + "..." | |||
} | |||
// TruncateString returns a truncated string with given limit, | |||
// it returns input string if length is not reached limit. | |||
func TruncateString(str string, limit int) string { | |||
if len(str) < limit { | |||
return str | |||
} | |||
return str[:limit] | |||
} | |||
// StringsToInt64s converts a slice of string to a slice of int64. | |||
func StringsToInt64s(strs []string) []int64 { | |||
ints := make([]int64, len(strs)) | |||
@@ -18,7 +18,7 @@ function initCommentPreviewTab($form) { | |||
var $preview_tab = $form.find('.tab.segment[data-tab="' + $tab_menu.data('preview') + '"]'); | |||
$preview_tab.html(data); | |||
emojify.run($preview_tab[0]); | |||
$('pre code', $preview_tab[0]).each(function(i, block) { | |||
$('pre code', $preview_tab[0]).each(function (i, block) { | |||
hljs.highlightBlock(block); | |||
}); | |||
} | |||
@@ -322,8 +322,7 @@ function initRepository() { | |||
}; | |||
$('#edit-title').click(editTitleToggle); | |||
$('#cancel-edit-title').click(editTitleToggle); | |||
$('#save-edit-title').click(editTitleToggle). | |||
click(function () { | |||
$('#save-edit-title').click(editTitleToggle).click(function () { | |||
if ($edit_input.val().length == 0 || | |||
$edit_input.val() == $issue_title.text()) { | |||
$edit_input.val($issue_title.text()); | |||
@@ -385,7 +384,7 @@ function initRepository() { | |||
} else { | |||
$render_content.html(data.content); | |||
emojify.run($render_content[0]); | |||
$('pre code', $render_content[0]).each(function(i, block) { | |||
$('pre code', $render_content[0]).each(function (i, block) { | |||
hljs.highlightBlock(block); | |||
}); | |||
} | |||
@@ -521,10 +520,8 @@ function initOrganization() { | |||
} | |||
} | |||
function initUser() { | |||
if ($('.user').length == 0) { | |||
return; | |||
} | |||
function initUserSettings() { | |||
console.log('initUserSettings'); | |||
// Options | |||
if ($('.user.settings.profile').length > 0) { | |||
@@ -796,10 +793,7 @@ $(document).ready(function () { | |||
// Show exact time | |||
$('.time-since').each(function () { | |||
$(this).addClass('poping up'). | |||
attr('data-content', $(this).attr('title')). | |||
attr('data-variation', 'inverted tiny'). | |||
attr('title', ''); | |||
$(this).addClass('poping up').attr('data-content', $(this).attr('title')).attr('data-variation', 'inverted tiny').attr('title', ''); | |||
}); | |||
// Semantic UI modules. | |||
@@ -879,8 +873,8 @@ $(document).ready(function () { | |||
ignore_emoticons: true | |||
}); | |||
var hasEmoji = document.getElementsByClassName('has-emoji'); | |||
for (var i = 0; i < hasEmoji.length; i++) { | |||
emojify.run(hasEmoji[i]); | |||
for (var i = 0; i < hasEmoji.length; i++) { | |||
emojify.run(hasEmoji[i]); | |||
} | |||
// Clipboard JS | |||
@@ -928,6 +922,14 @@ $(document).ready(function () { | |||
$('.show-modal.button').click(function () { | |||
$($(this).data('modal')).modal('show'); | |||
}); | |||
$('.delete-post.button').click(function(){ | |||
var $this = $(this); | |||
$.post($this.data('request-url'),{ | |||
"_csrf": csrf | |||
}).done(function(){ | |||
window.location.href = $this.data('done-url'); | |||
}); | |||
}); | |||
// Set anchor. | |||
$('.markdown').each(function () { | |||
@@ -953,15 +955,25 @@ $(document).ready(function () { | |||
searchUsers(); | |||
searchRepositories(); | |||
initCommentForm(); | |||
initInstall(); | |||
initRepository(); | |||
initWiki(); | |||
initOrganization(); | |||
initUser(); | |||
initWebhook(); | |||
initAdmin(); | |||
var routes = { | |||
'div.user.settings': initUserSettings | |||
}; | |||
var selector; | |||
for (selector in routes) { | |||
if ($(selector).length > 0) { | |||
routes[selector](); | |||
break; | |||
} | |||
} | |||
}); | |||
$(window).load(function () { | |||
@@ -1053,11 +1065,12 @@ $(window).load(function () { | |||
case 'ssh': | |||
if ($('#repo-clone-ssh').click().length === 0) { | |||
$('#repo-clone-https').click(); | |||
}; | |||
} | |||
; | |||
break; | |||
default: | |||
$('#repo-clone-https').click(); | |||
break; | |||
} | |||
} | |||
}); | |||
}); |
@@ -8,6 +8,7 @@ import ( | |||
"errors" | |||
"fmt" | |||
"io/ioutil" | |||
"os" | |||
"strings" | |||
"github.com/Unknwon/com" | |||
@@ -39,12 +40,13 @@ func Settings(ctx *middleware.Context) { | |||
ctx.HTML(200, SETTINGS_PROFILE) | |||
} | |||
func handlerUsernameChange(ctx *middleware.Context, newName string) { | |||
func handleUsernameChange(ctx *middleware.Context, newName string) { | |||
// Non-local users are not allowed to change their username. | |||
if len(newName) == 0 || !ctx.User.IsLocal() { | |||
return | |||
} | |||
// Check if user name has been changed. | |||
// Check if user name has been changed | |||
if ctx.User.LowerName != strings.ToLower(newName) { | |||
if err := models.ChangeUserName(ctx.User, newName); err != nil { | |||
switch { | |||
@@ -67,7 +69,8 @@ func handlerUsernameChange(ctx *middleware.Context, newName string) { | |||
} | |||
log.Trace("User name changed: %s -> %s", ctx.User.Name, newName) | |||
} | |||
// In case it's just a case change. | |||
// In case it's just a case change | |||
ctx.User.Name = newName | |||
ctx.User.LowerName = strings.ToLower(newName) | |||
} | |||
@@ -81,7 +84,7 @@ func SettingsPost(ctx *middleware.Context, form auth.UpdateProfileForm) { | |||
return | |||
} | |||
handlerUsernameChange(ctx, form.Name) | |||
handleUsernameChange(ctx, form.Name) | |||
if ctx.Written() { | |||
return | |||
} | |||
@@ -98,7 +101,8 @@ func SettingsPost(ctx *middleware.Context, form auth.UpdateProfileForm) { | |||
ctx.Handle(500, "UpdateUser", err) | |||
return | |||
} | |||
log.Trace("User setting updated: %s", ctx.User.Name) | |||
log.Trace("User settings updated: %s", ctx.User.Name) | |||
ctx.Flash.Success(ctx.Tr("settings.update_profile_success")) | |||
ctx.Redirect(setting.AppSubUrl + "/user/settings") | |||
} | |||
@@ -112,10 +116,11 @@ func UpdateAvatarSetting(ctx *middleware.Context, form auth.UploadAvatarForm, ct | |||
if err != nil { | |||
return fmt.Errorf("Avatar.Open: %v", err) | |||
} | |||
defer fr.Close() | |||
data, err := ioutil.ReadAll(fr) | |||
if err != nil { | |||
return fmt.Errorf("ReadAll: %v", err) | |||
return fmt.Errorf("ioutil.ReadAll: %v", err) | |||
} | |||
if _, ok := base.IsImageFile(data); !ok { | |||
return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image")) | |||
@@ -124,9 +129,12 @@ func UpdateAvatarSetting(ctx *middleware.Context, form auth.UploadAvatarForm, ct | |||
return fmt.Errorf("UploadAvatar: %v", err) | |||
} | |||
} else { | |||
// In case no avatar at all. | |||
if form.Enable && !com.IsFile(ctx.User.CustomAvatarPath()) { | |||
return errors.New(ctx.Tr("settings.no_custom_avatar_available")) | |||
// No avatar is uploaded but setting has been changed to enable, | |||
// generate a random one when needed. | |||
if form.Enable && !com.IsFile(ctxUser.CustomAvatarPath()) { | |||
if err := ctxUser.GenerateRandomAvatar(); err != nil { | |||
log.Error(4, "GenerateRandomAvatar[%d]: %v", ctxUser.Id, err) | |||
} | |||
} | |||
} | |||
@@ -147,6 +155,16 @@ func SettingsAvatar(ctx *middleware.Context, form auth.UploadAvatarForm) { | |||
ctx.Redirect(setting.AppSubUrl + "/user/settings") | |||
} | |||
func SettingsDeleteAvatar(ctx *middleware.Context) { | |||
os.Remove(ctx.User.CustomAvatarPath()) | |||
ctx.User.UseCustomAvatar = false | |||
if err := models.UpdateUser(ctx.User); err != nil { | |||
ctx.Flash.Error(fmt.Sprintf("UpdateUser: %v", err)) | |||
} | |||
ctx.Redirect(setting.AppSubUrl + "/user/settings") | |||
} | |||
func SettingsPassword(ctx *middleware.Context) { | |||
ctx.Data["Title"] = ctx.Tr("settings") | |||
ctx.Data["PageIsSettingsPassword"] = true | |||
@@ -1 +1 @@ | |||
0.8.57.0304 | |||
0.8.57.0305 |
@@ -52,10 +52,9 @@ | |||
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data"> | |||
{{.CsrfTokenHtml}} | |||
<div class="inline field"> | |||
<label>{{.i18n.Tr "settings.enable_custom_avatar"}}</label> | |||
<div class="ui checkbox"> | |||
<input name="enable" type="checkbox" {{if .SignedUser.UseCustomAvatar}}checked{{end}}> | |||
<label>{{.i18n.Tr "settings.enable_custom_avatar_helper"}}</label> | |||
<label>{{.i18n.Tr "settings.enable_custom_avatar"}}</label> | |||
</div> | |||
</div> | |||
<div class="inline field"> | |||
@@ -65,6 +64,7 @@ | |||
<div class="field"> | |||
<button class="ui green button">{{$.i18n.Tr "settings.update_avatar"}}</button> | |||
<a class="ui red button delete-post" data-request-url="{{.Link}}/avatar/delete" data-done-url="{{.Link}}">{{$.i18n.Tr "settings.delete_current_avatar"}}</a> | |||
</div> | |||
</form> | |||
</div> | |||