diff --git a/.dockerignore b/.dockerignore index 86cc8f608..a1611a1ca 100644 --- a/.dockerignore +++ b/.dockerignore @@ -94,6 +94,9 @@ cpu.out /.air /.go-licenses +# Files and folders that were previously generated +/public/assets/img/webpack + # Snapcraft snap/.snapcraft/ parts/ diff --git a/.gitattributes b/.gitattributes index 51131c7d8..4e748c071 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ * text=auto eol=lf *.tmpl linguist-language=go-html-template +*.pb.go linguist-generated /assets/*.json linguist-generated /public/assets/img/svg/*.svg linguist-generated /templates/swagger/v1_json.tmpl linguist-generated diff --git a/.gitignore b/.gitignore index be8db3b51..ebbed981e 100644 --- a/.gitignore +++ b/.gitignore @@ -101,6 +101,9 @@ cpu.out /.go-licenses /.cur-deadcode-out +# Files and folders that were previously generated +/public/assets/img/webpack + # Snapcraft /gitea_a*.txt snap/.snapcraft/ diff --git a/Makefile b/Makefile index 5116f25dc..f8f7fffb1 100644 --- a/Makefile +++ b/Makefile @@ -312,7 +312,7 @@ clean: .PHONY: fmt fmt: - GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}' + @GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}' $(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl')) @# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only @# whitespace before it diff --git a/build/code-batch-process.go b/build/code-batch-process.go index b3ee39942..cc2ab6802 100644 --- a/build/code-batch-process.go +++ b/build/code-batch-process.go @@ -69,6 +69,7 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error) co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`)) + co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`\.pb\.go$`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`)) @@ -203,17 +204,6 @@ Example: `, "file-batch-exec") } -func getGoVersion() string { - goModFile, err := os.ReadFile("go.mod") - if err != nil { - log.Fatalf(`Faild to read "go.mod": %v`, err) - os.Exit(1) - } - goModVersionRegex := regexp.MustCompile(`go \d+\.\d+`) - goModVersionLine := goModVersionRegex.Find(goModFile) - return string(goModVersionLine[3:]) -} - func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) { fileFilter := mainOptions["file-filter"] if fileFilter == "" { @@ -278,7 +268,8 @@ func main() { log.Print("the -d option is not supported by gitea-fmt") } cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w"))) - cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", getGoVersion()}, substArgs...))) + cmdErrors = append(cmdErrors, passThroughCmd("gofmt", append([]string{"-w", "-r", "interface{} -> any"}, substArgs...))) + cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra"}, substArgs...))) default: log.Fatalf("unknown cmd: %s %v", subCmd, subArgs) } diff --git a/cmd/web.go b/cmd/web.go index ef82486c1..44babd51c 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -114,7 +114,7 @@ func showWebStartupMessage(msg string) { log.Info("* WorkPath: %s", setting.AppWorkPath) log.Info("* CustomPath: %s", setting.CustomPath) log.Info("* ConfigFile: %s", setting.CustomConf) - log.Info("%s", msg) + log.Info("%s", msg) // show startup message } func serveInstall(ctx *cli.Context) error { diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go index b806550b8..ecd2c1121 100644 --- a/models/actions/schedule_list.go +++ b/models/actions/schedule_list.go @@ -44,6 +44,9 @@ func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error { schedule.TriggerUser = user_model.NewActionsUser() } else { schedule.TriggerUser = users[schedule.TriggerUserID] + if schedule.TriggerUser == nil { + schedule.TriggerUser = user_model.NewGhostUser() + } } } return nil diff --git a/models/actions/task.go b/models/actions/task.go index 96a6d2e80..9946cf523 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -11,6 +11,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -227,7 +228,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask if runner.RepoID != 0 { jobCond = builder.Eq{"repo_id": runner.RepoID} } else if runner.OwnerID != 0 { - jobCond = builder.In("repo_id", builder.Select("id").From("repository").Where(builder.Eq{"owner_id": runner.OwnerID})) + jobCond = builder.In("repo_id", builder.Select("`repository`.id").From("repository"). + Join("INNER", "repo_unit", "`repository`.id = `repo_unit`.repo_id"). + Where(builder.Eq{"`repository`.owner_id": runner.OwnerID, "`repo_unit`.type": unit.TypeActions})) } if jobCond.IsValid() { jobCond = builder.In("run_id", builder.Select("id").From("action_run").Where(jobCond)) diff --git a/models/asymkey/ssh_key_fingerprint.go b/models/asymkey/ssh_key_fingerprint.go index b9cfb1b25..1ed3b5df2 100644 --- a/models/asymkey/ssh_key_fingerprint.go +++ b/models/asymkey/ssh_key_fingerprint.go @@ -76,23 +76,14 @@ func calcFingerprintNative(publicKeyContent string) (string, error) { // CalcFingerprint calculate public key's fingerprint func CalcFingerprint(publicKeyContent string) (string, error) { // Call the method based on configuration - var ( - fnName, fp string - err error - ) - if len(setting.SSH.KeygenPath) == 0 { - fnName = "calcFingerprintNative" - fp, err = calcFingerprintNative(publicKeyContent) - } else { - fnName = "calcFingerprintSSHKeygen" - fp, err = calcFingerprintSSHKeygen(publicKeyContent) - } + useNative := setting.SSH.KeygenPath == "" + calcFn := util.Iif(useNative, calcFingerprintNative, calcFingerprintSSHKeygen) + fp, err := calcFn(publicKeyContent) if err != nil { if IsErrKeyUnableVerify(err) { - log.Info("%s", publicKeyContent) return "", err } - return "", fmt.Errorf("%s: %w", fnName, err) + return "", fmt.Errorf("CalcFingerprint(%s): %w", util.Iif(useNative, "native", "ssh-keygen"), err) } return fp, nil } diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 83d60e3ab..5eabc7d9b 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -144,6 +144,11 @@ func (app *OAuth2Application) TableName() string { // ContainsRedirectURI checks if redirectURI is allowed for app func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool { + // OAuth2 requires the redirect URI to be an exact match, no dynamic parts are allowed. + // https://stackoverflow.com/questions/55524480/should-dynamic-query-parameters-be-present-in-the-redirection-uri-for-an-oauth2 + // https://www.rfc-editor.org/rfc/rfc6819#section-5.2.3.3 + // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest + // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-12#section-3.1 contains := func(s string) bool { s = strings.TrimSuffix(strings.ToLower(s), "/") for _, u := range app.RedirectURIs { diff --git a/models/repo/issue.go b/models/repo/issue.go index 6f6b565a0..0dd4fd5ed 100644 --- a/models/repo/issue.go +++ b/models/repo/issue.go @@ -53,7 +53,7 @@ func (repo *Repository) IsDependenciesEnabled(ctx context.Context) bool { var u *RepoUnit var err error if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil { - log.Trace("%s", err) + log.Trace("IsDependenciesEnabled: %v", err) return setting.Service.DefaultEnableDependencies } return u.IssuesConfig().EnableDependencies diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go index 6688e78cd..b120a0edf 100644 --- a/modules/optional/serialization.go +++ b/modules/optional/serialization.go @@ -35,7 +35,7 @@ func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error { return nil } -func (o Option[T]) MarshalYAML() (interface{}, error) { +func (o Option[T]) MarshalYAML() (any, error) { if !o.Has() { return nil, nil } diff --git a/modules/session/store.go b/modules/session/store.go index 4fa4d2848..2f7ab7760 100644 --- a/modules/session/store.go +++ b/modules/session/store.go @@ -6,6 +6,9 @@ package session import ( "net/http" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web/middleware" + "gitea.com/go-chi/session" ) @@ -18,6 +21,10 @@ type Store interface { // RegenerateSession regenerates the underlying session and returns the new store func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) { + // Ensure that a cookie with a trailing slash does not take precedence over + // the cookie written by the middleware. + middleware.DeleteLegacySiteCookie(resp, setting.SessionConfig.CookieName) + s, err := session.RegenerateSession(resp, req) return s, err } diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go index 6c1b4ab24..774385483 100644 --- a/modules/templates/util_misc.go +++ b/modules/templates/util_misc.go @@ -142,35 +142,39 @@ type remoteAddress struct { Password string } -func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress { - a := remoteAddress{} - - remoteURL := m.OriginalURL - if ignoreOriginalURL || remoteURL == "" { - var err error - remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName) - if err != nil { - log.Error("GetRemoteURL %v", err) - return a - } +func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress { + ret := remoteAddress{} + remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName) + if err != nil { + log.Error("GetRemoteURL %v", err) + return ret } u, err := giturl.Parse(remoteURL) if err != nil { log.Error("giturl.Parse %v", err) - return a + return ret } if u.Scheme != "ssh" && u.Scheme != "file" { if u.User != nil { - a.Username = u.User.Username() - a.Password, _ = u.User.Password() + ret.Username = u.User.Username() + ret.Password, _ = u.User.Password() } - u.User = nil } - a.Address = u.String() - return a + // The URL stored in the git repo could contain authentication, + // erase it, or it will be shown in the UI. + u.User = nil + ret.Address = u.String() + // Why not use m.OriginalURL to set ret.Address? + // It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository. + // However, the old code has already stored authentication in m.OriginalURL when updating mirror settings. + // That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased. + // Instead of doing this, why not directly use the authentication-erased URL from the Git repository? + // It should be the same as long as there are no bugs. + + return ret } func FilenameIsImage(filename string) bool { diff --git a/modules/util/util.go b/modules/util/util.go index 7d1a75537..b6ea28355 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -213,6 +213,14 @@ func ToPointer[T any](val T) *T { return &val } +// Iif is an "inline-if", it returns "trueVal" if "condition" is true, otherwise "falseVal" +func Iif[T any](condition bool, trueVal, falseVal T) T { + if condition { + return trueVal + } + return falseVal +} + func ReserveLineBreakForTextarea(input string) string { // Since the content is from a form which is a textarea, the line endings are \r\n. // It's a standard behavior of HTML. diff --git a/modules/web/middleware/cookie.go b/modules/web/middleware/cookie.go index 621640895..0bed72679 100644 --- a/modules/web/middleware/cookie.go +++ b/modules/web/middleware/cookie.go @@ -45,10 +45,32 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) { SameSite: setting.SessionConfig.SameSite, } resp.Header().Add("Set-Cookie", cookie.String()) - if maxAge < 0 { - // There was a bug in "setting.SessionConfig.CookiePath" code, the old default value of it was empty "". - // So we have to delete the cookie on path="" again, because some old code leaves cookies on path="". - cookie.Path = strings.TrimSuffix(setting.SessionConfig.CookiePath, "/") - resp.Header().Add("Set-Cookie", cookie.String()) - } + // Previous versions would use a cookie path with a trailing /. + // These are more specific than cookies without a trailing /, so + // we need to delete these if they exist. + DeleteLegacySiteCookie(resp, name) +} + +// DeleteLegacySiteCookie deletes the cookie with the given name at the cookie +// path with a trailing /, which would unintentionally override the cookie. +func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) { + if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") { + // If the cookie path ends with /, no legacy cookies will take + // precedence, so do nothing. The exception is that cookies with no + // path could override other cookies, but it's complicated and we don't + // currently handle that. + return + } + + cookie := &http.Cookie{ + Name: name, + Value: "", + MaxAge: -1, + Path: setting.SessionConfig.CookiePath + "/", + Domain: setting.SessionConfig.Domain, + Secure: setting.SessionConfig.Secure, + HttpOnly: true, + SameSite: setting.SessionConfig.SameSite, + } + resp.Header().Add("Set-Cookie", cookie.String()) } diff --git a/options/license/APL-1.0 b/options/license/APL-1.0 index 261f2d687..0748f90cd 100644 --- a/options/license/APL-1.0 +++ b/options/license/APL-1.0 @@ -210,21 +210,21 @@ PART 1: INITIAL CONTRIBUTOR AND DESIGNATED WEB SITE The Initial Contributor is: ____________________________________________________ -  + [Enter full name of Initial Contributor] Address of Initial Contributor: ________________________________________________ -  + ________________________________________________ -  + ________________________________________________ -  + [Enter address above] The Designated Web Site is: __________________________________________________ -  + [Enter URL for Designated Web Site of Initial Contributor] NOTE: The Initial Contributor is to complete this Part 1, along with Parts 2, 3, and 5, and, if applicable, Parts 4 and 6. @@ -237,27 +237,27 @@ The date on which the Initial Work was first available under this License: _____ PART 3: GOVERNING JURISDICTION -For the purposes of this License, the Governing Jurisdiction is _________________________________________________. 
[Initial Contributor to Enter Governing Jurisdiction here] +For the purposes of this License, the Governing Jurisdiction is _________________________________________________. [Initial Contributor to Enter Governing Jurisdiction here] PART 4: THIRD PARTIES For the purposes of this License, "Third Party" has the definition set forth below in the ONE paragraph selected by the Initial Contributor from paragraphs A, B, C, D and E when the Initial Work is distributed or otherwise made available by the Initial Contributor. To select one of the following paragraphs, the Initial Contributor must place an "X" or "x" in the selection box alongside the one respective paragraph selected. SELECTION -  + BOX PARAGRAPH -[  ] A. "THIRD PARTY" means any third party. -  -  -[  ] B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b). -  -  -[  ] C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest. -  -  -[  ] D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. -  -  -[  ] E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. +[ ] A. "THIRD PARTY" means any third party. + + +[ ] B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b). + + +[ ] C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest. + + +[ ] D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. + + +[ ] E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. The default definition of "THIRD PARTY" is the definition set forth in paragraph A, if NONE OR MORE THAN ONE of paragraphs A, B, C, D or E in this Part 4 are selected by the Initial Contributor. PART 5: NOTICE @@ -271,8 +271,8 @@ PART 6: PATENT LICENSING TERMS For the purposes of this License, paragraphs A, B, C, D and E of this Part 6 of Exhibit A are only incorporated and form part of the terms of the License if the Initial Contributor places an "X" or "x" in the selection box alongside the YES answer to the question immediately below. Is this a Patents-Included License pursuant to Section 2.2 of the License? -YES [      ] -NO [      ] +YES [ ] +NO [ ] By default, if YES is not selected by the Initial Contributor, the answer is NO. diff --git a/options/license/IBM-pibs b/options/license/IBM-pibs index 49454b8b1..ee9c7be36 100644 --- a/options/license/IBM-pibs +++ b/options/license/IBM-pibs @@ -4,5 +4,5 @@ Any user of this software should understand that IBM cannot provide technical su Any person who transfers this source code or any derivative work must include the IBM copyright notice, this paragraph, and the preceding two paragraphs in the transferred software. -COPYRIGHT   I B M   CORPORATION 2002 -LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M +COPYRIGHT I B M CORPORATION 2002 +LICENSED MATERIAL - PROGRAM PROPERTY OF I B M diff --git a/options/license/NCGL-UK-2.0 b/options/license/NCGL-UK-2.0 index 31fbad6f8..15c4f63c2 100644 --- a/options/license/NCGL-UK-2.0 +++ b/options/license/NCGL-UK-2.0 @@ -12,15 +12,15 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations. You are free to: - copy, publish, distribute and transmit the Information; + copy, publish, distribute and transmit the Information; adapt the Information; exploit the Information for Non-Commercial purposes for example, by combining it with other information in your own product or application. You are not permitted to: - exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. + exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. You must, where you do any of the above: - acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; + acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; If the Information Provider does not provide a specific attribution statement, you must use the following: Contains information licensed under the Non-Commercial Government Licence v2.0. diff --git a/options/license/NPL-1.1 b/options/license/NPL-1.1 index 62c529640..0d5457ff0 100644 --- a/options/license/NPL-1.1 +++ b/options/license/NPL-1.1 @@ -2,7 +2,7 @@ Netscape Public LIcense version 1.1 AMENDMENTS -The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License.  Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1. +The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License. Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1. Additional Terms applicable to the Netscape Public License. @@ -28,7 +28,7 @@ Additional Terms applicable to the Netscape Public License. Notwithstanding the limitations of Section 11 above, the provisions regarding litigation in Section 11(a), (b) and (c) of the License shall apply to all disputes relating to this License. EXHIBIT A-Netscape Public License. -   + "The contents of this file are subject to the Netscape Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/NPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. @@ -37,8 +37,8 @@ The Original Code is Mozilla Communicator client code, released March 31, 1998. The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1998-1999 Netscape Communications Corporation. All Rights Reserved. Contributor(s): ______________________________________. -   -Alternatively, the contents of this file may be used under the terms of the _____ license (the  "[___] License"), in which case the provisions of [______] License are applicable  instead of those above.  If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting  the provisions above and replace  them with the notice and other provisions required by the [___] License.  If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License." + +Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License." Mozilla Public License Version 1.1 diff --git a/options/license/OCCT-PL b/options/license/OCCT-PL index 85df3c73c..9b6fccc1c 100644 --- a/options/license/OCCT-PL +++ b/options/license/OCCT-PL @@ -6,7 +6,7 @@ OPEN CASCADE releases and makes publicly available the source code of the softwa It is not the purpose of this license to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this license has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. Please read this license carefully and completely before downloading this software. By downloading, using, modifying, distributing and sublicensing this software, you indicate your acceptance to be bound by the terms and conditions of this license. If you do not want to accept or cannot accept for any reasons the terms and conditions of this license, please do not download or use in any manner this software. -  + 1. Definitions Unless there is something in the subject matter or in the context inconsistent therewith, the capitalized terms used in this License shall have the following meaning. @@ -26,13 +26,13 @@ Unless there is something in the subject matter or in the context inconsistent t "Software": means the Original Code, the Modifications, the combination of Original Code and any Modifications or any respective portions thereof. "You" or "Your": means an individual or a legal entity exercising rights under this License -  + 2. Acceptance of license By using, reproducing, modifying, distributing or sublicensing the Software or any portion thereof, You expressly indicate Your acceptance of the terms and conditions of this License and undertake to act in accordance with all the provisions of this License applicable to You. -  + 3. Scope and purpose This License applies to the Software and You may not use, reproduce, modify, distribute, sublicense or circulate the Software, or any portion thereof, except as expressly provided under this License. Any attempt to otherwise use, reproduce, modify, distribute or sublicense the Software is void and will automatically terminate Your rights under this License. -  + 4. Contributor license Subject to the terms and conditions of this License, the Initial Developer and each of the Contributors hereby grant You a world-wide, royalty-free, irrevocable and non-exclusive license under the Applicable Intellectual Property Rights they own or control, to use, reproduce, modify, distribute and sublicense the Software provided that: diff --git a/options/license/OGL-UK-1.0 b/options/license/OGL-UK-1.0 index a761c9916..867c0e353 100644 --- a/options/license/OGL-UK-1.0 +++ b/options/license/OGL-UK-1.0 @@ -10,20 +10,20 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations. You are free to: - copy, publish, distribute and transmit the Information; + copy, publish, distribute and transmit the Information; adapt the Information; exploit the Information commercially for example, by combining it with other Information, or by including it in your own product or application. You must, where you do any of the above: - acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; -  If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following:
 Contains public sector information licensed under the Open Government Licence v1.0. + acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; + If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following: Contains public sector information licensed under the Open Government Licence v1.0. ensure that you do not use the Information in a way that suggests any official status or that the Information Provider endorses you or your use of the Information; ensure that you do not mislead others or misrepresent the Information or its source; ensure that your use of the Information does not breach the Data Protection Act 1998 or the Privacy and Electronic Communications (EC Directive) Regulations 2003. These are important conditions of this licence and if you fail to comply with them the rights granted to you under this licence, or any similar licence granted by the Licensor, will end automatically. - Exemptions + Exemptions This licence does not cover the use of: - personal data in the Information; @@ -48,22 +48,22 @@ Definitions In this licence, the terms below have the following meanings: -‘Information’
