enhance: actionsVisibility instead of heatmapVisibility

This commit is contained in:
Tim-Niclas Oelschläger 2024-02-22 03:05:41 +01:00
parent 9cab4a716b
commit 4239ba94f5
No known key found for this signature in database
16 changed files with 132 additions and 158 deletions

View File

@ -425,15 +425,14 @@ func (a *Action) GetIssueContent(ctx context.Context) string {
// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
db.ListOptions
RequestedUser *user_model.User // the user we want activity for
RequestedTeam *organization.Team // the team we want activity for
RequestedRepo *repo_model.Repository // the repo we want activity for
Actor *user_model.User // the user viewing the activity
IncludePrivate bool // include private actions
OnlyPerformedBy bool // only actions performed by requested user
IncludeDeleted bool // include deleted actions
Date string // the day we want activity for: YYYY-MM-DD
IncludePrivateRepos bool // include private repos
RequestedUser *user_model.User // the user we want activity for
RequestedTeam *organization.Team // the team we want activity for
RequestedRepo *repo_model.Repository // the repo we want activity for
Actor *user_model.User // the user viewing the activity
IncludePrivate bool // include private actions
OnlyPerformedBy bool // only actions performed by requested user
IncludeDeleted bool // include deleted actions
Date string // the day we want activity for: YYYY-MM-DD
}
// GetFeeds returns actions according to the provided options
@ -469,7 +468,7 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
// ActivityReadable return whether doer can read activities of user
func ActivityReadable(user, doer *user_model.User) bool {
return !user.KeepActivityPrivate ||
return !user.ActionsVisibility.ShowNone() ||
doer != nil && (doer.IsAdmin || user.ID == doer.ID)
}
@ -515,8 +514,9 @@ func activityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
cond = cond.And(builder.In("act_user_id", uidCond))
}
includePrivateRepos := opts.RequestedUser != nil && opts.RequestedUser.ActionsVisibility.ShowAll()
// check readable repositories by doer/actor
if opts.Actor == nil || !opts.IncludePrivateRepos && !opts.Actor.IsAdmin {
if opts.Actor == nil || !includePrivateRepos && !opts.Actor.IsAdmin {
cond = cond.And(builder.In("repo_id", repo_model.AccessibleRepoIDsQuery(opts.Actor)))
}

View File

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
)
@ -165,21 +166,21 @@ func TestActivityReadable(t *testing.T) {
result: true,
}, {
desc: "anon should NOT see activity",
user: &user_model.User{ID: 1, KeepActivityPrivate: true},
user: &user_model.User{ID: 1, ActionsVisibility: structs.ActionsVisibilityNone},
result: false,
}, {
desc: "user should see own activity if private too",
user: &user_model.User{ID: 1, KeepActivityPrivate: true},
user: &user_model.User{ID: 1, ActionsVisibility: structs.ActionsVisibilityNone},
doer: &user_model.User{ID: 1},
result: true,
}, {
desc: "other user should NOT see activity",
user: &user_model.User{ID: 1, KeepActivityPrivate: true},
user: &user_model.User{ID: 1, ActionsVisibility: structs.ActionsVisibilityNone},
doer: &user_model.User{ID: 2},
result: false,
}, {
desc: "admin should see activity",
user: &user_model.User{ID: 1, KeepActivityPrivate: true},
user: &user_model.User{ID: 1, ActionsVisibility: structs.ActionsVisibilityNone},
doer: &user_model.User{ID: 2, IsAdmin: true},
result: true,
}}

View File

