Compare commits

..

No commits in common. "787a05a3a2db1715aa90517029a364c36403a6b1" and "ffce336f1802b2f2298fc8fd27e815086702c812" have entirely different histories.

29 changed files with 87 additions and 241 deletions

View File

@ -25,7 +25,7 @@
fork_id: 0 fork_id: 0
is_template: false is_template: false
template_id: 0 template_id: 0
size: 7028 size: 6708
is_fsck_enabled: true is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false close_issues_via_commit_in_any_branch: false

View File

@ -52,16 +52,13 @@ func listPullRequestStatement(baseRepoID int64, opts *PullRequestsOptions) (*xor
// GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged // GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged
// by given head information (repo and branch). // by given head information (repo and branch).
// arg `includeClosed` controls whether the SQL returns closed PRs func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) {
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string, includeClosed bool) ([]*PullRequest, error) {
prs := make([]*PullRequest, 0, 2) prs := make([]*PullRequest, 0, 2)
sess := db.GetEngine(db.DefaultContext). return prs, db.GetEngine(db.DefaultContext).
Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ? AND flow = ?",
repoID, branch, false, false, PullRequestFlowGithub).
Join("INNER", "issue", "issue.id = pull_request.issue_id"). Join("INNER", "issue", "issue.id = pull_request.issue_id").
Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND flow = ?", repoID, branch, false, PullRequestFlowGithub) Find(&prs)
if !includeClosed {
sess.Where("issue.is_closed = ?", false)
}
return prs, sess.Find(&prs)
} }
// CanMaintainerWriteToBranch check whether user is a maintainer and could write to the branch // CanMaintainerWriteToBranch check whether user is a maintainer and could write to the branch
@ -74,7 +71,7 @@ func CanMaintainerWriteToBranch(p access_model.Permission, branch string, user *
return false return false
} }
prs, err := GetUnmergedPullRequestsByHeadInfo(p.Units[0].RepoID, branch, false) prs, err := GetUnmergedPullRequestsByHeadInfo(p.Units[0].RepoID, branch)
if err != nil { if err != nil {
return false return false
} }

View File

@ -118,7 +118,7 @@ func TestHasUnmergedPullRequestsByHeadInfo(t *testing.T) {
func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) { func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2", false) prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2")
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, prs, 1) assert.Len(t, prs, 1)
for _, pr := range prs { for _, pr := range prs {

View File

@ -41,8 +41,9 @@ var RecommendedHashAlgorithms = []string{
"pbkdf2_hi", "pbkdf2_hi",
} }
// hashAlgorithmToSpec converts an algorithm name or a specification to a full algorithm specification // SetDefaultPasswordHashAlgorithm will take a provided algorithmName and dealias it to
func hashAlgorithmToSpec(algorithmName string) string { // a complete algorithm specification.
func SetDefaultPasswordHashAlgorithm(algorithmName string) (string, *PasswordHashAlgorithm) {
if algorithmName == "" { if algorithmName == "" {
algorithmName = DefaultHashAlgorithmName algorithmName = DefaultHashAlgorithmName
} }
@ -51,26 +52,10 @@ func hashAlgorithmToSpec(algorithmName string) string {
algorithmName = alias algorithmName = alias
alias, has = aliasAlgorithmNames[algorithmName] alias, has = aliasAlgorithmNames[algorithmName]
} }
return algorithmName
}
// SetDefaultPasswordHashAlgorithm will take a provided algorithmName and de-alias it to // algorithmName should now be a full algorithm specification
// a complete algorithm specification. // e.g. pbkdf2$50000$50 rather than pbdkf2
func SetDefaultPasswordHashAlgorithm(algorithmName string) (string, *PasswordHashAlgorithm) { DefaultHashAlgorithm = Parse(algorithmName)
algoSpec := hashAlgorithmToSpec(algorithmName)
// now we get a full specification, e.g. pbkdf2$50000$50 rather than pbdkf2
DefaultHashAlgorithm = Parse(algoSpec)
return algoSpec, DefaultHashAlgorithm
}
// ConfigHashAlgorithm will try to find a "recommended algorithm name" defined by RecommendedHashAlgorithms for config return algorithmName, DefaultHashAlgorithm
// This function is not fast and is only used for the installation page
func ConfigHashAlgorithm(algorithm string) string {
algorithm = hashAlgorithmToSpec(algorithm)
for _, recommAlgo := range RecommendedHashAlgorithms {
if algorithm == hashAlgorithmToSpec(recommAlgo) {
return recommAlgo
}
}
return algorithm
} }

