Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
dfe0183
Add support for federated avatars
strk May 21, 2016
e419ad5
Run gofmt on all modified files
strk Jun 27, 2016
7083bc4
Move Avatar form in its own page
strk Jun 27, 2016
263c4c5
Add go-libravatar dependency to vendor/ dir
strk Jun 28, 2016
2f19a11
Revert "Add go-libravatar dependency to vendor/ dir"
strk Jul 2, 2016
b1e900d
Make federated avatar setting a global configuration
strk Jul 2, 2016
3bc34b9
Move avatar handling back to base tool, disable federated avatar in o…
strk Jul 2, 2016
a9089ab
Format, handle error
strk Jul 2, 2016
eb02cc6
Properly set fallback host
strk Jul 2, 2016
34364fd
Use unsupported github.com mirror for importing go-libravatar
strk Jul 3, 2016
f0c5e34
Remove comment showing life exists outside of github.com
strk Jul 17, 2016
d4e1cf5
Use Combo for Get and Post methods over /avatar
strk Aug 5, 2016
9c0d4e6
FEDERATED_AVATAR -> ENABLE_FEDERATED_AVATAR
strk Aug 5, 2016
c51eeb5
Fix persistance of federated avatar lookup checkbox at install time
strk Aug 5, 2016
7899268
Federated Avatars -> Enable Federated Avatars
strk Aug 5, 2016
c6f3a53
Use len(string) == 0 instead of string == ""
strk Aug 5, 2016
1c15251
Move import line where it belong
strk Aug 5, 2016
f5eb2bd
Save a line (and waste much more expensive time)
strk Aug 5, 2016
c6ae15b
Remove redundant parens
strk Aug 5, 2016
8fd46fd
Remove an empty line
strk Aug 5, 2016
1bcd929
Remove empty lines
strk Aug 5, 2016
0ef1ca8
Reorder lines to make diff smaller
strk Aug 5, 2016
62571de
Remove another newline
strk Aug 5, 2016
ede0577
Move DISABLE_GRAVATAR and ENABLE_FEDERATED_AVATAR after OFFLINE_MODE
strk Aug 5, 2016
856d773
Remove newlines that weren't there before my intervention
strk Aug 5, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cmd/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ func runWeb(ctx *cli.Context) error {
m.Group("/user/settings", func() {
m.Get("", user.Settings)
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar)
m.Combo("/avatar").Get(user.SettingsAvatar).
Post(binding.MultipartForm(auth.AvatarForm{}), user.SettingsAvatarPost)
m.Post("/avatar/delete", user.SettingsDeleteAvatar)
m.Combo("/email").Get(user.SettingsEmails).
Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost)
Expand Down Expand Up @@ -375,7 +376,7 @@ func runWeb(ctx *cli.Context) error {
m.Group("/settings", func() {
m.Combo("").Get(org.Settings).
Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), org.SettingsAvatar)
m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), org.SettingsAvatar)
m.Post("/avatar/delete", org.SettingsDeleteAvatar)