means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence. +‘Information’ means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence. -‘Information Provider’
means the person or organisation providing the Information under this licence. +‘Information Provider’ means the person or organisation providing the Information under this licence. -‘Licensor’
means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence. +‘Licensor’ means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence. -‘Use’
as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format. +‘Use’ as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format. -‘You’
means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence. +‘You’ means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence. About the Open Government Licence The Controller of Her Majesty’s Stationery Office (HMSO) has developed this licence as a tool to enable Information Providers in the public sector to license the use and re-use of their Information under a common open licence. The Controller invites public sector bodies owning their own copyright and database rights to permit the use of their Information under this licence. -The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework. +The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework. This is version 1.0 of the Open Government Licence. The Controller of HMSO may, from time to time, issue new versions of the Open Government Licence. However, you may continue to use Information licensed under this version should you wish to do so. These terms have been aligned to be interoperable with any Creative Commons Attribution Licence, which covers copyright, and Open Data Commons Attribution License, which covers database rights and applicable copyrights. -Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website. +Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website. diff --git a/options/license/OSET-PL-2.1 b/options/license/OSET-PL-2.1 index 15f0c7758..e0ed2e139 100644 --- a/options/license/OSET-PL-2.1 +++ b/options/license/OSET-PL-2.1 @@ -100,7 +100,8 @@ If it is impossible for You to comply with any of the terms of this License with 5.1 Failure to Comply The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60-days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30-days after Your receipt of the notice. - 5.2 Patent Infringement Claims
 If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. + 5.2 Patent Infringement Claims + If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3 Additional Compliance Terms Notwithstanding the foregoing in this Section 5, for purposes of this Section, if You breach Section 3.1 (Distribution of Source Form), Section 3.2 (Distribution of Executable Form), Section 3.3 (Distribution of a Larger Work), or Section 3.4 (Notices), then becoming compliant as described in Section 5.1 must also include, no later than 30 days after receipt by You of notice of such violation by a Contributor, making the Covered Software available in Source Code Form as required by this License on a publicly available computer network for a period of no less than three (3) years. diff --git a/options/license/SHL-2.0 b/options/license/SHL-2.0 index e522a396f..9218b47a7 100644 --- a/options/license/SHL-2.0 +++ b/options/license/SHL-2.0 @@ -1,22 +1,22 @@ # Solderpad Hardware Licence Version 2.0 -This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation. +This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation. 1. The definitions set out in the Apache License are modified as follows: -Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below). +Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below). -Contribution also includes any design, as well as any work of authorship. +Contribution also includes any design, as well as any work of authorship. -Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof. +Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof. -Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works). +Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works). -Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks). +Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks). -Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files. -Work also includes a design or work of authorship, whether in Source form or other Object form. +Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files. +Work also includes a design or work of authorship, whether in Source form or other Object form. 2. Grant of Licence -2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist. +2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist. diff --git a/options/license/SHL-2.1 b/options/license/SHL-2.1 index 4815a9e5e..c9ae53741 100644 --- a/options/license/SHL-2.1 +++ b/options/license/SHL-2.1 @@ -19,7 +19,7 @@ The following definitions shall replace the corresponding definitions in the Apa "License" shall mean this Solderpad Hardware License version 2.1, being the terms and conditions for use, manufacture, instantiation, adaptation, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License. -  + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship or design. For the purposes of this License, Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the Work and Derivative Works thereof. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object or material and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works). diff --git a/options/license/SISSL b/options/license/SISSL index 7d6ad9d66..af38d02d9 100644 --- a/options/license/SISSL +++ b/options/license/SISSL @@ -36,13 +36,13 @@ Sun Industry Standards Source License - Version 1.1 2.0 SOURCE CODE LICENSE - 2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:  + 2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. - (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications.  + (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications. 3.0 DISTRIBUTION OBLIGATIONS @@ -92,14 +92,14 @@ This License represents the complete agreement concerning subject matter hereof. EXHIBIT A - Sun Standards License -"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________. +"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________. -Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either  +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is ______________________________________. -The Initial Developer of the Original Code is:  +The Initial Developer of the Original Code is: Sun Microsystems, Inc.. Portions created by: _______________________________________ diff --git a/options/license/W3C-19980720 b/options/license/W3C-19980720 index a8554039e..134879044 100644 --- a/options/license/W3C-19980720 +++ b/options/license/W3C-19980720 @@ -4,7 +4,7 @@ Copyright (c) 1994-2002 World Wide Web Consortium, (Massachusetts Institute of T This W3C work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions: -Permission to use, copy, modify, and distribute this software and its documentation, with or without modification,  for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make: +Permission to use, copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make: 1. The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 2640819a5..0d304dd66 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -311,7 +311,7 @@ func SearchIssues(ctx *context.APIContext) { ctx.SetLinkHeader(int(total), limit) ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues)) } // ListIssues list the issues of a repository @@ -548,7 +548,7 @@ func ListIssues(ctx *context.APIContext) { ctx.SetLinkHeader(int(total), listOptions.PageSize) ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues)) } func getUserIDForFilter(ctx *context.APIContext, queryName string) int64 { @@ -614,7 +614,7 @@ func GetIssue(ctx *context.APIContext) { ctx.NotFound() return } - ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue)) + ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue)) } // CreateIssue create an issue of a repository @@ -737,7 +737,7 @@ func CreateIssue(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "GetIssueByID", err) return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue)) } // EditIssue modify an issue of a repository @@ -913,7 +913,7 @@ func EditIssue(ctx *context.APIContext) { ctx.InternalServerError(err) return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue)) } func DeleteIssue(ctx *context.APIContext) { diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go index fc9e88d63..6f3e4b5e7 100644 --- a/routers/api/v1/repo/issue_attachment.go +++ b/routers/api/v1/repo/issue_attachment.go @@ -108,7 +108,7 @@ func ListIssueAttachments(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue).Attachments) + ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue).Attachments) } // CreateIssueAttachment creates an attachment and saves the given file diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go index a42920d4f..c40e92c01 100644 --- a/routers/api/v1/repo/issue_dependency.go +++ b/routers/api/v1/repo/issue_dependency.go @@ -153,7 +153,7 @@ func GetIssueDependencies(ctx *context.APIContext) { blockerIssues = append(blockerIssues, &blocker.Issue) } - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, blockerIssues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, blockerIssues)) } // CreateIssueDependency create a new issue dependencies @@ -214,7 +214,7 @@ func CreateIssueDependency(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target)) } // RemoveIssueDependency remove an issue dependency @@ -275,7 +275,7 @@ func RemoveIssueDependency(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target)) } // GetIssueBlocks list issues that are blocked by this issue @@ -381,7 +381,7 @@ func GetIssueBlocks(ctx *context.APIContext) { issues = append(issues, &depMeta.Issue) } - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues)) } // CreateIssueBlocking block the issue given in the body by the issue in path @@ -438,7 +438,7 @@ func CreateIssueBlocking(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency)) } // RemoveIssueBlocking unblock the issue given in the body by the issue in path @@ -495,7 +495,7 @@ func RemoveIssueBlocking(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency)) } func getParamsIssue(ctx *context.APIContext) *issues_model.Issue { diff --git a/routers/api/v1/repo/issue_pin.go b/routers/api/v1/repo/issue_pin.go index 8fcf670fd..af3e06332 100644 --- a/routers/api/v1/repo/issue_pin.go +++ b/routers/api/v1/repo/issue_pin.go @@ -207,7 +207,7 @@ func ListPinnedIssues(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues)) } // ListPinnedPullRequests returns a list of all pinned PRs diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index c64051588..f83855efa 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -138,7 +138,7 @@ func ListTrackedTimes(ctx *context.APIContext) { } ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes)) + ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes)) } // AddTime add time manual to the given issue @@ -225,7 +225,7 @@ func AddTime(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, trackedTime)) + ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, user, trackedTime)) } // ResetIssueTime reset time manual to the given issue @@ -455,7 +455,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes)) + ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes)) } // ListTrackedTimesByRepository lists all tracked times of the repository @@ -567,7 +567,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { } ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes)) + ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes)) } // ListMyTrackedTimes lists all tracked times of the current user @@ -629,5 +629,5 @@ func ListMyTrackedTimes(ctx *context.APIContext) { } ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes)) + ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes)) } diff --git a/routers/private/actions.go b/routers/private/actions.go index 397f20a09..425c480b3 100644 --- a/routers/private/actions.go +++ b/routers/private/actions.go @@ -27,7 +27,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) { defer rd.Close() if err := json.NewDecoder(rd).Decode(&genRequest); err != nil { - log.Error("%v", err) + log.Error("JSON Decode failed: %v", err) ctx.JSON(http.StatusInternalServerError, private.Response{ Err: err.Error(), }) @@ -36,7 +36,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) { owner, repo, err := parseScope(ctx, genRequest.Scope) if err != nil { - log.Error("%v", err) + log.Error("parseScope failed: %v", err) ctx.JSON(http.StatusInternalServerError, private.Response{ Err: err.Error(), }) @@ -46,18 +46,18 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) { if errors.Is(err, util.ErrNotExist) || (token != nil && !token.IsActive) { token, err = actions_model.NewRunnerToken(ctx, owner, repo) if err != nil { - err := fmt.Sprintf("error while creating runner token: %v", err) - log.Error("%v", err) + errMsg := fmt.Sprintf("error while creating runner token: %v", err) + log.Error("NewRunnerToken failed: %v", errMsg) ctx.JSON(http.StatusInternalServerError, private.Response{ - Err: err, + Err: errMsg, }) return } } else if err != nil { - err := fmt.Sprintf("could not get unactivated runner token: %v", err) - log.Error("%v", err) + errMsg := fmt.Sprintf("could not get unactivated runner token: %v", err) + log.Error("GetLatestRunnerToken failed: %v", errMsg) ctx.JSON(http.StatusInternalServerError, private.Response{ - Err: err, + Err: errMsg, }) return } diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go index 42b8e5abe..764c976fa 100644 --- a/routers/private/hook_verification.go +++ b/routers/private/hook_verification.go @@ -47,7 +47,7 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env [] _ = stdoutWriter.Close() err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) if err != nil { - log.Error("%v", err) + log.Error("readAndVerifyCommitsFromShaReader failed: %v", err) cancel() } _ = stdoutReader.Close() @@ -66,7 +66,6 @@ func readAndVerifyCommitsFromShaReader(input io.ReadCloser, repo *git.Repository line := scanner.Text() err := readAndVerifyCommit(line, repo, env) if err != nil { - log.Error("%v", err) return err } } diff --git a/routers/private/mail.go b/routers/private/mail.go index c19ee6789..cf3abb31c 100644 --- a/routers/private/mail.go +++ b/routers/private/mail.go @@ -35,7 +35,7 @@ func SendEmail(ctx *context.PrivateContext) { defer rd.Close() if err := json.NewDecoder(rd).Decode(&mail); err != nil { - log.Error("%v", err) + log.Error("JSON Decode failed: %v", err) ctx.JSON(http.StatusInternalServerError, private.Response{ Err: err.Error(), }) diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 3dcf0d2aa..15bd667a4 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -403,7 +403,6 @@ func EditUserPost(ctx *context.Context) { ctx.Data["Err_Password"] = true ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplUserEdit, &form) case password.IsErrIsPwnedRequest(err): - log.Error("%s", err.Error()) ctx.Data["Err_Password"] = true ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplUserEdit, &form) default: diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go index d47aeb5e9..d15a8b814 100644 --- a/routers/web/auth/password.go +++ b/routers/web/auth/password.go @@ -214,7 +214,6 @@ func ResetPasswdPost(ctx *context.Context) { case errors.Is(err, password.ErrIsPwned): ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplResetPassword, nil) case password.IsErrIsPwnedRequest(err): - log.Error("%s", err.Error()) ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplResetPassword, nil) default: ctx.ServerError("UpdateAuth", err) @@ -298,7 +297,6 @@ func MustChangePasswordPost(ctx *context.Context) { ctx.Data["Err_Password"] = true ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplMustChangePassword, &form) case password.IsErrIsPwnedRequest(err): - log.Error("%s", err.Error()) ctx.Data["Err_Password"] = true ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplMustChangePassword, &form) default: diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index db1dac012..38f9b85ac 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "net/http" - "net/url" "strconv" "strings" @@ -195,14 +194,15 @@ func NewProjectPost(ctx *context.Context) { // ChangeProjectStatus updates the status of a project between "open" and "close" func ChangeProjectStatus(ctx *context.Context) { - toClose := false + var toClose bool switch ctx.Params(":action") { case "open": toClose = false case "close": toClose = true default: - ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects") + ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects") + return } id := ctx.ParamsInt64(":id") @@ -210,7 +210,7 @@ func ChangeProjectStatus(ctx *context.Context) { ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err) return } - ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action"))) + ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id)) } // DeleteProject delete a project diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index da811c8a7..520737809 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2174,7 +2174,7 @@ func GetIssueInfo(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, convert.ToIssue(ctx, issue)) + ctx.JSON(http.StatusOK, convert.ToIssue(ctx, ctx.Doer, issue)) } // UpdateIssueTitle change issue's title @@ -2708,7 +2708,7 @@ func SearchIssues(ctx *context.Context) { } ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues)) } func getUserIDForFilter(ctx *context.Context, queryName string) int64 { @@ -2878,7 +2878,7 @@ func ListIssues(ctx *context.Context) { } ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues)) } func BatchDeleteIssues(ctx *context.Context) { diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index aa4258559..a0c044fd6 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "net/http" - "net/url" "strings" "code.gitea.io/gitea/models/db" @@ -180,14 +179,10 @@ func ChangeProjectStatus(ctx *context.Context) { id := ctx.ParamsInt64(":id") if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, ctx.Repo.Repository.ID, id, toClose); err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", err) - } else { - ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err) - } + ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err) return } - ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action"))) + ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id)) } // DeleteProject delete a project diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 2e4b9dae6..1893e9122 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -376,7 +376,7 @@ func Action(ctx *context.Context) { } if redirectViaJSON { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.ContextUser.HomeLink(), }) return diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 9cf93320a..795ee59d5 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -74,7 +74,6 @@ func AccountPost(ctx *context.Context) { case errors.Is(err, password.ErrIsPwned): ctx.Flash.Error(ctx.Tr("auth.password_pwned")) case password.IsErrIsPwnedRequest(err): - log.Error("%s", err.Error()) ctx.Flash.Error(ctx.Tr("auth.password_pwned_err")) default: ctx.ServerError("UpdateAuth", err) diff --git a/services/actions/auth_test.go b/services/actions/auth_test.go index f73ae8ae4..12db2bae5 100644 --- a/services/actions/auth_test.go +++ b/services/actions/auth_test.go @@ -20,7 +20,7 @@ func TestCreateAuthorizationToken(t *testing.T) { assert.Nil(t, err) assert.NotEqual(t, "", token) claims := jwt.MapClaims{} - _, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) { + _, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) { return setting.GetGeneralTokenSigningSecret(), nil }) assert.Nil(t, err) diff --git a/services/actions/notifier.go b/services/actions/notifier.go index eec5f814d..6551da39e 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -49,7 +49,7 @@ func (n *actionsNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu newNotifyInputFromIssue(issue, webhook_module.HookEventIssues).WithPayload(&api.IssuePayload{ Action: api.HookIssueOpened, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, issue.Poster, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, issue.Poster, nil), }).Notify(withMethod(ctx, "NewIssue")) @@ -89,7 +89,7 @@ func (n *actionsNotifier) IssueChangeContent(ctx context.Context, doer *user_mod WithPayload(&api.IssuePayload{ Action: api.HookIssueEdited, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }). @@ -127,7 +127,7 @@ func (n *actionsNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode } apiIssue := &api.IssuePayload{ Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), } @@ -229,7 +229,7 @@ func notifyIssueChange(ctx context.Context, doer *user_model.User, issue *issues WithPayload(&api.IssuePayload{ Action: action, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }). @@ -293,7 +293,7 @@ func notifyIssueCommentChange(ctx context.Context, doer *user_model.User, commen payload := &api.IssueCommentPayload{ Action: action, - Issue: convert.ToAPIIssue(ctx, comment.Issue), + Issue: convert.ToAPIIssue(ctx, doer, comment.Issue), Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment), Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 5d1e2c122..ef37ff87e 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -560,12 +560,9 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository) } // We need a notifyInput to call handleSchedules - // Here we use the commit author as the Doer of the notifyInput - commitUser, err := user_model.GetUserByEmail(ctx, commit.Author.Email) - if err != nil { - return fmt.Errorf("get user by email: %w", err) - } - notifyInput := newNotifyInput(repo, commitUser, webhook_module.HookEventSchedule) + // if repo is a mirror, commit author maybe an external user, + // so we use action user as the Doer of the notifyInput + notifyInput := newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule) return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch) } diff --git a/services/auth/source/oauth2/store.go b/services/auth/source/oauth2/store.go index 394bf9946..90fa96560 100644 --- a/services/auth/source/oauth2/store.go +++ b/services/auth/source/oauth2/store.go @@ -9,6 +9,7 @@ import ( "net/http" "code.gitea.io/gitea/modules/log" + session_module "code.gitea.io/gitea/modules/session" chiSession "gitea.com/go-chi/session" "github.com/gorilla/sessions" @@ -65,7 +66,7 @@ func (st *SessionsStore) Save(r *http.Request, w http.ResponseWriter, session *s chiStore := chiSession.GetSession(r) if session.IsNew { - _, _ = chiSession.RegenerateSession(w, r) + _, _ = session_module.RegenerateSession(w, r) session.IsNew = false } diff --git a/services/context/captcha.go b/services/context/captcha.go index a1999900c..fa8d779f5 100644 --- a/services/context/captcha.go +++ b/services/context/captcha.go @@ -79,11 +79,11 @@ func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) { case setting.CfTurnstile: valid, err = turnstile.Verify(ctx, ctx.Req.Form.Get(cfTurnstileResponseField)) default: - ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) + ctx.ServerError("Unknown Captcha Type", fmt.Errorf("unknown Captcha Type: %s", setting.Service.CaptchaType)) return } if err != nil { - log.Debug("%v", err) + log.Debug("Captcha Verify failed: %v", err) } if !valid { diff --git a/services/convert/issue.go b/services/convert/issue.go index c6e06180c..54b00cd88 100644 --- a/services/convert/issue.go +++ b/services/convert/issue.go @@ -18,19 +18,19 @@ import ( api "code.gitea.io/gitea/modules/structs" ) -func ToIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue { - return toIssue(ctx, issue, WebAssetDownloadURL) +func ToIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue { + return toIssue(ctx, doer, issue, WebAssetDownloadURL) } // ToAPIIssue converts an Issue to API format // it assumes some fields assigned with values: // Required - Poster, Labels, // Optional - Milestone, Assignee, PullRequest -func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue { - return toIssue(ctx, issue, APIAssetDownloadURL) +func ToAPIIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue { + return toIssue(ctx, doer, issue, APIAssetDownloadURL) } -func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue { +func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue { if err := issue.LoadLabels(ctx); err != nil { return &api.Issue{} } @@ -44,7 +44,7 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func apiIssue := &api.Issue{ ID: issue.ID, Index: issue.Index, - Poster: ToUser(ctx, issue.Poster, nil), + Poster: ToUser(ctx, issue.Poster, doer), Title: issue.Title, Body: issue.Content, Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL), @@ -114,25 +114,25 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func } // ToIssueList converts an IssueList to API format -func ToIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue { +func ToIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue { result := make([]*api.Issue, len(il)) for i := range il { - result[i] = ToIssue(ctx, il[i]) + result[i] = ToIssue(ctx, doer, il[i]) } return result } // ToAPIIssueList converts an IssueList to API format -func ToAPIIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue { +func ToAPIIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue { result := make([]*api.Issue, len(il)) for i := range il { - result[i] = ToAPIIssue(ctx, il[i]) + result[i] = ToAPIIssue(ctx, doer, il[i]) } return result } // ToTrackedTime converts TrackedTime to API format -func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api.TrackedTime) { +func ToTrackedTime(ctx context.Context, doer *user_model.User, t *issues_model.TrackedTime) (apiT *api.TrackedTime) { apiT = &api.TrackedTime{ ID: t.ID, IssueID: t.IssueID, @@ -141,7 +141,7 @@ func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api. Created: t.Created, } if t.Issue != nil { - apiT.Issue = ToAPIIssue(ctx, t.Issue) + apiT.Issue = ToAPIIssue(ctx, doer, t.Issue) } if t.User != nil { apiT.UserName = t.User.Name @@ -192,10 +192,10 @@ func ToStopWatches(ctx context.Context, sws []*issues_model.Stopwatch) (api.Stop } // ToTrackedTimeList converts TrackedTimeList to API format -func ToTrackedTimeList(ctx context.Context, tl issues_model.TrackedTimeList) api.TrackedTimeList { +func ToTrackedTimeList(ctx context.Context, doer *user_model.User, tl issues_model.TrackedTimeList) api.TrackedTimeList { result := make([]*api.TrackedTime, 0, len(tl)) for _, t := range tl { - result = append(result, ToTrackedTime(ctx, t)) + result = append(result, ToTrackedTime(ctx, doer, t)) } return result } diff --git a/services/convert/issue_comment.go b/services/convert/issue_comment.go index b034a5089..9ffaf1e84 100644 --- a/services/convert/issue_comment.go +++ b/services/convert/issue_comment.go @@ -120,7 +120,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu return nil } - comment.TrackedTime = ToTrackedTime(ctx, c.Time) + comment.TrackedTime = ToTrackedTime(ctx, doer, c.Time) } if c.RefIssueID != 0 { @@ -129,7 +129,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu log.Error("GetIssueByID(%d): %v", c.RefIssueID, err) return nil } - comment.RefIssue = ToAPIIssue(ctx, issue) + comment.RefIssue = ToAPIIssue(ctx, doer, issue) } if c.RefCommentID != 0 { @@ -180,7 +180,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu } if c.DependentIssue != nil { - comment.DependentIssue = ToAPIIssue(ctx, c.DependentIssue) + comment.DependentIssue = ToAPIIssue(ctx, doer, c.DependentIssue) } return comment diff --git a/services/convert/pull.go b/services/convert/pull.go index 6d98121ed..775bf3806 100644 --- a/services/convert/pull.go +++ b/services/convert/pull.go @@ -33,7 +33,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u return nil } - apiIssue := ToAPIIssue(ctx, pr.Issue) + apiIssue := ToAPIIssue(ctx, doer, pr.Issue) if err := pr.LoadBaseRepo(ctx); err != nil { log.Error("GetRepositoryById[%d]: %v", pr.ID, err) return nil diff --git a/services/migrations/gitbucket.go b/services/migrations/gitbucket.go index 5f1155583..4fe9e30a3 100644 --- a/services/migrations/gitbucket.go +++ b/services/migrations/gitbucket.go @@ -72,6 +72,11 @@ func (g *GitBucketDownloader) LogString() string { // NewGitBucketDownloader creates a GitBucket downloader func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader { githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName) + // Gitbucket 4.40 uses different internal hard-coded perPage values. + // Issues, PRs, and other major parts use 25. Release page uses 10. + // Some API doesn't support paging yet. Sounds difficult, but using + // minimum number among them worked out very well. + githubDownloader.maxPerPage = 10 githubDownloader.SkipReactions = true githubDownloader.SkipReviews = true return &GitBucketDownloader{ diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 2a38d4ba5..fa23986c5 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -13,6 +13,7 @@ import ( system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + giturl "code.gitea.io/gitea/modules/git/url" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -30,10 +31,15 @@ const gitShortEmptySha = "0000000" // UpdateAddress writes new address to Git repository and database func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error { + u, err := giturl.Parse(addr) + if err != nil { + return fmt.Errorf("invalid addr: %v", err) + } + remoteName := m.GetRemoteName() repoPath := m.GetRepository(ctx).RepoPath() // Remove old remote - _, _, err := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) + _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -70,7 +76,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } } - m.Repo.OriginalURL = addr + // erase authentication before storing in database + u.User = nil + m.Repo.OriginalURL = u.String() return repo_model.UpdateRepositoryCols(ctx, m.Repo, "original_url") } @@ -449,19 +457,17 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { return false } - var gitRepo *git.Repository - if len(results) == 0 { - log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo) - } else { - log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) - gitRepo, err = gitrepo.OpenRepository(ctx, m.Repo) - if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err) - return false - } - defer gitRepo.Close() + gitRepo, err := gitrepo.OpenRepository(ctx, m.Repo) + if err != nil { + log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err) + return false + } + defer gitRepo.Close() + log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) + if len(results) > 0 { if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok { + log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err) return false } } @@ -534,16 +540,24 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { } log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo) - // Get latest commit date and update to current repository updated time - commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath()) + isEmpty, err := gitRepo.IsEmpty() if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err) + log.Error("SyncMirrors [repo: %-v]: unable to check empty git repo: %v", m.Repo, err) return false } + if !isEmpty { + // Get latest commit date and update to current repository updated time + commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath()) + if err != nil { + log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err) + return false + } + + if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil { + log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err) + return false + } - if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err) - return false } log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo) diff --git a/services/notify/notify.go b/services/notify/notify.go index 9cb329d30..5ed63646a 100644 --- a/services/notify/notify.go +++ b/services/notify/notify.go @@ -91,7 +91,7 @@ func AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues // NewPullRequest notifies new pull request to notifiers func NewPullRequest(ctx context.Context, pr *issues_model.PullRequest, mentions []*user_model.User) { if err := pr.LoadIssue(ctx); err != nil { - log.Error("%v", err) + log.Error("LoadIssue failed: %v", err) return } if err := pr.Issue.LoadPoster(ctx); err != nil { @@ -112,7 +112,7 @@ func PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *iss // PullRequestReview notifies new pull request review func PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) { if err := review.LoadReviewer(ctx); err != nil { - log.Error("%v", err) + log.Error("LoadReviewer failed: %v", err) return } for _, notifier := range notifiers { diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go index 613b46d8f..451a18215 100644 --- a/services/repository/files/cherry_pick.go +++ b/services/repository/files/cherry_pick.go @@ -28,7 +28,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { - log.Error("%v", err) + log.Error("NewTemporaryUploadRepository failed: %v", err) } defer t.Close() if err := t.Clone(opts.OldBranch, false); err != nil { diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go index f6d5643dc..e5f7e2af9 100644 --- a/services/repository/files/patch.go +++ b/services/repository/files/patch.go @@ -111,7 +111,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { - log.Error("%v", err) + log.Error("NewTemporaryUploadRepository failed: %v", err) } defer t.Close() if err := t.Clone(opts.OldBranch, true); err != nil { diff --git a/services/repository/files/update.go b/services/repository/files/update.go index a6c813f02..e677949e8 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -143,7 +143,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { - log.Error("%v", err) + log.Error("NewTemporaryUploadRepository failed: %v", err) } defer t.Close() hasOldBranch := true diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index 03c5bede8..a9b342265 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -67,7 +67,7 @@ func (m *webhookNotifier) IssueClearLabels(ctx context.Context, doer *user_model err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{ Action: api.HookIssueLabelCleared, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) @@ -168,7 +168,7 @@ func (m *webhookNotifier) IssueChangeAssignee(ctx context.Context, doer *user_mo permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, doer) apiIssue := &api.IssuePayload{ Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), } @@ -214,7 +214,7 @@ func (m *webhookNotifier) IssueChangeTitle(ctx context.Context, doer *user_model From: oldTitle, }, }, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) @@ -250,7 +250,7 @@ func (m *webhookNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode } else { apiIssue := &api.IssuePayload{ Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), CommitID: commitID, @@ -281,7 +281,7 @@ func (m *webhookNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssues, &api.IssuePayload{ Action: api.HookIssueOpened, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, issue.Poster, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, issue.Poster, nil), }); err != nil { @@ -349,7 +349,7 @@ func (m *webhookNotifier) IssueChangeContent(ctx context.Context, doer *user_mod From: oldContent, }, }, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) @@ -384,7 +384,7 @@ func (m *webhookNotifier) UpdateComment(ctx context.Context, doer *user_model.Us permission, _ := access_model.GetUserRepoPermission(ctx, c.Issue.Repo, doer) if err := PrepareWebhooks(ctx, EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentEdited, - Issue: convert.ToAPIIssue(ctx, c.Issue), + Issue: convert.ToAPIIssue(ctx, doer, c.Issue), Comment: convert.ToAPIComment(ctx, c.Issue.Repo, c), Changes: &api.ChangesPayload{ Body: &api.ChangesFromPayload{ @@ -412,7 +412,7 @@ func (m *webhookNotifier) CreateIssueComment(ctx context.Context, doer *user_mod permission, _ := access_model.GetUserRepoPermission(ctx, repo, doer) if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Comment: convert.ToAPIComment(ctx, repo, comment), Repository: convert.ToRepo(ctx, repo, permission), Sender: convert.ToUser(ctx, doer, nil), @@ -449,7 +449,7 @@ func (m *webhookNotifier) DeleteComment(ctx context.Context, doer *user_model.Us permission, _ := access_model.GetUserRepoPermission(ctx, comment.Issue.Repo, doer) if err := PrepareWebhooks(ctx, EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentDeleted, - Issue: convert.ToAPIIssue(ctx, comment.Issue), + Issue: convert.ToAPIIssue(ctx, doer, comment.Issue), Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment), Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), @@ -533,7 +533,7 @@ func (m *webhookNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{ Action: api.HookIssueLabelUpdated, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) @@ -575,7 +575,7 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueMilestone, &api.IssuePayload{ Action: hookAction, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index f2505e82c..ec5d0fc9e 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -209,7 +209,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model if isOldWikiExist { err := gitRepo.RemoveFilesFromIndex(oldWikiPath) if err != nil { - log.Error("%v", err) + log.Error("RemoveFilesFromIndex failed: %v", err) return err } } @@ -219,18 +219,18 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model objectHash, err := gitRepo.HashObject(strings.NewReader(content)) if err != nil { - log.Error("%v", err) + log.Error("HashObject failed: %v", err) return err } if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil { - log.Error("%v", err) + log.Error("AddObjectToIndex failed: %v", err) return err } tree, err := gitRepo.WriteTree() if err != nil { - log.Error("%v", err) + log.Error("WriteTree failed: %v", err) return err } @@ -255,7 +255,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), committer, tree, commitTreeOpts) if err != nil { - log.Error("%v", err) + log.Error("CommitTree failed: %v", err) return err } @@ -270,11 +270,11 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model 0, ), }); err != nil { - log.Error("%v", err) + log.Error("Push failed: %v", err) if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) { return err } - return fmt.Errorf("Push: %w", err) + return fmt.Errorf("failed to push: %w", err) } return nil diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index 5ea003e5e..33d8a2f96 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -62,10 +62,7 @@ {{template "admin/layout_footer" .}} diff --git a/templates/org/projects/list.tmpl b/templates/org/projects/list.tmpl index ec9cfece9..80dde1c4d 100644 --- a/templates/org/projects/list.tmpl +++ b/templates/org/projects/list.tmpl @@ -13,11 +13,9 @@
{{template "shared/user/profile_big_avatar" .}}
-
-
+
{{template "user/overview/header" .}} -
- {{template "projects/list" .}} + {{template "projects/list" .}}
diff --git a/templates/org/projects/view.tmpl b/templates/org/projects/view.tmpl index 495204b06..e1ab81c4c 100644 --- a/templates/org/projects/view.tmpl +++ b/templates/org/projects/view.tmpl @@ -1,7 +1,7 @@ {{template "base/head" .}}
{{template "shared/user/org_profile_avatar" .}} -
+
{{template "user/overview/header" .}}
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 49a0b445b..7fec88cb7 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -18,10 +18,10 @@ {{end}} {{end}}
-
+

