Merge branch 'main' into xormigrate

This commit is contained in:
qwerty287 2024-07-25 17:10:50 +02:00 committed by GitHub
commit 68d2ba9828
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 136 additions and 57 deletions

View File

@ -395,6 +395,7 @@ type SearchEmailOptions struct {
// SearchEmailResult is an e-mail address found in the user or email_address table // SearchEmailResult is an e-mail address found in the user or email_address table
type SearchEmailResult struct { type SearchEmailResult struct {
ID int64
UID int64 UID int64
Email string Email string
IsActivated bool IsActivated bool

View File

@ -2982,6 +2982,10 @@ emails.not_updated = Failed to update the requested email address: %v
emails.duplicate_active = This email address is already active for a different user. emails.duplicate_active = This email address is already active for a different user.
emails.change_email_header = Update Email Properties emails.change_email_header = Update Email Properties
emails.change_email_text = Are you sure you want to update this email address? emails.change_email_text = Are you sure you want to update this email address?
emails.delete = Delete Email
emails.delete_desc = Are you sure you want to delete this email address?
emails.deletion_success = The email address has been deleted.
emails.delete_primary_email_error = You can not delete the primary email.
orgs.org_manage_panel = Organization Management orgs.org_manage_panel = Organization Management
orgs.name = Name orgs.name = Name

View File

@ -477,6 +477,7 @@ activate_email=メール アドレスを確認します
activate_email.title=%s さん、メールアドレス確認をお願いします activate_email.title=%s さん、メールアドレス確認をお願いします
activate_email.text=あなたのメールアドレスを確認するため、<b>%s</b>以内に次のリンクをクリックしてください: activate_email.text=あなたのメールアドレスを確認するため、<b>%s</b>以内に次のリンクをクリックしてください:
register_notify=%s へようこそ
register_notify.title=%[1]s さん、%[2]s にようこそ register_notify.title=%[1]s さん、%[2]s にようこそ
register_notify.text_1=これは %s への登録確認メールです! register_notify.text_1=これは %s への登録確認メールです!
register_notify.text_2=あなたはユーザー名 %s でログインできるようになりました。 register_notify.text_2=あなたはユーザー名 %s でログインできるようになりました。
@ -913,6 +914,7 @@ create_oauth2_application_success=新しいOAuth2アプリケーションを作
update_oauth2_application_success=OAuth2アプリケーションを更新しました。 update_oauth2_application_success=OAuth2アプリケーションを更新しました。
oauth2_application_name=アプリケーション名 oauth2_application_name=アプリケーション名
oauth2_confidential_client=コンフィデンシャルクライアント。 ウェブアプリのように秘密情報を機密にできるアプリの場合に選択します。 デスクトップアプリやモバイルアプリなどのネイティブアプリには選択しないでください。 oauth2_confidential_client=コンフィデンシャルクライアント。 ウェブアプリのように秘密情報を機密にできるアプリの場合に選択します。 デスクトップアプリやモバイルアプリなどのネイティブアプリには選択しないでください。
oauth2_skip_secondary_authorization=一度アクセスを許可した後、公開クライアントの認証をスキップします。 <strong>セキュリティ上のリスクが生じる可能性があります。</strong>
oauth2_redirect_uris=リダイレクトURI (複数可)。 URIごとに改行してください。 oauth2_redirect_uris=リダイレクトURI (複数可)。 URIごとに改行してください。
save_application=保存 save_application=保存
oauth2_client_id=クライアントID oauth2_client_id=クライアントID

View File

@ -42,7 +42,7 @@ func GetRawFile(ctx *context.APIContext) {
// --- // ---
// summary: Get a file from a repository // summary: Get a file from a repository
// produces: // produces:
// - application/json // - application/octet-stream
// parameters: // parameters:
// - name: owner // - name: owner
// in: path // in: path
@ -67,6 +67,8 @@ func GetRawFile(ctx *context.APIContext) {
// responses: // responses:
// 200: // 200:
// description: Returns raw file content. // description: Returns raw file content.
// schema:
// type: file
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
@ -92,6 +94,8 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/media/{filepath} repository repoGetRawFileOrLFS // swagger:operation GET /repos/{owner}/{repo}/media/{filepath} repository repoGetRawFileOrLFS
// --- // ---
// summary: Get a file or it's LFS object from a repository // summary: Get a file or it's LFS object from a repository
// produces:
// - application/octet-stream
// parameters: // parameters:
// - name: owner // - name: owner
// in: path // in: path
@ -116,6 +120,8 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
// responses: // responses:
// 200: // 200:
// description: Returns raw file content. // description: Returns raw file content.
// schema:
// type: file
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"

View File

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/user"
) )
const ( const (
@ -150,3 +151,32 @@ func ActivateEmail(ctx *context.Context) {
redirect.RawQuery = q.Encode() redirect.RawQuery = q.Encode()
ctx.Redirect(redirect.String()) ctx.Redirect(redirect.String())
} }
// DeleteEmail serves a POST request for delete a user's email
func DeleteEmail(ctx *context.Context) {
u, err := user_model.GetUserByID(ctx, ctx.FormInt64("Uid"))
if err != nil || u == nil {
ctx.ServerError("GetUserByID", err)
return
}
email, err := user_model.GetEmailAddressByID(ctx, u.ID, ctx.FormInt64("id"))
if err != nil || email == nil {
ctx.ServerError("GetEmailAddressByID", err)
return
}
if err := user.DeleteEmailAddresses(ctx, u, []string{email.Email}); err != nil {
if user_model.IsErrPrimaryEmailCannotDelete(err) {
ctx.Flash.Error(ctx.Tr("admin.emails.delete_primary_email_error"))
ctx.JSONRedirect("")
return
}
ctx.ServerError("DeleteEmailAddresses", err)
return
}
log.Trace("Email address deleted: %s %s", u.Name, email.Email)
ctx.Flash.Success(ctx.Tr("admin.emails.deletion_success"))
ctx.JSONRedirect("")
}

View File

@ -355,6 +355,7 @@ func IntrospectOAuth(ctx *context.Context) {
var response struct { var response struct {
Active bool `json:"active"` Active bool `json:"active"`
Scope string `json:"scope,omitempty"` Scope string `json:"scope,omitempty"`
Username string `json:"username,omitempty"`
jwt.RegisteredClaims jwt.RegisteredClaims
} }
@ -371,6 +372,9 @@ func IntrospectOAuth(ctx *context.Context) {
response.Audience = []string{app.ClientID} response.Audience = []string{app.ClientID}
response.Subject = fmt.Sprint(grant.UserID) response.Subject = fmt.Sprint(grant.UserID)
} }
if user, err := user_model.GetUserByID(ctx, grant.UserID); err == nil {
response.Username = user.Name
}
} }
} }

