Skip to content

Commit 51d578f

Browse files
derSuessmannlunny
authored andcommitted
Add Keep email private (see issue #571). (#571)
- Add site-wide option DEFAULT_KEEP_EMAIL_PRIVATE. - Add the new option to the install and admin/config pages. - Add the new option to app.ini in the service section. - Add the new option to the settings struct. - Add English text strings to i18n. - Add field KeepEmailPrivate to user struct. - Add field KeepEmailPrivate to user form. - Add option to UI. - Add using noreply email address if user has "Keep Email Private". An email address <LowerName>@<NO_REPLY_ADDRESS> is now used in commit messages (and hopefully all other git log relevant places). The change relies on the fact that git commands should use user.NetGitSig(). - Add hiding of email address in UI, if user has set "Keep Email Private". - Add condition to show email address only on explore/users and user pages, if user has not set "Keep Email Private". - Add noreply email in API if set "Keep Email Private". - Add a new service setting NO_REPLY_ADDRESS. The value of this setting is used as the domain part for the user's email address in git log, iff he decides to keep his email address private. If the user decides to keep his email address private and this option is not set 'noreply.example.org' is used, which no MTA should send email to. Add NO_REPLY_ADDRESS to conf/app.ini.
1 parent 6072b03 commit 51d578f

File tree

12 files changed

+88
-28
lines changed

12 files changed

+88
-28
lines changed

conf/app.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,13 @@ ENABLE_REVERSE_PROXY_AUTHENTICATION = false
190190
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
191191
; Enable captcha validation for registration
192192
ENABLE_CAPTCHA = true
193+
; Default value for KeepEmailPrivate
194+
; New user will get the value of this setting copied into their profile
195+
DEFAULT_KEEP_EMAIL_PRIVATE = false
196+
; Default value for the domain part of the user's email address in the git log
197+
; if he has set KeepEmailPrivate true. The user's email replaced with a
198+
; concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
199+
NO_REPLY_ADDRESS = noreply.example.org
193200

194201
[webhook]
195202
; Hook task queue length, increase if webhook shooting starts hanging

models/user.go

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,20 @@ type User struct {
7575
Name string `xorm:"UNIQUE NOT NULL"`
7676
FullName string
7777
// Email is the primary email address (to be used for communication)
78-
Email string `xorm:"NOT NULL"`
79-
Passwd string `xorm:"NOT NULL"`
80-
LoginType LoginType
81-
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
82-
LoginName string
83-
Type UserType
84-
OwnedOrgs []*User `xorm:"-"`
85-
Orgs []*User `xorm:"-"`
86-
Repos []*Repository `xorm:"-"`
87-
Location string
88-
Website string
89-
Rands string `xorm:"VARCHAR(10)"`
90-
Salt string `xorm:"VARCHAR(10)"`
78+
Email string `xorm:"NOT NULL"`
79+
KeepEmailPrivate bool
80+
Passwd string `xorm:"NOT NULL"`
81+
LoginType LoginType
82+
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
83+
LoginName string
84+
Type UserType
85+
OwnedOrgs []*User `xorm:"-"`
86+
Orgs []*User `xorm:"-"`
87+
Repos []*Repository `xorm:"-"`
88+
Location string
89+
Website string
90+
Rands string `xorm:"VARCHAR(10)"`
91+
Salt string `xorm:"VARCHAR(10)"`
9192

9293
Created time.Time `xorm:"-"`
9394
CreatedUnix int64 `xorm:"INDEX"`
@@ -170,13 +171,22 @@ func (u *User) AfterSet(colName string, _ xorm.Cell) {
170171
}
171172
}
172173

174+
// getEmail returns an noreply email, if the user has set to keep his
175+
// email address private, otherwise the primary email address.
176+
func (u *User) getEmail() string {
177+
if u.KeepEmailPrivate {
178+
return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress)
179+
}
180+
return u.Email
181+
}
182+
173183
// APIFormat converts a User to api.User
174184
func (u *User) APIFormat() *api.User {
175185
return &api.User{
176186
ID: u.ID,
177187
UserName: u.Name,
178188
FullName: u.FullName,
179-
Email: u.Email,
189+
Email: u.getEmail(),
180190
AvatarURL: u.AvatarLink(),
181191
}
182192
}
@@ -361,7 +371,7 @@ func (u *User) GetFollowing(page int) ([]*User, error) {
361371
func (u *User) NewGitSig() *git.Signature {
362372
return &git.Signature{
363373
Name: u.DisplayName(),
364-
Email: u.Email,
374+
Email: u.getEmail(),
365375
When: time.Now(),
366376
}
367377
}
@@ -616,6 +626,8 @@ func CreateUser(u *User) (err error) {
616626
return ErrEmailAlreadyUsed{u.Email}
617627
}
618628

629+
u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
630+
619631
u.LowerName = strings.ToLower(u.Name)
620632
u.AvatarEmail = u.Email
621633
u.Avatar = base.HashEmail(u.AvatarEmail)

modules/auth/user_form.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ type InstallForm struct {
3838
RegisterConfirm bool
3939
MailNotify bool
4040

41-
OfflineMode bool
42-
DisableGravatar bool
43-
EnableFederatedAvatar bool
44-
DisableRegistration bool
45-
EnableCaptcha bool
46-
RequireSignInView bool
41+
OfflineMode bool
42+
DisableGravatar bool
43+
EnableFederatedAvatar bool
44+
DisableRegistration bool
45+
EnableCaptcha bool
46+
RequireSignInView bool
47+
DefaultKeepEmailPrivate bool
48+
NoReplyAddress string
4749

4850
AdminName string `binding:"OmitEmpty;AlphaDashDot;MaxSize(30)" locale:"install.admin_name"`
4951
AdminPasswd string `binding:"OmitEmpty;MaxSize(255)" locale:"install.admin_password"`
@@ -97,11 +99,12 @@ func (f *SignInForm) Validate(ctx *macaron.Context, errs binding.Errors) binding
9799

98100
// UpdateProfileForm form for updating profile
99101
type UpdateProfileForm struct {
100-
Name string `binding:"OmitEmpty;MaxSize(35)"`
101-
FullName string `binding:"MaxSize(100)"`
102-
Email string `binding:"Required;Email;MaxSize(254)"`
103-
Website string `binding:"Url;MaxSize(100)"`
104-
Location string `binding:"MaxSize(50)"`
102+
Name string `binding:"OmitEmpty;MaxSize(35)"`
103+
FullName string `binding:"MaxSize(100)"`
104+
Email string `binding:"Required;Email;MaxSize(254)"`
105+
KeepEmailPrivate bool
106+
Website string `binding:"Url;MaxSize(100)"`
107+
Location string `binding:"MaxSize(50)"`
105108
}
106109

107110
// Validate valideates the fields

modules/setting/setting.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,8 @@ var Service struct {
838838
EnableReverseProxyAuth bool
839839
EnableReverseProxyAutoRegister bool
840840
EnableCaptcha bool
841+
DefaultKeepEmailPrivate bool
842+
NoReplyAddress string
841843
}
842844

843845
func newService() {
@@ -850,6 +852,8 @@ func newService() {
850852
Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
851853
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
852854
Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool()
855+
Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool()
856+
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply.example.org")
853857
}
854858

855859
var logLevels = map[string]string{

options/locale/locale_en-US.ini

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ save_config_failed = Fail to save configuration: %v
124124
invalid_admin_setting = Admin account setting is invalid: %v
125125
install_success = Welcome! We're glad that you chose Gitea, have fun and take care.
126126
invalid_log_root_path = Log root path is invalid: %v
127+
default_keep_email_private = Default Value for Keep Email Private
128+
default_keep_email_private_popup = This is the default value for the visibility of the user's email address. If set to true the email address of all new users will be hidden until the user changes his setting.
129+
no_reply_address = No-reply Address
130+
no_reply_address_helper = Domain for the user's email address in git logs if he keeps his email address private. E.g. user 'joe' and 'noreply.example.org' will be '[email protected]'
127131
128132
[home]
129133
uname_holder = Username or email
@@ -307,6 +311,8 @@ add_new_email = Add new email address
307311
add_email = Add email
308312
add_email_confirmation_sent = A new confirmation email has been sent to '%s', please check your inbox within the next %d hours to complete the confirmation process.
309313
add_email_success = Your new email address was successfully added.
314+
keep_email_private = Keep Email Address Private
315+
keep_email_private_popup = Your email address will be hidden from other users if this option is set.
310316
311317
manage_ssh_keys = Manage SSH Keys
312318
add_key = Add Key
@@ -1112,6 +1118,8 @@ config.disable_key_size_check = Disable Minimum Key Size Check
11121118
config.enable_captcha = Enable Captcha
11131119
config.active_code_lives = Active Code Lives
11141120
config.reset_password_code_lives = Reset Password Code Lives
1121+
config.default_keep_email_private = Default Value for Keep Email Private
1122+
config.no_reply_address = No-reply Address
11151123

11161124
config.webhook_config = Webhook Configuration
11171125
config.queue_length = Queue Length

routers/install.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ func Install(ctx *context.Context) {
111111
form.DisableRegistration = setting.Service.DisableRegistration
112112
form.EnableCaptcha = setting.Service.EnableCaptcha
113113
form.RequireSignInView = setting.Service.RequireSignInView
114+
form.DefaultKeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate
115+
form.NoReplyAddress = setting.Service.NoReplyAddress
114116

115117
auth.AssignForm(form, ctx.Data)
116118
ctx.HTML(200, tplInstall)
@@ -291,6 +293,8 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
291293
cfg.Section("service").Key("DISABLE_REGISTRATION").SetValue(com.ToStr(form.DisableRegistration))
292294
cfg.Section("service").Key("ENABLE_CAPTCHA").SetValue(com.ToStr(form.EnableCaptcha))
293295
cfg.Section("service").Key("REQUIRE_SIGNIN_VIEW").SetValue(com.ToStr(form.RequireSignInView))
296+
cfg.Section("service").Key("DEFAULT_KEEP_EMAIL_PRIVATE").SetValue(com.ToStr(form.DefaultKeepEmailPrivate))
297+
cfg.Section("service").Key("NO_REPLY_ADDRESS").SetValue(com.ToStr(form.NoReplyAddress))
294298

295299
cfg.Section("").Key("RUN_MODE").SetValue("prod")
296300

routers/user/setting.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ func SettingsPost(ctx *context.Context, form auth.UpdateProfileForm) {
9191

9292
ctx.User.FullName = form.FullName
9393
ctx.User.Email = form.Email
94+
ctx.User.KeepEmailPrivate = form.KeepEmailPrivate
9495
ctx.User.Website = form.Website
9596
ctx.User.Location = form.Location
9697
if err := models.UpdateUser(ctx.User); err != nil {

templates/admin/config.tmpl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@
119119
<dd><i class="fa fa{{if .Service.DisableMinimumKeySizeCheck}}-check{{end}}-square-o"></i></dd>*/}}
120120
<dt>{{.i18n.Tr "admin.config.enable_captcha"}}</dt>
121121
<dd><i class="fa fa{{if .Service.EnableCaptcha}}-check{{end}}-square-o"></i></dd>
122+
<dt>{{.i18n.Tr "admin.config.default_keep_email_private"}}</dt>
123+
<dd><i class="fa fa{{if .Service.DefaultKeepEmailPrivate}}-check{{end}}-square-o"></i></dd>
124+
<dt>{{.i18n.Tr "admin.config.no_reply_address"}}</dt>
125+
<dd>{{if .Service.NoReplyAddress}}{{.Service.NoReplyAddress}}{{else}}-{{end}}</dd>
122126
<div class="ui divider"></div>
123127
<dt>{{.i18n.Tr "admin.config.active_code_lives"}}</dt>
124128
<dd>{{.Service.ActiveCodeLives}} {{.i18n.Tr "tool.raw_minutes"}}</dd>

templates/explore/users.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
{{if .Location}}
1717
<i class="octicon octicon-location"></i> {{.Location}}
1818
{{end}}
19-
{{if and $.ShowUserEmail .Email $.IsSigned}}
19+
{{if and $.ShowUserEmail .Email $.IsSigned (not .KeepEmailPrivate)}}
2020
<i class="octicon octicon-mail"></i>
2121
<a href="mailto:{{.Email}}" rel="nofollow">{{.Email}}</a>
2222
{{end}}

templates/install.tmpl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,17 @@
206206
<input name="require_sign_in_view" type="checkbox" {{if .require_sign_in_view}}checked{{end}}>
207207
</div>
208208
</div>
209+
<div class="inline field">
210+
<div class="ui checkbox">
211+
<label class="poping up" data-content="{{.i18n.Tr "install.default_keep_email_private_popup"}}"><strong>{{.i18n.Tr "install.default_keep_email_private"}}</strong></label>
212+
<input name="default_keep_email_private" type="checkbox" {{if .default_keep_email_private}}checked{{end}}>
213+
</div>
214+
</div>
215+
<div class="inline field">
216+
<label for="no_reply_address">{{.i18n.Tr "install.no_reply_address"}}</label>
217+
<input id="_no_reply_address" name="no_reply_address" value="{{.no_reply_address}}">
218+
<span class="help">{{.i18n.Tr "install.no_reply_address_helper"}}</span>
219+
</div>
209220
</div>
210221
</div>
211222

templates/user/profile.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
{{if .Owner.Location}}
2323
<li><i class="octicon octicon-location"></i> {{.Owner.Location}}</li>
2424
{{end}}
25-
{{if and $.ShowUserEmail .Owner.Email .IsSigned}}
25+
{{if or (and $.ShowUserEmail .Owner.Email .IsSigned) (and .Owner.Email .IsSigned (not .Owner.KeepEmailPrivate))}}
2626
<li>
2727
<i class="octicon octicon-mail"></i>
2828
<a href="mailto:{{.Owner.Email}}" rel="nofollow">{{.Owner.Email}}</a>

templates/user/settings/profile.tmpl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
<label for="email">{{.i18n.Tr "email"}}</label>
2828
<input id="email" name="email" value="{{.SignedUser.Email}}">
2929
</div>
30+
<div class="inline field">
31+
<div class="ui checkbox" id="keep-email-private">
32+
<label class="poping up" data-content="{{.i18n.Tr "settings.keep_email_private_popup"}}"><strong>{{.i18n.Tr "settings.keep_email_private"}}</strong></label>
33+
<input name="keep_email_private" type="checkbox" {{if .SignedUser.KeepEmailPrivate}}checked{{end}}>
34+
</div>
35+
</div>
3036
<div class="field {{if .Err_Website}}error{{end}}">
3137
<label for="website">{{.i18n.Tr "settings.website"}}</label>
3238
<input id="website" name="website" type="url" value="{{.SignedUser.Website}}">

0 commit comments

Comments
 (0)