View File

@ -277,18 +277,11 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
// GetFilesChangedBetween returns a list of all files that have been changed between the given commits // GetFilesChangedBetween returns a list of all files that have been changed between the given commits
func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) { func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) {
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", "-z").AddDynamicArguments(base + ".." + head).RunStdString(&RunOpts{Dir: repo.Path}) stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(base + ".." + head).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil { if err != nil {
return nil, err return nil, err
} }
split := strings.Split(stdout, "\000") return strings.Split(stdout, "\n"), err
// Because Git will always emit filenames with a terminal NUL ignore the last entry in the split - which will always be empty.
if len(split) > 0 {
split = split[:len(split)-1]
}
return split, err
} }
// GetDiffFromMergeBase generates and return patch data from merge base to head // GetDiffFromMergeBase generates and return patch data from merge base to head

View File

@ -237,6 +237,7 @@ internal_token_failed = Failed to generate internal token: %v
secret_key_failed = Failed to generate secret key: %v secret_key_failed = Failed to generate secret key: %v
save_config_failed = Failed to save configuration: %v save_config_failed = Failed to save configuration: %v
invalid_admin_setting = Administrator account setting is invalid: %v invalid_admin_setting = Administrator account setting is invalid: %v
install_success = Welcome! Thank you for choosing Gitea. Have fun and take care!
invalid_log_root_path = The log path is invalid: %v invalid_log_root_path = The log path is invalid: %v
default_keep_email_private = Hide Email Addresses by Default default_keep_email_private = Hide Email Addresses by Default
default_keep_email_private_popup = Hide email addresses of new user accounts by default. default_keep_email_private_popup = Hide email addresses of new user accounts by default.
@ -247,7 +248,6 @@ default_enable_timetracking_popup = Enable time tracking for new repositories by
no_reply_address = Hidden Email Domain no_reply_address = Hidden Email Domain
no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'. no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'.
password_algorithm = Password Hash Algorithm password_algorithm = Password Hash Algorithm
invalid_password_algorithm = Invalid password hash algorithm
password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. `argon2` whilst having good characteristics uses a lot of memory and may be inappropriate for small systems. password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. `argon2` whilst having good characteristics uses a lot of memory and may be inappropriate for small systems.
enable_update_checker = Enable Update Checker enable_update_checker = Enable Update Checker
enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io. enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io.

View File

@ -52,7 +52,7 @@ func (s *Service) Register(
} }
if runnerToken.IsActive { if runnerToken.IsActive {
return nil, errors.New("runner token has already been activated") return nil, errors.New("runner token has already activated")
} }
// create new runner // create new runner

View File

@ -59,6 +59,11 @@ func Init(ctx goctx.Context) func(next http.Handler) http.Handler {
dbTypeNames := getSupportedDbTypeNames() dbTypeNames := getSupportedDbTypeNames()
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if setting.InstallLock {
resp.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login")
_ = rnd.HTML(resp, http.StatusOK, string(tplPostInstall), nil)
return
}
locale := middleware.Locale(resp, req) locale := middleware.Locale(resp, req)
startTime := time.Now() startTime := time.Now()
ctx := context.Context{ ctx := context.Context{
@ -88,11 +93,6 @@ func Init(ctx goctx.Context) func(next http.Handler) http.Handler {
// Install render installation page // Install render installation page
func Install(ctx *context.Context) { func Install(ctx *context.Context) {
if setting.InstallLock {
InstallDone(ctx)
return
}
form := forms.InstallForm{} form := forms.InstallForm{}
// Database settings // Database settings
@ -162,7 +162,7 @@ func Install(ctx *context.Context) {
form.DefaultAllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization form.DefaultAllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization
form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking
form.NoReplyAddress = setting.Service.NoReplyAddress form.NoReplyAddress = setting.Service.NoReplyAddress
form.PasswordAlgorithm = hash.ConfigHashAlgorithm(setting.PasswordHashAlgo) form.PasswordAlgorithm = setting.PasswordHashAlgo
middleware.AssignForm(form, ctx.Data) middleware.AssignForm(form, ctx.Data)
ctx.HTML(http.StatusOK, tplInstall) ctx.HTML(http.StatusOK, tplInstall)
@ -234,11 +234,6 @@ func checkDatabase(ctx *context.Context, form *forms.InstallForm) bool {
// SubmitInstall response for submit install items // SubmitInstall response for submit install items
func SubmitInstall(ctx *context.Context) { func SubmitInstall(ctx *context.Context) {
if setting.InstallLock {
InstallDone(ctx)
return
}
var err error var err error
form := *web.GetForm(ctx).(*forms.InstallForm) form := *web.GetForm(ctx).(*forms.InstallForm)
@ -282,6 +277,7 @@ func SubmitInstall(ctx *context.Context) {
setting.Database.Charset = form.Charset setting.Database.Charset = form.Charset
setting.Database.Path = form.DbPath setting.Database.Path = form.DbPath
setting.Database.LogSQL = !setting.IsProd setting.Database.LogSQL = !setting.IsProd
setting.PasswordHashAlgo = form.PasswordAlgorithm
if !checkDatabase(ctx, &form) { if !checkDatabase(ctx, &form) {
return return
@ -503,12 +499,6 @@ func SubmitInstall(ctx *context.Context) {
} }
if len(form.PasswordAlgorithm) > 0 { if len(form.PasswordAlgorithm) > 0 {
var algorithm *hash.PasswordHashAlgorithm
setting.PasswordHashAlgo, algorithm = hash.SetDefaultPasswordHashAlgorithm(form.PasswordAlgorithm)
if algorithm == nil {
ctx.RenderWithErr(ctx.Tr("install.invalid_password_algorithm"), tplInstall, &form)
return
}
cfg.Section("security").Key("PASSWORD_HASH_ALGO").SetValue(form.PasswordAlgorithm) cfg.Section("security").Key("PASSWORD_HASH_ALGO").SetValue(form.PasswordAlgorithm)
} }
@ -581,26 +571,18 @@ func SubmitInstall(ctx *context.Context) {
} }
log.Info("First-time run install finished!") log.Info("First-time run install finished!")
InstallDone(ctx)
ctx.Flash.Success(ctx.Tr("install.install_success"))
ctx.RespHeader().Add("Refresh", "1; url="+setting.AppURL+"user/login")
ctx.HTML(http.StatusOK, tplPostInstall)
// Now get the http.Server from this request and shut it down
// NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown
srv := ctx.Value(http.ServerContextKey).(*http.Server)
go func() { go func() {
// Sleep for a while to make sure the user's browser has loaded the post-install page and its assets (images, css, js)
// What if this duration is not long enough? That's impossible -- if the user can't load the simple page in time, how could they install or use Gitea in the future ....
time.Sleep(3 * time.Second)
// Now get the http.Server from this request and shut it down
// NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown
srv := ctx.Value(http.ServerContextKey).(*http.Server)
if err := srv.Shutdown(graceful.GetManager().HammerContext()); err != nil { if err := srv.Shutdown(graceful.GetManager().HammerContext()); err != nil {
log.Error("Unable to shutdown the install server! Error: %v", err) log.Error("Unable to shutdown the install server! Error: %v", err)
} }
// After the HTTP server for "install" shuts down, the `runWeb()` will continue to run the "normal" server
}() }()
} }
// InstallDone shows the "post-install" page, makes it easier to develop the page.
// The name is not called as "PostInstall" to avoid misinterpretation as a handler for "POST /install"
func InstallDone(ctx *context.Context) { //nolint
ctx.HTML(http.StatusOK, tplPostInstall)
}

View File

@ -6,7 +6,6 @@ package install
import ( import (
goctx "context" goctx "context"
"fmt" "fmt"
"html"
"net/http" "net/http"
"path" "path"
@ -38,7 +37,7 @@ func installRecovery(ctx goctx.Context) func(next http.Handler) http.Handler {
// Why we need this? The first recover will try to render a beautiful // Why we need this? The first recover will try to render a beautiful
// error page for user, but the process can still panic again, then // error page for user, but the process can still panic again, then
// we have to just recover twice and send a simple error page that // we have to just recover twice and send a simple error page that
// should not panic anymore. // should not panic any more.
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, log.Stack(2)) combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, log.Stack(2))
@ -108,9 +107,8 @@ func Routes(ctx goctx.Context) *web.Route {
r.Use(installRecovery(ctx)) r.Use(installRecovery(ctx))
r.Use(Init(ctx)) r.Use(Init(ctx))
r.Get("/", Install) // it must be on the root, because the "install.js" use the window.location to replace the "localhost" AppURL r.Get("/", Install)
r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall) r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall)
r.Get("/post-install", InstallDone)
r.Get("/api/healthz", healthcheck.Check) r.Get("/api/healthz", healthcheck.Check)
r.NotFound(web.Wrap(installNotFound)) r.NotFound(web.Wrap(installNotFound))
@ -118,10 +116,5 @@ func Routes(ctx goctx.Context) *web.Route {
} }
func installNotFound(w http.ResponseWriter, req *http.Request) { func installNotFound(w http.ResponseWriter, req *http.Request) {
w.Header().Add("Content-Type", "text/html; charset=utf-8") http.Redirect(w, req, setting.AppURL, http.StatusFound)
w.Header().Add("Refresh", fmt.Sprintf("1; url=%s", setting.AppSubURL+"/"))
// do not use 30x status, because the "post-install" page needs to use 404/200 to detect if Gitea has been installed.
// the fetch API could follow 30x requests to the page with 200 status.
w.WriteHeader(http.StatusNotFound)
_, _ = fmt.Fprintf(w, `Not Found. <a href="%s">Go to default page</a>.`, html.EscapeString(setting.AppSubURL+"/"))
} }

View File

@ -1420,12 +1420,11 @@ func ViewIssue(ctx *context.Context) {
} }
var ( var (
role issues_model.RoleDescriptor role issues_model.RoleDescriptor
ok bool ok bool
marked = make(map[int64]issues_model.RoleDescriptor) marked = make(map[int64]issues_model.RoleDescriptor)
comment *issues_model.Comment comment *issues_model.Comment
participants = make([]*user_model.User, 1, 10) participants = make([]*user_model.User, 1, 10)
latestCloseCommentID int64
) )
if ctx.Repo.Repository.IsTimetrackerEnabled(ctx) { if ctx.Repo.Repository.IsTimetrackerEnabled(ctx) {
if ctx.IsSigned { if ctx.IsSigned {
@ -1623,15 +1622,9 @@ func ViewIssue(ctx *context.Context) {
comment.Type == issues_model.CommentTypeStopTracking { comment.Type == issues_model.CommentTypeStopTracking {
// drop error since times could be pruned from DB.. // drop error since times could be pruned from DB..
_ = comment.LoadTime() _ = comment.LoadTime()
} else if comment.Type == issues_model.CommentTypeClose {
// record ID of latest closed comment.
// if PR is closed, the comments whose type is CommentTypePullRequestPush(29) after latestCloseCommentID won't be rendered.
latestCloseCommentID = comment.ID
} }
} }
ctx.Data["LatestCloseCommentID"] = latestCloseCommentID
// Combine multiple label assignments into a single comment // Combine multiple label assignments into a single comment
combineLabelComments(issue) combineLabelComments(issue)

View File

@ -587,7 +587,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
ctx.Data["HeadBranchCommitID"] = headBranchSha ctx.Data["HeadBranchCommitID"] = headBranchSha
ctx.Data["PullHeadCommitID"] = sha ctx.Data["PullHeadCommitID"] = sha
if pull.HeadRepo == nil || !headBranchExist || (!pull.Issue.IsClosed && (headBranchSha != sha)) { if pull.HeadRepo == nil || !headBranchExist || headBranchSha != sha {
ctx.Data["IsPullRequestBroken"] = true ctx.Data["IsPullRequestBroken"] = true
if pull.IsSameRepo() { if pull.IsSameRepo() {
ctx.Data["HeadTarget"] = pull.HeadBranch ctx.Data["HeadTarget"] = pull.HeadBranch

View File

@ -186,7 +186,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
return return
} }
renderReadmeFile(ctx, readmeFile, fmt.Sprintf("%s/%s", treeLink, readmeFile.name)) renderReadmeFile(ctx, readmeFile, treeLink)
} }
// localizedExtensions prepends the provided language code with and without a // localizedExtensions prepends the provided language code with and without a

View File

@ -499,13 +499,6 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum] theCommits.Commits = theCommits.Commits[:setting.UI.FeedMaxCommitNum]
} }
if newCommit, err := gitRepo.GetCommit(newCommitID); err != nil {
log.Error("SyncMirrors [repo: %-v]: unable to get commit %s: %v", m.Repo, newCommitID, err)
continue
} else {
theCommits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
}
theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID) theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID)
notification.NotifySyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ notification.NotifySyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{

View File

@ -257,7 +257,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string,
// If you don't let it run all the way then you will lose data // If you don't let it run all the way then you will lose data
// TODO: graceful: AddTestPullRequestTask needs to become a queue! // TODO: graceful: AddTestPullRequestTask needs to become a queue!
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repoID, branch, true) prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
if err != nil { if err != nil {
log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err) log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
return return
@ -500,7 +500,7 @@ func (errs errlist) Error() string {
// CloseBranchPulls close all the pull requests who's head branch is the branch // CloseBranchPulls close all the pull requests who's head branch is the branch
func CloseBranchPulls(doer *user_model.User, repoID int64, branch string) error { func CloseBranchPulls(doer *user_model.User, repoID int64, branch string) error {
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repoID, branch, false) prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
if err != nil { if err != nil {
return err return err
} }
@ -536,7 +536,7 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re
var errs errlist var errs errlist
for _, branch := range branches { for _, branch := range branches {
prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repo.ID, branch.Name, false) prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(repo.ID, branch.Name)
if err != nil { if err != nil {
return err return err
} }

View File

@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title> <title>{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title>
{{if .ManifestData}}<link rel="manifest" href="data:{{.ManifestData}}">{{end}} <link rel="manifest" href="data:{{.ManifestData}}">
<meta name="theme-color" content="{{ThemeColorMetaTag}}"> <meta name="theme-color" content="{{ThemeColorMetaTag}}">
<meta name="default-theme" content="{{DefaultTheme}}"> <meta name="default-theme" content="{{DefaultTheme}}">
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}"> <meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}">

View File

@ -8,7 +8,7 @@
<title>{{.Subject}}</title> <title>{{.Subject}}</title>
</head> </head>
{{$repo_url := printf "<a href='%s'>%s</a>" (Escape .Issue.Repo.HTMLURL) (Escape .Issue.Repo.FullName)}} {{$repo_url := printf "<a href='%s'>%s</a>" (Escape .Issue.Repo.Link) (Escape .Issue.Repo.FullName)}}
{{$link := printf "<a href='%s'>#%d</a>" (Escape .Link) .Issue.Index}} {{$link := printf "<a href='%s'>#%d</a>" (Escape .Link) .Issue.Index}}
<body> <body>
<p> <p>

View File

@ -20,11 +20,11 @@
{{if eq .ActionName "push"}} {{if eq .ActionName "push"}}
<p> <p>
{{if .Comment.IsForcePush}} {{if .Comment.IsForcePush}}
{{$oldCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.OldCommit}} {{$oldCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.Link .Comment.OldCommit}}
{{$oldShortSha := ShortSha .Comment.OldCommit}} {{$oldShortSha := ShortSha .Comment.OldCommit}}
{{$oldCommitLink := printf "<a href='%[1]s'><b>%[2]s</b></a>" (Escape $oldCommitUrl) (Escape $oldShortSha)}} {{$oldCommitLink := printf "<a href='%[1]s'><b>%[2]s</b></a>" (Escape $oldCommitUrl) (Escape $oldShortSha)}}
{{$newCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.NewCommit}} {{$newCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.Link .Comment.NewCommit}}
{{$newShortSha := ShortSha .Comment.NewCommit}} {{$newShortSha := ShortSha .Comment.NewCommit}}
{{$newCommitLink := printf "<a href='%[1]s'><b>%[2]s</b></a>" (Escape $newCommitUrl) (Escape $newShortSha)}} {{$newCommitLink := printf "<a href='%[1]s'><b>%[2]s</b></a>" (Escape $newCommitUrl) (Escape $newShortSha)}}
@ -72,7 +72,7 @@
<ul> <ul>
{{range .Comment.Commits}} {{range .Comment.Commits}}
<li> <li>
<a href="{{$.Comment.Issue.PullRequest.BaseRepo.HTMLURL}}/commit/{{.ID}}"> <a href="{{$.Comment.Issue.PullRequest.BaseRepo.Link}}/commit/{{.ID}}">
{{ShortSha .ID.String}} {{ShortSha .ID.String}}
</a> - {{.Summary}} </a> - {{.Summary}}
</li> </li>

View File

@ -11,8 +11,8 @@
</head> </head>
{{$release_url := printf "<a href='%s'>%s</a>" (.Release.HTMLURL | Escape) (.Release.TagName | Escape)}} {{$release_url := printf "<a href='%s'>%s</a>" (.Release.Link | Escape) (.Release.TagName | Escape)}}
{{$repo_url := printf "<a href='%s'>%s</a>" (.Release.Repo.HTMLURL | Escape) (.Release.Repo.FullName | Escape)}} {{$repo_url := printf "<a href='%s'>%s</a>" (.Release.Repo.Link | Escape) (.Release.Repo.FullName | Escape)}}
<body> <body>
<p> <p>
{{.locale.Tr "mail.release.new.text" .Release.Publisher.Name $release_url $repo_url | Str2html}} {{.locale.Tr "mail.release.new.text" .Release.Publisher.Name $release_url $repo_url | Str2html}}

View File

@ -1,5 +1,5 @@
{{template "base/head" .}} {{template "base/head" .}}
<div role="main" aria-label="{{.Title}}" class="page-content install post-install"> <div role="main" aria-label="{{.Title}}" class="page-content install">
<div class="ui container"> <div class="ui container">
<div class="ui grid"> <div class="ui grid">
<div class="sixteen wide column content"> <div class="sixteen wide column content">
@ -13,7 +13,7 @@
</div> </div>
<div class="ui stackable middle very relaxed page grid"> <div class="ui stackable middle very relaxed page grid">
<div class="sixteen wide center aligned centered column"> <div class="sixteen wide center aligned centered column">
<p><a id="goto-user-login" href="{{AppSubUrl}}/user/login">{{.locale.Tr "loading"}}</a></p> <p><a href="{{AppSubUrl}}/user/login">{{AppSubUrl}}/user/login</a></p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -697,10 +697,6 @@
</span> </span>
</div> </div>
{{else if and (eq .Type 29) (or (gt .CommitsNum 0) .IsForcePush)}} {{else if and (eq .Type 29) (or (gt .CommitsNum 0) .IsForcePush)}}
<!-- If PR is closed, the comments whose type is CommentTypePullRequestPush(29) after latestCloseCommentID won't be rendered. //-->
{{if and .Issue.IsClosed (gt .ID $.LatestCloseCommentID)}}
{{continue}}
{{end}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-repo-push"}}</span> <span class="badge">{{svg "octicon-repo-push"}}</span>
<span class="text grey muted-links"> <span class="text grey muted-links">

View File

@ -1 +0,0 @@
78fb907e3a3309eae4fe8fef030874cebbf1cd5e

View File

@ -256,23 +256,3 @@ func TestViewRepoDirectory(t *testing.T) {
assert.Zero(t, repoTopics.Length()) assert.Zero(t, repoTopics.Length())
assert.Zero(t, repoSummary.Length()) assert.Zero(t, repoSummary.Length())
} }
func TestMarkDownImage(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user2/repo1/src/branch/home-md-img-check")
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
_, exists := htmlDoc.doc.Find(`img[src="/user2/repo1/media/branch/home-md-img-check/test-fake-img.jpg"]`).Attr("src")
assert.True(t, exists, "Repo home page markdown image link check failed")
req = NewRequest(t, "GET", "/user2/repo1/src/branch/home-md-img-check/README.md")
resp = session.MakeRequest(t, req, http.StatusOK)
htmlDoc = NewHTMLParser(t, resp.Body)
_, exists = htmlDoc.doc.Find(`img[src="/user2/repo1/media/branch/home-md-img-check/test-fake-img.jpg"]`).Attr("src")
assert.True(t, exists, "Repo src page markdown image link check failed")
}

View File

@ -2,18 +2,10 @@ import $ from 'jquery';
import {hideElem, showElem} from '../utils/dom.js'; import {hideElem, showElem} from '../utils/dom.js';
export function initInstall() { export function initInstall() {
const $page = $('.page-content.install'); if ($('.page-content.install').length === 0) {
if ($page.length === 0) {
return; return;
} }
if ($page.is('.post-install')) {
initPostInstall();
} else {
initPreInstall();
}
}
function initPreInstall() {
const defaultDbUser = 'gitea'; const defaultDbUser = 'gitea';
const defaultDbName = 'gitea'; const defaultDbName = 'gitea';
@ -48,18 +40,6 @@ function initPreInstall() {
} // else: for SQLite3, the default path is always prepared by backend code (setting) } // else: for SQLite3, the default path is always prepared by backend code (setting)
}).trigger('change'); }).trigger('change');
const $appUrl = $('#app_url');
const configAppUrl = $appUrl.val();
if (configAppUrl.includes('://localhost')) {
$appUrl.val(window.location.href);
}
const $domain = $('#domain');
const configDomain = $domain.val().trim();
if (configDomain === 'localhost') {
$domain.val(window.location.hostname);
}
// TODO: better handling of exclusive relations. // TODO: better handling of exclusive relations.
$('#offline-mode input').on('change', function () { $('#offline-mode input').on('change', function () {
if ($(this).is(':checked')) { if ($(this).is(':checked')) {
@ -103,20 +83,3 @@ function initPreInstall() {
} }
}); });
} }
function initPostInstall() {
const el = document.getElementById('goto-user-login');
if (!el) return;
const targetUrl = el.getAttribute('href');
let tid = setInterval(async () => {
try {
const resp = await fetch(targetUrl);
if (tid && resp.status === 200) {
clearInterval(tid);
tid = null;
window.location.href = targetUrl;
}
} catch {}
}, 1000);
}

View File

@ -91,27 +91,24 @@ export function initRepoEditor() {
$('#commit-button').text($(this).attr('button_text')); $('#commit-button').text($(this).attr('button_text'));
}); });
const joinTreePath = ($fileNameEl) => {
const parts = [];
$('.breadcrumb span.section').each(function () {
const element = $(this);
if (element.find('a').length) {
parts.push(element.find('a').text());
} else {
parts.push(element.text());
}
});
if ($fileNameEl.val()) parts.push($fileNameEl.val());
$('#tree_path').val(parts.join('/'));
};
const $editFilename = $('#file-name'); const $editFilename = $('#file-name');
$editFilename.on('input', function () { $editFilename.on('keyup', function (e) {
const parts = $(this).val().split('/'); const $section = $('.breadcrumb span.section');
const $divider = $('.breadcrumb div.divider');
let value;
let parts;
if (parts.length > 1) { if (e.keyCode === 8 && getCursorPosition($(this)) === 0 && $section.length > 0) {
value = $section.last().find('a').text();
$(this).val(value + $(this).val());
$(this)[0].setSelectionRange(value.length, value.length);
$section.last().remove();
$divider.last().remove();
}
if (e.keyCode === 191) {
parts = $(this).val().split('/');
for (let i = 0; i < parts.length; ++i) { for (let i = 0; i < parts.length; ++i) {
const value = parts[i]; value = parts[i];
if (i < parts.length - 1) { if (i < parts.length - 1) {
if (value.length) { if (value.length) {
$(`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`).insertBefore($(this)); $(`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`).insertBefore($(this));
@ -120,28 +117,21 @@ export function initRepoEditor() {
} else { } else {
$(this).val(value); $(this).val(value);
} }
this.setSelectionRange(0, 0); $(this)[0].setSelectionRange(0, 0);
} }
} }
parts = [];
joinTreePath($(this)); $('.breadcrumb span.section').each(function () {
}); const element = $(this);
if (element.find('a').length) {
$editFilename.on('keydown', function (e) { parts.push(element.find('a').text());
const $section = $('.breadcrumb span.section'); } else {
parts.push(element.text());
// Jump back to last directory once the filename is empty }
if (e.code === 'Backspace' && getCursorPosition($(this)) === 0 && $section.length > 0) { });
e.preventDefault(); if ($(this).val()) parts.push($(this).val());
const $divider = $('.breadcrumb div.divider'); $('#tree_path').val(parts.join('/'));
const value = $section.last().find('a').text(); }).trigger('keyup');
$(this).val(value + $(this).val());
this.setSelectionRange(value.length, value.length);
$section.last().remove();
$divider.last().remove();
joinTreePath($(this));
}
});
const $editArea = $('.repository.editor textarea#edit_area'); const $editArea = $('.repository.editor textarea#edit_area');
if (!$editArea.length) return; if (!$editArea.length) return;

View File

@ -1,19 +0,0 @@
# Web Components
This `webcomponents` directory contains the source code for the web components used in the Gitea Web UI.
https://developer.mozilla.org/en-US/docs/Web/Web_Components
# Guidelines
* These components are loaded in `<head>` (before DOM body),
so they should have their own dependencies and should be very light,
then they won't affect the page loading time too much.
* If the component is not a public one, it's suggested to have its own `Gitea` or `gitea-` prefix to avoid conflicts.
# TODO
There are still some components that are not migrated to web components yet:
* `<span class="js-pretty-number">`
* `<time data-format>`

View File

@ -2121,7 +2121,8 @@ a.ui.label:hover {
font: 12px var(--fonts-monospace); font: 12px var(--fonts-monospace);
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-all; word-break: break-all;
overflow-wrap: anywhere; overflow-wrap: break-word;
word-wrap: break-word;
} }
.blame .code-inner { .blame .code-inner {