| @@ -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> | |||