Merge pull request '[gitea] v1.21 cherry-pick' (#2566) from earl-warren/forgejo:wip-v1.21-gitea-cherry-pick into v1.21/forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2566
This commit is contained in:
Earl Warren 2024-03-06 05:50:42 +00:00
commit 6e877f02ab
17 changed files with 71 additions and 28 deletions

View file

@ -334,7 +334,9 @@ func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitR
} }
if err := PushUpdateAddTag(ctx, repo, gitRepo, tagName, sha1, refname); err != nil { if err := PushUpdateAddTag(ctx, repo, gitRepo, tagName, sha1, refname); err != nil {
return fmt.Errorf("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %w", tagName, repo.ID, repo.OwnerName, repo.Name, err) // sometimes, some tags will be sync failed. i.e. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tag/?h=v2.6.11
// this is a tree object, not a tag object which created before git
log.Error("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %v", tagName, repo.ID, repo.OwnerName, repo.Name, err)
} }
return nil return nil

View file

@ -246,6 +246,7 @@ email_title = Email Settings
smtp_addr = SMTP Host smtp_addr = SMTP Host
smtp_port = SMTP Port smtp_port = SMTP Port
smtp_from = Send Email As smtp_from = Send Email As
smtp_from_invalid = The "Send Email As" address is invalid
smtp_from_helper = Email address Gitea will use. Enter a plain email address or use the "Name" <email@example.com> format. smtp_from_helper = Email address Gitea will use. Enter a plain email address or use the "Name" <email@example.com> format.
mailer_user = SMTP Username mailer_user = SMTP Username
mailer_password = SMTP Password mailer_password = SMTP Password

View file

@ -7,6 +7,7 @@ package install
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"net/mail"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -423,6 +424,11 @@ func SubmitInstall(ctx *context.Context) {
} }
if len(strings.TrimSpace(form.SMTPAddr)) > 0 { if len(strings.TrimSpace(form.SMTPAddr)) > 0 {
if _, err := mail.ParseAddress(form.SMTPFrom); err != nil {
ctx.RenderWithErr(ctx.Tr("install.smtp_from_invalid"), tplInstall, &form)
return
}
cfg.Section("mailer").Key("ENABLED").SetValue("true") cfg.Section("mailer").Key("ENABLED").SetValue("true")
cfg.Section("mailer").Key("SMTP_ADDR").SetValue(form.SMTPAddr) cfg.Section("mailer").Key("SMTP_ADDR").SetValue(form.SMTPAddr)
cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort) cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort)

View file

@ -61,17 +61,17 @@ func List(ctx *context.Context) {
var workflows []Workflow var workflows []Workflow
if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil { if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.ServerError("IsEmpty", err)
return return
} else if !empty { } else if !empty {
commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.ServerError("GetBranchCommit", err)
return return
} }
entries, err := actions.ListWorkflows(commit) entries, err := actions.ListWorkflows(commit)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.ServerError("ListWorkflows", err)
return return
} }
@ -96,7 +96,7 @@ func List(ctx *context.Context) {
workflow := Workflow{Entry: *entry} workflow := Workflow{Entry: *entry}
content, err := actions.GetContentFromEntry(entry) content, err := actions.GetContentFromEntry(entry)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.ServerError("GetContentFromEntry", err)
return return
} }
wf, err := model.ReadWorkflow(bytes.NewReader(content)) wf, err := model.ReadWorkflow(bytes.NewReader(content))
@ -173,7 +173,7 @@ func List(ctx *context.Context) {
runs, total, err := actions_model.FindRuns(ctx, opts) runs, total, err := actions_model.FindRuns(ctx, opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.ServerError("FindAndCount", err)
return return
} }
@ -182,7 +182,7 @@ func List(ctx *context.Context) {
} }
if err := runs.LoadTriggerUser(ctx); err != nil { if err := runs.LoadTriggerUser(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.ServerError("LoadTriggerUser", err)
return return
} }
@ -190,7 +190,7 @@ func List(ctx *context.Context) {
actors, err := actions_model.GetActors(ctx, ctx.Repo.Repository.ID) actors, err := actions_model.GetActors(ctx, ctx.Repo.Repository.ID)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.ServerError("GetActors", err)
return return
} }
ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx.Doer, actors) ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx.Doer, actors)

View file

@ -6,6 +6,7 @@ package user
import ( import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@ -126,5 +127,21 @@ func LoadHeaderCount(ctx *context.Context) error {
} }
ctx.Data["RepoCount"] = repoCount ctx.Data["RepoCount"] = repoCount
var projectType project_model.Type
if ctx.ContextUser.IsOrganization() {
projectType = project_model.TypeOrganization
} else {
projectType = project_model.TypeIndividual
}
projectCount, err := project_model.CountProjects(ctx, project_model.SearchOptions{
OwnerID: ctx.ContextUser.ID,
IsClosed: util.OptionalBoolOf(false),
Type: projectType,
})
if err != nil {
return err
}
ctx.Data["ProjectCount"] = projectCount
return nil return nil
} }