@ -32,7 +32,7 @@ func GetUserHeatmapDataByUserTeam(ctx context.Context, user *user_model.User, te
func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
hdata := make([]*UserHeatmapData, 0)
if !ActivityReadable(user, doer) || user.HeatmapVisibility.ShowNone() {
if !ActivityReadable(user, doer) {
return hdata, nil
}
@ -56,8 +56,7 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi
// * Heatmaps for individual users only include actions that the user themself did.
// * For organizations actions by all users that were made in owned
// repositories are counted.
OnlyPerformedBy: !user.IsOrganization(),
IncludePrivateRepos: user.HeatmapVisibility.ShowAll(),
OnlyPerformedBy: !user.IsOrganization(),
})
if err != nil {
return nil, err

View File

@ -142,10 +142,9 @@ type User struct {
RepoAdminChangeTeamAccess bool `xorm:"NOT NULL DEFAULT false"`
// Preferences
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
Theme string `xorm:"NOT NULL DEFAULT ''"`
KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"`
HeatmapVisibility structs.HeatmapVisibility `xorm:"NOT NULL DEFAULT 0"`
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
Theme string `xorm:"NOT NULL DEFAULT ''"`
ActionsVisibility structs.ActionsVisibility `xorm:"NOT NULL DEFAULT 0"`
}
func init() {

View File

@ -0,0 +1,50 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package structs
// ActionsVisibility defines the activities shown
type ActionsVisibility int
const (
// ActionsVisibilityPublic show public activities
ActionsVisibilityPublic ActionsVisibility = iota
// ActionsVisibilityAll shows all activities
ActionsVisibilityAll
// ActionsVisibilityNone show no activities
ActionsVisibilityNone
)
// ActionsVisibilities is a map of ActionsVisibility types
var ActionsVisibilities = map[string]ActionsVisibility{
"public": ActionsVisibilityPublic,
"all": ActionsVisibilityAll,
"none": ActionsVisibilityNone,
}
// ShowPublic returns true if ActionsVisibility is public
func (vt ActionsVisibility) ShowPublic() bool {
return vt == ActionsVisibilityPublic
}
// ShowAll returns true if ActionsVisibility is all
func (vt ActionsVisibility) ShowAll() bool {
return vt == ActionsVisibilityAll
}
// ShowNone returns true if ActionsVisibility is none
func (vt ActionsVisibility) ShowNone() bool {
return vt == ActionsVisibilityNone
}
// String provides the mode string of the visibility type (public, all, none)
func (vt ActionsVisibility) String() string {
for k, v := range ActionsVisibilities {
if vt == v {
return k
}
}
return ""
}

View File

@ -1,50 +0,0 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package structs
// HeatmapVisibility defines the activities shown in heatmap
type HeatmapVisibility int
const (
// HeatmapVisibilityPublic show public activities in heatmap
HeatmapVisibilityPublic HeatmapVisibility = iota
// HeatmapVisibilityAll shows all activities in heatmap
HeatmapVisibilityAll
// HeatmapVisibilityNone show no activities in heatmap
HeatmapVisibilityNone
)
// HeatmapVisibilities is a map of HeatmapVisibility types
var HeatmapVisibilities = map[string]HeatmapVisibility{
"public": HeatmapVisibilityPublic,
"all": HeatmapVisibilityAll,
"none": HeatmapVisibilityNone,
}
// ShowPublic returns true if HeatmapVisibility is public
func (vt HeatmapVisibility) ShowPublic() bool {
return vt == HeatmapVisibilityPublic
}
// ShowAll returns true if HeatmapVisibility is all
func (vt HeatmapVisibility) ShowAll() bool {
return vt == HeatmapVisibilityAll
}
// ShowNone returns true if HeatmapVisibility is none
func (vt HeatmapVisibility) ShowNone() bool {
return vt == HeatmapVisibilityNone
}
// String provides the mode string of the visibility type (public, all, none)
func (vt HeatmapVisibility) String() string {
for k, v := range HeatmapVisibilities {
if vt == v {
return k
}
}
return ""
}

View File

@ -75,8 +75,8 @@ type UserSettings struct {
Theme string `json:"theme"`
DiffViewStyle string `json:"diff_view_style"`
// Privacy
HideEmail bool `json:"hide_email"`
HideActivity bool `json:"hide_activity"`
HideEmail bool `json:"hide_email"`
ActionsVisibility ActionsVisibility `json:"actions_visibility"`
}
// UserSettingsOptions represents options to change user settings
@ -90,8 +90,8 @@ type UserSettingsOptions struct {
Theme *string `json:"theme"`
DiffViewStyle *string `json:"diff_view_style"`
// Privacy
HideEmail *bool `json:"hide_email"`
HideActivity *bool `json:"hide_activity"`
HideEmail *bool `json:"hide_email"`
ActionsVisibility *ActionsVisibility `json:"actions_visibility"`
}
// RenameUserOption options when renaming a user

View File

@ -69,6 +69,8 @@ your_starred = Starred
your_settings = Settings
all = All
none = None
public = Public
sources = Sources
mirrors = Mirrors
collaborative = Collaborative
@ -608,7 +610,6 @@ follow = Follow
unfollow = Unfollow
user_bio = Biography
disabled_public_activity = This user has disabled the public visibility of the activity.
disabled_public_heatmap = This user has disabled the public visibility of the heatmap.
email_visibility.limited = Your email address is visible to all authenticated users
email_visibility.private = Your email address is only visible to you and administrators
show_on_map = Show this place on a map
@ -678,16 +679,11 @@ comment_type_group_project = Project
comment_type_group_issue_ref = Issue reference
saved_successfully = Your settings were saved successfully.
privacy = Privacy
keep_activity_private = Hide Activity from profile page
keep_activity_private_popup = Makes the activity visible only for you and the admins
heatmap_visibility_popup = Specify which activities are visible in heatmap
heatmap_visibility = Visible activities in heatmap
heatmap_visibility.public_popup = Only activies from repositories which can be visited from the viewer are in the heatmap
heatmap_visibility.public = Public
heatmap_visibility.all_popup = Only activies from repositories which can be visited from the viewer are in the heatmap
heatmap_visibility.all = All
heatmap_visibility.none_popup = Don't show the heatmap
heatmap_visibility.none = None
actions_visibility_popup = Specify which activities are visible
actions_visibility = Visible activities
actions_visibility.public_popup = Only activies from repositories which can be accessed by the viewer are visible
actions_visibility.all_popup = All actions are visible, but from repositories which are not accessable by the viewer
actions_visibility.none_popup = Don't show actions (expect to admins)
lookup_avatar_by_mail = Look Up Avatar by Email Address
federated_avatar_lookup = Federated Avatar Lookup

View File

@ -46,15 +46,15 @@ func UpdateUserSettings(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.UserSettingsOptions)
opts := &user_service.UpdateOptions{
FullName: optional.FromPtr(form.FullName),
Description: optional.FromPtr(form.Description),
Website: optional.FromPtr(form.Website),
Location: optional.FromPtr(form.Location),
Language: optional.FromPtr(form.Language),
Theme: optional.FromPtr(form.Theme),
DiffViewStyle: optional.FromPtr(form.DiffViewStyle),
KeepEmailPrivate: optional.FromPtr(form.HideEmail),
KeepActivityPrivate: optional.FromPtr(form.HideActivity),
FullName: optional.FromPtr(form.FullName),
Description: optional.FromPtr(form.Description),
Website: optional.FromPtr(form.Website),
Location: optional.FromPtr(form.Location),
Language: optional.FromPtr(form.Language),
Theme: optional.FromPtr(form.Theme),
DiffViewStyle: optional.FromPtr(form.DiffViewStyle),
KeepEmailPrivate: optional.FromPtr(form.HideEmail),
ActionsVisibility: optional.FromPtr(form.ActionsVisibility),
}
if err := user_service.UpdateUser(ctx, ctx.Doer, opts); err != nil {
ctx.InternalServerError(err)

View File

@ -87,14 +87,13 @@ func ProfilePost(ctx *context.Context) {
}
opts := &user_service.UpdateOptions{
FullName: optional.Some(form.FullName),
KeepEmailPrivate: optional.Some(form.KeepEmailPrivate),
Description: optional.Some(form.Description),
Website: optional.Some(form.Website),
Location: optional.Some(form.Location),
Visibility: optional.Some(form.Visibility),
HeatmapVisibility: optional.Some(form.HeatmapVisibility),
KeepActivityPrivate: optional.Some(form.KeepActivityPrivate),
FullName: optional.Some(form.FullName),
KeepEmailPrivate: optional.Some(form.KeepEmailPrivate),
Description: optional.Some(form.Description),
Website: optional.Some(form.Website),
Location: optional.Some(form.Location),
Visibility: optional.Some(form.Visibility),
ActionsVisibility: optional.Some(form.ActionsVisibility),
}
if err := user_service.UpdateUser(ctx, ctx.Doer, opts); err != nil {
ctx.ServerError("UpdateUser", err)

View File

@ -86,15 +86,15 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap
// User2UserSettings return UserSettings based on a user
func User2UserSettings(user *user_model.User) api.UserSettings {
return api.UserSettings{
FullName: user.FullName,
Website: user.Website,
Location: user.Location,
Language: user.Language,
Description: user.Description,
Theme: user.Theme,
HideEmail: user.KeepEmailPrivate,
HideActivity: user.KeepActivityPrivate,
DiffViewStyle: user.DiffViewStyle,
FullName: user.FullName,
Website: user.Website,
Location: user.Location,
Language: user.Language,
Description: user.Description,
Theme: user.Theme,
HideEmail: user.KeepEmailPrivate,
ActionsVisibility: user.ActionsVisibility,
DiffViewStyle: user.DiffViewStyle,
}
}

View File

@ -217,15 +217,14 @@ func (f *IntrospectTokenForm) Validate(req *http.Request, errs binding.Errors) b
// UpdateProfileForm form for updating profile
type UpdateProfileForm struct {
Name string `binding:"Username;MaxSize(40)"`
FullName string `binding:"MaxSize(100)"`
KeepEmailPrivate bool
Website string `binding:"ValidSiteUrl;MaxSize(255)"`
Location string `binding:"MaxSize(50)"`
Description string `binding:"MaxSize(255)"`
Visibility structs.VisibleType
HeatmapVisibility structs.HeatmapVisibility
KeepActivityPrivate bool
Name string `binding:"Username;MaxSize(40)"`
FullName string `binding:"MaxSize(100)"`
KeepEmailPrivate bool
Website string `binding:"ValidSiteUrl;MaxSize(255)"`
Location string `binding:"MaxSize(50)"`
Description string `binding:"MaxSize(255)"`
Visibility structs.VisibleType
ActionsVisibility structs.ActionsVisibility
}
// Validate validates the fields

View File

@ -27,8 +27,7 @@ type UpdateOptions struct {
MaxRepoCreation optional.Option[int]
IsRestricted optional.Option[bool]
Visibility optional.Option[structs.VisibleType]
HeatmapVisibility optional.Option[structs.HeatmapVisibility]
KeepActivityPrivate optional.Option[bool]
ActionsVisibility optional.Option[structs.ActionsVisibility]
Language optional.Option[string]
Theme optional.Option[string]
DiffViewStyle optional.Option[string]
@ -130,15 +129,10 @@ func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) er
cols = append(cols, "visibility")
}
if opts.HeatmapVisibility.Has() {
u.HeatmapVisibility = opts.HeatmapVisibility.Value()
if opts.ActionsVisibility.Has() {
u.ActionsVisibility = opts.ActionsVisibility.Value()
cols = append(cols, "heatmap_visibility")
}
if opts.KeepActivityPrivate.Has() {
u.KeepActivityPrivate = opts.KeepActivityPrivate.Value()
cols = append(cols, "keep_activity_private")
cols = append(cols, "actions_visibility")
}
if opts.AllowCreateOrganization.Has() {

View File

@ -40,7 +40,7 @@ func TestUpdateUser(t *testing.T) {
IsActive: optional.Some(false),
IsAdmin: optional.Some(true),
Visibility: optional.Some(structs.VisibleTypePrivate),
KeepActivityPrivate: optional.Some(true),
ActionsVisibility: optional.Some(structs.ActionsVisibilityNone),
Language: optional.Some("lang"),
Theme: optional.Some("theme"),
DiffViewStyle: optional.Some("split"),
@ -62,7 +62,7 @@ func TestUpdateUser(t *testing.T) {
assert.Equal(t, opts.IsActive.Value(), user.IsActive)
assert.Equal(t, opts.IsAdmin.Value(), user.IsAdmin)
assert.Equal(t, opts.Visibility.Value(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.Value(), user.KeepActivityPrivate)
assert.Equal(t, opts.ActionsVisibility.Value(), user.ActionsVisibility)
assert.Equal(t, opts.Language.Value(), user.Language)
assert.Equal(t, opts.Theme.Value(), user.Theme)
assert.Equal(t, opts.DiffViewStyle.Value(), user.DiffViewStyle)
@ -82,7 +82,7 @@ func TestUpdateUser(t *testing.T) {
assert.Equal(t, opts.IsActive.Value(), user.IsActive)
assert.Equal(t, opts.IsAdmin.Value(), user.IsAdmin)
assert.Equal(t, opts.Visibility.Value(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.Value(), user.KeepActivityPrivate)
assert.Equal(t, opts.ActionsVisibility.Value(), user.ActionsVisibility)
assert.Equal(t, opts.Language.Value(), user.Language)
assert.Equal(t, opts.Theme.Value(), user.Theme)
assert.Equal(t, opts.DiffViewStyle.Value(), user.DiffViewStyle)

View File

@ -11,18 +11,12 @@
</div>
{{if eq .TabName "activity"}}
{{if .ContextUser.KeepActivityPrivate}}
{{if and .ContextUser.ActionsVisibility.ShowNone (not .IsAdmin)}}
<div class="ui info message">
<p>{{ctx.Locale.Tr "user.disabled_public_activity"}}</p>
</div>
{{else}}
{{if .ContextUser.HeatmapVisibility.ShowNone}}
<div class="ui info message">
<p>{{ctx.Locale.Tr "user.disabled_public_heatmap"}}</p>
</div>
{{else}}
{{template "user/heatmap" .}}
{{end}}
{{template "user/heatmap" .}}
{{template "user/dashboard/feeds" .}}
{{end}}
{{else if eq .TabName "stars"}}

View File

@ -78,27 +78,20 @@
</div>
</div>
<div class="field">
<div class="ui checkbox" id="keep-activity-private">
<label data-tooltip-content="{{ctx.Locale.Tr "settings.keep_activity_private_popup"}}"><strong>{{ctx.Locale.Tr "settings.keep_activity_private"}}</strong></label>
<input name="keep_activity_private" type="checkbox" {{if .SignedUser.KeepActivityPrivate}}checked{{end}}>
</div>
</div>
<div class="inline field">
<span class="inline field" data-tooltip-content="{{ctx.Locale.Tr "settings.heatmap_visibility_popup"}}"><label>{{ctx.Locale.Tr "settings.heatmap_visibility"}}</label></span>
<span class="inline field" data-tooltip-content="{{ctx.Locale.Tr "settings.actions_visibility_popup"}}"><label>{{ctx.Locale.Tr "settings.actions_visibility"}}</label></span>
<div class="ui selection type dropdown">
<input type="hidden" id="heatmap_visibility" name="heatmap_visibility" value="{{printf "%d" .SignedUser.HeatmapVisibility}}">
<input type="hidden" id="actions_visibility" name="actions_visibility" value="{{printf "%d" .SignedUser.ActionsVisibility}}">
<div class="text">
{{if .SignedUser.HeatmapVisibility.ShowPublic}}{{ctx.Locale.Tr "settings.heatmap_visibility.public"}}{{end}}
{{if .SignedUser.HeatmapVisibility.ShowAll}}{{ctx.Locale.Tr "settings.heatmap_visibility.all"}}{{end}}
{{if .SignedUser.HeatmapVisibility.ShowNone}}{{ctx.Locale.Tr "settings.heatmap_visibility.none"}}{{end}}
{{if .SignedUser.ActionsVisibility.ShowPublic}}{{ctx.Locale.Tr "public"}}{{end}}
{{if .SignedUser.ActionsVisibility.ShowAll}}{{ctx.Locale.Tr "all"}}{{end}}
{{if .SignedUser.ActionsVisibility.ShowNone}}{{ctx.Locale.Tr "none"}}{{end}}
</div>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<div class="item" data-tooltip-content="{{ctx.Locale.Tr "settings.heatmap_visibility.public_popup"}}" data-value="0">{{ctx.Locale.Tr "settings.heatmap_visibility.public"}}</div>
<div class="item" data-tooltip-content="{{ctx.Locale.Tr "settings.heatmap_visibility.all_popup"}}" data-value="1">{{ctx.Locale.Tr "settings.heatmap_visibility.all"}}</div>
<div class="item" data-tooltip-content="{{ctx.Locale.Tr "settings.heatmap_visibility.none_popup"}}" data-value="2">{{ctx.Locale.Tr "settings.heatmap_visibility.none"}}</div>
<div class="item" data-tooltip-content="{{ctx.Locale.Tr "settings.actions_visibility.public_popup"}}" data-value="0">{{ctx.Locale.Tr "public"}}</div>
<div class="item" data-tooltip-content="{{ctx.Locale.Tr "settings.actions_visibility.all_popup"}}" data-value="1">{{ctx.Locale.Tr "all"}}</div>
<div class="item" data-tooltip-content="{{ctx.Locale.Tr "settings.actions_visibility.none_popup"}}" data-value="2">{{ctx.Locale.Tr "none"}}</div>
</div>
</div>
</div>