forked from NYANDEV/forgejo
Add 'Show on a map' button to Location in profile, fix layout (#26214)
Not too important, but I think that it'd be a pretty neat touch. Also fixes some layout bugs introduced by a previous PR. --------- Co-authored-by: Gusted <postmaster@gusted.xyz> Co-authored-by: Caesar Schinas <caesar@caesarschinas.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
72363be7ca
commit
d58c542579
9 changed files with 54 additions and 11 deletions
|
@ -827,6 +827,15 @@ LEVEL = Info
|
||||||
;; Dependencies can be added from any repository where the user is granted access or only from the current repository depending on this setting.
|
;; Dependencies can be added from any repository where the user is granted access or only from the current repository depending on this setting.
|
||||||
;ALLOW_CROSS_REPOSITORY_DEPENDENCIES = true
|
;ALLOW_CROSS_REPOSITORY_DEPENDENCIES = true
|
||||||
;;
|
;;
|
||||||
|
;; Default map service. No external API support has been included. A service has to allow
|
||||||
|
;; searching using URL parameters, the location will be appended to the URL as escaped query parameter.
|
||||||
|
;; Disabled by default, some example values are:
|
||||||
|
;; - OpenStreetMap: https://www.openstreetmap.org/search?query=
|
||||||
|
;; - Google Maps: https://www.google.com/maps/place/
|
||||||
|
;; - MapQuest: https://www.mapquest.com/search/
|
||||||
|
;; - Bing Maps: https://www.bing.com/maps?where1=
|
||||||
|
; USER_LOCATION_MAP_URL =
|
||||||
|
;;
|
||||||
;; Enable heatmap on users profiles.
|
;; Enable heatmap on users profiles.
|
||||||
;ENABLE_USER_HEATMAP = true
|
;ENABLE_USER_HEATMAP = true
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -648,6 +648,7 @@ And the following unique queues:
|
||||||
- `DEFAULT_USER_IS_RESTRICTED`: **false**: Give new users restricted permissions by default
|
- `DEFAULT_USER_IS_RESTRICTED`: **false**: Give new users restricted permissions by default
|
||||||
- `DEFAULT_ENABLE_DEPENDENCIES`: **true**: Enable this to have dependencies enabled by default.
|
- `DEFAULT_ENABLE_DEPENDENCIES`: **true**: Enable this to have dependencies enabled by default.
|
||||||
- `ALLOW_CROSS_REPOSITORY_DEPENDENCIES` : **true** Enable this to allow dependencies on issues from any repository where the user is granted access.
|
- `ALLOW_CROSS_REPOSITORY_DEPENDENCIES` : **true** Enable this to allow dependencies on issues from any repository where the user is granted access.
|
||||||
|
- `USER_LOCATION_MAP_URL`: **""**: A map service URL to show user's location on a map. The location will be appended to the URL as escaped query parameter.
|
||||||
- `ENABLE_USER_HEATMAP`: **true**: Enable this to display the heatmap on users profiles.
|
- `ENABLE_USER_HEATMAP`: **true**: Enable this to display the heatmap on users profiles.
|
||||||
- `ENABLE_TIMETRACKING`: **true**: Enable Timetracking feature.
|
- `ENABLE_TIMETRACKING`: **true**: Enable Timetracking feature.
|
||||||
- `DEFAULT_ENABLE_TIMETRACKING`: **true**: Allow repositories to use timetracking by default.
|
- `DEFAULT_ENABLE_TIMETRACKING`: **true**: Allow repositories to use timetracking by default.
|
||||||
|
|
|
@ -73,6 +73,7 @@ var Service = struct {
|
||||||
AllowCrossRepositoryDependencies bool
|
AllowCrossRepositoryDependencies bool
|
||||||
DefaultAllowOnlyContributorsToTrackTime bool
|
DefaultAllowOnlyContributorsToTrackTime bool
|
||||||
NoReplyAddress string
|
NoReplyAddress string
|
||||||
|
UserLocationMapURL string
|
||||||
EnableUserHeatmap bool
|
EnableUserHeatmap bool
|
||||||
AutoWatchNewRepos bool
|
AutoWatchNewRepos bool
|
||||||
AutoWatchOnChanges bool
|
AutoWatchOnChanges bool
|
||||||
|
@ -185,6 +186,7 @@ func loadServiceFrom(rootCfg ConfigProvider) {
|
||||||
Service.AllowCrossRepositoryDependencies = sec.Key("ALLOW_CROSS_REPOSITORY_DEPENDENCIES").MustBool(true)
|
Service.AllowCrossRepositoryDependencies = sec.Key("ALLOW_CROSS_REPOSITORY_DEPENDENCIES").MustBool(true)
|
||||||
Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true)
|
Service.DefaultAllowOnlyContributorsToTrackTime = sec.Key("DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME").MustBool(true)
|
||||||
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply." + Domain)
|
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply." + Domain)
|
||||||
|
Service.UserLocationMapURL = sec.Key("USER_LOCATION_MAP_URL").String()
|
||||||
Service.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true)
|
Service.EnableUserHeatmap = sec.Key("ENABLE_USER_HEATMAP").MustBool(true)
|
||||||
Service.AutoWatchNewRepos = sec.Key("AUTO_WATCH_NEW_REPOS").MustBool(true)
|
Service.AutoWatchNewRepos = sec.Key("AUTO_WATCH_NEW_REPOS").MustBool(true)
|
||||||
Service.AutoWatchOnChanges = sec.Key("AUTO_WATCH_ON_CHANGES").MustBool(false)
|
Service.AutoWatchOnChanges = sec.Key("AUTO_WATCH_ON_CHANGES").MustBool(false)
|
||||||
|
|
|
@ -601,6 +601,7 @@ user_bio = Biography
|
||||||
disabled_public_activity = This user has disabled the public visibility of the activity.
|
disabled_public_activity = This user has disabled the public visibility of the activity.
|
||||||
email_visibility.limited = Your email address is visible to all authenticated users
|
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
|
email_visibility.private = Your email address is only visible to you and administrators
|
||||||
|
show_on_map = Show this place on a map
|
||||||
|
|
||||||
form.name_reserved = The username "%s" is reserved.
|
form.name_reserved = The username "%s" is reserved.
|
||||||
form.name_pattern_not_allowed = The pattern "%s" is not allowed in a username.
|
form.name_pattern_not_allowed = The pattern "%s" is not allowed in a username.
|
||||||
|
@ -627,6 +628,7 @@ webauthn = Security Keys
|
||||||
|
|
||||||
public_profile = Public Profile
|
public_profile = Public Profile
|
||||||
biography_placeholder = Tell us a little bit about yourself
|
biography_placeholder = Tell us a little bit about yourself
|
||||||
|
location_placeholder = Share your approximate location with others
|
||||||
profile_desc = Your email address will be used for notifications and other operations.
|
profile_desc = Your email address will be used for notifications and other operations.
|
||||||
password_username_disabled = Non-local users are not allowed to change their username. Please contact your site administrator for more details.
|
password_username_disabled = Non-local users are not allowed to change their username. Please contact your site administrator for more details.
|
||||||
full_name = Full Name
|
full_name = Full Name
|
||||||
|
|
|
@ -52,6 +52,7 @@ func userProfile(ctx *context.Context) {
|
||||||
|
|
||||||
ctx.Data["Title"] = ctx.ContextUser.DisplayName()
|
ctx.Data["Title"] = ctx.ContextUser.DisplayName()
|
||||||
ctx.Data["PageIsUserProfile"] = true
|
ctx.Data["PageIsUserProfile"] = true
|
||||||
|
ctx.Data["UserLocationMapURL"] = setting.Service.UserLocationMapURL
|
||||||
|
|
||||||
// prepare heatmap data
|
// prepare heatmap data
|
||||||
if setting.Service.EnableUserHeatmap {
|
if setting.Service.EnableUserHeatmap {
|
||||||
|
|
|
@ -24,19 +24,28 @@
|
||||||
<div class="extra content gt-word-break">
|
<div class="extra content gt-word-break">
|
||||||
<ul>
|
<ul>
|
||||||
{{if .ContextUser.Location}}
|
{{if .ContextUser.Location}}
|
||||||
<li>{{svg "octicon-location"}} {{.ContextUser.Location}}</li>
|
<li>
|
||||||
|
{{svg "octicon-location"}}
|
||||||
|
<span class="gt-f1">{{.ContextUser.Location}}</span>
|
||||||
|
{{if .UserLocationMapURL}}
|
||||||
|
{{/* We presume that the UserLocationMapURL is safe, as it is provided by the site administrator. */}}
|
||||||
|
<a href="{{.UserLocationMapURL | Safe}}{{.ContextUser.Location | QueryEscape}}" rel="nofollow noreferrer" data-tooltip-content="{{.locale.Tr "user.show_on_map"}}">
|
||||||
|
{{svg "octicon-link-external"}}
|
||||||
|
</a>
|
||||||
|
{{end}}
|
||||||
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if (eq .SignedUserID .ContextUser.ID)}}
|
{{if (eq .SignedUserID .ContextUser.ID)}}
|
||||||
<li>
|
<li>
|
||||||
{{svg "octicon-mail"}}
|
{{svg "octicon-mail"}}
|
||||||
<a href="mailto:{{.ContextUser.Email}}" rel="nofollow">{{.ContextUser.Email}}</a>
|
<a class="gt-f1" href="mailto:{{.ContextUser.Email}}" rel="nofollow">{{.ContextUser.Email}}</a>
|
||||||
<a href="{{AppSubUrl}}/user/settings#keep-email-private">
|
<a href="{{AppSubUrl}}/user/settings#keep-email-private">
|
||||||
{{if .ShowUserEmail}}
|
{{if .ShowUserEmail}}
|
||||||
<i class="ui right" data-tooltip-content="{{.locale.Tr "user.email_visibility.limited"}}">
|
<i data-tooltip-content="{{.locale.Tr "user.email_visibility.limited"}}">
|
||||||
{{svg "octicon-unlock"}}
|
{{svg "octicon-unlock"}}
|
||||||
</i>
|
</i>
|
||||||
{{else}}
|
{{else}}
|
||||||
<i class="ui right" data-tooltip-content="{{.locale.Tr "user.email_visibility.private"}}">
|
<i data-tooltip-content="{{.locale.Tr "user.email_visibility.private"}}">
|
||||||
{{svg "octicon-lock"}}
|
{{svg "octicon-lock"}}
|
||||||
</i>
|
</i>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -69,7 +78,7 @@
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<li>{{svg "octicon-calendar"}} {{.locale.Tr "user.joined_on" (DateTime "short" .ContextUser.CreatedUnix) | Safe}}</li>
|
<li>{{svg "octicon-calendar"}} <span>{{.locale.Tr "user.joined_on" (DateTime "short" .ContextUser.CreatedUnix) | Safe}}</span></li>
|
||||||
{{if and .Orgs .HasOrgsVisible}}
|
{{if and .Orgs .HasOrgsVisible}}
|
||||||
<li>
|
<li>
|
||||||
<ul class="user-orgs">
|
<ul class="user-orgs">
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="location">{{.locale.Tr "settings.location"}}</label>
|
<label for="location">{{.locale.Tr "settings.location"}}</label>
|
||||||
<input id="location" name="location" value="{{.SignedUser.Location}}" maxlength="50">
|
<input id="location" name="location" placeholder="{{.locale.Tr "settings.location_placeholder"}}" value="{{.SignedUser.Location}}" maxlength="50">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/test"
|
"code.gitea.io/gitea/modules/test"
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
|
@ -276,3 +277,23 @@ func TestListStopWatches(t *testing.T) {
|
||||||
assert.Greater(t, apiWatches[0].Seconds, int64(0))
|
assert.Greater(t, apiWatches[0].Seconds, int64(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUserLocationMapLink(t *testing.T) {
|
||||||
|
setting.Service.UserLocationMapURL = "https://example/foo/"
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
session := loginUser(t, "user2")
|
||||||
|
req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
|
||||||
|
"_csrf": GetCSRF(t, session, "/user/settings"),
|
||||||
|
"name": "user2",
|
||||||
|
"email": "user@example.com",
|
||||||
|
"language": "en-US",
|
||||||
|
"location": "A/b",
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", "/user2/")
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
htmlDoc.AssertElement(t, `a[href="https://example/foo/A%2Fb"]`, true)
|
||||||
|
}
|
||||||
|
|
|
@ -22,18 +22,16 @@
|
||||||
|
|
||||||
.user.profile .ui.card .extra.content > ul > li {
|
.user.profile .ui.card .extra.content > ul > li {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user.profile .ui.card .extra.content > ul > li:not(:last-child) {
|
.user.profile .ui.card .extra.content > ul > li:not(:last-child) {
|
||||||
border-bottom: 1px solid var(--color-secondary);
|
border-bottom: 1px solid var(--color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.user.profile .ui.card .extra.content > ul > li .svg {
|
|
||||||
margin-left: 1px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user.profile .ui.card .extra.content > ul > li.follow .ui.button {
|
.user.profile .ui.card .extra.content > ul > li.follow .ui.button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue