loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Better logic for showing user feed/public activity elements (#4189)

There are a few changes of template logic which defines when which elements should be shown on profile page. The motivation is to have the elements when needed and don't when they're not relevant.

## Changes

### RSS button

Now displayed if:
* feeds are enabled
AND one or more of:
* the current user is an admin
* the current user is viewing their profile
* the activity is publicly available

So, basically in cases when the .rss feed actually contains any events. Before this change this button was constantly shown and was giving an empty feed if it was unavailable.

### Public activity tab

The tab is displayed if:
* the current user is an admin
* the current user is viewing their profile
* the activity is publicly available
* the current tab is this exact tab, for example, in case it was accessed by adding `?tab=activity` to the URL, so that the UI is not broken w/o a highlighted tab

So, this tab is not displayed when it's not going to contain any information, but still can be accessed.

### Banner "This user has disabled the public visibility of the activity."

For admins:
* always show the big blue banner to warn that sharing a screenshot of this publicly is bad idea

For self:
* always display a little note about the current visibility status with a "Change" link

For others:
* only display a little note to explain why the activity is not shown

### Heatmap and activity feed

Elements are only displayed when relevant, instead of keeping empty leftovers, for easier testing. This template change is also covered by test. **Everything in this Changes section is covered by test unless I forgot something.**

## Preview

There's obviously too many states to screenshot, here are highlights:

![](https://codeberg.org/attachments/47559531-9bcd-46c0-90d4-8b51512da752)
_Warning admin for why they're seeing the information_

![](https://codeberg.org/attachments/3107bf62-955b-4fe5-bce3-6305a928afe1)
_Viewing self - private_

![](https://codeberg.org/attachments/afb63ead-fb0b-4fc7-9d8b-c6c09e9ae62b)
_Viewing self - public_

![](https://codeberg.org/attachments/df3c090a-7490-4827-b33b-771fd4fa0a9f)
_Don't have access to the information_

![](https://codeberg.org/attachments/2dd2b0ac-2fe0-4453-aa4b-e91fd08f4411)
_The tab is not shown when the activity can't be accessed_

![](https://codeberg.org/attachments/ed4c61de-b3b7-4523-b92b-bc76e1d8b7c5)
_Can't access the RSS feed_

![](https://codeberg.org/attachments/5a27f2be-d79c-4fb4-85a5-758348398f1b)
_Can access the RSS feed_

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4189
Reviewed-by: Otto <otto@codeberg.org>

0ko 1496bb60 104ceef5

+135 -10
+6 -1
options/locale/locale_en-US.ini
··· 679 679 block = Block 680 680 unblock = Unblock 681 681 user_bio = Biography 682 - disabled_public_activity = This user has disabled the public visibility of the activity. 683 682 email_visibility.limited = Your email address is visible to all authenticated users 684 683 show_on_map = Show this place on a map 685 684 settings = User settings 685 + 686 + disabled_public_activity = This user has disabled the public visibility of the activity. 687 + public_activity.visibility_hint.self_public = Your activity is visible to everyone, except for interactions in private spaces. <a href="%s">Configure</a>. 688 + public_activity.visibility_hint.admin_public = This activity is visible to everyone, but as an administrator you can also see interactions in private spaces. 689 + public_activity.visibility_hint.self_private = Your activity is only visible to you and the instance administrators. <a href="%s">Configure</a>. 690 + public_activity.visibility_hint.admin_private = This activity is visible to you because you're an administrator, but the user wants it to remain private. 686 691 687 692 form.name_reserved = The username "%s" is reserved. 688 693 form.name_pattern_not_allowed = The pattern "%s" is not allowed in a username.
+1
release-notes/8.0.0/4189.md
··· 1 + User profiles: only show RSS feed button and Public activity tab when the activity can be accessed, add messages about visibility
+1 -1
templates/shared/user/profile_big_avatar.tmpl
··· 20 20 {{end}}</span> 21 21 <div class="tw-mt-2"> 22 22 <a class="muted" href="{{.ContextUser.HomeLink}}?tab=followers">{{svg "octicon-people" 18 "tw-mr-1"}}{{ctx.Locale.TrN .NumFollowers "user.followers_one" "user.followers_few" .NumFollowers}}</a> · <a class="muted" href="{{.ContextUser.HomeLink}}?tab=following">{{ctx.Locale.TrN .NumFollowing "user.following_one" "user.following_few" .NumFollowing}}</a> 23 - {{if .EnableFeed}} 23 + {{if and .EnableFeed (or .IsAdmin (eq .SignedUserID .ContextUser.ID) (not .ContextUser.KeepActivityPrivate))}} 24 24 <a href="{{.ContextUser.HomeLink}}.rss"><i class="ui text grey tw-ml-2" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">{{svg "octicon-rss" 18}}</i></a> 25 25 {{end}} 26 26 </div>
+5 -3
templates/user/overview/header.tmpl
··· 32 32 </a> 33 33 {{end}} 34 34 {{if .ContextUser.IsIndividual}} 35 - <a class="{{if eq .TabName "activity"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=activity"> 36 - {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}} 37 - </a> 35 + {{if or (eq .TabName "activity") .IsAdmin (eq .SignedUserID .ContextUser.ID) (not .ContextUser.KeepActivityPrivate)}} 36 + <a class="{{if eq .TabName "activity"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=activity"> 37 + {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}} 38 + </a> 39 + {{end}} 38 40 {{if not .DisableStars}} 39 41 <a class="{{if eq .TabName "stars"}}active {{end}}item" href="{{.ContextUser.HomeLink}}?tab=stars"> 40 42 {{svg "octicon-star"}} {{ctx.Locale.Tr "user.starred"}}
+25 -5
templates/user/profile.tmpl
··· 9 9 <div class="ui twelve wide column tw-mb-4"> 10 10 {{template "user/overview/header" .}} 11 11 {{if eq .TabName "activity"}} 12 - {{if .ContextUser.KeepActivityPrivate}} 13 - <div class="ui info message"> 14 - <p>{{ctx.Locale.Tr "user.disabled_public_activity"}}</p> 12 + {{if eq .SignedUserID .ContextUser.ID}} 13 + <p id="visibility-hint"> 14 + {{if .ContextUser.KeepActivityPrivate}} 15 + {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_private" "/user/settings#keep-activity-private"}} 16 + {{else}} 17 + {{ctx.Locale.Tr "user.public_activity.visibility_hint.self_public" "/user/settings#keep-activity-private"}} 18 + {{end}} 19 + </p> 20 + {{else}} 21 + {{if .IsAdmin}} 22 + <div id="visibility-hint" class="ui info message"> 23 + {{if .ContextUser.KeepActivityPrivate}} 24 + {{ctx.Locale.Tr "user.public_activity.visibility_hint.admin_private"}} 25 + {{else}} 26 + {{ctx.Locale.Tr "user.public_activity.visibility_hint.admin_public"}} 27 + {{end}} 15 28 </div> 29 + {{else}} 30 + {{if .ContextUser.KeepActivityPrivate}} 31 + <p id="visibility-hint">{{ctx.Locale.Tr "user.disabled_public_activity"}}</p> 32 + {{end}} 33 + {{end}} 16 34 {{end}} 17 - {{template "user/heatmap" .}} 18 - {{template "user/dashboard/feeds" .}} 35 + {{if or .IsAdmin (eq .SignedUserID .ContextUser.ID) (not .ContextUser.KeepActivityPrivate)}} 36 + {{template "user/heatmap" .}} 37 + {{template "user/dashboard/feeds" .}} 38 + {{end}} 19 39 {{else if eq .TabName "stars"}} 20 40 <div class="stars"> 21 41 {{template "shared/repo_search" .}}
+97
tests/integration/user_profile_activity_test.go
··· 1 + // Copyright 2024 The Forgejo Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package integration 5 + 6 + import ( 7 + "net/http" 8 + "net/url" 9 + "testing" 10 + 11 + "github.com/stretchr/testify/assert" 12 + ) 13 + 14 + // TestUserProfileActivity ensures visibility and correctness of elements related to activity of a user: 15 + // - RSS feed button (doesn't test `other.ENABLE_FEED:false`) 16 + // - Public activity tab 17 + // - Banner/hint in the tab 18 + // - "Configure" link in the hint 19 + func TestUserProfileActivity(t *testing.T) { 20 + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { 21 + // This test needs multiple users with different access statuses to check for all possible states 22 + userAdmin := loginUser(t, "user1") 23 + userRegular := loginUser(t, "user2") 24 + // Activity availability should be the same for guest and another non-admin user, so this is not tested separately 25 + userGuest := emptyTestSession(t) 26 + 27 + // The hint may contain "Configure" link with an anchor. Verify that it works. 28 + response := userRegular.MakeRequest(t, NewRequest(t, "GET", "/user/settings"), http.StatusOK) 29 + page := NewHTMLParser(t, response.Body) 30 + assert.True(t, page.Find(".checkbox#keep-activity-private").Length() > 0) 31 + 32 + // = Public = 33 + 34 + // Set activity visibility of user2 to public. This is the default, but won't hurt to set it before testing. 35 + testChangeUserActivityVisibility(t, userRegular, "off") 36 + 37 + // Verify availability of RSS button and activity tab 38 + testUser2ActivityButtonsAvailability(t, userAdmin, true) 39 + testUser2ActivityButtonsAvailability(t, userRegular, true) 40 + testUser2ActivityButtonsAvailability(t, userGuest, true) 41 + 42 + // Verify the hint for all types of users: admin, self, guest 43 + testUser2ActivityVisibility(t, userAdmin, "This activity is visible to everyone, but as an administrator you can also see interactions in private spaces.", true) 44 + testUser2ActivityVisibility(t, userRegular, "Your activity is visible to everyone, except for interactions in private spaces. Configure.", true) 45 + testUser2ActivityVisibility(t, userGuest, "", true) 46 + 47 + // = Private = 48 + 49 + // Set activity visibility of user2 to private 50 + testChangeUserActivityVisibility(t, userRegular, "on") 51 + 52 + // Verify availability of RSS button and activity tab 53 + testUser2ActivityButtonsAvailability(t, userAdmin, true) 54 + testUser2ActivityButtonsAvailability(t, userRegular, true) 55 + testUser2ActivityButtonsAvailability(t, userGuest, false) 56 + 57 + // Verify the hint for all types of users: admin, self, guest 58 + testUser2ActivityVisibility(t, userAdmin, "This activity is visible to you because you're an administrator, but the user wants it to remain private.", true) 59 + testUser2ActivityVisibility(t, userRegular, "Your activity is only visible to you and the instance administrators. Configure.", true) 60 + testUser2ActivityVisibility(t, userGuest, "This user has disabled the public visibility of the activity.", false) 61 + }) 62 + } 63 + 64 + // testChangeUserActivityVisibility allows to easily change visibility of public activity for a user 65 + func testChangeUserActivityVisibility(t *testing.T, session *TestSession, newState string) { 66 + t.Helper() 67 + session.MakeRequest(t, NewRequestWithValues(t, "POST", "/user/settings", 68 + map[string]string{ 69 + "_csrf": GetCSRF(t, session, "/user/settings"), 70 + "keep_activity_private": newState, 71 + }), http.StatusSeeOther) 72 + } 73 + 74 + // testUser2ActivityVisibility checks visibility of UI elements on /<user>?tab=activity 75 + func testUser2ActivityVisibility(t *testing.T, session *TestSession, hint string, availability bool) { 76 + response := session.MakeRequest(t, NewRequest(t, "GET", "/user2?tab=activity"), http.StatusOK) 77 + page := NewHTMLParser(t, response.Body) 78 + // Check hint visibility and correctness 79 + testSelectorEquals(t, page, "#visibility-hint", hint) 80 + 81 + // Check that the hint aligns with the actual feed availability 82 + assert.EqualValues(t, availability, page.Find("#activity-feed").Length() > 0) 83 + 84 + // Check availability of RSS feed button too 85 + assert.EqualValues(t, availability, page.Find("#profile-avatar-card a[href='/user2.rss']").Length() > 0) 86 + 87 + // Check that the current tab is displayed and is active regardless of it's actual availability 88 + // For example, on /<user> it wouldn't be available to guest, but it should be still present on /<user>?tab=activity 89 + assert.True(t, page.Find("overflow-menu .active.item[href='/user2?tab=activity']").Length() > 0) 90 + } 91 + 92 + // testUser2ActivityButtonsAvailability check visibility of Public activity tab on main profile page 93 + func testUser2ActivityButtonsAvailability(t *testing.T, session *TestSession, buttons bool) { 94 + response := session.MakeRequest(t, NewRequest(t, "GET", "/user2"), http.StatusOK) 95 + page := NewHTMLParser(t, response.Body) 96 + assert.EqualValues(t, buttons, page.Find("overflow-menu .item[href='/user2?tab=activity']").Length() > 0) 97 + }