{{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}

{{if not $.PageIsWiki}} -
+
{{ctx.Locale.Tr "repo.diff.browse_source"}} diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index 9ede83dcb..6b343e4ee 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -111,7 +111,7 @@ {{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.IsArchived) $.IsShowingAllCommits}}

-
+
- {{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}} - - {{if $file.IsGenerated}} - {{ctx.Locale.Tr "repo.diff.generated"}} - {{end}} - {{if $file.IsVendored}} - {{ctx.Locale.Tr "repo.diff.vendored"}} - {{end}} - {{if and $file.Mode $file.OldMode}} - {{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}} - {{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} - {{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}} - {{else if $file.Mode}} - {{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} - {{end}} + {{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}} + {{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}} + + {{if $file.IsGenerated}} + {{ctx.Locale.Tr "repo.diff.generated"}} + {{end}} + {{if $file.IsVendored}} + {{ctx.Locale.Tr "repo.diff.vendored"}} + {{end}} + {{if and $file.Mode $file.OldMode}} + {{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}} + {{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} + {{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}} + {{else if $file.Mode}} + {{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} + {{end}} +
{{if $showFileViewToggle}} diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index d02eb7d7a..3c994af19 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -58,8 +58,11 @@
{{end}} {{template "repo/sub_menu" .}} + {{$n := len .TreeNames}} + {{$l := Eval $n "-" 1}} + {{$isHomepage := (eq $n 0)}}
-
+
{{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}} {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} {{$cmpBranch := ""}} @@ -74,9 +77,7 @@ {{end}} - {{$n := len .TreeNames}} - {{$l := Eval $n "-" 1}} - {{if eq $n 0}} + {{if $isHomepage}} {{ctx.Locale.Tr "repo.find_file.go_to_file"}} {{end}} @@ -100,20 +101,20 @@ {{end}} - {{if and (eq $n 0) (.Repository.IsTemplate)}} + {{if and $isHomepage (.Repository.IsTemplate)}} {{ctx.Locale.Tr "repo.use_template"}} {{end}} - {{if ne $n 0}} + {{if (not $isHomepage)}} {{StringUtils.EllipsisString .Repository.Name 30}} {{- range $i, $v := .TreeNames -}} / {{- if eq $i $l -}} - {{StringUtils.EllipsisString $v 30}} + {{$v}} {{- else -}} - {{$p := index $.Paths $i}}{{StringUtils.EllipsisString $v 30}} + {{$p := index $.Paths $i}}{{$v}} {{- end -}} {{- end -}} @@ -121,7 +122,7 @@
- {{if eq $n 0}} + {{if $isHomepage}}
{{template "repo/clone_buttons" .}}
{{template "repo/cite/cite_modal" .}} {{end}} - {{if and (ne $n 0) (not .IsViewFile) (not .IsBlame)}} + {{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}} {{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 8b73afe6d..1d1fdb36e 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -156,7 +156,7 @@
- {{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName false}} + {{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName}}
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 2680b374c..333121c20 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -11,13 +11,13 @@ {{end}} {{if not .ReadmeInList}} -
-
+
+
{{template "repo/latest_commit" .}}
{{if .LatestCommit}} {{if .LatestCommit.Committer}} -
+
{{TimeSince .LatestCommit.Committer.When ctx.Locale}}
{{end}} diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl index 7c463c50a..fb257bd47 100644 --- a/templates/repo/view_list.tmpl +++ b/templates/repo/view_list.tmpl @@ -1,8 +1,12 @@ - diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl index 04e79ba74..bf3b51ee3 100644 --- a/templates/user/notification/notification_div.tmpl +++ b/templates/user/notification/notification_div.tmpl @@ -1,7 +1,7 @@
{{$notificationUnreadCount := call .NotificationUnreadCount}} -
+ diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index 5880c80cc..51b7a4f0e 100644 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -6,11 +6,8 @@
{{template "shared/user/profile_big_avatar" .}}
-
-
- {{template "user/overview/header" .}} -
- +
+ {{template "user/overview/header" .}} {{if eq .TabName "activity"}} {{if .ContextUser.KeepActivityPrivate}}
diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 8fd55d7fa..adba52fee 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -621,7 +621,7 @@ func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile schema, err := jsonschema.Compile(schemaFilePath) assert.NoError(t, err) - var data interface{} + var data any err = json.Unmarshal(resp.Body.Bytes(), &data) assert.NoError(t, err) diff --git a/web_src/css/actions.css b/web_src/css/actions.css index e7b9a3855..1d5bea239 100644 --- a/web_src/css/actions.css +++ b/web_src/css/actions.css @@ -44,7 +44,7 @@ } .run-list-item-right { - flex: 0 0 15%; + flex: 0 0 min(20%, 130px); display: flex; flex-direction: column; gap: 3px; diff --git a/web_src/css/base.css b/web_src/css/base.css index 17c3f2ee6..d7ef9598b 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -24,7 +24,21 @@ --repo-header-issue-min-height: 41px; --min-height-textarea: 132px; /* padding + 6 lines + border = calc(1.57142em + 6lh + 2px), but lh is not fully supported */ --tab-size: 4; - --checkbox-size: 16px; /* height and width of checkbox and radio inputs */ + --checkbox-size: 15px; /* height and width of checkbox and radio inputs */ + --page-spacing: 16px; /* space between page elements */ + --page-margin-x: 32px; /* minimum space on left and right side of page */ +} + +@media (min-width: 768px) and (max-width: 1200px) { + :root { + --page-margin-x: 16px; + } +} + +@media (max-width: 767.98px) { + :root { + --page-margin-x: 8px; + } } :root * { @@ -594,11 +608,14 @@ img.ui.avatar, margin-bottom: 14px; } -/* add padding to all content when there is no .secondary.nav. this uses padding instead of - margin because with the negative margin on .ui.grid we would have to set margin-top: 0, - but that does not work universally for all pages */ +/* add margin to all pages when there is no .secondary.nav */ .page-content > :first-child:not(.secondary-nav) { - padding-top: 14px; + margin-top: var(--page-spacing); +} +/* if .ui.grid is the first child the first grid-column has 'padding-top: 1rem' which we need + to compensate here */ +.page-content > :first-child.ui.grid { + margin-top: calc(var(--page-spacing) - 1rem); } .ui.pagination.menu .active.item { @@ -1256,6 +1273,7 @@ overflow-menu .ui.label { white-space: pre-wrap; word-break: break-all; overflow-wrap: anywhere; + line-height: inherit; /* needed for inline code preview in markup */ } .blame .code-inner { diff --git a/web_src/css/form.css b/web_src/css/form.css index 2a976c056..a8f73b6b6 100644 --- a/web_src/css/form.css +++ b/web_src/css/form.css @@ -249,21 +249,6 @@ textarea:focus, .user.signup form .optional .title { margin-left: 250px !important; } - .user.activate form .inline.field > input, - .user.forgot.password form .inline.field > input, - .user.reset.password form .inline.field > input, - .user.link-account form .inline.field > input, - .user.signin form .inline.field > input, - .user.signup form .inline.field > input, - .user.activate form .inline.field > textarea, - .user.forgot.password form .inline.field > textarea, - .user.reset.password form .inline.field > textarea, - .user.link-account form .inline.field > textarea, - .user.signin form .inline.field > textarea, - .user.signup form .inline.field > textarea, - .oauth-login-link { - width: 50%; - } } @media (max-width: 767.98px) { @@ -310,14 +295,7 @@ textarea:focus, .user.reset.password form .inline.field > label, .user.link-account form .inline.field > label, .user.signin form .inline.field > label, - .user.signup form .inline.field > label, - .user.activate form input, - .user.forgot.password form input, - .user.reset.password form input, - .user.link-account form input, - .user.signin form input, - .user.signup form input, - .oauth-login-link { + .user.signup form .inline.field > label { width: 100% !important; } } @@ -435,9 +413,9 @@ textarea:focus, .repository.new.repo form label, .repository.new.migrate form label, .repository.new.fork form label, - .repository.new.repo form input, - .repository.new.migrate form input, - .repository.new.fork form input, + .repository.new.repo form .inline.field > input, + .repository.new.migrate form .inline.field > input, + .repository.new.fork form .inline.field > input, .repository.new.fork form .field a, .repository.new.repo form .selection.dropdown, .repository.new.migrate form .selection.dropdown, diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css index 9238e0b3f..d3e45714a 100644 --- a/web_src/css/modules/checkbox.css +++ b/web_src/css/modules/checkbox.css @@ -20,7 +20,7 @@ input[type="radio"] { .ui.checkbox input[type="checkbox"], .ui.checkbox input[type="radio"] { position: absolute; - top: 0; + top: 1px; left: 0; width: var(--checkbox-size); height: var(--checkbox-size); diff --git a/web_src/css/modules/container.css b/web_src/css/modules/container.css index dc854f89d..f394d6c06 100644 --- a/web_src/css/modules/container.css +++ b/web_src/css/modules/container.css @@ -49,30 +49,11 @@ /* overwrite width of containers inside the main page content div (div with class "page-content") */ .page-content .ui.ui.ui.container:not(.fluid) { width: 1280px; - max-width: calc(100% - 64px); + max-width: calc(100% - calc(2 * var(--page-margin-x))); margin-left: auto; margin-right: auto; } .ui.container.fluid.padded { - padding: 0 32px; -} - -/* enable fluid page widths for medium size viewports */ -@media (min-width: 768px) and (max-width: 1200px) { - .page-content .ui.ui.ui.container:not(.fluid) { - max-width: calc(100% - 32px); - } - .ui.container.fluid.padded { - padding: 0 16px; - } -} - -@media (max-width: 767.98px) { - .page-content .ui.ui.ui.container:not(.fluid) { - max-width: calc(100% - 16px); - } - .ui.container.fluid.padded { - padding: 0 8px; - } + padding: 0 var(--page-margin-x); } diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css index 0b559f1e7..1ca513687 100644 --- a/web_src/css/modules/flexcontainer.css +++ b/web_src/css/modules/flexcontainer.css @@ -2,7 +2,8 @@ .flex-container { display: flex !important; - gap: 16px; + gap: var(--page-spacing); + margin-top: var(--page-spacing); } .flex-container-nav { diff --git a/web_src/css/modules/list.css b/web_src/css/modules/list.css index 73760390d..32c71e802 100644 --- a/web_src/css/modules/list.css +++ b/web_src/css/modules/list.css @@ -126,6 +126,12 @@ cursor: pointer; } +.ui.list .list > .item [class*="right floated"], +.ui.list > .item [class*="right floated"] { + float: right; + margin: 0 0 0 1em; +} + .ui.menu .ui.list > .item, .ui.menu .ui.list .list > .item { display: list-item; diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 66555cad2..bd2d2fdcf 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -177,12 +177,44 @@ } } -.repository.file.list .repo-path { - word-break: break-word; +.commit-summary { + flex: 1; + overflow-wrap: anywhere; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } -.repository.file.list #repo-files-table { - table-layout: fixed; +.commit-header .commit-summary, +td .commit-summary { + white-space: normal; +} + +.latest-commit { + display: flex; + flex: 1; + align-items: center; + overflow: hidden; + text-overflow: ellipsis; +} + +@media (max-width: 767.98px) { + .latest-commit .sha { + display: none; + } + .latest-commit .commit-summary { + margin-left: 8px; + } +} + +.repo-path { + display: flex; + overflow-wrap: anywhere; +} + +/* this is what limits the commit table width to a value that works on all viewport sizes */ +#repo-files-table th:first-of-type { + max-width: calc(calc(min(100vw, 1280px)) - 145px - calc(2 * var(--page-margin-x))); } .repository.file.list #repo-files-table thead th { @@ -262,7 +294,6 @@ } .repository.file.list #repo-files-table td.age { - width: 120px; color: var(--color-text-light-1); } @@ -1223,10 +1254,6 @@ margin: 0; } -.repository #commits-table td.message { - text-overflow: unset; -} - .repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n) { background-color: var(--color-light) !important; } @@ -2153,6 +2180,20 @@ display: inline-block !important; } +.commit-header-buttons { + display: flex; + gap: 4px; + align-items: flex-start; + white-space: nowrap; +} + +@media (max-width: 767.98px) { + .commit-header-buttons { + flex-direction: column; + align-items: stretch; + } +} + .settings.webhooks .list > .item:not(:first-child), .settings.githooks .list > .item:not(:first-child), .settings.actions .list > .item:not(:first-child) { @@ -2384,7 +2425,7 @@ tbody.commit-list { .author-wrapper { overflow: hidden; text-overflow: ellipsis; - max-width: calc(100% - 50px); + max-width: 100%; display: inline-block; vertical-align: middle; } @@ -2409,10 +2450,6 @@ tbody.commit-list { tr.commit-list { width: 100%; } - th .message-wrapper { - display: block; - max-width: calc(100vw - 70px); - } .author-wrapper { max-width: 80px; } @@ -2422,27 +2459,18 @@ tbody.commit-list { tr.commit-list { width: 723px; } - th .message-wrapper { - max-width: 120px; - } } @media (min-width: 992px) and (max-width: 1200px) { tr.commit-list { width: 933px; } - th .message-wrapper { - max-width: 350px; - } } @media (min-width: 1201px) { tr.commit-list { width: 1127px; } - th .message-wrapper { - max-width: 525px; - } } .commit-list .commit-status-link { @@ -2770,7 +2798,7 @@ tbody.commit-list { .repository.file.list #repo-files-table .entry td.message, .repository.file.list #repo-files-table .commit-list td.message, .repository.file.list #repo-files-table .entry span.commit-summary, - .repository.file.list #repo-files-table .commit-list span.commit-summary { + .repository.file.list #repo-files-table .commit-list tr span.commit-summary { display: none !important; } .repository.view.issue .comment-list .timeline, diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css index b98b1e638..c74f334c2 100644 --- a/web_src/css/themes/theme-gitea-dark.css +++ b/web_src/css/themes/theme-gitea-dark.css @@ -65,7 +65,7 @@ --color-console-fg-subtle: #bec4c8; --color-console-bg: #171b1e; --color-console-border: #2e353b; - --color-console-hover-bg: #e8e8ff16; + --color-console-hover-bg: #292d31; --color-console-active-bg: #2e353b; --color-console-menu-bg: #252b30; --color-console-menu-border: #424b51; diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css index 75fece1dd..01dd8ba4f 100644 --- a/web_src/css/themes/theme-gitea-light.css +++ b/web_src/css/themes/theme-gitea-light.css @@ -63,12 +63,12 @@ /* console colors - used for actions console and console files */ --color-console-fg: #f8f8f9; --color-console-fg-subtle: #bec4c8; - --color-console-bg: #181b1d; - --color-console-border: #313538; - --color-console-hover-bg: #ffffff16; - --color-console-active-bg: #313538; - --color-console-menu-bg: #272b2e; - --color-console-menu-border: #464a4d; + --color-console-bg: #171b1e; + --color-console-border: #2e353b; + --color-console-hover-bg: #292d31; + --color-console-active-bg: #2e353b; + --color-console-menu-bg: #252b30; + --color-console-menu-border: #424b51; /* named colors */ --color-red: #db2828; --color-orange: #f2711c; diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index f10694cde..378f72668 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -526,8 +526,16 @@ export function initRepositoryActionView() { .action-summary { display: flex; + flex-wrap: wrap; gap: 5px; - margin: 0 0 0 28px; + margin-left: 28px; +} + +@media (max-width: 767.98px) { + .action-commit-summary { + margin-left: 0; + margin-top: 8px; + } } /* ================ */ @@ -540,6 +548,14 @@ export function initRepositoryActionView() { top: 12px; max-height: 100vh; overflow-y: auto; + background: var(--color-body); + z-index: 2; /* above .job-info-header */ +} + +@media (max-width: 767.98px) { + .action-view-left { + position: static; /* can not sticky because multiple jobs would overlap into right view */ + } } .job-artifacts-title { @@ -701,7 +717,9 @@ export function initRepositoryActionView() { position: sticky; top: 0; height: 60px; - z-index: 1; + z-index: 1; /* above .job-step-container */ + background: var(--color-console-bg); + border-radius: 3px; } .job-info-header:has(+ .job-step-container) { @@ -739,7 +757,7 @@ export function initRepositoryActionView() { .job-step-container .job-step-summary.step-expandable:hover { color: var(--color-console-fg); - background-color: var(--color-console-hover-bg); + background: var(--color-console-hover-bg); } .job-step-container .job-step-summary .step-summary-msg { @@ -757,17 +775,15 @@ export function initRepositoryActionView() { top: 60px; } -@media (max-width: 768px) { +@media (max-width: 767.98px) { .action-view-body { flex-direction: column; } .action-view-left, .action-view-right { width: 100%; } - .action-view-left { max-width: none; - overflow-y: hidden; } } diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index f388b1122..b35502d52 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -207,13 +207,13 @@ export function initAdminCommon() { // Notice if (document.querySelector('.admin.notice')) { - const $detailModal = document.getElementById('detail-modal'); + const detailModal = document.getElementById('detail-modal'); // Attach view detail modals $('.view-detail').on('click', function () { - $detailModal.find('.content pre').text($(this).parents('tr').find('.notice-description').text()); - $detailModal.find('.sub.header').text(this.closest('tr')?.querySelector('relative-time')?.getAttribute('title')); - $detailModal.modal('show'); + const description = this.closest('tr').querySelector('.notice-description').textContent; + detailModal.querySelector('.content pre').textContent = description; + $(detailModal).modal('show'); return false; }); diff --git a/web_src/js/features/repo-graph.js b/web_src/js/features/repo-graph.js index a5b61bff5..0086b9202 100644 --- a/web_src/js/features/repo-graph.js +++ b/web_src/js/features/repo-graph.js @@ -1,14 +1,16 @@ import $ from 'jquery'; +import {hideElem, showElem} from '../utils/dom.js'; import {GET} from '../modules/fetch.js'; export function initRepoGraphGit() { const graphContainer = document.getElementById('git-graph-container'); if (!graphContainer) return; - $('#flow-color-monochrome').on('click', () => { - $('#flow-color-monochrome').addClass('active'); - $('#flow-color-colored').removeClass('active'); - $('#git-graph-container').removeClass('colored').addClass('monochrome'); + document.getElementById('flow-color-monochrome')?.addEventListener('click', () => { + document.getElementById('flow-color-monochrome').classList.add('active'); + document.getElementById('flow-color-colored')?.classList.remove('active'); + graphContainer.classList.remove('colored'); + graphContainer.classList.add('monochrome'); const params = new URLSearchParams(window.location.search); params.set('mode', 'monochrome'); const queryString = params.toString(); @@ -17,29 +19,31 @@ export function initRepoGraphGit() { } else { window.history.replaceState({}, '', window.location.pathname); } - $('.pagination a').each((_, that) => { - const href = that.getAttribute('href'); - if (!href) return; + for (const link of document.querySelectorAll('.pagination a')) { + const href = link.getAttribute('href'); + if (!href) continue; const url = new URL(href, window.location); const params = url.searchParams; params.set('mode', 'monochrome'); url.search = `?${params.toString()}`; - that.setAttribute('href', url.href); - }); + link.setAttribute('href', url.href); + } }); - $('#flow-color-colored').on('click', () => { - $('#flow-color-colored').addClass('active'); - $('#flow-color-monochrome').removeClass('active'); - $('#git-graph-container').addClass('colored').removeClass('monochrome'); - $('.pagination a').each((_, that) => { - const href = that.getAttribute('href'); - if (!href) return; + + document.getElementById('flow-color-colored')?.addEventListener('click', () => { + document.getElementById('flow-color-colored').classList.add('active'); + document.getElementById('flow-color-monochrome')?.classList.remove('active'); + graphContainer.classList.add('colored'); + graphContainer.classList.remove('monochrome'); + for (const link of document.querySelectorAll('.pagination a')) { + const href = link.getAttribute('href'); + if (!href) continue; const url = new URL(href, window.location); const params = url.searchParams; params.delete('mode'); url.search = `?${params.toString()}`; - that.setAttribute('href', url.href); - }); + link.setAttribute('href', url.href); + } const params = new URLSearchParams(window.location.search); params.delete('mode'); const queryString = params.toString(); @@ -56,20 +60,21 @@ export function initRepoGraphGit() { const ajaxUrl = new URL(url); ajaxUrl.searchParams.set('div-only', 'true'); window.history.replaceState({}, '', queryString ? `?${queryString}` : window.location.pathname); - $('#pagination').empty(); - $('#rel-container').addClass('tw-hidden'); - $('#rev-container').addClass('tw-hidden'); - $('#loading-indicator').removeClass('tw-hidden'); + document.getElementById('pagination').innerHTML = ''; + hideElem('#rel-container'); + hideElem('#rev-container'); + showElem('#loading-indicator'); (async () => { const response = await GET(String(ajaxUrl)); const html = await response.text(); - const $div = $(html); - $('#pagination').html($div.find('#pagination').html()); - $('#rel-container').html($div.find('#rel-container').html()); - $('#rev-container').html($div.find('#rev-container').html()); - $('#loading-indicator').addClass('tw-hidden'); - $('#rel-container').removeClass('tw-hidden'); - $('#rev-container').removeClass('tw-hidden'); + const div = document.createElement('div'); + div.innerHTML = html; + document.getElementById('pagination').innerHTML = div.getElementById('pagination').innerHTML; + document.getElementById('rel-container').innerHTML = div.getElementById('rel-container').innerHTML; + document.getElementById('rev-container').innerHTML = div.getElementById('rev-container').innerHTML; + hideElem('#loading-indicator'); + showElem('#rel-container'); + showElem('#rev-container'); })(); }; const dropdownSelected = params.getAll('branch'); @@ -77,8 +82,9 @@ export function initRepoGraphGit() { dropdownSelected.splice(0, 0, '...flow-hide-pr-refs'); } - $('#flow-select-refs-dropdown').dropdown('set selected', dropdownSelected); - $('#flow-select-refs-dropdown').dropdown({ + const flowSelectRefsDropdown = document.getElementById('flow-select-refs-dropdown'); + $(flowSelectRefsDropdown).dropdown('set selected', dropdownSelected); + $(flowSelectRefsDropdown).dropdown({ clearable: true, fullTextSeach: 'exact', onRemove(toRemove) { @@ -104,36 +110,46 @@ export function initRepoGraphGit() { updateGraph(); }, }); - $('#git-graph-container').on('mouseenter', '#rev-list li', (e) => { - const flow = $(e.currentTarget).data('flow'); - if (flow === 0) return; - $(`#flow-${flow}`).addClass('highlight'); - $(e.currentTarget).addClass('hover'); - $(`#rev-list li[data-flow='${flow}']`).addClass('highlight'); + + graphContainer.addEventListener('mouseenter', (e) => { + if (e.target.matches('#rev-list li')) { + const flow = e.target.getAttribute('data-flow'); + if (flow === '0') return; + document.getElementById(`flow-${flow}`)?.classList.add('highlight'); + e.target.classList.add('hover'); + for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) { + item.classList.add('highlight'); + } + } else if (e.target.matches('#rel-container .flow-group')) { + e.target.classList.add('highlight'); + const flow = e.target.getAttribute('data-flow'); + for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) { + item.classList.add('highlight'); + } + } else if (e.target.matches('#rel-container .flow-commit')) { + const rev = e.target.getAttribute('data-rev'); + document.querySelector(`#rev-list li#commit-${rev}`)?.classList.add('hover'); + } }); - $('#git-graph-container').on('mouseleave', '#rev-list li', (e) => { - const flow = $(e.currentTarget).data('flow'); - if (flow === 0) return; - $(`#flow-${flow}`).removeClass('highlight'); - $(e.currentTarget).removeClass('hover'); - $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight'); - }); - $('#git-graph-container').on('mouseenter', '#rel-container .flow-group', (e) => { - $(e.currentTarget).addClass('highlight'); - const flow = $(e.currentTarget).data('flow'); - $(`#rev-list li[data-flow='${flow}']`).addClass('highlight'); - }); - $('#git-graph-container').on('mouseleave', '#rel-container .flow-group', (e) => { - $(e.currentTarget).removeClass('highlight'); - const flow = $(e.currentTarget).data('flow'); - $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight'); - }); - $('#git-graph-container').on('mouseenter', '#rel-container .flow-commit', (e) => { - const rev = $(e.currentTarget).data('rev'); - $(`#rev-list li#commit-${rev}`).addClass('hover'); - }); - $('#git-graph-container').on('mouseleave', '#rel-container .flow-commit', (e) => { - const rev = $(e.currentTarget).data('rev'); - $(`#rev-list li#commit-${rev}`).removeClass('hover'); + + graphContainer.addEventListener('mouseleave', (e) => { + if (e.target.matches('#rev-list li')) { + const flow = e.target.getAttribute('data-flow'); + if (flow === '0') return; + document.getElementById(`flow-${flow}`)?.classList.remove('highlight'); + e.target.classList.remove('hover'); + for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) { + item.classList.remove('highlight'); + } + } else if (e.target.matches('#rel-container .flow-group')) { + e.target.classList.remove('highlight'); + const flow = e.target.getAttribute('data-flow'); + for (const item of document.querySelectorAll(`#rev-list li[data-flow='${flow}']`)) { + item.classList.remove('highlight'); + } + } else if (e.target.matches('#rel-container .flow-commit')) { + const rev = e.target.getAttribute('data-rev'); + document.querySelector(`#rev-list li#commit-${rev}`)?.classList.remove('hover'); + } }); } diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 0d326aae5..2b2eed58b 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -449,12 +449,10 @@ export function initRepoPullRequestReview() { offset += $('.diff-detail-box').outerHeight() + $(diffHeader).outerHeight(); } - document.getElementById(`show-outdated-${id}`).classList.add('tw-hidden'); - document.getElementById(`code-comments-${id}`).classList.remove('tw-hidden'); - document.getElementById(`code-preview-${id}`).classList.remove('tw-hidden'); - document.getElementById(`hide-outdated-${id}`).classList.remove('tw-hidden'); + hideElem(`#show-outdated-${id}`); + showElem(`#code-comments-${id}, #code-preview-${id}, #hide-outdated-${id}`); // if the comment box is folded, expand it - if (ancestorDiffBox.getAttribute('data-folded') === 'true') { + if (ancestorDiffBox?.getAttribute('data-folded') === 'true') { setFileFolding(ancestorDiffBox, ancestorDiffBox.querySelector('.fold-file'), false); }
- {{template "repo/latest_commit" .}} + +
+
+ {{template "repo/latest_commit" .}} +
+
{{if .LatestCommit}}{{if .LatestCommit.Committer}}{{TimeSince .LatestCommit.Committer.When ctx.Locale}}{{end}}{{end}}