View file

@ -38,6 +38,7 @@ func isContainerPath(req *http.Request) bool {
var ( var (
gitRawOrAttachPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|(?:raw/)|(?:releases/download/)|(?:attachments/))`) gitRawOrAttachPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|(?:raw/)|(?:releases/download/)|(?:attachments/))`)
lfsPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/info/lfs/`) lfsPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/info/lfs/`)
archivePathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/archive/`)
) )
func isGitRawOrAttachPath(req *http.Request) bool { func isGitRawOrAttachPath(req *http.Request) bool {
@ -54,6 +55,10 @@ func isGitRawOrAttachOrLFSPath(req *http.Request) bool {
return false return false
} }
func isArchivePath(req *http.Request) bool {
return archivePathRe.MatchString(req.URL.Path)
}
// handleSignIn clears existing session variables and stores new ones for the specified user object // handleSignIn clears existing session variables and stores new ones for the specified user object
func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *user_model.User) { func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *user_model.User) {
// We need to regenerate the session... // We need to regenerate the session...

View file

@ -127,7 +127,7 @@ func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store Dat
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
// These paths are not API paths, but we still want to check for tokens because they maybe in the API returned URLs // These paths are not API paths, but we still want to check for tokens because they maybe in the API returned URLs
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) && if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) &&
!isGitRawOrAttachPath(req) { !isGitRawOrAttachPath(req) && !isArchivePath(req) {
return nil, nil return nil, nil
} }

View file

@ -11,7 +11,6 @@ import (
"sync" "sync"
"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/avatars"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
gitea_context "code.gitea.io/gitea/modules/context" gitea_context "code.gitea.io/gitea/modules/context"
@ -163,12 +162,9 @@ func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) {
func (s *SSPI) newUser(ctx context.Context, username string, cfg *sspi.Source) (*user_model.User, error) { func (s *SSPI) newUser(ctx context.Context, username string, cfg *sspi.Source) (*user_model.User, error) {
email := gouuid.New().String() + "@localhost.localdomain" email := gouuid.New().String() + "@localhost.localdomain"
user := &user_model.User{ user := &user_model.User{
Name: username, Name: username,
Email: email, Email: email,
Passwd: gouuid.New().String(), Language: cfg.DefaultLanguage,
Language: cfg.DefaultLanguage,
UseCustomAvatar: true,
Avatar: avatars.DefaultAvatarLink(),
} }
emailNotificationPreference := user_model.EmailNotificationsDisabled emailNotificationPreference := user_model.EmailNotificationsDisabled
overwriteDefault := &user_model.CreateUserOverwriteOptions{ overwriteDefault := &user_model.CreateUserOverwriteOptions{

View file

@ -9,6 +9,9 @@
{{if .CanReadProjects}} {{if .CanReadProjects}}
<a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects"> <a class="{{if .PageIsViewProjects}}active {{end}}item" href="{{$.Org.HomeLink}}/-/projects">
{{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}} {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}}
{{if .ProjectCount}}
<div class="ui small label">{{.ProjectCount}}</div>
{{end}}
</a> </a>
{{end}} {{end}}
{{if and .IsPackageEnabled .CanReadPackages}} {{if and .IsPackageEnabled .CanReadPackages}}

View file

@ -13,7 +13,7 @@
<a class="item" href="{{$.RepoLink}}/commit/{{PathEscape .Commit.ID.String}}.diff" download="{{ShortSha .Commit.ID.String}}.diff">{{ctx.Locale.Tr "repo.diff.download_diff"}}</a> <a class="item" href="{{$.RepoLink}}/commit/{{PathEscape .Commit.ID.String}}.diff" download="{{ShortSha .Commit.ID.String}}.diff">{{ctx.Locale.Tr "repo.diff.download_diff"}}</a>
{{end}} {{end}}
<a id="expand-files-btn" class="item">{{ctx.Locale.Tr "repo.pulls.expand_files"}}</a> <a id="expand-files-btn" class="item">{{ctx.Locale.Tr "repo.pulls.expand_files"}}</a>
<a id="collapse-files-btn"class="item">{{ctx.Locale.Tr "repo.pulls.collapse_files"}}</a> <a id="collapse-files-btn" class="item">{{ctx.Locale.Tr "repo.pulls.collapse_files"}}</a>
{{if .Issue.Index}} {{if .Issue.Index}}
{{if .ShowOutdatedComments}} {{if .ShowOutdatedComments}}
<a class="item" href="?style={{if $.IsSplitStyle}}split{{else}}unified{{end}}&whitespace={{$.WhitespaceBehavior}}&show-outdated=false"> <a class="item" href="?style={{if $.IsSplitStyle}}split{{else}}unified{{end}}&whitespace={{$.WhitespaceBehavior}}&show-outdated=false">

View file

@ -9,7 +9,7 @@
{{ctx.Locale.Tr "repo.editor.commit_changes"}} {{ctx.Locale.Tr "repo.editor.commit_changes"}}
{{- end}}</h3> {{- end}}</h3>
<div class="field"> <div class="field">
<input name="commit_summary" placeholder="{{if .PageIsDelete}}{{ctx.Locale.Tr "repo.editor.delete" .TreePath}}{{else if .PageIsUpload}}{{ctx.Locale.Tr "repo.editor.upload_files_to_dir" .TreePath}}{{else if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.add_tmpl"}}{{else if .PageIsPatch}}{{ctx.Locale.Tr "repo.editor.patch"}}{{else}}{{ctx.Locale.Tr "repo.editor.update" .TreePath}}{{end}}" value="{{.commit_summary}}" autofocus> <input name="commit_summary" maxlength="100" placeholder="{{if .PageIsDelete}}{{ctx.Locale.Tr "repo.editor.delete" .TreePath}}{{else if .PageIsUpload}}{{ctx.Locale.Tr "repo.editor.upload_files_to_dir" .TreePath}}{{else if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.add_tmpl"}}{{else if .PageIsPatch}}{{ctx.Locale.Tr "repo.editor.patch"}}{{else}}{{ctx.Locale.Tr "repo.editor.update" .TreePath}}{{end}}" value="{{.commit_summary}}" autofocus>
</div> </div>
<div class="field"> <div class="field">
<textarea name="commit_message" placeholder="{{ctx.Locale.Tr "repo.editor.commit_message_desc"}}" rows="5">{{.commit_message}}</textarea> <textarea name="commit_message" placeholder="{{ctx.Locale.Tr "repo.editor.commit_message_desc"}}" rows="5">{{.commit_message}}</textarea>
@ -60,7 +60,7 @@
<div class="quick-pull-branch-name {{if not (eq .commit_choice "commit-to-new-branch")}}gt-hidden{{end}}"> <div class="quick-pull-branch-name {{if not (eq .commit_choice "commit-to-new-branch")}}gt-hidden{{end}}">
<div class="new-branch-name-input field {{if .Err_NewBranchName}}error{{end}}"> <div class="new-branch-name-input field {{if .Err_NewBranchName}}error{{end}}">
{{svg "octicon-git-branch"}} {{svg "octicon-git-branch"}}
<input type="text" name="new_branch_name" value="{{.new_branch_name}}" class="input-contrast gt-mr-2 js-quick-pull-new-branch-name" placeholder="{{ctx.Locale.Tr "repo.editor.new_branch_name_desc"}}" {{if eq .commit_choice "commit-to-new-branch"}}required{{end}} title="{{ctx.Locale.Tr "repo.editor.new_branch_name"}}"> <input type="text" name="new_branch_name" maxlength="100" value="{{.new_branch_name}}" class="input-contrast gt-mr-2 js-quick-pull-new-branch-name" placeholder="{{ctx.Locale.Tr "repo.editor.new_branch_name_desc"}}" {{if eq .commit_choice "commit-to-new-branch"}}required{{end}} title="{{ctx.Locale.Tr "repo.editor.new_branch_name"}}">
<span class="text-muted js-quick-pull-normalization-info"></span> <span class="text-muted js-quick-pull-normalization-info"></span>
</div> </div>
</div> </div>

View file

@ -15,7 +15,7 @@
{{range $i, $v := .TreeNames}} {{range $i, $v := .TreeNames}}
<div class="breadcrumb-divider">/</div> <div class="breadcrumb-divider">/</div>
{{if eq $i $l}} {{if eq $i $l}}
<input id="file-name" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.name_your_file"}}" data-editorconfig="{{$.EditorconfigJson}}" required autofocus> <input id="file-name" maxlength="500" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.name_your_file"}}" data-editorconfig="{{$.EditorconfigJson}}" required autofocus>
<span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span> <span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span>
{{else}} {{else}}
<span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span> <span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>

View file

@ -15,7 +15,7 @@
<a class="section" href="{{$.BranchLink}}">{{.BranchName}}</a> <a class="section" href="{{$.BranchLink}}">{{.BranchName}}</a>
<span>{{ctx.Locale.Tr "repo.editor.or"}} <a href="{{$.BranchLink}}">{{ctx.Locale.Tr "repo.editor.cancel_lower"}}</a></span> <span>{{ctx.Locale.Tr "repo.editor.or"}} <a href="{{$.BranchLink}}">{{ctx.Locale.Tr "repo.editor.cancel_lower"}}</a></span>
<input type="hidden" id="tree_path" name="tree_path" value="" required> <input type="hidden" id="tree_path" name="tree_path" value="" required>
<input id="file-name" type="hidden" value="diff.patch"> <input id="file-name" maxlength="500" type="hidden" value="diff.patch">
</div> </div>
</div> </div>
<div class="field"> <div class="field">

View file

@ -13,7 +13,7 @@
{{range $i, $v := .TreeNames}} {{range $i, $v := .TreeNames}}
<div class="breadcrumb-divider">/</div> <div class="breadcrumb-divider">/</div>
{{if eq $i $l}} {{if eq $i $l}}
<input type="text" id="file-name" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.add_subdir"}}" autofocus> <input type="text" id="file-name" maxlength="500" value="{{$v}}" placeholder="{{ctx.Locale.Tr "repo.editor.add_subdir"}}" autofocus>
<span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span> <span data-tooltip-content="{{ctx.Locale.Tr "repo.editor.filename_help"}}">{{svg "octicon-info"}}</span>
{{else}} {{else}}
<span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span> <span class="section"><a href="{{$.BranchLink}}/{{index $.TreePaths $i | PathEscapeSegments}}">{{$v}}</a></span>

View file

@ -13,6 +13,9 @@
{{if or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects)}} {{if or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects)}}
<a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item"> <a href="{{.ContextUser.HomeLink}}/-/projects" class="{{if .PageIsViewProjects}}active {{end}}item">
{{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}} {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}}
{{if .ProjectCount}}
<div class="ui small label">{{.ProjectCount}}</div>
{{end}}
</a> </a>
{{end}} {{end}}
{{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}} {{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}}

View file

@ -29,17 +29,26 @@ export function showGlobalErrorMessage(msg) {
* @param {ErrorEvent} e * @param {ErrorEvent} e
*/ */
function processWindowErrorEvent(e) { function processWindowErrorEvent(e) {
const err = e.error ?? e.reason;
const assetBaseUrl = String(new URL(__webpack_public_path__, window.location.origin));
// error is likely from browser extension or inline script. Do not show these in production builds.
if (!err.stack?.includes(assetBaseUrl) && window.config?.runModeIsProd) return;
let message;
if (e.type === 'unhandledrejection') { if (e.type === 'unhandledrejection') {
showGlobalErrorMessage(`JavaScript promise rejection: ${e.reason}. Open browser console to see more details.`); message = `JavaScript promise rejection: ${err.message}.`;
return; } else {
message = `JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}).`;
} }
if (!e.error && e.lineno === 0 && e.colno === 0 && e.filename === '' && window.navigator.userAgent.includes('FxiOS/')) { if (!e.error && e.lineno === 0 && e.colno === 0 && e.filename === '' && window.navigator.userAgent.includes('FxiOS/')) {
// At the moment, Firefox (iOS) (10x) has an engine bug. See https://github.com/go-gitea/gitea/issues/20240 // At the moment, Firefox (iOS) (10x) has an engine bug. See https://github.com/go-gitea/gitea/issues/20240
// If a script inserts a newly created (and content changed) element into DOM, there will be a nonsense error event reporting: Script error: line 0, col 0. // If a script inserts a newly created (and content changed) element into DOM, there will be a nonsense error event reporting: Script error: line 0, col 0.
return; // ignore such nonsense error event return; // ignore such nonsense error event
} }
showGlobalErrorMessage(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); showGlobalErrorMessage(`${message} Open browser console to see more details.`);
} }
function initGlobalErrorHandler() { function initGlobalErrorHandler() {

View file

@ -2,13 +2,14 @@ import {encode, decode} from 'uint8-to-base64';
// transform /path/to/file.ext to file.ext // transform /path/to/file.ext to file.ext
export function basename(path = '') { export function basename(path = '') {
return path ? path.replace(/^.*\//, '') : ''; const lastSlashIndex = path.lastIndexOf('/');
return lastSlashIndex < 0 ? path : path.substring(lastSlashIndex + 1);
} }
// transform /path/to/file.ext to .ext // transform /path/to/file.ext to .ext
export function extname(path = '') { export function extname(path = '') {
const [_, ext] = /.+(\.[^.]+)$/.exec(path) || []; const lastPointIndex = path.lastIndexOf('.');
return ext || ''; return lastPointIndex < 0 ? '' : path.substring(lastPointIndex);
} }
// test whether a variable is an object // test whether a variable is an object