View File

@ -83,7 +83,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
link := &feeds.Link{Href: act.GetCommentHTMLURL(ctx)} link := &feeds.Link{Href: act.GetCommentHTMLURL(ctx)}
// title // title
title = act.ActUser.DisplayName() + " " title = act.ActUser.GetDisplayName() + " "
var titleExtra template.HTML var titleExtra template.HTML
switch act.OpType { switch act.OpType {
case activities_model.ActionCreateRepo: case activities_model.ActionCreateRepo:
@ -252,7 +252,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
Description: desc, Description: desc,
IsPermaLink: "false", IsPermaLink: "false",
Author: &feeds.Author{ Author: &feeds.Author{
Name: act.ActUser.DisplayName(), Name: act.ActUser.GetDisplayName(),
Email: act.ActUser.GetEmail(), Email: act.ActUser.GetEmail(),
}, },
Id: fmt.Sprintf("%v: %v", strconv.FormatInt(act.ID, 10), link.Href), Id: fmt.Sprintf("%v: %v", strconv.FormatInt(act.ID, 10), link.Href),
@ -313,7 +313,7 @@ func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) (
Link: link, Link: link,
Created: rel.CreatedUnix.AsTime(), Created: rel.CreatedUnix.AsTime(),
Author: &feeds.Author{ Author: &feeds.Author{
Name: rel.Publisher.DisplayName(), Name: rel.Publisher.GetDisplayName(),
Email: rel.Publisher.GetEmail(), Email: rel.Publisher.GetEmail(),
}, },
Id: fmt.Sprintf("%v: %v", strconv.FormatInt(rel.ID, 10), link.Href), Id: fmt.Sprintf("%v: %v", strconv.FormatInt(rel.ID, 10), link.Href),

View File

@ -725,6 +725,7 @@ func registerRoutes(m *web.Router) {
m.Group("/emails", func() { m.Group("/emails", func() {
m.Get("", admin.Emails) m.Get("", admin.Emails)
m.Post("/activate", admin.ActivateEmail) m.Post("/activate", admin.ActivateEmail)
m.Post("/delete", admin.DeleteEmail)
}) })
m.Group("/orgs", func() { m.Group("/orgs", func() {

View File

@ -38,6 +38,7 @@
</th> </th>
<th>{{ctx.Locale.Tr "admin.emails.primary"}}</th> <th>{{ctx.Locale.Tr "admin.emails.primary"}}</th>
<th>{{ctx.Locale.Tr "admin.emails.activated"}}</th> <th>{{ctx.Locale.Tr "admin.emails.activated"}}</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -59,6 +60,11 @@
{{svg (Iif .IsActivated "octicon-check" "octicon-x")}} {{svg (Iif .IsActivated "octicon-check" "octicon-x")}}
{{end}} {{end}}
</td> </td>
<td>
<div class="tw-flex tw-gap-2">
<a class="delete-button" href="" data-url="{{$.Link}}/delete" data-id="{{.ID}}" data-data-uid="{{.UID}}">{{svg "octicon-trash"}}</a>
</div>
</td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>
@ -95,4 +101,16 @@
</div> </div>
</div> </div>
<div class="ui g-modal-confirm delete modal">
<div class="header">
{{svg "octicon-trash"}}
{{ctx.Locale.Tr "admin.emails.delete"}}
</div>
<div class="content">
{{ctx.Locale.Tr "admin.emails.delete_desc"}}
</div>
{{template "base/modal_actions_confirm" .}}
</div>
{{template "admin/layout_footer" .}} {{template "admin/layout_footer" .}}

View File

@ -10609,6 +10609,9 @@
}, },
"/repos/{owner}/{repo}/media/{filepath}": { "/repos/{owner}/{repo}/media/{filepath}": {
"get": { "get": {
"produces": [
"application/octet-stream"
],
"tags": [ "tags": [
"repository" "repository"
], ],
@ -10645,7 +10648,10 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "Returns raw file content." "description": "Returns raw file content.",
"schema": {
"type": "file"
}
}, },
"404": { "404": {
"$ref": "#/responses/notFound" "$ref": "#/responses/notFound"
@ -12682,7 +12688,7 @@
"/repos/{owner}/{repo}/raw/{filepath}": { "/repos/{owner}/{repo}/raw/{filepath}": {
"get": { "get": {
"produces": [ "produces": [
"application/json" "application/octet-stream"
], ],
"tags": [ "tags": [
"repository" "repository"
@ -12720,7 +12726,10 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "Returns raw file content." "description": "Returns raw file content.",
"schema": {
"type": "file"
}
}, },
"404": { "404": {
"$ref": "#/responses/notFound" "$ref": "#/responses/notFound"

View File

@ -452,10 +452,12 @@ func TestOAuthIntrospection(t *testing.T) {
type introspectResponse struct { type introspectResponse struct {
Active bool `json:"active"` Active bool `json:"active"`
Scope string `json:"scope,omitempty"` Scope string `json:"scope,omitempty"`
Username string `json:"username"`
} }
introspectParsed := new(introspectResponse) introspectParsed := new(introspectResponse)
assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), introspectParsed)) assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), introspectParsed))
assert.True(t, introspectParsed.Active) assert.True(t, introspectParsed.Active)
assert.Equal(t, "user1", introspectParsed.Username)
// successful request with a valid client_id/client_secret, but an invalid token // successful request with a valid client_id/client_secret, but an invalid token
req = NewRequestWithValues(t, "POST", "/login/oauth/introspect", map[string]string{ req = NewRequestWithValues(t, "POST", "/login/oauth/introspect", map[string]string{

View File

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import {SvgIcon} from '../svg.ts'; import {SvgIcon} from '../svg.ts';
import {GET} from '../modules/fetch.ts'; import {GET} from '../modules/fetch.ts';
import {generateAriaId} from '../modules/fomantic/base.ts';
export default { export default {
components: {SvgIcon}, components: {SvgIcon},
@ -9,12 +10,16 @@ export default {
return { return {
menuVisible: false, menuVisible: false,
isLoading: false, isLoading: false,
queryParams: el.getAttribute('data-queryparams'),
issueLink: el.getAttribute('data-issuelink'),
locale: { locale: {
filter_changes_by_commit: el.getAttribute('data-filter_changes_by_commit'), filter_changes_by_commit: el.getAttribute('data-filter_changes_by_commit'),
}, },
commits: [], commits: [],
hoverActivated: false, hoverActivated: false,
lastReviewCommitSha: null, lastReviewCommitSha: null,
uniqueIdMenu: generateAriaId(),
uniqueIdShowAll: generateAriaId(),
}; };
}, },
computed: { computed: {
@ -24,12 +29,6 @@ export default {
} }
return 0; return 0;
}, },
queryParams() {
return this.$el.parentNode.getAttribute('data-queryparams');
},
issueLink() {
return this.$el.parentNode.getAttribute('data-issuelink');
},
}, },
mounted() { mounted() {
document.body.addEventListener('click', this.onBodyClick); document.body.addEventListener('click', this.onBodyClick);
@ -68,6 +67,11 @@ export default {
this.toggleMenu(); this.toggleMenu();
break; break;
} }
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
const item = document.activeElement; // try to highlight the selected commits
const commitIdx = item?.matches('.item') ? item.getAttribute('data-commit-idx') : null;
if (commitIdx) this.highlight(this.commits[commitIdx]);
}
}, },
onKeyUp(event) { onKeyUp(event) {
if (!this.menuVisible) return; if (!this.menuVisible) return;
@ -113,12 +117,10 @@ export default {
} }
// set correct tabindex to allow easier navigation // set correct tabindex to allow easier navigation
this.$nextTick(() => { this.$nextTick(() => {
const expandBtn = this.$el.querySelector('#diff-commit-list-expand');
const showAllChanges = this.$el.querySelector('#diff-commit-list-show-all');
if (this.menuVisible) { if (this.menuVisible) {
this.focusElem(showAllChanges, expandBtn); this.focusElem(this.$refs.showAllChanges, this.$refs.expandBtn);
} else { } else {
this.focusElem(expandBtn, showAllChanges); this.focusElem(this.$refs.expandBtn, this.$refs.showAllChanges);
} }
}); });
}, },
@ -189,22 +191,23 @@ export default {
}; };
</script> </script>
<template> <template>
<div class="ui scrolling dropdown custom"> <div class="ui scrolling dropdown custom diff-commit-selector">
<button <button
ref="expandBtn"
class="ui basic button" class="ui basic button"
id="diff-commit-list-expand"
@click.stop="toggleMenu()" @click.stop="toggleMenu()"
:data-tooltip-content="locale.filter_changes_by_commit" :data-tooltip-content="locale.filter_changes_by_commit"
aria-haspopup="true" aria-haspopup="true"
aria-controls="diff-commit-selector-menu"
:aria-label="locale.filter_changes_by_commit" :aria-label="locale.filter_changes_by_commit"
aria-activedescendant="diff-commit-list-show-all" :aria-controls="uniqueIdMenu"
:aria-activedescendant="uniqueIdShowAll"
> >
<svg-icon name="octicon-git-commit"/> <svg-icon name="octicon-git-commit"/>
</button> </button>
<div class="left menu" id="diff-commit-selector-menu" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak :aria-expanded="menuVisible ? 'true': 'false'"> <!-- this dropdown is not managed by Fomantic UI, so it needs some classes like "transition" explicitly -->
<div class="left menu transition" :id="uniqueIdMenu" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak :aria-expanded="menuVisible ? 'true': 'false'">
<div class="loading-indicator is-loading" v-if="isLoading"/> <div class="loading-indicator is-loading" v-if="isLoading"/>
<div v-if="!isLoading" class="vertical item" id="diff-commit-list-show-all" role="menuitem" @keydown.enter="showAllChanges()" @click="showAllChanges()"> <div v-if="!isLoading" class="item" :id="uniqueIdShowAll" ref="showAllChanges" role="menuitem" @keydown.enter="showAllChanges()" @click="showAllChanges()">
<div class="gt-ellipsis"> <div class="gt-ellipsis">
{{ locale.show_all_commits }} {{ locale.show_all_commits }}
</div> </div>
@ -214,8 +217,8 @@ export default {
</div> </div>
<!-- only show the show changes since last review if there is a review AND we are commits ahead of the last review --> <!-- only show the show changes since last review if there is a review AND we are commits ahead of the last review -->
<div <div
v-if="lastReviewCommitSha != null" role="menuitem" v-if="lastReviewCommitSha != null"
class="vertical item" class="item" role="menuitem"
:class="{disabled: !commitsSinceLastReview}" :class="{disabled: !commitsSinceLastReview}"
@keydown.enter="changesSinceLastReviewClick()" @keydown.enter="changesSinceLastReviewClick()"
@click="changesSinceLastReviewClick()" @click="changesSinceLastReviewClick()"
@ -228,10 +231,11 @@ export default {
</div> </div>
</div> </div>
<span v-if="!isLoading" class="info text light-2">{{ locale.select_commit_hold_shift_for_range }}</span> <span v-if="!isLoading" class="info text light-2">{{ locale.select_commit_hold_shift_for_range }}</span>
<template v-for="commit in commits" :key="commit.id"> <template v-for="(commit, idx) in commits" :key="commit.id">
<div <div
class="vertical item" role="menuitem" class="item" role="menuitem"
:class="{selection: commit.selected, hovered: commit.hovered}" :class="{selected: commit.selected, hovered: commit.hovered}"
:data-commit-idx="idx"
@keydown.enter.exact="commitClicked(commit.id)" @keydown.enter.exact="commitClicked(commit.id)"
@keydown.enter.shift.exact="commitClickedShift(commit)" @keydown.enter.shift.exact="commitClickedShift(commit)"
@mouseover.shift="highlight(commit)" @mouseover.shift="highlight(commit)"
@ -261,46 +265,44 @@ export default {
</div> </div>
</template> </template>
<style scoped> <style scoped>
.hovered:not(.selection) { .ui.dropdown.diff-commit-selector .menu {
background-color: var(--color-small-accent) !important; margin-top: 0.25em;
}
.selection {
background-color: var(--color-accent) !important;
}
.info {
display: inline-block;
padding: 7px 14px !important;
line-height: 1.4;
width: 100%;
}
#diff-commit-selector-menu {
overflow-x: hidden; overflow-x: hidden;
max-height: 450px; max-height: 450px;
} }
#diff-commit-selector-menu .loading-indicator { .ui.dropdown.diff-commit-selector .menu .loading-indicator {
height: 200px; height: 200px;
width: 350px; width: 350px;
} }
#diff-commit-selector-menu .item, .ui.dropdown.diff-commit-selector .menu > .item,
#diff-commit-selector-menu .info { .ui.dropdown.diff-commit-selector .menu > .info {
display: flex !important; display: flex;
flex-direction: row; flex-direction: row;
line-height: 1.4; line-height: 1.4;
padding: 7px 14px !important;
border-top: 1px solid var(--color-secondary) !important;
gap: 0.25em; gap: 0.25em;
padding: 7px 14px !important;
} }
#diff-commit-selector-menu .item:focus { .ui.dropdown.diff-commit-selector .menu > .item:not(:first-child),
color: var(--color-text); .ui.dropdown.diff-commit-selector .menu > .info:not(:first-child) {
background: var(--color-hover); border-top: 1px solid var(--color-secondary) !important;
} }
#diff-commit-selector-menu .commit-list-summary { .ui.dropdown.diff-commit-selector .menu > .item:focus {
background: var(--color-active);
}
.ui.dropdown.diff-commit-selector .menu > .item.hovered {
background-color: var(--color-small-accent);
}
.ui.dropdown.diff-commit-selector .menu > .item.selected {
background-color: var(--color-accent);
}
.ui.dropdown.diff-commit-selector .menu .commit-list-summary {
max-width: min(380px, 96vw); max-width: min(380px, 96vw);
} }
</style> </style>