m.Group("/hooks", func() {
Expand Down
3 changes: 3 additions & 0 deletions conf/app.ini
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ AVATAR_UPLOAD_PATH = data/avatars
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
GRAVATAR_SOURCE = gravatar
DISABLE_GRAVATAR = false
; Federated avatar lookup uses DNS to discover avatar associated
; with emails, see http://www.libravatar.org
ENABLE_FEDERATED_AVATAR = false

[attachment]
; Whether attachments are enabled. Defaults to `true`
Expand Down
7 changes: 6 additions & 1 deletion conf/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ offline_mode = Enable Offline Mode
offline_mode_popup = Disable CDN even in production mode, all resource files will be served locally.
disable_gravatar = Disable Gravatar Service
disable_gravatar_popup = Disable Gravatar and custom sources, all avatars are uploaded by users or default.
federated_avatar_lookup = Enable Federated Avatars Lookup
disable_registration = Disable Self-registration
disable_registration_popup = Disable user self-registration, only admin can create accounts.
enable_captcha = Enable Captcha
Expand Down Expand Up @@ -239,6 +240,7 @@ form.name_pattern_not_allowed = Username pattern '%s' is not allowed.
[settings]
profile = Profile
password = Password
avatar = Avatar
ssh_keys = SSH Keys
social = Social Accounts
applications = Applications
Expand All @@ -259,7 +261,9 @@ change_username_prompt = This change will affect the way how links relate to you
continue = Continue
cancel = Cancel

enable_custom_avatar = Enable Custom Avatar
lookup_avatar_by_mail = Lookup Avatar by mail
federated_avatar_lookup = Federated Avatar Lookup
enable_custom_avatar = Use Custom Avatar
choose_new_avatar = Choose new avatar
update_avatar = Update Avatar Setting
delete_current_avatar = Delete Current Avatar
Expand Down Expand Up @@ -1044,6 +1048,7 @@ config.cookie_life_time = Cookie Life Time
config.picture_config = Picture Configuration
config.picture_service = Picture Service
config.disable_gravatar = Disable Gravatar
config.enable_federated_avatar = Enable Federated Avatars
config.log_config = Log Configuration
config.log_mode = Log Mode

Expand Down
2 changes: 1 addition & 1 deletion models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ func (u *User) RelAvatarLink() string {

return "/avatars/" + com.ToStr(u.ID)
}
return setting.GravatarSource + u.Avatar
return base.AvatarLink(u.AvatarEmail)
}

// AvatarLink returns user avatar link.
Expand Down
27 changes: 17 additions & 10 deletions modules/auth/user_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ type InstallForm struct {
RegisterConfirm bool
MailNotify bool

OfflineMode bool
DisableGravatar bool
DisableRegistration bool
EnableCaptcha bool
RequireSignInView bool
OfflineMode bool
DisableGravatar bool
EnableFederatedAvatar bool
DisableRegistration bool
EnableCaptcha bool
RequireSignInView bool

AdminName string `binding:"OmitEmpty;AlphaDashDot;MaxSize(30)" locale:"install.admin_name"`
AdminPasswd string `binding:"OmitEmpty;MaxSize(255)" locale:"install.admin_password"`
Expand Down Expand Up @@ -93,19 +94,25 @@ type UpdateProfileForm struct {
Email string `binding:"Required;Email;MaxSize(254)"`
Website string `binding:"Url;MaxSize(100)"`
Location string `binding:"MaxSize(50)"`
Gravatar string `binding:"OmitEmpty;Email;MaxSize(254)"`
}

func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

type UploadAvatarForm struct {
Enable bool
Avatar *multipart.FileHeader
const (
AVATAR_LOCAL string = "local"
AVATAR_BYMAIL string = "bymail"
)

type AvatarForm struct {
Source string
Avatar *multipart.FileHeader
Gravatar string `binding:"OmitEmpty;Email;MaxSize(254)"`
Federavatar bool
}

func (f *UploadAvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
func (f *AvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

Expand Down
22 changes: 18 additions & 4 deletions modules/base/tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,26 @@ func HashEmail(email string) string {
}

// AvatarLink returns avatar link by given email.
func AvatarLink(email string) string {
if setting.DisableGravatar || setting.OfflineMode {
return setting.AppSubUrl + "/img/avatar_default.png"
func AvatarLink(email string) (url string) {

if !setting.OfflineMode {
if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think OfflineMode should only be checked in setting.go, so when it is true, setting.EnableFederatedAvatar should be force to be false at first place. Then continue avatar option check in this AvatarLink function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Note that I didn't change that to keep focused on the topic of this PR. What you suggest is also valid in current develop branch (no need to check both OfflineMode and DisableGravatar). How about fixing that in develop so that I then clean it up here on next rebase ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will do the fix if the code actually works, but just coding style issues. You can leave it to me.

var err error
url, err = setting.LibravatarService.FromEmail(email)
if err != nil {
log.Error(1, "LibravatarService.FromEmail:: %v", err)
}
}
if len(url) == 0 && !setting.DisableGravatar {
url = setting.GravatarSource + HashEmail(email)
}
}

if len(url) == 0 {
url = setting.AppSubUrl + "/img/avatar_default.png"
}

return setting.GravatarSource + HashEmail(email)
return url
}

// Seconds-based time units
Expand Down
25 changes: 22 additions & 3 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/go-macaron/session"
_ "github.com/go-macaron/session/redis"
"gopkg.in/ini.v1"
"github.com/strk/go-libravatar"

"github.com/gogits/gogs/modules/bindata"
"github.com/gogits/gogs/modules/log"
Expand Down Expand Up @@ -140,9 +141,11 @@ var (
}

// Picture settings
AvatarUploadPath string
GravatarSource string
DisableGravatar bool
AvatarUploadPath string
GravatarSource string
DisableGravatar bool
EnableFederatedAvatar bool
LibravatarService *libravatar.Libravatar

// Log settings
LogRootPath string
Expand Down Expand Up @@ -460,8 +463,24 @@ func NewContext() {
GravatarSource = source
}
DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool()
if OfflineMode {
DisableGravatar = true
EnableFederatedAvatar = false
}

if !DisableGravatar && EnableFederatedAvatar {
LibravatarService = libravatar.New()
parts := strings.Split(GravatarSource, "/")
if len(parts) >= 3 {
if parts[0] == "https:" {
LibravatarService.SetUseHTTPS(true)
LibravatarService.SetSecureFallbackHost(parts[2])
} else {
LibravatarService.SetUseHTTPS(false)
LibravatarService.SetFallbackHost(parts[2])
}
}
}

if err = Cfg.Section("ui").MapTo(&UI); err != nil {
Expand Down
1 change: 1 addition & 0 deletions routers/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ func Config(ctx *context.Context) {
ctx.Data["SessionConfig"] = setting.SessionConfig

ctx.Data["DisableGravatar"] = setting.DisableGravatar
ctx.Data["EnableFederatedAvatar"] = setting.EnableFederatedAvatar

type logger struct {
Mode, Config string
Expand Down
2 changes: 2 additions & 0 deletions routers/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func Install(ctx *context.Context) {
// Server and other services settings
form.OfflineMode = setting.OfflineMode
form.DisableGravatar = setting.DisableGravatar
form.EnableFederatedAvatar = setting.EnableFederatedAvatar
form.DisableRegistration = setting.Service.DisableRegistration
form.EnableCaptcha = setting.Service.EnableCaptcha
form.RequireSignInView = setting.Service.RequireSignInView
Expand Down Expand Up @@ -329,6 +330,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {

cfg.Section("server").Key("OFFLINE_MODE").SetValue(com.ToStr(form.OfflineMode))
cfg.Section("picture").Key("DISABLE_GRAVATAR").SetValue(com.ToStr(form.DisableGravatar))
cfg.Section("picture").Key("ENABLE_FEDERATED_AVATAR").SetValue(com.ToStr(form.EnableFederatedAvatar))
cfg.Section("service").Key("DISABLE_REGISTRATION").SetValue(com.ToStr(form.DisableRegistration))
cfg.Section("service").Key("ENABLE_CAPTCHA").SetValue(com.ToStr(form.EnableCaptcha))
cfg.Section("service").Key("REQUIRE_SIGNIN_VIEW").SetValue(com.ToStr(form.RequireSignInView))
Expand Down
4 changes: 2 additions & 2 deletions routers/org/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ func SettingsPost(ctx *context.Context, form auth.UpdateOrgSettingForm) {
ctx.Redirect(ctx.Org.OrgLink + "/settings")
}

func SettingsAvatar(ctx *context.Context, form auth.UploadAvatarForm) {
form.Enable = true
func SettingsAvatar(ctx *context.Context, form auth.AvatarForm) {
form.Source = auth.AVATAR_LOCAL
if err := user.UpdateAvatarSetting(ctx, form, ctx.Org.Organization); err != nil {
ctx.Flash.Error(err.Error())
} else {
Expand Down
27 changes: 17 additions & 10 deletions routers/user/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

const (
SETTINGS_PROFILE base.TplName = "user/settings/profile"
SETTINGS_AVATAR base.TplName = "user/settings/avatar"
SETTINGS_PASSWORD base.TplName = "user/settings/password"
SETTINGS_EMAILS base.TplName = "user/settings/email"
SETTINGS_SSH_KEYS base.TplName = "user/settings/sshkeys"
Expand Down Expand Up @@ -91,10 +92,6 @@ func SettingsPost(ctx *context.Context, form auth.UpdateProfileForm) {
ctx.User.Email = form.Email
ctx.User.Website = form.Website
ctx.User.Location = form.Location
if len(form.Gravatar) > 0 {
ctx.User.Avatar = base.EncodeMD5(form.Gravatar)
ctx.User.AvatarEmail = form.Gravatar
}
if err := models.UpdateUser(ctx.User); err != nil {
ctx.Handle(500, "UpdateUser", err)
return
Expand All @@ -106,8 +103,12 @@ func SettingsPost(ctx *context.Context, form auth.UpdateProfileForm) {
}

// FIXME: limit size.
func UpdateAvatarSetting(ctx *context.Context, form auth.UploadAvatarForm, ctxUser *models.User) error {
ctxUser.UseCustomAvatar = form.Enable
func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *models.User) error {
ctxUser.UseCustomAvatar = form.Source == auth.AVATAR_LOCAL
if len(form.Gravatar) > 0 {
ctxUser.Avatar = base.EncodeMD5(form.Gravatar)
ctxUser.AvatarEmail = form.Gravatar
}

if form.Avatar != nil {
fr, err := form.Avatar.Open()
Expand All @@ -129,7 +130,7 @@ func UpdateAvatarSetting(ctx *context.Context, form auth.UploadAvatarForm, ctxUs
} else {
// 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 ctxUser.UseCustomAvatar && !com.IsFile(ctxUser.CustomAvatarPath()) {
if err := ctxUser.GenerateRandomAvatar(); err != nil {
log.Error(4, "GenerateRandomAvatar[%d]: %v", ctxUser.ID, err)
}
Expand All @@ -143,22 +144,28 @@ func UpdateAvatarSetting(ctx *context.Context, form auth.UploadAvatarForm, ctxUs
return nil
}

func SettingsAvatar(ctx *context.Context, form auth.UploadAvatarForm) {
func SettingsAvatar(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsAvatar"] = true
ctx.HTML(200, SETTINGS_AVATAR)
}

func SettingsAvatarPost(ctx *context.Context, form auth.AvatarForm) {
if err := UpdateAvatarSetting(ctx, form, ctx.User); err != nil {
ctx.Flash.Error(err.Error())
} else {
ctx.Flash.Success(ctx.Tr("settings.update_avatar_success"))
}

ctx.Redirect(setting.AppSubUrl + "/user/settings")
ctx.Redirect(setting.AppSubUrl + "/user/settings/avatar")
}

func SettingsDeleteAvatar(ctx *context.Context) {
if err := ctx.User.DeleteAvatar(); err != nil {
ctx.Flash.Error(err.Error())
}

ctx.Redirect(setting.AppSubUrl + "/user/settings")
ctx.Redirect(setting.AppSubUrl + "/user/settings/avatar")
}

func SettingsPassword(ctx *context.Context) {
Expand Down
6 changes: 6 additions & 0 deletions templates/admin/config.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@
<dd><i class="fa fa{{if .DisableGravatar}}-check{{end}}-square-o"></i></dd>
</dl>
</div>
<div class="ui attached table segment">
<dl class="dl-horizontal admin-dl-horizontal">
<dt>{{.i18n.Tr "admin.config.enable_federated_avatar"}}</dt>
<dd><i class="fa fa{{if .EnableFederatedAvatar}}-check{{end}}-square-o"></i></dd>
</dl>
</div>

<h4 class="ui top attached header">
{{.i18n.Tr "admin.config.log_config"}}
Expand Down
6 changes: 6 additions & 0 deletions templates/install.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@
<input name="disable_gravatar" type="checkbox" {{if .disable_gravatar}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="ui checkbox" id="federated-avatar-lookup">
<label class="poping up" data-content="{{.i18n.Tr "install.federated_avatar_lookup"}}"><strong>{{.i18n.Tr "install.federated_avatar_lookup"}}</strong></label>
<input name="enable_federated_avatar" type="checkbox" {{if .enable_federated_avatar}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="ui checkbox" id="disable-registration">
<label class="poping up" data-content="{{.i18n.Tr "install.disable_registration_popup"}}"><strong>{{.i18n.Tr "install.disable_registration"}}</strong></label>
Expand Down
50 changes: 50 additions & 0 deletions templates/user/settings/avatar.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{{template "base/head" .}}
<div class="user settings avatar">
<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.avatar"}}
</h4>
<div class="ui attached segment">

<form class="ui form" action="{{.Link}}" method="post" enctype="multipart/form-data">
{{.CsrfTokenHtml}}
{{if not DisableGravatar}}
<div class="inline field">
<div class="ui radio">
<input name="source" value="lookup" type="radio" {{if not .SignedUser.UseCustomAvatar}}checked{{end}}>
<label>{{.i18n.Tr "settings.lookup_avatar_by_mail"}}</label>
</div>
</div>
<div class="field {{if .Err_Gravatar}}error{{end}}">
<label for="gravatar">Avatar {{.i18n.Tr "email"}}</label>
<input id="gravatar" name="gravatar" value="{{.SignedUser.AvatarEmail}}" />
</div>
{{end}}

<div class="inline field">
<div class="ui radio">
<input name="source" value="local" type="radio" {{if .SignedUser.UseCustomAvatar}}checked{{end}}>
<label>{{.i18n.Tr "settings.enable_custom_avatar"}}</label>
</div>
</div>

<div class="inline field">
<label for="avatar">{{.i18n.Tr "settings.choose_new_avatar"}}</label>
<input name="avatar" type="file" >
</div>

<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}}/delete" data-done-url="{{.Link}}">{{$.i18n.Tr "settings.delete_current_avatar"}}</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}
3 changes: 3 additions & 0 deletions templates/user/settings/navbar.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<a class="{{if .PageIsSettingsProfile}}active{{end}} item" href="{{AppSubUrl}}/user/settings">
{{.i18n.Tr "settings.profile"}}
</a>
<a class="{{if .PageIsSettingsAvatar}}active{{end}} item" href="{{AppSubUrl}}/user/settings/avatar">
{{.i18n.Tr "settings.avatar"}}
</a>
<a class="{{if .PageIsSettingsPassword}}active{{end}} item" href="{{AppSubUrl}}/user/settings/password">
{{.i18n.Tr "settings.password"}}
</a>
Expand Down
Loading