diff --git a/models/git/lfs_lock.go b/models/git/lfs_lock.go index 2f65833fe3..07ce7d4abf 100644 --- a/models/git/lfs_lock.go +++ b/models/git/lfs_lock.go @@ -6,6 +6,7 @@ package git import ( "context" "errors" + "fmt" "strings" "time" @@ -21,11 +22,12 @@ import ( // LFSLock represents a git lfs lock of repository. type LFSLock struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX NOT NULL"` - OwnerID int64 `xorm:"INDEX NOT NULL"` - Path string `xorm:"TEXT"` - Created time.Time `xorm:"created"` + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX NOT NULL"` + OwnerID int64 `xorm:"INDEX NOT NULL"` + Owner *user_model.User `xorm:"-"` + Path string `xorm:"TEXT"` + Created time.Time `xorm:"created"` } func init() { @@ -37,6 +39,35 @@ func (l *LFSLock) BeforeInsert() { l.Path = util.PathJoinRel(l.Path) } +// LoadAttributes loads attributes of the lock. +func (l *LFSLock) LoadAttributes(ctx context.Context) error { + // Load owner + if err := l.LoadOwner(ctx); err != nil { + return fmt.Errorf("load owner: %w", err) + } + + return nil +} + +// LoadOwner loads owner of the lock. +func (l *LFSLock) LoadOwner(ctx context.Context) error { + if l.Owner != nil { + return nil + } + + owner, err := user_model.GetUserByID(ctx, l.OwnerID) + if err != nil { + if user_model.IsErrUserNotExist(err) { + l.Owner = user_model.NewGhostUser() + return nil + } + return err + } + l.Owner = owner + + return nil +} + // CreateLFSLock creates a new lock. func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) { dbCtx, committer, err := db.TxContext(ctx) @@ -94,7 +125,7 @@ func GetLFSLockByID(ctx context.Context, id int64) (*LFSLock, error) { } // GetLFSLockByRepoID returns a list of locks of repository. -func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) ([]*LFSLock, error) { +func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) (LFSLockList, error) { e := db.GetEngine(ctx) if page >= 0 && pageSize > 0 { start := 0 @@ -103,7 +134,7 @@ func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) ( } e.Limit(pageSize, start) } - lfsLocks := make([]*LFSLock, 0, pageSize) + lfsLocks := make(LFSLockList, 0, pageSize) return lfsLocks, e.Find(&lfsLocks, &LFSLock{RepoID: repoID}) } diff --git a/models/git/lfs_lock_list.go b/models/git/lfs_lock_list.go new file mode 100644 index 0000000000..cab1e61cab --- /dev/null +++ b/models/git/lfs_lock_list.go @@ -0,0 +1,54 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package git + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" +) + +// LFSLockList is a list of LFSLock +type LFSLockList []*LFSLock + +// LoadAttributes loads the attributes for the given locks +func (locks LFSLockList) LoadAttributes(ctx context.Context) error { + if len(locks) == 0 { + return nil + } + + if err := locks.LoadOwner(ctx); err != nil { + return fmt.Errorf("load owner: %w", err) + } + + return nil +} + +// LoadOwner loads the owner of the locks +func (locks LFSLockList) LoadOwner(ctx context.Context) error { + if len(locks) == 0 { + return nil + } + + usersIDs := container.FilterSlice(locks, func(lock *LFSLock) (int64, bool) { + return lock.OwnerID, true + }) + users := make(map[int64]*user_model.User, len(usersIDs)) + if err := db.GetEngine(ctx). + In("id", usersIDs). + Find(&users); err != nil { + return fmt.Errorf("find users: %w", err) + } + for _, v := range locks { + v.Owner = users[v.OwnerID] + if v.Owner == nil { // not exist + v.Owner = user_model.NewGhostUser() + } + } + + return nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index cca068a3a2..b30504edd7 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1891,6 +1891,7 @@ pulls.cmd_instruction_checkout_title = Checkout pulls.cmd_instruction_checkout_desc = From your project repository, check out a new branch and test the changes. pulls.cmd_instruction_merge_title = Merge pulls.cmd_instruction_merge_desc = Merge the changes and update on Gitea. +pulls.cmd_instruction_merge_warning = Warning: This operation can not merge pull request because "autodetect manual merge" was not enable pulls.clear_merge_message = Clear merge message pulls.clear_merge_message_hint = Clearing the merge message will only remove the commit message content and keep generated git trailers such as "Co-Authored-By …". @@ -2465,6 +2466,18 @@ settings.thread_id = Thread ID settings.matrix.homeserver_url = Homeserver URL settings.matrix.room_id = Room ID settings.matrix.message_type = Message Type +settings.visibility.private.button = Make Private +settings.visibility.private.text = Changing the visibility to private will not only make the repo visible to only allowed members but may remove the relation between it and forks, watchers, and stars. +settings.visibility.private.bullet_title = Changing the visibility to private will: +settings.visibility.private.bullet_one = Make the repo visible to only allowed members. +settings.visibility.private.bullet_two = May remove the relation between it and forks, watchers, and stars. +settings.visibility.public.button = Make Public +settings.visibility.public.text = Changing the visibility to public will make the repo visible to anyone. +settings.visibility.public.bullet_title= Changing the visibility to public will: +settings.visibility.public.bullet_one = Make the repo visible to anyone. +settings.visibility.success = Repository visibility changed. +settings.visibility.error = An error occurred while trying to change the repo visibility. +settings.visibility.fork_error = Can't change the visibility of a forked repo. settings.archive.button = Archive Repo settings.archive.header = Archive This Repo settings.archive.text = Archiving the repo will make it entirely read-only. It will be hidden from the dashboard. Nobody (not even you!) will be able to make new commits, or open any issues or pull requests. diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 89cb776b69..5526a00fc3 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1475,6 +1475,7 @@ issues.remove_labels=removeu os rótulos %s %s issues.add_remove_labels=adicionou o(s) rótulo(s) %s e removeu %s %s issues.add_milestone_at=`adicionou esta questão à etapa %s %s` issues.add_project_at=`adicionou esta questão ao planeamento %s %s` +issues.move_to_column_of_project=`isto foi movido para %s dentro de %s em %s` issues.change_milestone_at=`modificou a etapa de %s para %s %s` issues.change_project_at=`modificou o planeamento de %s para %s %s` issues.remove_milestone_at=`removeu esta questão da etapa %s %s` diff --git a/package-lock.json b/package-lock.json index 846cf6f838..5c56531ec0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -108,6 +108,7 @@ "stylelint-declaration-strict-value": "1.10.6", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.3.2", + "type-fest": "4.23.0", "updates": "16.3.7", "vite-string-plugin": "1.3.4", "vitest": "2.0.5" @@ -7439,6 +7440,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -12287,13 +12301,13 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.23.0.tgz", + "integrity": "sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/package.json b/package.json index 730c47f90d..d1a624f116 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "stylelint-declaration-strict-value": "1.10.6", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.3.2", + "type-fest": "4.23.0", "updates": "16.3.7", "vite-string-plugin": "1.3.4", "vitest": "2.0.5" diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 4773cc9adc..691de94290 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1869,6 +1869,8 @@ func ViewIssue(ctx *context.Context) { } prConfig := prUnit.PullRequestsConfig() + ctx.Data["AutodetectManualMerge"] = prConfig.AutodetectManualMerge + var mergeStyle repo_model.MergeStyle // Check correct values and select default if ms, ok := ctx.Data["MergeStyle"].(repo_model.MergeStyle); !ok || diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index 3b96f93ff2..fad6359668 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -95,6 +95,11 @@ func LFSLocks(ctx *context.Context) { ctx.ServerError("LFSLocks", err) return } + if err := lfsLocks.LoadAttributes(ctx); err != nil { + ctx.ServerError("LFSLocks", err) + return + } + ctx.Data["LFSLocks"] = lfsLocks if len(lfsLocks) == 0 { diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 1e0349cdee..3f9140857a 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -170,15 +170,7 @@ func SettingsPost(ctx *context.Context) { form.Private = repo.BaseRepo.IsPrivate || repo.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate } - visibilityChanged := repo.IsPrivate != form.Private - // when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public - if visibilityChanged && setting.Repository.ForcePrivate && !form.Private && !ctx.Doer.IsAdmin { - ctx.RenderWithErr(ctx.Tr("form.repository_force_private"), tplSettingsOptions, form) - return - } - - repo.IsPrivate = form.Private - if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil { + if err := repo_service.UpdateRepository(ctx, repo, false); err != nil { ctx.ServerError("UpdateRepository", err) return } @@ -940,6 +932,39 @@ func SettingsPost(ctx *context.Context) { log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name) ctx.Redirect(ctx.Repo.RepoLink + "/settings") + case "visibility": + if repo.IsFork { + ctx.Flash.Error(ctx.Tr("repo.settings.visibility.fork_error")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings") + return + } + + var err error + + // when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public + if setting.Repository.ForcePrivate && repo.IsPrivate && !ctx.Doer.IsAdmin { + ctx.RenderWithErr(ctx.Tr("form.repository_force_private"), tplSettingsOptions, form) + return + } + + if repo.IsPrivate { + err = repo_service.MakeRepoPublic(ctx, repo) + } else { + err = repo_service.MakeRepoPrivate(ctx, repo) + } + + if err != nil { + log.Error("Tried to change the visibility of the repo: %s", err) + ctx.Flash.Error(ctx.Tr("repo.settings.visibility.error")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings") + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.visibility.success")) + + log.Trace("Repository visibility changed: %s/%s", ctx.Repo.Owner.Name, repo.Name) + ctx.Redirect(ctx.Repo.RepoLink + "/settings") + default: ctx.NotFound("", nil) } diff --git a/services/repository/repository.go b/services/repository/repository.go index b7aac3cfe0..5306e7d45c 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -122,6 +122,31 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili return committer.Commit() } +func UpdateRepositoryVisibility(ctx context.Context, repo *repo_model.Repository, isPrivate bool) (err error) { + ctx, committer, err := db.TxContext(ctx) + if err != nil { + return err + } + + defer committer.Close() + + repo.IsPrivate = isPrivate + + if err = repo_module.UpdateRepository(ctx, repo, true); err != nil { + return fmt.Errorf("UpdateRepositoryVisibility: %w", err) + } + + return committer.Commit() +} + +func MakeRepoPublic(ctx context.Context, repo *repo_model.Repository) (err error) { + return UpdateRepositoryVisibility(ctx, repo, false) +} + +func MakeRepoPrivate(ctx context.Context, repo *repo_model.Repository) (err error) { + return UpdateRepositoryVisibility(ctx, repo, true) +} + // LinkedRepository returns the linked repo if any func LinkedRepository(ctx context.Context, a *repo_model.Attachment) (*repo_model.Repository, unit.Type, error) { if a.IssueID != 0 { diff --git a/templates/repo/issue/view_content/pull_merge_instruction.tmpl b/templates/repo/issue/view_content/pull_merge_instruction.tmpl index bb59b49719..9a3e2cb7d7 100644 --- a/templates/repo/issue/view_content/pull_merge_instruction.tmpl +++ b/templates/repo/issue/view_content/pull_merge_instruction.tmpl @@ -15,7 +15,13 @@
git checkout {{$localBranch}}
{{if .ShowMergeInstructions}} -

{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_title"}}

{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_desc"}}
+
+

{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_title"}}

+ {{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_desc"}} + {{if not .AutodetectManualMerge}} +
{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_warning"}}
+ {{end}} +
git checkout {{.PullRequest.BaseBranch}}
diff --git a/templates/repo/settings/lfs_locks.tmpl b/templates/repo/settings/lfs_locks.tmpl index 98f0e49097..9a18f525e9 100644 --- a/templates/repo/settings/lfs_locks.tmpl +++ b/templates/repo/settings/lfs_locks.tmpl @@ -30,9 +30,9 @@ {{end}} - - {{ctx.AvatarUtils.Avatar $.Owner}} - {{$.Owner.DisplayName}} + + {{ctx.AvatarUtils.Avatar $lock.Owner}} + {{$lock.Owner.DisplayName}} {{TimeSince .Created ctx.Locale}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 4f98133df3..f12bbbdf4a 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -23,20 +23,6 @@
- {{if not .Repository.IsFork}} -
- -
- {{if .IsAdmin}} - - {{else}} - - {{if and .Repository.IsPrivate $.ForcePrivate}}{{end}} - {{end}} - -
-
- {{end}}
@@ -786,6 +772,27 @@
+ {{if not .Repository.IsFork}} +
+
+
{{ctx.Locale.Tr "repo.visibility"}}
+ {{if .Repository.IsPrivate}} +
{{ctx.Locale.Tr "repo.settings.visibility.public.text"}}
+ {{else}} +
{{ctx.Locale.Tr "repo.settings.visibility.private.text"}}
+ {{end}} +
+
+ +
+
+ {{end}} {{if .Repository.IsMirror}}
@@ -1012,6 +1019,34 @@
+ {{if not .Repository.IsFork}} + + {{end}} + {{if .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeWiki}}