From 86bd92cd9649c8584214c234f9eec89cfb1ae7a7 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sat, 12 Jan 2019 22:36:25 +0100 Subject: [PATCH 01/89] add migrations --- models/migrations/migrations.go | 2 ++ models/migrations/v82.go | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 models/migrations/v82.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 1fde096fbeb9c..ad6e5c9a075f0 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -217,6 +217,8 @@ var migrations = []Migration{ NewMigration("add is locked to issues", addIsLockedToIssues), // v81 -> v82 NewMigration("update U2F counter type", changeU2FCounterType), + // v82 -> v83 + NewMigration("create repo transfer table", addRepoTransfer), } // Migrate database to current version diff --git a/models/migrations/v82.go b/models/migrations/v82.go new file mode 100644 index 0000000000000..38888f4e66591 --- /dev/null +++ b/models/migrations/v82.go @@ -0,0 +1,24 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "code.gitea.io/gitea/modules/util" + "github.com/go-xorm/xorm" +) + +func addRepoTransfer(x *xorm.Engine) error { + type RepoTransfer struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 + RecipientID int64 + RepoID int64 + CreatedUnix util.TimeStamp `xorm:"INDEX NOT NULL created"` + UpdatedUnix util.TimeStamp `xorm:"INDEX NOT NULL updated"` + Status bool + } + + return x.Sync(new(RepoTransfer)) +} From a904524ce4f54e88da45a766a63eb780a9e9d5fb Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sat, 12 Jan 2019 23:24:50 +0100 Subject: [PATCH 02/89] save to repo_transfer and don't actually perform a transfer just yet --- models/migrations/v78.go | 1 - models/repo_transfer.go | 169 ++++++++++++++++++++++++++++++++ options/locale/locale_en-US.ini | 1 + routers/repo/setting.go | 9 +- 4 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 models/repo_transfer.go diff --git a/models/migrations/v78.go b/models/migrations/v78.go index 382fd9e795309..a2b0a6e367499 100644 --- a/models/migrations/v78.go +++ b/models/migrations/v78.go @@ -43,7 +43,6 @@ func renameRepoIsBareToIsEmpty(x *xorm.Engine) error { if err = sess.Commit(); err != nil { return err } - if err := sess.Begin(); err != nil { return err } diff --git a/models/repo_transfer.go b/models/repo_transfer.go new file mode 100644 index 0000000000000..e1bf61d8e30c5 --- /dev/null +++ b/models/repo_transfer.go @@ -0,0 +1,169 @@ +package models + +import ( + "fmt" + "os" + + "code.gitea.io/gitea/modules/util" + "github.com/Unknwon/com" +) + +type RepoStatus uint8 + +const ( + Rejected RepoStatus = iota + Pending + Accepted +) + +type RepoTransfer struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 + RecipientID int64 + RepoID int64 + CreatedUnix util.TimeStamp `xorm:"INDEX NOT NULL created"` + UpdatedUnix util.TimeStamp `xorm:"INDEX NOT NULL updated"` + Status RepoStatus +} + +func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) error { + + newOwner, err := GetUserByName(newOwnerName) + if err != nil { + return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) + } + + // Check if new owner has repository with same name. + has, err := IsRepositoryExist(newOwner, repo.Name) + if err != nil { + return fmt.Errorf("IsRepositoryExist: %v", err) + } else if has { + return ErrRepoAlreadyExist{newOwnerName, repo.Name} + } + + transfer := &RepoTransfer{ + RepoID: repo.ID, + UserID: doer.ID, + RecipientID: newOwner.ID, + Status: Pending, + CreatedUnix: util.TimeStampNow(), + UpdatedUnix: util.TimeStampNow(), + } + + _, err = x.Insert(transfer) + return err +} + +// TransferOwnership transfers all corresponding setting from old user to new one. +func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error { + newOwner, err := GetUserByName(newOwnerName) + if err != nil { + return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) + } + + // Check if new owner has repository with same name. + has, err := IsRepositoryExist(newOwner, repo.Name) + if err != nil { + return fmt.Errorf("IsRepositoryExist: %v", err) + } else if has { + return ErrRepoAlreadyExist{newOwnerName, repo.Name} + } + + sess := x.NewSession() + defer sess.Close() + if err = sess.Begin(); err != nil { + return fmt.Errorf("sess.Begin: %v", err) + } + + owner := repo.Owner + + // Note: we have to set value here to make sure recalculate accesses is based on + // new owner. + repo.OwnerID = newOwner.ID + repo.Owner = newOwner + + // Update repository. + if _, err := sess.ID(repo.ID).Update(repo); err != nil { + return fmt.Errorf("update owner: %v", err) + } + + // Remove redundant collaborators. + collaborators, err := repo.getCollaborators(sess) + if err != nil { + return fmt.Errorf("getCollaborators: %v", err) + } + + // Dummy object. + collaboration := &Collaboration{RepoID: repo.ID} + for _, c := range collaborators { + if c.ID != newOwner.ID { + isMember, err := newOwner.IsOrgMember(c.ID) + if err != nil { + return fmt.Errorf("IsOrgMember: %v", err) + } else if !isMember { + continue + } + } + collaboration.UserID = c.ID + if _, err = sess.Delete(collaboration); err != nil { + return fmt.Errorf("remove collaborator '%d': %v", c.ID, err) + } + } + + // Remove old team-repository relations. + if owner.IsOrganization() { + if err = owner.removeOrgRepo(sess, repo.ID); err != nil { + return fmt.Errorf("removeOrgRepo: %v", err) + } + } + + if newOwner.IsOrganization() { + t, err := newOwner.getOwnerTeam(sess) + if err != nil { + return fmt.Errorf("getOwnerTeam: %v", err) + } else if err = t.addRepository(sess, repo); err != nil { + return fmt.Errorf("add to owner team: %v", err) + } + } else { + // Organization called this in addRepository method. + if err = repo.recalculateAccesses(sess); err != nil { + return fmt.Errorf("recalculateAccesses: %v", err) + } + } + + // Update repository count. + if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos+1 WHERE id=?", newOwner.ID); err != nil { + return fmt.Errorf("increase new owner repository count: %v", err) + } else if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", owner.ID); err != nil { + return fmt.Errorf("decrease old owner repository count: %v", err) + } + + if err = watchRepo(sess, doer.ID, repo.ID, true); err != nil { + return fmt.Errorf("watchRepo: %v", err) + } else if err = transferRepoAction(sess, doer, owner, repo); err != nil { + return fmt.Errorf("transferRepoAction: %v", err) + } + + // Rename remote repository to new path and delete local copy. + dir := UserPath(newOwner.Name) + + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return fmt.Errorf("Failed to create dir %s: %v", dir, err) + } + + if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { + return fmt.Errorf("rename repository directory: %v", err) + } + RemoveAllWithNotice("Delete repository local copy", repo.LocalCopyPath()) + + // Rename remote wiki repository to new path and delete local copy. + wikiPath := WikiPath(owner.Name, repo.Name) + if com.IsExist(wikiPath) { + RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath()) + if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { + return fmt.Errorf("rename repository wiki: %v", err) + } + } + + return sess.Commit() +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6662400ad98b5..82805c62a9bfc 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1117,6 +1117,7 @@ settings.update_settings_success = The repository settings have been updated. settings.transfer_owner = New Owner settings.make_transfer = Perform Transfer settings.transfer_succeed = The repository has been transferred. +settings.transfer_started = This repository has been marked for transfer and awaits confirmation from %s settings.confirm_delete = Delete Repository settings.add_collaborator = Add Collaborator settings.add_collaborator_success = The collaborator has been added. diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 853c34311564c..261ea9f8935f9 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -321,8 +321,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } - oldOwnerID := ctx.Repo.Owner.ID - if err = models.TransferOwnership(ctx.User, newOwner, repo); err != nil { + if err = models.StartRepositoryTransfer(ctx.User, newOwner, repo); err != nil { if models.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) } else { @@ -337,9 +336,9 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } - log.Trace("Repository transferred: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) - ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed")) - ctx.Redirect(setting.AppSubURL + "/" + newOwner + "/" + repo.Name) + log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) + ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner)) + ctx.Redirect(setting.AppSubURL + "/" + ctx.User.Name + "/" + repo.Name + "/settings") case "delete": if !ctx.Repo.IsOwner() { From e5fb9317a1c95a7f6e4dbff33f1f2f3f3a5d38d0 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sat, 12 Jan 2019 23:41:12 +0100 Subject: [PATCH 03/89] make sure there can only be one transfer process at any given time --- models/error.go | 17 +++++++++++++++++ models/repo_transfer.go | 16 ++++++++++++++++ options/locale/locale_en-US.ini | 1 + routers/repo/setting.go | 2 ++ 4 files changed, 36 insertions(+) diff --git a/models/error.go b/models/error.go index 6a135bda1ab48..af0acc399083f 100644 --- a/models/error.go +++ b/models/error.go @@ -639,6 +639,23 @@ func (err ErrRepoNotExist) Error() string { err.ID, err.UID, err.OwnerName, err.Name) } +// ErrRepoTransferInProgress represents the state of a repository that has an +// ongoing transfer +type ErrRepoTransferInProgress struct { + Uname string + Name string +} + +// IsErrRepoTransferInProgress checks if an error is a ErrRepoTransferInProgress. +func IsErrRepoTransferInProgress(err error) bool { + _, ok := err.(ErrRepoTransferInProgress) + return ok +} + +func (err ErrRepoTransferInProgress) Error() string { + return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name) +} + // ErrRepoAlreadyExist represents a "RepoAlreadyExist" kind of error. type ErrRepoAlreadyExist struct { Uname string diff --git a/models/repo_transfer.go b/models/repo_transfer.go index e1bf61d8e30c5..d3feddc030f02 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -27,6 +27,22 @@ type RepoTransfer struct { } func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) error { + // Make sure the repo isn't being transferred to someone currently + // Only one transfer process can be initiated at a time. + // It has to be cancelled for a new transfer to occur + + n, err := x.Count(&RepoTransfer{ + RepoID: repo.ID, + UserID: doer.ID, + }) + + if err != nil { + return err + } + + if n > 0 { + return ErrRepoTransferInProgress{newOwnerName, repo.Name} + } newOwner, err := GetUserByName(newOwnerName) if err != nil { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 82805c62a9bfc..22f5f3ee1a053 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1118,6 +1118,7 @@ settings.transfer_owner = New Owner settings.make_transfer = Perform Transfer settings.transfer_succeed = The repository has been transferred. settings.transfer_started = This repository has been marked for transfer and awaits confirmation from %s +settings.transfer_in_progress = There is currently an ongoing transfer. Please cancel it if you will like to transfer this repository to another user. settings.confirm_delete = Delete Repository settings.add_collaborator = Add Collaborator settings.add_collaborator_success = The collaborator has been added. diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 261ea9f8935f9..a401406c202bb 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -324,6 +324,8 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { if err = models.StartRepositoryTransfer(ctx.User, newOwner, repo); err != nil { if models.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) + } else if models.IsErrRepoTransferInProgress(err) { + ctx.RenderWithErr(ctx.Tr("repo.settings.transfer_in_progress"), tplSettingsOptions, nil) } else { ctx.ServerError("TransferOwnership", err) } From f08acee9b5c41bae3b96cb754c3af448635b1d58 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sat, 12 Jan 2019 23:42:56 +0100 Subject: [PATCH 04/89] add status to check to make sure we check for only 'pending' transfers --- models/repo_transfer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index d3feddc030f02..1fa0f0d435299 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -34,6 +34,7 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) n, err := x.Count(&RepoTransfer{ RepoID: repo.ID, UserID: doer.ID, + Status: Pending, }) if err != nil { From be9b779e4094a24a808ef3820d35e42c6e44a802 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sun, 13 Jan 2019 14:47:21 +0100 Subject: [PATCH 05/89] start working on cancellation of a repo transfer request --- models/error.go | 18 ++++++++++++++ models/repo_transfer.go | 37 +++++++++++++++++++++++++++- modules/context/repo.go | 20 +++++++++++++-- options/locale/locale_en-US.ini | 3 ++- templates/repo/settings/options.tmpl | 12 +++++++++ 5 files changed, 86 insertions(+), 4 deletions(-) diff --git a/models/error.go b/models/error.go index af0acc399083f..10ae5c8408211 100644 --- a/models/error.go +++ b/models/error.go @@ -639,6 +639,24 @@ func (err ErrRepoNotExist) Error() string { err.ID, err.UID, err.OwnerName, err.Name) } +// ErrNoPendingRepoTransfer is an error type for repositories without a pending +// transfer request +type ErrNoPendingRepoTransfer struct { + RepoID int64 + UserID int64 +} + +func (e ErrNoPendingRepoTransfer) Error() string { + return "" +} + +// IsErrNoPendingTransfer is an error type when a repository has no pending +// transfers +func IsErrNoPendingTransfer(err error) bool { + _, ok := err.(ErrNoPendingRepoTransfer) + return ok +} + // ErrRepoTransferInProgress represents the state of a repository that has an // ongoing transfer type ErrRepoTransferInProgress struct { diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 1fa0f0d435299..e35a1ae86e8ed 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -16,16 +16,52 @@ const ( Accepted ) +// RepoTransfer is used to manage repository transfers type RepoTransfer struct { ID int64 `xorm:"pk autoincr"` UserID int64 RecipientID int64 + Recipient *User `xorm:"-"` RepoID int64 CreatedUnix util.TimeStamp `xorm:"INDEX NOT NULL created"` UpdatedUnix util.TimeStamp `xorm:"INDEX NOT NULL updated"` Status RepoStatus } +// LoadRecipient fetches the transfer recipient from the database +func (r *RepoTransfer) LoadRecipient() error { + if r.Recipient != nil { + return nil + } + + u, err := GetUserByID(r.RecipientID) + if err != nil { + return err + } + + r.Recipient = u + return nil +} + +func GetPendingRepositoryTransfer(repo *Repository, doer *User) (*RepoTransfer, error) { + var transfer = new(RepoTransfer) + + _, err := x.Where("status = ? AND repo_id = ? AND user_id = ?", Pending, repo.ID, doer.ID). + Get(transfer) + + if err != nil { + return nil, err + } + + if transfer.ID == 0 { + return nil, ErrNoPendingRepoTransfer{RepoID: repo.ID, UserID: doer.ID} + } + + return transfer, nil +} + +// StartRepositoryTransfer marks the repository transfer as "pending". It +// doesn't actually transfer the repository until the user acks the transfer. func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) error { // Make sure the repo isn't being transferred to someone currently // Only one transfer process can be initiated at a time. @@ -36,7 +72,6 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) UserID: doer.ID, Status: Pending, }) - if err != nil { return err } diff --git a/modules/context/repo.go b/modules/context/repo.go index 8f2622fa82d60..25410a4b88942 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -19,8 +19,8 @@ import ( "code.gitea.io/gitea/modules/setting" "github.com/Unknwon/com" - "gopkg.in/editorconfig/editorconfig-core-go.v1" - "gopkg.in/macaron.v1" + editorconfig "gopkg.in/editorconfig/editorconfig-core-go.v1" + macaron "gopkg.in/macaron.v1" ) // PullRequest contains informations to make a pull request @@ -452,6 +452,22 @@ func RepoAssignment() macaron.Handler { } ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository, ctx.User) + if err == nil && repoTransfer != nil { + if err := repoTransfer.LoadRecipient(); err != nil { + ctx.ServerError("LoadRecipient", err) + return + } + + ctx.Data["RepoTransfer"] = repoTransfer + ctx.Data["IsRepoTransferInProgress"] = true + } + + if err != nil && !models.IsErrNoPendingTransfer(err) { + ctx.ServerError("GetPendingRepositoryTransfer", err) + return + } + if ctx.Query("go-get") == "1" { ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name) prefix := setting.AppURL + path.Join(owner.Name, repo.Name, "src", "branch", ctx.Repo.BranchName) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 22f5f3ee1a053..f752333ee658d 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1116,8 +1116,9 @@ settings.deletion_success = The repository has been deleted. settings.update_settings_success = The repository settings have been updated. settings.transfer_owner = New Owner settings.make_transfer = Perform Transfer +settings.abort_transfer = Cancel transfer settings.transfer_succeed = The repository has been transferred. -settings.transfer_started = This repository has been marked for transfer and awaits confirmation from %s +settings.transfer_started = This repository has been marked for transfer and awaits confirmation from "%s" settings.transfer_in_progress = There is currently an ongoing transfer. Please cancel it if you will like to transfer this repository to another user. settings.confirm_delete = Delete Repository settings.add_collaborator = Add Collaborator diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 94fbcbe651b57..f4bbc2c5db89a 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -296,11 +296,23 @@ {{end}}
+ {{ if .IsRepoTransferInProgress }} +
+ {{.CsrfTokenHtml}} + + +
+ {{ else }} + {{ end }}
{{.i18n.Tr "repo.settings.transfer"}}
+ {{if .IsRepoTransferInProgress }} +

{{.i18n.Tr "repo.settings.transfer_started" .RepoTransfer.Recipient.Name}}

+ {{else}}

{{.i18n.Tr "repo.settings.transfer_desc"}}

+ {{end}}
From 771f8146a8c842d5def1cd212da3c307f38a19ee Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sun, 13 Jan 2019 19:23:19 +0100 Subject: [PATCH 06/89] add ability to cancel an ongoing transfer --- models/repo_transfer.go | 12 ++++++++++++ options/locale/locale_en-US.ini | 2 ++ routers/repo/setting.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index e35a1ae86e8ed..5a79ea0a2947a 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -43,6 +43,8 @@ func (r *RepoTransfer) LoadRecipient() error { return nil } +// GetPendingRepositoryTransfer fetches the most recent and ongoing transfer +// process for the repository func GetPendingRepositoryTransfer(repo *Repository, doer *User) (*RepoTransfer, error) { var transfer = new(RepoTransfer) @@ -60,6 +62,16 @@ func GetPendingRepositoryTransfer(repo *Repository, doer *User) (*RepoTransfer, return transfer, nil } +// CancelRepositoryTransfer makes sure to set the transfer process as +// "rejected". Thus ending the transfer process +func CancelRepositoryTransfer(repoTransfer *RepoTransfer) error { + repoTransfer.Status = Rejected + repoTransfer.UpdatedUnix = util.TimeStampNow() + _, err := x.ID(repoTransfer.ID).Cols("updated_unix", "status"). + Update(repoTransfer) + return err +} + // StartRepositoryTransfer marks the repository transfer as "pending". It // doesn't actually transfer the repository until the user acks the transfer. func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) error { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index f752333ee658d..d01616f08914a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1117,6 +1117,8 @@ settings.update_settings_success = The repository settings have been updated. settings.transfer_owner = New Owner settings.make_transfer = Perform Transfer settings.abort_transfer = Cancel transfer +settings.abort_transfer_invalid = You cannot cancel a non existent repository transfer. +settings.abort_transfer_success = The repository transfer to %s was successfully cancelled. settings.transfer_succeed = The repository has been transferred. settings.transfer_started = This repository has been marked for transfer and awaits confirmation from "%s" settings.transfer_in_progress = There is currently an ongoing transfer. Please cancel it if you will like to transfer this repository to another user. diff --git a/routers/repo/setting.go b/routers/repo/setting.go index a401406c202bb..0b28e8fe3e6f4 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -342,6 +342,38 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner)) ctx.Redirect(setting.AppSubURL + "/" + ctx.User.Name + "/" + repo.Name + "/settings") + case "cancel_transfer": + + if !ctx.Repo.IsOwner() { + ctx.Error(404) + return + } + + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository, ctx.User) + if err != nil { + if models.IsErrNoPendingTransfer(err) { + ctx.Flash.Error("repo.settings.abort_transfer_invalid") + ctx.Redirect(setting.AppSubURL + "/" + ctx.User.Name + "/" + repo.Name + "/settings") + } else { + ctx.ServerError("GetPendingRepositoryTransfer", err) + } + + return + } + + if err := repoTransfer.LoadRecipient(); err != nil { + ctx.ServerError("LoadRecipient", err) + return + } + + if err := models.CancelRepositoryTransfer(repoTransfer); err != nil { + ctx.ServerError("CancelRepositoryTransfer", err) + return + } + + log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name) + ctx.Flash.Success(ctx.Tr("repo.settings.abort_transfer_success", repoTransfer.Recipient.Name)) + ctx.Redirect(setting.AppSubURL + "/" + ctx.User.Name + "/" + repo.Name + "/settings") case "delete": if !ctx.Repo.IsOwner() { ctx.Error(404) From ace506231511e6092b9ff92186bb048bd29a67ae Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 15 Jan 2019 12:25:29 +0100 Subject: [PATCH 07/89] little cleanup --- models/error.go | 3 +-- models/repo_transfer.go | 11 ++++------- modules/context/repo.go | 2 +- routers/repo/setting.go | 4 ++-- routers/routes/routes.go | 1 + 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/models/error.go b/models/error.go index 10ae5c8408211..68cc2240f91b9 100644 --- a/models/error.go +++ b/models/error.go @@ -643,11 +643,10 @@ func (err ErrRepoNotExist) Error() string { // transfer request type ErrNoPendingRepoTransfer struct { RepoID int64 - UserID int64 } func (e ErrNoPendingRepoTransfer) Error() string { - return "" + return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", e.RepoID) } // IsErrNoPendingTransfer is an error type when a repository has no pending diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 5a79ea0a2947a..0ed8e491b1fc5 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -19,7 +19,6 @@ const ( // RepoTransfer is used to manage repository transfers type RepoTransfer struct { ID int64 `xorm:"pk autoincr"` - UserID int64 RecipientID int64 Recipient *User `xorm:"-"` RepoID int64 @@ -45,10 +44,10 @@ func (r *RepoTransfer) LoadRecipient() error { // GetPendingRepositoryTransfer fetches the most recent and ongoing transfer // process for the repository -func GetPendingRepositoryTransfer(repo *Repository, doer *User) (*RepoTransfer, error) { +func GetPendingRepositoryTransfer(repo *Repository) (*RepoTransfer, error) { var transfer = new(RepoTransfer) - _, err := x.Where("status = ? AND repo_id = ? AND user_id = ?", Pending, repo.ID, doer.ID). + _, err := x.Where("status = ? AND repo_id = ? ", Pending, repo.ID). Get(transfer) if err != nil { @@ -56,7 +55,7 @@ func GetPendingRepositoryTransfer(repo *Repository, doer *User) (*RepoTransfer, } if transfer.ID == 0 { - return nil, ErrNoPendingRepoTransfer{RepoID: repo.ID, UserID: doer.ID} + return nil, ErrNoPendingRepoTransfer{RepoID: repo.ID} } return transfer, nil @@ -74,14 +73,13 @@ func CancelRepositoryTransfer(repoTransfer *RepoTransfer) error { // StartRepositoryTransfer marks the repository transfer as "pending". It // doesn't actually transfer the repository until the user acks the transfer. -func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) error { +func StartRepositoryTransfer(newOwnerName string, repo *Repository) error { // Make sure the repo isn't being transferred to someone currently // Only one transfer process can be initiated at a time. // It has to be cancelled for a new transfer to occur n, err := x.Count(&RepoTransfer{ RepoID: repo.ID, - UserID: doer.ID, Status: Pending, }) if err != nil { @@ -107,7 +105,6 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) transfer := &RepoTransfer{ RepoID: repo.ID, - UserID: doer.ID, RecipientID: newOwner.ID, Status: Pending, CreatedUnix: util.TimeStampNow(), diff --git a/modules/context/repo.go b/modules/context/repo.go index 25410a4b88942..efe1c5dc423fc 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -452,7 +452,7 @@ func RepoAssignment() macaron.Handler { } ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository, ctx.User) + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) if err == nil && repoTransfer != nil { if err := repoTransfer.LoadRecipient(); err != nil { ctx.ServerError("LoadRecipient", err) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 0b28e8fe3e6f4..f3175c1144a2f 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -321,7 +321,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } - if err = models.StartRepositoryTransfer(ctx.User, newOwner, repo); err != nil { + if err = models.StartRepositoryTransfer(newOwner, repo); err != nil { if models.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) } else if models.IsErrRepoTransferInProgress(err) { @@ -349,7 +349,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository, ctx.User) + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) if err != nil { if models.IsErrNoPendingTransfer(err) { ctx.Flash.Error("repo.settings.abort_transfer_invalid") diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 1e98d3216af60..47b4b03a28a7d 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -579,6 +579,7 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.RepoRef()) m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.RepoMustNotBeArchived(), repo.Action) + m.Get("/:username/:reponame/settings/transfer/acknowledge", reqSignIn, context.RepoAssignment(), repo.TransferRepo) m.Group("/:username/:reponame", func() { m.Group("/issues", func() { From ac7749163d891c443681c4732f3af7dfbe52acd0 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 16 Jan 2019 15:05:35 +0100 Subject: [PATCH 08/89] fix build --- models/repo.go | 119 --------------------------- templates/repo/settings/options.tmpl | 2 +- 2 files changed, 1 insertion(+), 120 deletions(-) diff --git a/models/repo.go b/models/repo.go index 4d7320a7890d5..7cc588d0c5080 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1453,125 +1453,6 @@ func RepoPath(userName, repoName string) string { return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".git") } -// TransferOwnership transfers all corresponding setting from old user to new one. -func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error { - newOwner, err := GetUserByName(newOwnerName) - if err != nil { - return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) - } - - // Check if new owner has repository with same name. - has, err := IsRepositoryExist(newOwner, repo.Name) - if err != nil { - return fmt.Errorf("IsRepositoryExist: %v", err) - } else if has { - return ErrRepoAlreadyExist{newOwnerName, repo.Name} - } - - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return fmt.Errorf("sess.Begin: %v", err) - } - - owner := repo.Owner - - // Note: we have to set value here to make sure recalculate accesses is based on - // new owner. - repo.OwnerID = newOwner.ID - repo.Owner = newOwner - - // Update repository. - if _, err := sess.ID(repo.ID).Update(repo); err != nil { - return fmt.Errorf("update owner: %v", err) - } - - // Remove redundant collaborators. - collaborators, err := repo.getCollaborators(sess) - if err != nil { - return fmt.Errorf("getCollaborators: %v", err) - } - - // Dummy object. - collaboration := &Collaboration{RepoID: repo.ID} - for _, c := range collaborators { - if c.ID != newOwner.ID { - isMember, err := isOrganizationMember(sess, newOwner.ID, c.ID) - if err != nil { - return fmt.Errorf("IsOrgMember: %v", err) - } else if !isMember { - continue - } - } - collaboration.UserID = c.ID - if _, err = sess.Delete(collaboration); err != nil { - return fmt.Errorf("remove collaborator '%d': %v", c.ID, err) - } - } - - // Remove old team-repository relations. - if owner.IsOrganization() { - if err = owner.removeOrgRepo(sess, repo.ID); err != nil { - return fmt.Errorf("removeOrgRepo: %v", err) - } - } - - if newOwner.IsOrganization() { - t, err := newOwner.getOwnerTeam(sess) - if err != nil { - return fmt.Errorf("getOwnerTeam: %v", err) - } else if err = t.addRepository(sess, repo); err != nil { - return fmt.Errorf("add to owner team: %v", err) - } - } else { - // Organization called this in addRepository method. - if err = repo.recalculateAccesses(sess); err != nil { - return fmt.Errorf("recalculateAccesses: %v", err) - } - } - - // Update repository count. - if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos+1 WHERE id=?", newOwner.ID); err != nil { - return fmt.Errorf("increase new owner repository count: %v", err) - } else if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", owner.ID); err != nil { - return fmt.Errorf("decrease old owner repository count: %v", err) - } - - if err = watchRepo(sess, doer.ID, repo.ID, true); err != nil { - return fmt.Errorf("watchRepo: %v", err) - } else if err = transferRepoAction(sess, doer, owner, repo); err != nil { - return fmt.Errorf("transferRepoAction: %v", err) - } - - // Rename remote repository to new path and delete local copy. - dir := UserPath(newOwner.Name) - - if err := os.MkdirAll(dir, os.ModePerm); err != nil { - return fmt.Errorf("Failed to create dir %s: %v", dir, err) - } - - if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { - return fmt.Errorf("rename repository directory: %v", err) - } - removeAllWithNotice(sess, "Delete repository local copy", repo.LocalCopyPath()) - - // Rename remote wiki repository to new path and delete local copy. - wikiPath := WikiPath(owner.Name, repo.Name) - if com.IsExist(wikiPath) { - removeAllWithNotice(sess, "Delete repository wiki local copy", repo.LocalWikiPath()) - if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { - return fmt.Errorf("rename repository wiki: %v", err) - } - } - - // If there was previously a redirect at this location, remove it. - if err = deleteRepoRedirect(sess, newOwner.ID, repo.Name); err != nil { - return fmt.Errorf("delete repo redirect: %v", err) - } - - return sess.Commit() -} - // ChangeRepositoryName changes all corresponding setting from old repository name to new one. func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error) { oldRepoName = strings.ToLower(oldRepoName) diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index f4bbc2c5db89a..99c9c120b26a6 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -301,7 +301,7 @@ {{.CsrfTokenHtml}} - + {{ else }} {{ end }} From 1c642693d8c23e7d1ba5f44dcca6777f3ecde4d2 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 16 Jan 2019 15:14:33 +0100 Subject: [PATCH 09/89] added comments --- models/repo_transfer.go | 7 +++++++ routers/repo/repo_transfer.go | 8 ++++++++ routers/routes/routes.go | 2 ++ 3 files changed, 17 insertions(+) create mode 100644 routers/repo/repo_transfer.go diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 0ed8e491b1fc5..d0d3173a76b91 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -8,11 +8,18 @@ import ( "github.com/Unknwon/com" ) +// RepoStatus determines the current state of a transfer type RepoStatus uint8 const ( + // Rejected is a status for transfers that get cancelled by either the + // recipient or the user who initiated the transfer Rejected RepoStatus = iota + // Pending is the default repo transfer state. All initiated transfers + // automatically get this status. Pending + // Accepted is a repo transfer state for repository transfers that have + // been acknowledged by the recipient Accepted ) diff --git a/routers/repo/repo_transfer.go b/routers/repo/repo_transfer.go new file mode 100644 index 0000000000000..359c70a6e3ae7 --- /dev/null +++ b/routers/repo/repo_transfer.go @@ -0,0 +1,8 @@ +package repo + +import "code.gitea.io/gitea/modules/context" + +// AcknowledgeTranfer is used to accept a repository transfer. +func AcknowledgeTranfer(ctx *context.Context) { + +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 47b4b03a28a7d..9f3ce24bbfbfd 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -581,6 +581,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.RepoMustNotBeArchived(), repo.Action) m.Get("/:username/:reponame/settings/transfer/acknowledge", reqSignIn, context.RepoAssignment(), repo.TransferRepo) + m.Get("/:username/:reponame/transfer/acknowledge", reqSignIn, context.RepoAssignment(), repo.AcknowledgeTranfer) + m.Group("/:username/:reponame", func() { m.Group("/issues", func() { m.Combo("/new").Get(context.RepoRef(), repo.NewIssue). From 25386e16aaaf845958dc834f47e36ade19451a88 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Thu, 24 Jan 2019 15:36:21 +0100 Subject: [PATCH 10/89] fix review --- models/repo_transfer.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index d0d3173a76b91..b32229b71f075 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -1,3 +1,7 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + package models import ( @@ -5,19 +9,20 @@ import ( "os" "code.gitea.io/gitea/modules/util" + "github.com/Unknwon/com" ) -// RepoStatus determines the current state of a transfer -type RepoStatus uint8 +// TransferStatus determines the current state of a transfer +type TransferStatus uint8 const ( - // Rejected is a status for transfers that get cancelled by either the - // recipient or the user who initiated the transfer - Rejected RepoStatus = iota // Pending is the default repo transfer state. All initiated transfers // automatically get this status. - Pending + Pending TransferStatus = iota + // Rejected is a status for transfers that get cancelled by either the + // recipient or the user who initiated the transfer + Rejected // Accepted is a repo transfer state for repository transfers that have // been acknowledged by the recipient Accepted @@ -31,7 +36,7 @@ type RepoTransfer struct { RepoID int64 CreatedUnix util.TimeStamp `xorm:"INDEX NOT NULL created"` UpdatedUnix util.TimeStamp `xorm:"INDEX NOT NULL updated"` - Status RepoStatus + Status TransferStatus } // LoadRecipient fetches the transfer recipient from the database From 02906e262f2580491d0b47e5e5262246f918af4d Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sat, 9 Feb 2019 17:02:44 +0100 Subject: [PATCH 11/89] complete repo transfer --- models/repo_transfer.go | 67 +++++++++++++++++++++------------ modules/context/repo.go | 4 +- options/locale/locale_en-US.ini | 1 + routers/repo/repo.go | 33 +++++++++++++--- routers/repo/repo_transfer.go | 8 ---- routers/repo/setting.go | 4 +- routers/routes/routes.go | 2 - 7 files changed, 75 insertions(+), 44 deletions(-) delete mode 100644 routers/repo/repo_transfer.go diff --git a/models/repo_transfer.go b/models/repo_transfer.go index b32229b71f075..a5ab62586b2f4 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -31,6 +31,8 @@ const ( // RepoTransfer is used to manage repository transfers type RepoTransfer struct { ID int64 `xorm:"pk autoincr"` + UserID int64 + User *User `xorm:"-"` RecipientID int64 Recipient *User `xorm:"-"` RepoID int64 @@ -39,18 +41,30 @@ type RepoTransfer struct { Status TransferStatus } -// LoadRecipient fetches the transfer recipient from the database -func (r *RepoTransfer) LoadRecipient() error { - if r.Recipient != nil { +// LoadAttributes fetches the transfer recipient from the database +func (r *RepoTransfer) LoadAttributes() error { + if r.Recipient != nil && r.User != nil { return nil } - u, err := GetUserByID(r.RecipientID) - if err != nil { - return err + if r.Recipient == nil { + u, err := GetUserByID(r.RecipientID) + if err != nil { + return err + } + + r.Recipient = u + } + + if r.User == nil { + u, err := GetUserByID(r.UserID) + if err != nil { + return err + } + + r.User = u } - r.Recipient = u return nil } @@ -59,20 +73,26 @@ func (r *RepoTransfer) LoadRecipient() error { func GetPendingRepositoryTransfer(repo *Repository) (*RepoTransfer, error) { var transfer = new(RepoTransfer) - _, err := x.Where("status = ? AND repo_id = ? ", Pending, repo.ID). + has, err := x.Where("status = ? AND repo_id = ? ", Pending, repo.ID). Get(transfer) - if err != nil { return nil, err } - if transfer.ID == 0 { + if transfer.ID == 0 || !has { return nil, ErrNoPendingRepoTransfer{RepoID: repo.ID} } return transfer, nil } +func acceptRepositoryTransfer(repo *Repository) error { + _, err := x.Where("repo_id = ?", repo.ID).Cols("status").Update(&RepoTransfer{ + Status: Accepted, + }) + return err +} + // CancelRepositoryTransfer makes sure to set the transfer process as // "rejected". Thus ending the transfer process func CancelRepositoryTransfer(repoTransfer *RepoTransfer) error { @@ -85,15 +105,12 @@ func CancelRepositoryTransfer(repoTransfer *RepoTransfer) error { // StartRepositoryTransfer marks the repository transfer as "pending". It // doesn't actually transfer the repository until the user acks the transfer. -func StartRepositoryTransfer(newOwnerName string, repo *Repository) error { +func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) error { // Make sure the repo isn't being transferred to someone currently // Only one transfer process can be initiated at a time. - // It has to be cancelled for a new transfer to occur - - n, err := x.Count(&RepoTransfer{ - RepoID: repo.ID, - Status: Pending, - }) + // It has to be cancelled for a new one to occur + n, err := x.Where("status = ? AND repo_id = ?", Pending, repo.ID). + Count(new(RepoTransfer)) if err != nil { return err } @@ -121,25 +138,23 @@ func StartRepositoryTransfer(newOwnerName string, repo *Repository) error { Status: Pending, CreatedUnix: util.TimeStampNow(), UpdatedUnix: util.TimeStampNow(), + UserID: doer.ID, } _, err = x.Insert(transfer) return err } -// TransferOwnership transfers all corresponding setting from old user to new one. -func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error { - newOwner, err := GetUserByName(newOwnerName) - if err != nil { - return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) - } +// TransferOwnership transfers all corresponding setting from one user to +// another. +func TransferOwnership(doer, newOwner *User, repo *Repository) error { // Check if new owner has repository with same name. has, err := IsRepositoryExist(newOwner, repo.Name) if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { - return ErrRepoAlreadyExist{newOwnerName, repo.Name} + return ErrRepoAlreadyExist{newOwner.Name, repo.Name} } sess := x.NewSession() @@ -160,6 +175,10 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error return fmt.Errorf("update owner: %v", err) } + if err := acceptRepositoryTransfer(repo); err != nil { + return err + } + // Remove redundant collaborators. collaborators, err := repo.getCollaborators(sess) if err != nil { diff --git a/modules/context/repo.go b/modules/context/repo.go index efe1c5dc423fc..20a1bb57c2349 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -453,8 +453,8 @@ func RepoAssignment() macaron.Handler { ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) - if err == nil && repoTransfer != nil { - if err := repoTransfer.LoadRecipient(); err != nil { + if err == nil { + if err := repoTransfer.LoadAttributes(); err != nil { ctx.ServerError("LoadRecipient", err) return } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d01616f08914a..a79fed1a533d3 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1102,6 +1102,7 @@ settings.transfer_desc = Transfer this repository to a user or to an organizatio settings.transfer_notices_1 = - You will lose access to the repository if you transfer it to an individual user. settings.transfer_notices_2 = - You will keep access to the repository if you transfer it to an organization that you (co-)own. settings.transfer_form_title = Enter the repository name as confirmation: +settings.transfer.success = Repository transfer was successful. settings.wiki_delete = Delete Wiki Data settings.wiki_delete_desc = Deleting repository wiki data is permanent and cannot be undone. settings.wiki_delete_notices_1 = - This will permanently delete and disable the repository wiki for %s. diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 42dfd56268686..dc6252b8177a9 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -296,15 +296,36 @@ func Action(ctx *context.Context) { err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) case "unstar": err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) - case "desc": // FIXME: this is not used - if !ctx.Repo.IsOwner() { - ctx.Error(404) + case "acknowledge_transfer": + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) + if err != nil { + ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + } + + if repoTransfer.RecipientID != ctx.User.ID { + ctx.Status(404) return } - ctx.Repo.Repository.Description = ctx.Query("desc") - ctx.Repo.Repository.Website = ctx.Query("site") - err = models.UpdateRepository(ctx.Repo.Repository, false) + if err := repoTransfer.LoadAttributes(); err != nil { + ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + return + } + + if err := models.TransferOwnership(repoTransfer.User, repoTransfer.Recipient, ctx.Repo.Repository); err != nil { + ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + return + } + + repo, err := models.GetRepositoryByID(ctx.Repo.Repository.ID) + if err != nil { + ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success")) + ctx.Redirect(repo.HTMLURL()) + return } if err != nil { diff --git a/routers/repo/repo_transfer.go b/routers/repo/repo_transfer.go deleted file mode 100644 index 359c70a6e3ae7..0000000000000 --- a/routers/repo/repo_transfer.go +++ /dev/null @@ -1,8 +0,0 @@ -package repo - -import "code.gitea.io/gitea/modules/context" - -// AcknowledgeTranfer is used to accept a repository transfer. -func AcknowledgeTranfer(ctx *context.Context) { - -} diff --git a/routers/repo/setting.go b/routers/repo/setting.go index f3175c1144a2f..a82d834bd88a4 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -321,7 +321,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } - if err = models.StartRepositoryTransfer(newOwner, repo); err != nil { + if err = models.StartRepositoryTransfer(ctx.User, newOwner, repo); err != nil { if models.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) } else if models.IsErrRepoTransferInProgress(err) { @@ -361,7 +361,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } - if err := repoTransfer.LoadRecipient(); err != nil { + if err := repoTransfer.LoadAttributes(); err != nil { ctx.ServerError("LoadRecipient", err) return } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 9f3ce24bbfbfd..47b4b03a28a7d 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -581,8 +581,6 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.RepoMustNotBeArchived(), repo.Action) m.Get("/:username/:reponame/settings/transfer/acknowledge", reqSignIn, context.RepoAssignment(), repo.TransferRepo) - m.Get("/:username/:reponame/transfer/acknowledge", reqSignIn, context.RepoAssignment(), repo.AcknowledgeTranfer) - m.Group("/:username/:reponame", func() { m.Group("/issues", func() { m.Combo("/new").Get(context.RepoRef(), repo.NewIssue). From a7f76623019bcad398792672e546de0d586eae25 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sun, 24 Mar 2019 19:33:46 +0100 Subject: [PATCH 12/89] start work on email notification --- models/repo_transfer.go | 26 ++++++++++++++++++++++++ routers/repo/setting.go | 23 +++++++++++---------- routers/routes/routes.go | 2 +- templates/mail/notify/repo_transfer.tmpl | 17 ++++++++++++++++ 4 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 templates/mail/notify/repo_transfer.tmpl diff --git a/models/repo_transfer.go b/models/repo_transfer.go index a5ab62586b2f4..7d649aefa0cfd 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -5,10 +5,14 @@ package models import ( + "bytes" "fmt" "os" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/mailer" "code.gitea.io/gitea/modules/util" + "gopkg.in/macaron.v1" "github.com/Unknwon/com" ) @@ -145,6 +149,28 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) return err } +// SendRepoTransferNotifyMail triggers a notification e-mail when a repository +// transfer is initiated +func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { + data := map[string]interface{}{ + "Subject": c.Tr("mail.repo_transfer_notify"), + "RepoName": repo.FullName(), + "Link": repo.HTMLURL(), + } + + var content bytes.Buffer + + if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil { + log.Error(3, "Template: %v", err) + return + } + + msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.repo_transfer_notify"), content.String()) + msg.Info = fmt.Sprintf("UID: %d, repository transfer notification", u.ID) + + mailer.SendAsync(msg) +} + // TransferOwnership transfers all corresponding setting from one user to // another. func TransferOwnership(doer, newOwner *User, repo *Repository) error { diff --git a/routers/repo/setting.go b/routers/repo/setting.go index a82d834bd88a4..1cb582c0558ed 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -7,6 +7,7 @@ package repo import ( "errors" + "net/http" "strings" "time" @@ -303,7 +304,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { case "transfer": if !ctx.Repo.IsOwner() { - ctx.Error(404) + ctx.Error(http.StatusUnauthorized) return } if repo.Name != form.RepoName { @@ -312,13 +313,13 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { } newOwner := ctx.Query("new_owner_name") - isExist, err := models.IsUserExist(0, newOwner) - if err != nil { - ctx.ServerError("IsUserExist", err) - return - } else if !isExist { + u, err := models.GetUserByName(newOwner) + if models.IsErrUserNotExist(err) { ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil) return + } else if err != nil { + ctx.ServerError("GetUserByName", err) + return } if err = models.StartRepositoryTransfer(ctx.User, newOwner, repo); err != nil { @@ -329,13 +330,12 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { } else { ctx.ServerError("TransferOwnership", err) } + return } - err = models.NewRepoRedirect(oldOwnerID, repo.ID, repo.Name, repo.Name) - if err != nil { - ctx.ServerError("NewRepoRedirect", err) - return + if setting.Service.EnableNotifyMail { + models.SendRepoTransferNotifyMail(ctx.Context, u, ctx.Repo.Repository) } log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) @@ -345,7 +345,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { case "cancel_transfer": if !ctx.Repo.IsOwner() { - ctx.Error(404) + ctx.Error(http.StatusUnauthorized) return } @@ -374,6 +374,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name) ctx.Flash.Success(ctx.Tr("repo.settings.abort_transfer_success", repoTransfer.Recipient.Name)) ctx.Redirect(setting.AppSubURL + "/" + ctx.User.Name + "/" + repo.Name + "/settings") + case "delete": if !ctx.Repo.IsOwner() { ctx.Error(404) diff --git a/routers/routes/routes.go b/routers/routes/routes.go index b39c6104a1e87..f0440668459af 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -596,7 +596,7 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.RepoRef()) m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.RepoMustNotBeArchived(), repo.Action) - m.Get("/:username/:reponame/settings/transfer/acknowledge", reqSignIn, context.RepoAssignment(), repo.TransferRepo) + // m.Get("/:username/:reponame/settings/transfer/acknowledge", reqSignIn, context.RepoAssignment(), repo.TransferRepo) m.Group("/:username/:reponame", func() { m.Group("/issues", func() { diff --git a/templates/mail/notify/repo_transfer.tmpl b/templates/mail/notify/repo_transfer.tmpl new file mode 100644 index 0000000000000..37bb764655825 --- /dev/null +++ b/templates/mail/notify/repo_transfer.tmpl @@ -0,0 +1,17 @@ + + + + + {{.Subject}} + + + +

You have been added as an owner of the repository: + {{.RepoName}}. Please click here to accept the invitation

+

+ --- +
+ View it on Gitea. +

+ + From ec154fdbcf6c4cb2668aadd007663eae06e992d3 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 26 Mar 2019 22:15:18 +0100 Subject: [PATCH 13/89] cannot transfer an archived repo --- options/locale/locale_en-US.ini | 1 + routers/repo/setting.go | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0baba42893a22..d4b4d377c0ddc 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -578,6 +578,7 @@ reactions_more = and %d more archive.title = This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests. archive.issue.nocomment = This repo is archived. You cannot comment on issues. archive.pull.nocomment = This repo is archived. You cannot comment on pull requests. +archive.repo_transfer = This repo is archived. You cannot transfer this repo until it is un-archived form.reach_limit_of_creation = You have already reached your limit of %d repositories. form.name_reserved = The repository name '%s' is reserved. diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 1cb582c0558ed..9295a28ca8320 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -307,6 +307,13 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { ctx.Error(http.StatusUnauthorized) return } + + if ctx.Repo.Repository.IsArchived { + ctx.Flash.Error(ctx.Tr("repo.archive.repo_transfer")) + ctx.Redirect(setting.AppSubURL + "/" + ctx.Repo.Owner.Name + "/" + repo.Name + "/settings") + return + } + if repo.Name != form.RepoName { ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) return From ec3ece4a18d80823249c089b1fb3c1aeef15569a Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 26 Mar 2019 22:23:05 +0100 Subject: [PATCH 14/89] fix redirection --- routers/repo/setting.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 9295a28ca8320..206dd867f3891 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -347,7 +347,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner)) - ctx.Redirect(setting.AppSubURL + "/" + ctx.User.Name + "/" + repo.Name + "/settings") + ctx.Redirect(setting.AppSubURL + "/" + ctx.Repo.Owner.Name + "/" + repo.Name + "/settings") case "cancel_transfer": @@ -380,7 +380,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name) ctx.Flash.Success(ctx.Tr("repo.settings.abort_transfer_success", repoTransfer.Recipient.Name)) - ctx.Redirect(setting.AppSubURL + "/" + ctx.User.Name + "/" + repo.Name + "/settings") + ctx.Redirect(setting.AppSubURL + "/" + ctx.Repo.Owner.Name + "/" + repo.Name + "/settings") case "delete": if !ctx.Repo.IsOwner() { From b515fd6da214bfc7eb712004b11a70939a3ca31f Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 26 Mar 2019 23:14:50 +0100 Subject: [PATCH 15/89] send email to user --- models/mail.go | 1 + models/repo_transfer.go | 10 ++++++---- options/locale/locale_en-US.ini | 1 + routers/routes/routes.go | 3 ++- templates/mail/notify/repo_transfer.tmpl | 5 +++-- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/models/mail.go b/models/mail.go index 7e6d4e0265127..67187224247f5 100644 --- a/models/mail.go +++ b/models/mail.go @@ -30,6 +30,7 @@ const ( mailIssueMention base.TplName = "issue/mention" mailNotifyCollaborator base.TplName = "notify/collaborator" + mailRepoTransferNotify = "notify/repo_transfer" ) var templates *template.Template diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 7d649aefa0cfd..59ced7bfef3ce 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -153,14 +153,16 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) // transfer is initiated func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { data := map[string]interface{}{ - "Subject": c.Tr("mail.repo_transfer_notify"), - "RepoName": repo.FullName(), - "Link": repo.HTMLURL(), + "Subject": c.Tr("mail.repo_transfer_notify"), + "RepoName": repo.FullName(), + "Link": repo.HTMLURL(), + "AcceptTransferLink": repo.HTMLURL() + "/settings/transfer/accept", + "DeclineTransferLink": repo.HTMLURL() + "/settings/transfer/decline", } var content bytes.Buffer - if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil { + if err := templates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil { log.Error(3, "Template: %v", err) return } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d4b4d377c0ddc..de22a037a4ff8 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -257,6 +257,7 @@ activate_email = Verify your email address reset_password = Reset your password register_success = Registration successful register_notify = Welcome to Gitea +repo_transfer_notify = A repository has been transferred to you [modal] yes = Yes diff --git a/routers/routes/routes.go b/routers/routes/routes.go index f0440668459af..5b2d344b9b91f 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -596,7 +596,8 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.RepoRef()) m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.RepoMustNotBeArchived(), repo.Action) - // m.Get("/:username/:reponame/settings/transfer/acknowledge", reqSignIn, context.RepoAssignment(), repo.TransferRepo) + m.Get("/:username/:reponame/settings/transfer/:operation", + reqSignIn, context.RepoAssignment(), context.RepoMustNotBeArchived(), repo.TransferRepo) m.Group("/:username/:reponame", func() { m.Group("/issues", func() { diff --git a/templates/mail/notify/repo_transfer.tmpl b/templates/mail/notify/repo_transfer.tmpl index 37bb764655825..038afe2377823 100644 --- a/templates/mail/notify/repo_transfer.tmpl +++ b/templates/mail/notify/repo_transfer.tmpl @@ -6,8 +6,9 @@ -

You have been added as an owner of the repository: - {{.RepoName}}. Please click here to accept the invitation

+

You have been invited as an owner of the repository, {{.RepoName}}. + Please click here to accept the invitation or + here to reject the invitation

---
From 79f9952b5e2437d842d8fef658c08af43ec60b03 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 26 Mar 2019 23:47:51 +0100 Subject: [PATCH 16/89] transfer repository acknowledgement --- models/repo_transfer.go | 4 ++-- routers/repo/repo.go | 6 ++++-- routers/routes/routes.go | 5 ++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 59ced7bfef3ce..7a10ab56a6a1c 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -156,8 +156,8 @@ func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { "Subject": c.Tr("mail.repo_transfer_notify"), "RepoName": repo.FullName(), "Link": repo.HTMLURL(), - "AcceptTransferLink": repo.HTMLURL() + "/settings/transfer/accept", - "DeclineTransferLink": repo.HTMLURL() + "/settings/transfer/decline", + "AcceptTransferLink": repo.HTMLURL() + "/action/accept_transfer", + "DeclineTransferLink": repo.HTMLURL() + "/settings/decline_transfer", } var content bytes.Buffer diff --git a/routers/repo/repo.go b/routers/repo/repo.go index dc6252b8177a9..33430d6a97f78 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -5,6 +5,7 @@ package repo import ( + "errors" "fmt" "os" "path" @@ -296,14 +297,15 @@ func Action(ctx *context.Context) { err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) case "unstar": err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) - case "acknowledge_transfer": + case "accept_transfer": repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) if err != nil { ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + return } if repoTransfer.RecipientID != ctx.User.ID { - ctx.Status(404) + ctx.NotFound("RecipientID", errors.New("User IDs don't match")) return } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 5b2d344b9b91f..cb5195f546978 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -595,9 +595,8 @@ func RegisterRoutes(m *macaron.Macaron) { }) }, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.RepoRef()) - m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.RepoMustNotBeArchived(), repo.Action) - m.Get("/:username/:reponame/settings/transfer/:operation", - reqSignIn, context.RepoAssignment(), context.RepoMustNotBeArchived(), repo.TransferRepo) + m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), + context.UnitTypes(), context.RepoMustNotBeArchived(), repo.Action) m.Group("/:username/:reponame", func() { m.Group("/issues", func() { From 13984c2d8a770efbeb7aa295a0328b8c10d2fafe Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 27 Mar 2019 00:03:47 +0100 Subject: [PATCH 17/89] use member of owner team email address for repo transfers to an organization --- models/repo_transfer.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 7a10ab56a6a1c..535f575c869cc 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -167,7 +167,25 @@ func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { return } - msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.repo_transfer_notify"), content.String()) + var email = u.Email + + if u.IsOrganization() && u.Email == "" { + t, err := u.getOwnerTeam(x) + if err != nil { + log.Error(3, "Could not retrieve owners team for organization", err) + return + } + + if err := t.GetMembers(); err != nil { + log.Error(3, "Could not retrieve members of the owners team", err) + return + } + + // Just use the email address of the first user + email = t.Members[0].Email + } + + msg := mailer.NewMessage([]string{email}, c.Tr("mail.repo_transfer_notify"), content.String()) msg.Info = fmt.Sprintf("UID: %d, repository transfer notification", u.ID) mailer.SendAsync(msg) From 549aa0cd22c54802d508eee46969d51ba83f9987 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 27 Mar 2019 00:28:50 +0100 Subject: [PATCH 18/89] implement rejection of repo transfer --- models/repo_transfer.go | 31 +++++++++++++++++++++++ options/locale/locale_en-US.ini | 1 + routers/repo/repo.go | 44 +++++++++++++++++++++++++++++---- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 535f575c869cc..8f1a283be56e1 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -72,6 +72,37 @@ func (r *RepoTransfer) LoadAttributes() error { return nil } +// IsTransferForUser checks if the user has the rights to accept/decline a repo +// transfer. +// For organizations, this check if the user is a member of the owners team +func (r *RepoTransfer) IsTransferForUser(u *User) bool { + if err := r.LoadAttributes(); err != nil { + return false + } + + if !r.Recipient.IsOrganization() { + return r.RecipientID == u.ID + } + + t, err := r.Recipient.getOwnerTeam(x) + if err != nil { + return false + } + + if err := t.GetMembers(); err != nil { + return false + } + + for k := range t.Members { + fmt.Println(t.Members[k].ID, u.ID) + if t.Members[k].ID == u.ID { + return true + } + } + + return false +} + // GetPendingRepositoryTransfer fetches the most recent and ongoing transfer // process for the repository func GetPendingRepositoryTransfer(repo *Repository) (*RepoTransfer, error) { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index de22a037a4ff8..f01b1d4cd6e45 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1106,6 +1106,7 @@ settings.transfer_notices_1 = - You will lose access to the repository if you tr settings.transfer_notices_2 = - You will keep access to the repository if you transfer it to an organization that you (co-)own. settings.transfer_form_title = Enter the repository name as confirmation: settings.transfer.success = Repository transfer was successful. +settings.transfer.rejected = Repository transfer was rejected. settings.wiki_delete = Delete Wiki Data settings.wiki_delete_desc = Deleting repository wiki data is permanent and cannot be undone. settings.wiki_delete_notices_1 = - This will permanently delete and disable the repository wiki for %s. diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 33430d6a97f78..3d797ef607e7b 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -300,17 +300,21 @@ func Action(ctx *context.Context) { case "accept_transfer": repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) if err != nil { - ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + if models.IsErrNoPendingTransfer(err) { + ctx.Redirect(ctx.Repo.Repository.HTMLURL()) + } else { + ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + } return } - if repoTransfer.RecipientID != ctx.User.ID { - ctx.NotFound("RecipientID", errors.New("User IDs don't match")) + if err := repoTransfer.LoadAttributes(); err != nil { + ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) return } - if err := repoTransfer.LoadAttributes(); err != nil { - ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + if !repoTransfer.IsTransferForUser(ctx.User) { + ctx.NotFound("IsTransferForUser", errors.New("user does not have enough permissions")) return } @@ -328,6 +332,36 @@ func Action(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success")) ctx.Redirect(repo.HTMLURL()) return + + case "decline_transfer": + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) + if err != nil { + ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + return + } + + if err := repoTransfer.LoadAttributes(); err != nil { + ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + return + } + + if !repoTransfer.IsTransferForUser(ctx.User) { + ctx.NotFound("IsTransferForUser", errors.New("user does not have enough permissions")) + return + } + + if err := models.CancelRepositoryTransfer(repoTransfer); err != nil { + if models.IsErrNoPendingTransfer(err) { + ctx.Redirect(ctx.Repo.Repository.HTMLURL()) + } else { + ctx.ServerError("CancelRepositoryTransfer", err) + } + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected")) + ctx.Redirect(setting.AppSubURL + "/") + return } if err != nil { From d749dda3870da7f238ceb171dff270a096e7c0f3 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 27 Mar 2019 00:31:29 +0100 Subject: [PATCH 19/89] fix link --- models/repo_transfer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 8f1a283be56e1..0ce3fc88abaa9 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -188,7 +188,7 @@ func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { "RepoName": repo.FullName(), "Link": repo.HTMLURL(), "AcceptTransferLink": repo.HTMLURL() + "/action/accept_transfer", - "DeclineTransferLink": repo.HTMLURL() + "/settings/decline_transfer", + "DeclineTransferLink": repo.HTMLURL() + "/action/decline_transfer", } var content bytes.Buffer From 5b1b4c4fc67f180c3505223fd61d30001775d778 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 27 Mar 2019 01:18:15 +0100 Subject: [PATCH 20/89] try to fix tests --- models/fixtures/repo_transfer.yml | 8 ++++++++ models/repo_test.go | 9 +++++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 models/fixtures/repo_transfer.yml diff --git a/models/fixtures/repo_transfer.yml b/models/fixtures/repo_transfer.yml new file mode 100644 index 0000000000000..ddc40c1ca2f50 --- /dev/null +++ b/models/fixtures/repo_transfer.yml @@ -0,0 +1,8 @@ +- + id: 1 + user_id: 10 + recipient_id: 9 + status: 1 + repo_id: 10 + created_unix: 1553610671 + updated_unix: 1553610671 diff --git a/models/repo_test.go b/models/repo_test.go index 752ffc2dd9085..603eaa0f971da 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -157,10 +157,11 @@ func TestRepoLocalCopyPath(t *testing.T) { func TestTransferOwnership(t *testing.T) { assert.NoError(t, PrepareTestDatabase()) - doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + doer := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) + newOwner := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) repo.Owner = AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) - assert.NoError(t, TransferOwnership(doer, "user2", repo)) + assert.NoError(t, TransferOwnership(doer, newOwner, repo)) transferredRepo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) assert.EqualValues(t, 2, transferredRepo.OwnerID) @@ -169,9 +170,9 @@ func TestTransferOwnership(t *testing.T) { assert.True(t, com.IsExist(RepoPath("user2", "repo3"))) AssertExistsAndLoadBean(t, &Action{ OpType: ActionTransferRepo, - ActUserID: 2, + ActUserID: 3, RepoID: 3, - Content: "user3/repo3", + Content: "user2/repo3", }) CheckConsistencyFor(t, &Repository{}, &User{}, &Team{}) From a29a22e762df5ae07c02e6935c5ff08e8096222a Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 27 Mar 2019 18:23:54 +0100 Subject: [PATCH 21/89] tests for starting and cancelling a repo transfer --- models/fixtures/repo_transfer.yml | 6 ++--- models/models.go | 1 + models/repo_test.go | 44 ++++++++++++++++++------------- models/repo_transfer.go | 8 +++--- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/models/fixtures/repo_transfer.yml b/models/fixtures/repo_transfer.yml index ddc40c1ca2f50..5d8a5bd79c069 100644 --- a/models/fixtures/repo_transfer.yml +++ b/models/fixtures/repo_transfer.yml @@ -1,8 +1,8 @@ - id: 1 - user_id: 10 - recipient_id: 9 + user_id: 3 + recipient_id: 1 status: 1 - repo_id: 10 + repo_id: 3 created_unix: 1553610671 updated_unix: 1553610671 diff --git a/models/models.go b/models/models.go index e7ecc67fc5538..22bf0e33f39c8 100644 --- a/models/models.go +++ b/models/models.go @@ -129,6 +129,7 @@ func init() { new(OAuth2Application), new(OAuth2AuthorizationCode), new(OAuth2Grant), + new(RepoTransfer), ) gonicNames := []string{"SSL", "UID"} diff --git a/models/repo_test.go b/models/repo_test.go index 603eaa0f971da..46243674cdf15 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -154,26 +154,34 @@ func TestRepoLocalCopyPath(t *testing.T) { assert.Equal(t, expected, repo.LocalCopyPath()) } -func TestTransferOwnership(t *testing.T) { +func TestRepositoryTransfer(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) doer := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) - newOwner := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) - repo.Owner = AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) - assert.NoError(t, TransferOwnership(doer, newOwner, repo)) - - transferredRepo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) - assert.EqualValues(t, 2, transferredRepo.OwnerID) - - assert.False(t, com.IsExist(RepoPath("user3", "repo3"))) - assert.True(t, com.IsExist(RepoPath("user2", "repo3"))) - AssertExistsAndLoadBean(t, &Action{ - OpType: ActionTransferRepo, - ActUserID: 3, - RepoID: 3, - Content: "user2/repo3", - }) - - CheckConsistencyFor(t, &Repository{}, &User{}, &Team{}) + + transfer, err := GetPendingRepositoryTransfer(repo) + assert.Error(t, err) + assert.Nil(t, transfer) + assert.True(t, IsErrNoPendingTransfer(err)) + + assert.NoError(t, StartRepositoryTransfer(doer, "user2", repo)) + + transfer, err = GetPendingRepositoryTransfer(repo) + assert.Nil(t, err) + assert.NoError(t, transfer.LoadAttributes()) + assert.Equal(t, "user2", transfer.Recipient.Name) + + // Only transfer can be started at any given time + err = StartRepositoryTransfer(doer, "user6", repo) + assert.Error(t, err) + assert.True(t, IsErrRepoTransferInProgress(err)) + + // Unkown user + err = StartRepositoryTransfer(doer, "user1000", repo) + assert.Error(t, err) + + // Cancel transfer + assert.NoError(t, CancelRepositoryTransfer(transfer)) } diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 0ce3fc88abaa9..6d7cc475d29a2 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -15,6 +15,7 @@ import ( "gopkg.in/macaron.v1" "github.com/Unknwon/com" + "github.com/go-xorm/xorm" ) // TransferStatus determines the current state of a transfer @@ -94,7 +95,6 @@ func (r *RepoTransfer) IsTransferForUser(u *User) bool { } for k := range t.Members { - fmt.Println(t.Members[k].ID, u.ID) if t.Members[k].ID == u.ID { return true } @@ -121,8 +121,8 @@ func GetPendingRepositoryTransfer(repo *Repository) (*RepoTransfer, error) { return transfer, nil } -func acceptRepositoryTransfer(repo *Repository) error { - _, err := x.Where("repo_id = ?", repo.ID).Cols("status").Update(&RepoTransfer{ +func acceptRepositoryTransfer(sess *xorm.Session, repo *Repository) error { + _, err := sess.Where("repo_id = ?", repo.ID).Cols("status").Update(&RepoTransfer{ Status: Accepted, }) return err @@ -252,7 +252,7 @@ func TransferOwnership(doer, newOwner *User, repo *Repository) error { return fmt.Errorf("update owner: %v", err) } - if err := acceptRepositoryTransfer(repo); err != nil { + if err := acceptRepositoryTransfer(sess, repo); err != nil { return err } From 7d701c1988ea2d8e07018ec172b4a47c1dc695d0 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 27 Mar 2019 19:44:24 +0100 Subject: [PATCH 22/89] fix mispellings --- models/repo_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/repo_test.go b/models/repo_test.go index 46243674cdf15..f899a5c0e4ee1 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -178,7 +178,7 @@ func TestRepositoryTransfer(t *testing.T) { assert.Error(t, err) assert.True(t, IsErrRepoTransferInProgress(err)) - // Unkown user + // Unknown user err = StartRepositoryTransfer(doer, "user1000", repo) assert.Error(t, err) From a155de786ba360ca12982f8bb874553e103a8f1c Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 27 Mar 2019 20:02:07 +0100 Subject: [PATCH 23/89] add repo redirect after a successful repo transfer --- routers/repo/repo.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 3d797ef607e7b..79375e7d15b17 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -323,14 +323,14 @@ func Action(ctx *context.Context) { return } - repo, err := models.GetRepositoryByID(ctx.Repo.Repository.ID) + err = models.NewRepoRedirect(repoTransfer.User.ID, ctx.Repo.Repository.ID, ctx.Repo.Repository.Name, ctx.Repo.Repository.Name) if err != nil { - ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) + ctx.ServerError("NewRepoRedirect", err) return } ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success")) - ctx.Redirect(repo.HTMLURL()) + ctx.Redirect(ctx.Repo.Repository.HTMLURL()) return case "decline_transfer": From 008f94276dd6ffca2febe0f2dfb3cf671304c23c Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 27 Mar 2019 21:12:59 +0100 Subject: [PATCH 24/89] use 404 instead of unauthorized --- routers/repo/setting.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 218c7b7e4fc10..02ee7989e8944 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -7,7 +7,6 @@ package repo import ( "errors" - "net/http" "strings" "time" @@ -314,7 +313,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { case "transfer": if !ctx.Repo.IsOwner() { - ctx.Error(http.StatusUnauthorized) + ctx.Error(404) return } @@ -362,7 +361,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { case "cancel_transfer": if !ctx.Repo.IsOwner() { - ctx.Error(http.StatusUnauthorized) + ctx.Error(404) return } From 997d530a4321d18d78670151c709e060c044f2bb Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 17 Apr 2019 17:49:29 +0100 Subject: [PATCH 25/89] fix merge conflicts --- models/migrations/v83.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/models/migrations/v83.go b/models/migrations/v83.go index 286064bde43c6..947645153cb25 100644 --- a/models/migrations/v83.go +++ b/models/migrations/v83.go @@ -10,20 +10,6 @@ import ( "github.com/go-xorm/xorm" ) -<<<<<<< HEAD -func addRepoTransfer(x *xorm.Engine) error { - type RepoTransfer struct { - ID int64 `xorm:"pk autoincr"` - UserID int64 - RecipientID int64 - RepoID int64 - CreatedUnix util.TimeStamp `xorm:"INDEX NOT NULL created"` - UpdatedUnix util.TimeStamp `xorm:"INDEX NOT NULL updated"` - Status bool - } - - return x.Sync(new(RepoTransfer)) -======= func addUploaderIDForAttachment(x *xorm.Engine) error { type Attachment struct { ID int64 `xorm:"pk autoincr"` @@ -39,5 +25,4 @@ func addUploaderIDForAttachment(x *xorm.Engine) error { } return x.Sync2(new(Attachment)) ->>>>>>> origin/master } From 69a62f5d473a2f0097f77d9f3effcf3a1ad7f564 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Wed, 17 Apr 2019 17:58:38 +0100 Subject: [PATCH 26/89] fix build issues --- models/repo_transfer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 6d7cc475d29a2..0f0555de8b9d4 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -194,7 +194,7 @@ func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { var content bytes.Buffer if err := templates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil { - log.Error(3, "Template: %v", err) + log.Error("Template: %v", err) return } @@ -203,12 +203,12 @@ func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { if u.IsOrganization() && u.Email == "" { t, err := u.getOwnerTeam(x) if err != nil { - log.Error(3, "Could not retrieve owners team for organization", err) + log.Error("Could not retrieve owners team for organization", err) return } if err := t.GetMembers(); err != nil { - log.Error(3, "Could not retrieve members of the owners team", err) + log.Error("Could not retrieve members of the owners team", err) return } From b192f0317d380f3c05de499ca6d2876cb9369fa1 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Fri, 19 Apr 2019 18:55:23 +0100 Subject: [PATCH 27/89] write notifications --- models/notification.go | 26 +++++++++++++++++++ modules/notification/base/notifier.go | 1 + modules/notification/base/null.go | 2 ++ modules/notification/notification.go | 7 +++++ modules/notification/ui/ui.go | 22 +++++++++++++++- routers/repo/setting.go | 3 +++ templates/user/notification/notification.tmpl | 7 +++++ 7 files changed, 67 insertions(+), 1 deletion(-) diff --git a/models/notification.go b/models/notification.go index cda2916faeb1d..aea2ce63f7f14 100644 --- a/models/notification.go +++ b/models/notification.go @@ -33,6 +33,9 @@ const ( NotificationSourcePullRequest // NotificationSourceCommit is a notification of a commit NotificationSourceCommit + // NotificationSourceRepoTransfer is a notification for a repository + // transfer + NotificationSourceRepoTransfer ) // Notification represents a notification @@ -56,6 +59,29 @@ type Notification struct { UpdatedUnix util.TimeStamp `xorm:"updated INDEX NOT NULL"` } +// CreateRepoTransferNotification creates notification for the user a repository was transferred to +func CreateRepoTransferNotification(doerID, recipientID int64, repo *Repository) error { + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + notification := &Notification{ + UserID: recipientID, + RepoID: repo.ID, + Status: NotificationStatusUnread, + UpdatedBy: doerID, + Source: NotificationSourceRepoTransfer, + } + + if _, err := sess.Insert(notification); err != nil { + return err + } + + return sess.Commit() +} + // CreateOrUpdateIssueNotifications creates an issue notification // for each watcher, or updates it if already exists func CreateOrUpdateIssueNotifications(issue *Issue, notificationAuthorID int64) error { diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go index e44f3cc63216f..45f06c00a559a 100644 --- a/modules/notification/base/notifier.go +++ b/modules/notification/base/notifier.go @@ -17,6 +17,7 @@ type Notifier interface { NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) NotifyDeleteRepository(doer *models.User, repo *models.Repository) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) + NotifyTransferRepository(doer, user *models.User, repo *models.Repository) NotifyNewIssue(*models.Issue) NotifyIssueChangeStatus(*models.User, *models.Issue, bool) diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go index 12be1999f9019..82e0527e16444 100644 --- a/modules/notification/base/null.go +++ b/modules/notification/base/null.go @@ -21,6 +21,8 @@ var ( func (*NullNotifier) Run() { } +func (*NullNotifier) NotifyTransferRepository(doer, recipient *models.User, repo *models.Repository) {} + // NotifyCreateIssueComment places a place holder function func (*NullNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository, issue *models.Issue, comment *models.Comment) { diff --git a/modules/notification/notification.go b/modules/notification/notification.go index e0de88346d75f..e8db2472a3e8a 100644 --- a/modules/notification/notification.go +++ b/modules/notification/notification.go @@ -177,3 +177,10 @@ func NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Rep notifier.NotifyMigrateRepository(doer, u, repo) } } + +// NotifyTransferRepository notifies a repository transfer to recipients +func NotifyTransferRepo(doer, u *models.User, repo *models.Repository) { + for _, notifier := range notifiers { + notifier.NotifyTransferRepository(doer, u, repo) + } +} diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index a31591c2a708d..d39f112534313 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -15,22 +15,29 @@ type ( notificationService struct { base.NullNotifier issueQueue chan issueNotificationOpts + repoQueue chan repoNotificationOpts } issueNotificationOpts struct { issue *models.Issue notificationAuthorID int64 } + + repoNotificationOpts struct { + repo *models.Repository + recipientID, doerID int64 + } ) var ( - _ base.Notifier = ¬ificationService{} + _ base.Notifier = (*notificationService)(nil) ) // NewNotifier create a new notificationService notifier func NewNotifier() base.Notifier { return ¬ificationService{ issueQueue: make(chan issueNotificationOpts, 100), + repoQueue: make(chan repoNotificationOpts, 100), } } @@ -41,6 +48,11 @@ func (ns *notificationService) Run() { if err := models.CreateOrUpdateIssueNotifications(opts.issue, opts.notificationAuthorID); err != nil { log.Error("Was unable to create issue notification: %v", err) } + + case opts := <-ns.repoQueue: + if err := models.CreateRepoTransferNotification(opts.doerID, opts.recipientID, opts.repo); err != nil { + log.Error("Was unable to create notification for a repo transfer: %v", err) + } } } } @@ -87,3 +99,11 @@ func (ns *notificationService) NotifyPullRequestReview(pr *models.PullRequest, r r.Reviewer.ID, } } + +func (ns *notificationService) NotifyTransferRepository(doer, user *models.User, repo *models.Repository) { + ns.repoQueue <- repoNotificationOpts{ + repo: repo, + recipientID: user.ID, + doerID: doer.ID, + } +} diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 2ae53f7ab652e..3f57b13883171 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" @@ -390,6 +391,8 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { models.SendRepoTransferNotifyMail(ctx.Context, u, ctx.Repo.Repository) } + notification.NotifyTransferRepo(ctx.Repo.Owner, u, ctx.Repo.Repository) + log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner)) ctx.Redirect(setting.AppSubURL + "/" + ctx.Repo.Owner.Name + "/" + repo.Name + "/settings") diff --git a/templates/user/notification/notification.tmpl b/templates/user/notification/notification.tmpl index 3c30bbe3056e5..33d1965dc8f12 100644 --- a/templates/user/notification/notification.tmpl +++ b/templates/user/notification/notification.tmpl @@ -61,9 +61,16 @@ {{end}} + {{ if eq $notification.Source 4}} + + {{ $.i18n.Tr "mail.repo_transfer_notify"}} + + + {{ else }} #{{$issue.Index}} - {{$issue.Title}} + {{ end }} From 8954618723aed75c4d83a085453e2c9e518b5351 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Fri, 19 Apr 2019 19:12:01 +0100 Subject: [PATCH 28/89] fix godoc --- modules/notification/base/null.go | 1 + modules/notification/notification.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go index 82e0527e16444..c118aa4cf16e1 100644 --- a/modules/notification/base/null.go +++ b/modules/notification/base/null.go @@ -21,6 +21,7 @@ var ( func (*NullNotifier) Run() { } +// NotifyTransferRepository places a place holder function func (*NullNotifier) NotifyTransferRepository(doer, recipient *models.User, repo *models.Repository) {} // NotifyCreateIssueComment places a place holder function diff --git a/modules/notification/notification.go b/modules/notification/notification.go index e8db2472a3e8a..efe9388dac94a 100644 --- a/modules/notification/notification.go +++ b/modules/notification/notification.go @@ -178,7 +178,7 @@ func NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Rep } } -// NotifyTransferRepository notifies a repository transfer to recipients +// NotifyTransferRepo notifies a repository transfer to recipients func NotifyTransferRepo(doer, u *models.User, repo *models.Repository) { for _, notifier := range notifiers { notifier.NotifyTransferRepository(doer, u, repo) From efbbf45daed0b6bd1793875396ccbc72e03ed377 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Thu, 30 Jan 2020 21:54:06 +0100 Subject: [PATCH 29/89] fix merge conflicts --- go.mod | 3 - go.sum | 14 --- models/mail.go | 193 ------------------------------ models/migrations/v126.go | 8 +- models/repo.go | 129 -------------------- models/repo_transfer.go | 176 ++++++++++++++------------- modules/notification/base/null.go | 6 +- modules/notification/ui/ui.go | 22 ++-- routers/repo/repo.go | 8 +- routers/repo/setting.go | 6 +- 10 files changed, 109 insertions(+), 456 deletions(-) delete mode 100644 models/mail.go diff --git a/go.mod b/go.mod index 0c79c5b6071ff..943eb2a7924c3 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ require ( gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 github.com/PuerkitoBio/goquery v1.5.0 github.com/RoaringBitmap/roaring v0.4.21 // indirect - github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755 github.com/bgentry/speakeasy v0.1.0 // indirect github.com/blevesearch/bleve v0.8.1 github.com/blevesearch/blevex v0.0.0-20180227211930-4b158bb555a3 // indirect @@ -46,7 +45,6 @@ require ( github.com/go-redis/redis v6.15.2+incompatible github.com/go-sql-driver/mysql v1.4.1 github.com/go-swagger/go-swagger v0.21.0 - github.com/go-xorm/xorm v0.7.9 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 @@ -108,7 +106,6 @@ require ( gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.51.1 gopkg.in/ldap.v3 v3.0.2 - gopkg.in/macaron.v1 v1.3.4 gopkg.in/src-d/go-billy.v4 v4.3.2 gopkg.in/src-d/go-git.v4 v4.13.1 gopkg.in/testfixtures.v2 v2.5.0 diff --git a/go.sum b/go.sum index e630c864b4d84..039e820e29b61 100644 --- a/go.sum +++ b/go.sum @@ -89,7 +89,6 @@ github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 h1:U/lr3Dgy4WK github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/corbym/gocrest v1.0.3 h1:gwEdq6RkTmq+09CTuM29DfKOCtZ7G7bcyxs3IZ6EVdU= github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdTABShCs= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -169,8 +168,6 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 h1:NjHlg70DuOkcAMqgt0+XA+NHwtu66MkTVVgR4fFWbcI= -github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -237,11 +234,8 @@ github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= -github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0= -github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -318,8 +312,6 @@ github.com/issue9/assert v1.3.2 h1:IaTa37u4m1fUuTH9K9ldO5IONKVDXjLiUO1T9vj0OF0= github.com/issue9/assert v1.3.2/go.mod h1:9Ger+iz8X7r1zMYYwEhh++2wMGWcNN2oVI+zIQXxcio= github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c h1:A/PDn117UYld5mlxe58EpMguqpkeTMw5/FCo0ZPS/Ko= github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c/go.mod h1:5mTb/PQNkqmq2x3IxlQZE0aSnTksJg7fg/oWmJ5SKXQ= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= -github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d h1:ig/iUfDDg06RVW8OMby+GrmW6K2nPO3AFHlEIdvJSd4= github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= @@ -477,7 +469,6 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b h1:4kg1wyftSKxLtnPAvcRWakIPpokB9w780/KwrNLnfPA= github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0= @@ -658,7 +649,6 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= @@ -687,7 +677,6 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 h1:kJQZhwFzSwJS2BxboKjdZzWczQOZx8VuH7Y8hhuGUtM= golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -741,8 +730,6 @@ gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w= gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= -gopkg.in/macaron.v1 v1.3.4 h1:HvIscOwxhFhx3swWM/979wh2QMYyuXrNmrF9l+j3HZs= -gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= @@ -772,7 +759,6 @@ strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8= xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= -xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw= xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= xorm.io/core v0.7.3 h1:W8ws1PlrnkS1CZU1YWaYLMQcQilwAmQXU0BJDJon+H0= diff --git a/models/mail.go b/models/mail.go deleted file mode 100644 index 1276495c318f0..0000000000000 --- a/models/mail.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2016 The Gogs Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package models - -import ( - "bytes" - "fmt" - "html/template" - "path" - - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/mailer" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/markdown" - "code.gitea.io/gitea/modules/setting" - "gopkg.in/gomail.v2" - "gopkg.in/macaron.v1" -) - -const ( - mailAuthActivate base.TplName = "auth/activate" - mailAuthActivateEmail base.TplName = "auth/activate_email" - mailAuthResetPassword base.TplName = "auth/reset_passwd" - mailAuthRegisterNotify base.TplName = "auth/register_notify" - - mailIssueComment base.TplName = "issue/comment" - mailIssueMention base.TplName = "issue/mention" - - mailNotifyCollaborator base.TplName = "notify/collaborator" - mailRepoTransferNotify = "notify/repo_transfer" -) - -var templates *template.Template - -// InitMailRender initializes the macaron mail renderer -func InitMailRender(tmpls *template.Template) { - templates = tmpls -} - -// SendTestMail sends a test mail -func SendTestMail(email string) error { - return gomail.Send(mailer.Sender, mailer.NewMessage([]string{email}, "Gitea Test Email!", "Gitea Test Email!").Message) -} - -// SendUserMail sends a mail to the user -func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject, info string) { - data := map[string]interface{}{ - "DisplayName": u.DisplayName(), - "ActiveCodeLives": base.MinutesToFriendly(setting.Service.ActiveCodeLives, c.Locale.Language()), - "ResetPwdCodeLives": base.MinutesToFriendly(setting.Service.ResetPwdCodeLives, c.Locale.Language()), - "Code": code, - } - - var content bytes.Buffer - - if err := templates.ExecuteTemplate(&content, string(tpl), data); err != nil { - log.Error("Template: %v", err) - return - } - - msg := mailer.NewMessage([]string{u.Email}, subject, content.String()) - msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info) - - mailer.SendAsync(msg) -} - -// SendActivateAccountMail sends an activation mail to the user (new user registration) -func SendActivateAccountMail(c *macaron.Context, u *User) { - SendUserMail(c, u, mailAuthActivate, u.GenerateActivateCode(), c.Tr("mail.activate_account"), "activate account") -} - -// SendResetPasswordMail sends a password reset mail to the user -func SendResetPasswordMail(c *macaron.Context, u *User) { - SendUserMail(c, u, mailAuthResetPassword, u.GenerateActivateCode(), c.Tr("mail.reset_password"), "recover account") -} - -// SendActivateEmailMail sends confirmation email to confirm new email address -func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) { - data := map[string]interface{}{ - "DisplayName": u.DisplayName(), - "ActiveCodeLives": base.MinutesToFriendly(setting.Service.ActiveCodeLives, c.Locale.Language()), - "Code": u.GenerateEmailActivateCode(email.Email), - "Email": email.Email, - } - - var content bytes.Buffer - - if err := templates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil { - log.Error("Template: %v", err) - return - } - - msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), content.String()) - msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID) - - mailer.SendAsync(msg) -} - -// SendRegisterNotifyMail triggers a notify e-mail by admin created a account. -func SendRegisterNotifyMail(c *macaron.Context, u *User) { - data := map[string]interface{}{ - "DisplayName": u.DisplayName(), - "Username": u.Name, - } - - var content bytes.Buffer - - if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil { - log.Error("Template: %v", err) - return - } - - msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), content.String()) - msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID) - - mailer.SendAsync(msg) -} - -// SendCollaboratorMail sends mail notification to new collaborator. -func SendCollaboratorMail(u, doer *User, repo *Repository) { - repoName := path.Join(repo.Owner.Name, repo.Name) - subject := fmt.Sprintf("%s added you to %s", doer.DisplayName(), repoName) - - data := map[string]interface{}{ - "Subject": subject, - "RepoName": repoName, - "Link": repo.HTMLURL(), - } - - var content bytes.Buffer - - if err := templates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil { - log.Error("Template: %v", err) - return - } - - msg := mailer.NewMessage([]string{u.Email}, subject, content.String()) - msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID) - - mailer.SendAsync(msg) -} - -func composeTplData(subject, body, link string) map[string]interface{} { - data := make(map[string]interface{}, 10) - data["Subject"] = subject - data["Body"] = body - data["Link"] = link - return data -} - -func composeIssueCommentMessage(issue *Issue, doer *User, content string, comment *Comment, tplName base.TplName, tos []string, info string) *mailer.Message { - subject := issue.mailSubject() - issue.LoadRepo() - body := string(markup.RenderByType(markdown.MarkupName, []byte(content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas())) - - data := make(map[string]interface{}, 10) - if comment != nil { - data = composeTplData(subject, body, issue.HTMLURL()+"#"+comment.HashTag()) - } else { - data = composeTplData(subject, body, issue.HTMLURL()) - } - data["Doer"] = doer - - var mailBody bytes.Buffer - - if err := templates.ExecuteTemplate(&mailBody, string(tplName), data); err != nil { - log.Error("Template: %v", err) - } - - msg := mailer.NewMessageFrom(tos, doer.DisplayName(), setting.MailService.FromEmail, subject, mailBody.String()) - msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info) - return msg -} - -// SendIssueCommentMail composes and sends issue comment emails to target receivers. -func SendIssueCommentMail(issue *Issue, doer *User, content string, comment *Comment, tos []string) { - if len(tos) == 0 { - return - } - - mailer.SendAsync(composeIssueCommentMessage(issue, doer, content, comment, mailIssueComment, tos, "issue comment")) -} - -// SendIssueMentionMail composes and sends issue mention emails to target receivers. -func SendIssueMentionMail(issue *Issue, doer *User, content string, comment *Comment, tos []string) { - if len(tos) == 0 { - return - } - mailer.SendAsync(composeIssueCommentMessage(issue, doer, content, comment, mailIssueMention, tos, "issue mention")) -} diff --git a/models/migrations/v126.go b/models/migrations/v126.go index 3a16a206d20fe..2212b909e855f 100644 --- a/models/migrations/v126.go +++ b/models/migrations/v126.go @@ -5,9 +5,9 @@ package migrations import ( - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/timeutil" - "github.com/go-xorm/xorm" + "xorm.io/xorm" ) func addRepoTransfer(x *xorm.Engine) error { @@ -16,8 +16,8 @@ func addRepoTransfer(x *xorm.Engine) error { UserID int64 RecipientID int64 RepoID int64 - CreatedUnix util.TimeStamp `xorm:"INDEX NOT NULL created"` - UpdatedUnix util.TimeStamp `xorm:"INDEX NOT NULL updated"` + CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` Status bool } diff --git a/models/repo.go b/models/repo.go index 5528a8e9d2405..2cdd6d4b82d1e 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1114,135 +1114,6 @@ func IncrementRepoForkNum(ctx DBContext, repoID int64) error { return err } -// TransferOwnership transfers all corresponding setting from old user to new one. -func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error { - newOwner, err := GetUserByName(newOwnerName) - if err != nil { - return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) - } - - // Check if new owner has repository with same name. - has, err := IsRepositoryExist(newOwner, repo.Name) - if err != nil { - return fmt.Errorf("IsRepositoryExist: %v", err) - } else if has { - return ErrRepoAlreadyExist{newOwnerName, repo.Name} - } - - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return fmt.Errorf("sess.Begin: %v", err) - } - - oldOwner := repo.Owner - - // Note: we have to set value here to make sure recalculate accesses is based on - // new owner. - repo.OwnerID = newOwner.ID - repo.Owner = newOwner - repo.OwnerName = newOwner.Name - - // Update repository. - if _, err := sess.ID(repo.ID).Update(repo); err != nil { - return fmt.Errorf("update owner: %v", err) - } - - // Remove redundant collaborators. - collaborators, err := repo.getCollaborators(sess, ListOptions{}) - if err != nil { - return fmt.Errorf("getCollaborators: %v", err) - } - - // Dummy object. - collaboration := &Collaboration{RepoID: repo.ID} - for _, c := range collaborators { - if c.ID != newOwner.ID { - isMember, err := isOrganizationMember(sess, newOwner.ID, c.ID) - if err != nil { - return fmt.Errorf("IsOrgMember: %v", err) - } else if !isMember { - continue - } - } - collaboration.UserID = c.ID - if _, err = sess.Delete(collaboration); err != nil { - return fmt.Errorf("remove collaborator '%d': %v", c.ID, err) - } - } - - // Remove old team-repository relations. - if oldOwner.IsOrganization() { - if err = oldOwner.removeOrgRepo(sess, repo.ID); err != nil { - return fmt.Errorf("removeOrgRepo: %v", err) - } - } - - if newOwner.IsOrganization() { - if err := newOwner.GetTeams(&SearchTeamOptions{}); err != nil { - return fmt.Errorf("GetTeams: %v", err) - } - for _, t := range newOwner.Teams { - if t.IncludesAllRepositories { - if err := t.addRepository(sess, repo); err != nil { - return fmt.Errorf("addRepository: %v", err) - } - } - } - } else if err = repo.recalculateAccesses(sess); err != nil { - // Organization called this in addRepository method. - return fmt.Errorf("recalculateAccesses: %v", err) - } - - // Update repository count. - if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos+1 WHERE id=?", newOwner.ID); err != nil { - return fmt.Errorf("increase new owner repository count: %v", err) - } else if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", oldOwner.ID); err != nil { - return fmt.Errorf("decrease old owner repository count: %v", err) - } - - if err = watchRepo(sess, doer.ID, repo.ID, true); err != nil { - return fmt.Errorf("watchRepo: %v", err) - } - - // Remove watch for organization. - if oldOwner.IsOrganization() { - if err = watchRepo(sess, oldOwner.ID, repo.ID, false); err != nil { - return fmt.Errorf("watchRepo [false]: %v", err) - } - } - - // Rename remote repository to new path and delete local copy. - dir := UserPath(newOwner.Name) - - if err := os.MkdirAll(dir, os.ModePerm); err != nil { - return fmt.Errorf("Failed to create dir %s: %v", dir, err) - } - - if err = os.Rename(RepoPath(oldOwner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { - return fmt.Errorf("rename repository directory: %v", err) - } - - // Rename remote wiki repository to new path and delete local copy. - wikiPath := WikiPath(oldOwner.Name, repo.Name) - if com.IsExist(wikiPath) { - if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { - return fmt.Errorf("rename repository wiki: %v", err) - } - } - - // If there was previously a redirect at this location, remove it. - if err = deleteRepoRedirect(sess, newOwner.ID, repo.Name); err != nil { - return fmt.Errorf("delete repo redirect: %v", err) - } - - if err := NewRepoRedirect(DBContext{sess}, oldOwner.ID, repo.ID, repo.Name, repo.Name); err != nil { - return fmt.Errorf("NewRepoRedirect: %v", err) - } - - return sess.Commit() -} - // ChangeRepositoryName changes all corresponding setting from old repository name to new one. func ChangeRepositoryName(doer *User, repo *Repository, newRepoName string) (err error) { oldRepoName := repo.Name diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 0f0555de8b9d4..3aa9847723cca 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -5,17 +5,13 @@ package models import ( - "bytes" "fmt" "os" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/mailer" - "code.gitea.io/gitea/modules/util" - "gopkg.in/macaron.v1" + "code.gitea.io/gitea/modules/timeutil" + "xorm.io/xorm" - "github.com/Unknwon/com" - "github.com/go-xorm/xorm" + "github.com/unknwon/com" ) // TransferStatus determines the current state of a transfer @@ -41,8 +37,8 @@ type RepoTransfer struct { RecipientID int64 Recipient *User `xorm:"-"` RepoID int64 - CreatedUnix util.TimeStamp `xorm:"INDEX NOT NULL created"` - UpdatedUnix util.TimeStamp `xorm:"INDEX NOT NULL updated"` + CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` Status TransferStatus } @@ -90,7 +86,7 @@ func (r *RepoTransfer) IsTransferForUser(u *User) bool { return false } - if err := t.GetMembers(); err != nil { + if err := t.GetMembers(&SearchMembersOptions{}); err != nil { return false } @@ -132,7 +128,7 @@ func acceptRepositoryTransfer(sess *xorm.Session, repo *Repository) error { // "rejected". Thus ending the transfer process func CancelRepositoryTransfer(repoTransfer *RepoTransfer) error { repoTransfer.Status = Rejected - repoTransfer.UpdatedUnix = util.TimeStampNow() + repoTransfer.UpdatedUnix = timeutil.TimeStampNow() _, err := x.ID(repoTransfer.ID).Cols("updated_unix", "status"). Update(repoTransfer) return err @@ -171,8 +167,8 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) RepoID: repo.ID, RecipientID: newOwner.ID, Status: Pending, - CreatedUnix: util.TimeStampNow(), - UpdatedUnix: util.TimeStampNow(), + CreatedUnix: timeutil.TimeStampNow(), + UpdatedUnix: timeutil.TimeStampNow(), UserID: doer.ID, } @@ -180,58 +176,61 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) return err } -// SendRepoTransferNotifyMail triggers a notification e-mail when a repository -// transfer is initiated -func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { - data := map[string]interface{}{ - "Subject": c.Tr("mail.repo_transfer_notify"), - "RepoName": repo.FullName(), - "Link": repo.HTMLURL(), - "AcceptTransferLink": repo.HTMLURL() + "/action/accept_transfer", - "DeclineTransferLink": repo.HTMLURL() + "/action/decline_transfer", - } - - var content bytes.Buffer - - if err := templates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil { - log.Error("Template: %v", err) - return - } - - var email = u.Email - - if u.IsOrganization() && u.Email == "" { - t, err := u.getOwnerTeam(x) - if err != nil { - log.Error("Could not retrieve owners team for organization", err) - return - } - - if err := t.GetMembers(); err != nil { - log.Error("Could not retrieve members of the owners team", err) - return - } - - // Just use the email address of the first user - email = t.Members[0].Email +// // SendRepoTransferNotifyMail triggers a notification e-mail when a repository +// // transfer is initiated +// func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { +// data := map[string]interface{}{ +// "Subject": c.Tr("mail.repo_transfer_notify"), +// "RepoName": repo.FullName(), +// "Link": repo.HTMLURL(), +// "AcceptTransferLink": repo.HTMLURL() + "/action/accept_transfer", +// "DeclineTransferLink": repo.HTMLURL() + "/action/decline_transfer", +// } + +// var content bytes.Buffer + +// if err := templates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil { +// log.Error("Template: %v", err) +// return +// } + +// var email = u.Email + +// if u.IsOrganization() && u.Email == "" { +// t, err := u.getOwnerTeam(x) +// if err != nil { +// log.Error("Could not retrieve owners team for organization", err) +// return +// } + +// if err := t.GetMembers(); err != nil { +// log.Error("Could not retrieve members of the owners team", err) +// return +// } + +// // Just use the email address of the first user +// email = t.Members[0].Email +// } + +// msg := mailer.NewMessage([]string{email}, c.Tr("mail.repo_transfer_notify"), content.String()) +// msg.Info = fmt.Sprintf("UID: %d, repository transfer notification", u.ID) + +// mailer.SendAsync(msg) +// } + +// TransferOwnership transfers all corresponding setting from old user to new one. +func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error { + newOwner, err := GetUserByName(newOwnerName) + if err != nil { + return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) } - msg := mailer.NewMessage([]string{email}, c.Tr("mail.repo_transfer_notify"), content.String()) - msg.Info = fmt.Sprintf("UID: %d, repository transfer notification", u.ID) - - mailer.SendAsync(msg) -} - -// TransferOwnership transfers all corresponding setting from one user to -// another. -func TransferOwnership(doer, newOwner *User, repo *Repository) error { - // Check if new owner has repository with same name. has, err := IsRepositoryExist(newOwner, repo.Name) if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { - return ErrRepoAlreadyExist{newOwner.Name, repo.Name} + return ErrRepoAlreadyExist{newOwnerName, repo.Name} } sess := x.NewSession() @@ -240,24 +239,21 @@ func TransferOwnership(doer, newOwner *User, repo *Repository) error { return fmt.Errorf("sess.Begin: %v", err) } - owner := repo.Owner + oldOwner := repo.Owner // Note: we have to set value here to make sure recalculate accesses is based on // new owner. repo.OwnerID = newOwner.ID repo.Owner = newOwner + repo.OwnerName = newOwner.Name // Update repository. if _, err := sess.ID(repo.ID).Update(repo); err != nil { return fmt.Errorf("update owner: %v", err) } - if err := acceptRepositoryTransfer(sess, repo); err != nil { - return err - } - // Remove redundant collaborators. - collaborators, err := repo.getCollaborators(sess) + collaborators, err := repo.getCollaborators(sess, ListOptions{}) if err != nil { return fmt.Errorf("getCollaborators: %v", err) } @@ -266,7 +262,7 @@ func TransferOwnership(doer, newOwner *User, repo *Repository) error { collaboration := &Collaboration{RepoID: repo.ID} for _, c := range collaborators { if c.ID != newOwner.ID { - isMember, err := newOwner.IsOrgMember(c.ID) + isMember, err := isOrganizationMember(sess, newOwner.ID, c.ID) if err != nil { return fmt.Errorf("IsOrgMember: %v", err) } else if !isMember { @@ -280,37 +276,44 @@ func TransferOwnership(doer, newOwner *User, repo *Repository) error { } // Remove old team-repository relations. - if owner.IsOrganization() { - if err = owner.removeOrgRepo(sess, repo.ID); err != nil { + if oldOwner.IsOrganization() { + if err = oldOwner.removeOrgRepo(sess, repo.ID); err != nil { return fmt.Errorf("removeOrgRepo: %v", err) } } if newOwner.IsOrganization() { - t, err := newOwner.getOwnerTeam(sess) - if err != nil { - return fmt.Errorf("getOwnerTeam: %v", err) - } else if err = t.addRepository(sess, repo); err != nil { - return fmt.Errorf("add to owner team: %v", err) + if err := newOwner.GetTeams(&SearchTeamOptions{}); err != nil { + return fmt.Errorf("GetTeams: %v", err) } - } else { - // Organization called this in addRepository method. - if err = repo.recalculateAccesses(sess); err != nil { - return fmt.Errorf("recalculateAccesses: %v", err) + for _, t := range newOwner.Teams { + if t.IncludesAllRepositories { + if err := t.addRepository(sess, repo); err != nil { + return fmt.Errorf("addRepository: %v", err) + } + } } + } else if err = repo.recalculateAccesses(sess); err != nil { + // Organization called this in addRepository method. + return fmt.Errorf("recalculateAccesses: %v", err) } // Update repository count. if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos+1 WHERE id=?", newOwner.ID); err != nil { return fmt.Errorf("increase new owner repository count: %v", err) - } else if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", owner.ID); err != nil { + } else if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", oldOwner.ID); err != nil { return fmt.Errorf("decrease old owner repository count: %v", err) } if err = watchRepo(sess, doer.ID, repo.ID, true); err != nil { return fmt.Errorf("watchRepo: %v", err) - } else if err = transferRepoAction(sess, doer, owner, repo); err != nil { - return fmt.Errorf("transferRepoAction: %v", err) + } + + // Remove watch for organization. + if oldOwner.IsOrganization() { + if err = watchRepo(sess, oldOwner.ID, repo.ID, false); err != nil { + return fmt.Errorf("watchRepo [false]: %v", err) + } } // Rename remote repository to new path and delete local copy. @@ -320,19 +323,26 @@ func TransferOwnership(doer, newOwner *User, repo *Repository) error { return fmt.Errorf("Failed to create dir %s: %v", dir, err) } - if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { + if err = os.Rename(RepoPath(oldOwner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { return fmt.Errorf("rename repository directory: %v", err) } - RemoveAllWithNotice("Delete repository local copy", repo.LocalCopyPath()) // Rename remote wiki repository to new path and delete local copy. - wikiPath := WikiPath(owner.Name, repo.Name) + wikiPath := WikiPath(oldOwner.Name, repo.Name) if com.IsExist(wikiPath) { - RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath()) if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { return fmt.Errorf("rename repository wiki: %v", err) } } + // If there was previously a redirect at this location, remove it. + if err = deleteRepoRedirect(sess, newOwner.ID, repo.Name); err != nil { + return fmt.Errorf("delete repo redirect: %v", err) + } + + if err := NewRepoRedirect(DBContext{sess}, oldOwner.ID, repo.ID, repo.Name, repo.Name); err != nil { + return fmt.Errorf("NewRepoRedirect: %v", err) + } + return sess.Commit() } diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go index b23e876f95e4e..e4e0f5d6f2c3d 100644 --- a/modules/notification/base/null.go +++ b/modules/notification/base/null.go @@ -22,7 +22,7 @@ func (*NullNotifier) Run() { } // NotifyTransferRepository places a place holder function -func (*NullNotifier) NotifyTransferRepository(doer, recipient *models.User, repo *models.Repository) {} +func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, _ string) {} // NotifyCreateIssueComment places a place holder function func (*NullNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository, @@ -134,10 +134,6 @@ func (*NullNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, func (*NullNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) { } -// NotifyTransferRepository places a place holder function -func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) { -} - // NotifySyncPushCommits places a place holder function func (*NullNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) { } diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index 269f19377cdd5..87b646a533d8b 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -42,17 +42,9 @@ func NewNotifier() base.Notifier { } func (ns *notificationService) Run() { - for { - select { - case opts := <-ns.issueQueue: - if err := models.CreateOrUpdateIssueNotifications(opts.issue, opts.notificationAuthorID); err != nil { - log.Error("Was unable to create issue notification: %v", err) - } - - case opts := <-ns.repoQueue: - if err := models.CreateRepoTransferNotification(opts.doerID, opts.recipientID, opts.repo); err != nil { - log.Error("Was unable to create notification for a repo transfer: %v", err) - } + for opts := range ns.issueQueue { + if err := models.CreateOrUpdateIssueNotifications(opts.issueID, opts.commentID, opts.notificationAuthorID); err != nil { + log.Error("Was unable to create issue notification: %v", err) } } } @@ -108,10 +100,10 @@ func (ns *notificationService) NotifyPullRequestReview(pr *models.PullRequest, r ns.issueQueue <- opts } -func (ns *notificationService) NotifyTransferRepository(doer, user *models.User, repo *models.Repository) { +func (ns *notificationService) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) { ns.repoQueue <- repoNotificationOpts{ - repo: repo, - recipientID: user.ID, - doerID: doer.ID, + repo: repo, + // recipientID: user.ID, + doerID: doer.ID, } } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index da6ad3f60bcb1..fde5f05fbd122 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -415,17 +415,11 @@ func Action(ctx *context.Context) { return } - if err := models.TransferOwnership(repoTransfer.User, repoTransfer.Recipient, ctx.Repo.Repository); err != nil { + if err := models.TransferOwnership(repoTransfer.User, repoTransfer.Recipient.LoginName, ctx.Repo.Repository); err != nil { ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) return } - err = models.NewRepoRedirect(repoTransfer.User.ID, ctx.Repo.Repository.ID, ctx.Repo.Repository.Name, ctx.Repo.Repository.Name) - if err != nil { - ctx.ServerError("NewRepoRedirect", err) - return - } - ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success")) ctx.Redirect(ctx.Repo.Repository.HTMLURL()) return diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 0d35ce1503416..bef8ec9fcfe82 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -20,7 +20,6 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -379,6 +378,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { newOwner := ctx.Query("new_owner_name") u, err := models.GetUserByName(newOwner) + _ = u if models.IsErrUserNotExist(err) { ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil) return @@ -405,10 +405,10 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { } if setting.Service.EnableNotifyMail { - models.SendRepoTransferNotifyMail(ctx.Context, u, ctx.Repo.Repository) + // models.SendRepoTransferNotifyMail(ctx.Context, u, ctx.Repo.Repository) } - notification.NotifyTransferRepo(ctx.Repo.Owner, u, ctx.Repo.Repository) + // notification.NotifyTransferRepo(ctx.Repo.Owner, u.Name, ctx.Repo.Repository) log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner)) From 7e961ce0f853f8bb49aba7d99befeba0c43029ca Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Mon, 3 Feb 2020 21:27:34 +0100 Subject: [PATCH 30/89] fix merge conflicts --- routers/repo/setting.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 8f7fbb376f10f..2ec85bea3b974 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -376,16 +376,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } -<<<<<<< HEAD - newOwner := ctx.Query("new_owner_name") - u, err := models.GetUserByName(newOwner) - _ = u - if models.IsErrUserNotExist(err) { - ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil) - return - } else if err != nil { - ctx.ServerError("GetUserByName", err) -======= newOwner, err := models.GetUserByName(ctx.Query("new_owner_name")) if err != nil { if models.IsErrUserNotExist(err) { @@ -393,7 +383,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } ctx.ServerError("IsUserExist", err) ->>>>>>> origin return } @@ -453,15 +442,9 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } -<<<<<<< HEAD log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name) ctx.Flash.Success(ctx.Tr("repo.settings.abort_transfer_success", repoTransfer.Recipient.Name)) ctx.Redirect(setting.AppSubURL + "/" + ctx.Repo.Owner.Name + "/" + repo.Name + "/settings") -======= - log.Trace("Repository transferred: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) - ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed")) - ctx.Redirect(setting.AppSubURL + "/" + newOwner.Name + "/" + repo.Name) ->>>>>>> origin case "delete": if !ctx.Repo.IsOwner() { From f339291e1567e136ea032b1326473e701989f93b Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 4 Feb 2020 06:21:39 +0100 Subject: [PATCH 31/89] fix transfer logic and hook email up --- go.mod | 1 + go.sum | 6 ++++ models/repo_transfer.go | 53 ++----------------------------- routers/repo/setting.go | 9 +++--- services/mailer/mail.go | 2 ++ services/mailer/mail_repo.go | 55 +++++++++++++++++++++++++++++++++ services/repository/transfer.go | 5 +++ 7 files changed, 76 insertions(+), 55 deletions(-) create mode 100644 services/mailer/mail_repo.go diff --git a/go.mod b/go.mod index de67f582dace3..ef88d5785974d 100644 --- a/go.mod +++ b/go.mod @@ -108,6 +108,7 @@ require ( gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.51.1 gopkg.in/ldap.v3 v3.0.2 + gopkg.in/macaron.v1 v1.3.4 gopkg.in/src-d/go-billy.v4 v4.3.2 gopkg.in/src-d/go-git.v4 v4.13.1 gopkg.in/testfixtures.v2 v2.5.0 diff --git a/go.sum b/go.sum index 2296aef0dea59..4b0877828a0e0 100644 --- a/go.sum +++ b/go.sum @@ -172,6 +172,8 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 h1:NjHlg70DuOkcAMqgt0+XA+NHwtu66MkTVVgR4fFWbcI= +github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -655,6 +657,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= @@ -683,6 +686,7 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 h1:kJQZhwFzSwJS2BxboKjdZzWczQOZx8VuH7Y8hhuGUtM= golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -736,6 +740,8 @@ gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w= gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= +gopkg.in/macaron.v1 v1.3.4 h1:HvIscOwxhFhx3swWM/979wh2QMYyuXrNmrF9l+j3HZs= +gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 3aa9847723cca..fbcb0238452bf 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -136,7 +136,7 @@ func CancelRepositoryTransfer(repoTransfer *RepoTransfer) error { // StartRepositoryTransfer marks the repository transfer as "pending". It // doesn't actually transfer the repository until the user acks the transfer. -func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) error { +func StartRepositoryTransfer(doer, newOwner *User, repo *Repository) error { // Make sure the repo isn't being transferred to someone currently // Only one transfer process can be initiated at a time. // It has to be cancelled for a new one to occur @@ -147,12 +147,7 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) } if n > 0 { - return ErrRepoTransferInProgress{newOwnerName, repo.Name} - } - - newOwner, err := GetUserByName(newOwnerName) - if err != nil { - return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) + return ErrRepoTransferInProgress{newOwner.LowerName, repo.Name} } // Check if new owner has repository with same name. @@ -160,7 +155,7 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { - return ErrRepoAlreadyExist{newOwnerName, repo.Name} + return ErrRepoAlreadyExist{newOwner.LowerName, repo.Name} } transfer := &RepoTransfer{ @@ -176,48 +171,6 @@ func StartRepositoryTransfer(doer *User, newOwnerName string, repo *Repository) return err } -// // SendRepoTransferNotifyMail triggers a notification e-mail when a repository -// // transfer is initiated -// func SendRepoTransferNotifyMail(c *macaron.Context, u *User, repo *Repository) { -// data := map[string]interface{}{ -// "Subject": c.Tr("mail.repo_transfer_notify"), -// "RepoName": repo.FullName(), -// "Link": repo.HTMLURL(), -// "AcceptTransferLink": repo.HTMLURL() + "/action/accept_transfer", -// "DeclineTransferLink": repo.HTMLURL() + "/action/decline_transfer", -// } - -// var content bytes.Buffer - -// if err := templates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil { -// log.Error("Template: %v", err) -// return -// } - -// var email = u.Email - -// if u.IsOrganization() && u.Email == "" { -// t, err := u.getOwnerTeam(x) -// if err != nil { -// log.Error("Could not retrieve owners team for organization", err) -// return -// } - -// if err := t.GetMembers(); err != nil { -// log.Error("Could not retrieve members of the owners team", err) -// return -// } - -// // Just use the email address of the first user -// email = t.Members[0].Email -// } - -// msg := mailer.NewMessage([]string{email}, c.Tr("mail.repo_transfer_notify"), content.String()) -// msg.Info = fmt.Sprintf("UID: %d, repository transfer notification", u.ID) - -// mailer.SendAsync(msg) -// } - // TransferOwnership transfers all corresponding setting from old user to new one. func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error { newOwner, err := GetUserByName(newOwnerName) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 2ec85bea3b974..5dec734e88951 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -391,7 +391,8 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { ctx.Repo.GitRepo.Close() ctx.Repo.GitRepo = nil } - if err = repo_service.TransferOwnership(ctx.User, newOwner, repo, nil); err != nil { + + if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, repo); err != nil { if models.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) } else if models.IsErrRepoTransferInProgress(err) { @@ -404,13 +405,11 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { } if setting.Service.EnableNotifyMail { - // models.SendRepoTransferNotifyMail(ctx.Context, u, ctx.Repo.Repository) + mailer.SendRepoTransferNotifyMail(ctx.Locale, ctx.Repo.Owner, ctx.Repo.Repository) } - // notification.NotifyTransferRepo(ctx.Repo.Owner, u.Name, ctx.Repo.Repository) - log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) - ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner)) + ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.LowerName)) ctx.Redirect(setting.AppSubURL + "/" + ctx.Repo.Owner.Name + "/" + repo.Name + "/settings") case "cancel_transfer": diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 3241ae728d880..d8392fbe7586d 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -33,6 +33,8 @@ const ( mailNotifyCollaborator base.TplName = "notify/collaborator" + mailRepoTransferNotify base.TplName = "notify/repo_transfer" + // There's no actual limit for subject in RFC 5322 mailMaxSubjectRunes = 256 ) diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go new file mode 100644 index 0000000000000..afd5f037487ca --- /dev/null +++ b/services/mailer/mail_repo.go @@ -0,0 +1,55 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package mailer + +import ( + "bytes" + "fmt" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" +) + +// SendRepoTransferNotifyMail triggers a notification e-mail when a repository +// transfer is initiated +func SendRepoTransferNotifyMail(locale Locale, u *models.User, repo *models.Repository) { + data := map[string]interface{}{ + "Subject": locale.Tr("mail.repo_transfer_notify"), + "RepoName": repo.FullName(), + "Link": repo.HTMLURL(), + "AcceptTransferLink": repo.HTMLURL() + "/action/accept_transfer", + "DeclineTransferLink": repo.HTMLURL() + "/action/decline_transfer", + } + + var content bytes.Buffer + + if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil { + log.Error("Template: %v", err) + return + } + + var email = u.Email + + if u.IsOrganization() && u.Email == "" { + t, err := u.GetOwnerTeam() + if err != nil { + log.Error("Could not retrieve owners team for organization", err) + return + } + + if err := t.GetMembers(&models.SearchMembersOptions{}); err != nil { + log.Error("Could not retrieve members of the owners team", err) + return + } + + // Just use the email address of the first user + email = t.Members[0].Email + } + + msg := NewMessage([]string{email}, locale.Tr("mail.repo_transfer_notify"), content.String()) + msg.Info = fmt.Sprintf("UID: %d, repository transfer notification", u.ID) + + SendAsync(msg) +} diff --git a/services/repository/transfer.go b/services/repository/transfer.go index d34c812b86973..211cee91a0c3c 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -72,3 +72,8 @@ func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoNam return nil } + +// StartRepositoryTransfer marks the repository transfer as "pending". +func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repository) error { + return models.StartRepositoryTransfer(doer, newOwner, repo) +} From bba603e02102d139e55cbac42a28f4badb3efef7 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 4 Feb 2020 06:29:23 +0100 Subject: [PATCH 32/89] switch transfer logic to services/repo --- routers/repo/repo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/repo/repo.go b/routers/repo/repo.go index a2e58f9358389..0b7e896d49430 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -415,7 +415,7 @@ func Action(ctx *context.Context) { return } - if err := models.TransferOwnership(repoTransfer.User, repoTransfer.Recipient.LoginName, ctx.Repo.Repository); err != nil { + if err := repo_service.TransferOwnership(repoTransfer.User, repoTransfer.Recipient, ctx.Repo.Repository, nil); err != nil { ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) return } From 5c578fcb6f1acfc8d9da9c3a618db49b4a471da1 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 4 Feb 2020 06:37:35 +0100 Subject: [PATCH 33/89] make repository transfer as accepted when user accepts --- models/repo_transfer.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index fbcb0238452bf..4761384e34d6f 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -288,6 +288,10 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } } + if err := acceptRepositoryTransfer(sess, repo); err != nil { + return fmt.Errorf("accept repository transfer: %v", err) + } + // If there was previously a redirect at this location, remove it. if err = deleteRepoRedirect(sess, newOwner.ID, repo.Name); err != nil { return fmt.Errorf("delete repo redirect: %v", err) From e7501ed8156a993f32d4535e52b5453970b85bfa Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 4 Feb 2020 06:42:35 +0100 Subject: [PATCH 34/89] fix test --- models/repo_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/models/repo_test.go b/models/repo_test.go index eb84d23a3a3ac..743b119aa7a94 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -181,20 +181,24 @@ func TestRepositoryTransfer(t *testing.T) { assert.Nil(t, transfer) assert.True(t, IsErrNoPendingTransfer(err)) - assert.NoError(t, StartRepositoryTransfer(doer, "user2", repo)) + user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + + assert.NoError(t, StartRepositoryTransfer(doer, user2, repo)) transfer, err = GetPendingRepositoryTransfer(repo) assert.Nil(t, err) assert.NoError(t, transfer.LoadAttributes()) assert.Equal(t, "user2", transfer.Recipient.Name) + user6 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + // Only transfer can be started at any given time - err = StartRepositoryTransfer(doer, "user6", repo) + err = StartRepositoryTransfer(doer, user6, repo) assert.Error(t, err) assert.True(t, IsErrRepoTransferInProgress(err)) // Unknown user - err = StartRepositoryTransfer(doer, "user1000", repo) + err = StartRepositoryTransfer(doer, &User{ID: 1000, LowerName: "user1000"}, repo) assert.Error(t, err) // Cancel transfer From 4dff694040e1d3a3a2cdcb133a2afc78c36519c4 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Tue, 4 Feb 2020 06:57:18 +0100 Subject: [PATCH 35/89] fix ci --- go.mod | 1 - go.sum | 8 -------- integrations/api_repo_test.go | 1 - modules/notification/ui/ui.go | 15 --------------- modules/structs/repo.go | 2 -- routers/api/v1/repo/transfer.go | 28 +--------------------------- templates/swagger/v1_json.tmpl | 9 --------- 7 files changed, 1 insertion(+), 63 deletions(-) diff --git a/go.mod b/go.mod index ef88d5785974d..de67f582dace3 100644 --- a/go.mod +++ b/go.mod @@ -108,7 +108,6 @@ require ( gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.51.1 gopkg.in/ldap.v3 v3.0.2 - gopkg.in/macaron.v1 v1.3.4 gopkg.in/src-d/go-billy.v4 v4.3.2 gopkg.in/src-d/go-git.v4 v4.13.1 gopkg.in/testfixtures.v2 v2.5.0 diff --git a/go.sum b/go.sum index 4b0877828a0e0..2674c600022f6 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,6 @@ cloud.google.com/go v0.45.0 h1:bALuGBSgE+BD4rxsopAYlqjcwqcQtye6pWG4bC3N/k0= cloud.google.com/go v0.45.0/go.mod h1:452BcPOeI9AZfbvDw0Tbo7D32wA+WX9WME8AZwMEDZU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -gitea.com/lunny/levelqueue v0.1.0 h1:7wMk0VH6mvKN6vZEZCy9nUDgRmdPLgeNrm1NkW8EHNk= -gitea.com/lunny/levelqueue v0.1.0/go.mod h1:G7hVb908t0Bl0uk7zGSg14fyzNtxgtD9Shf04wkMK7s= gitea.com/lunny/levelqueue v0.2.0 h1:lR/5EAwQtFcn5YvPEkNMw0p9pAy2/O2nSP5ImECLA2E= gitea.com/lunny/levelqueue v0.2.0/go.mod h1:G7hVb908t0Bl0uk7zGSg14fyzNtxgtD9Shf04wkMK7s= gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b h1:vXt85uYV17KURaUlhU7v4GbCShkqRZDSfo0TkC0YCjQ= @@ -172,8 +170,6 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1T github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 h1:NjHlg70DuOkcAMqgt0+XA+NHwtu66MkTVVgR4fFWbcI= -github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -657,7 +653,6 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= @@ -686,7 +681,6 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 h1:kJQZhwFzSwJS2BxboKjdZzWczQOZx8VuH7Y8hhuGUtM= golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -740,8 +734,6 @@ gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w= gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= -gopkg.in/macaron.v1 v1.3.4 h1:HvIscOwxhFhx3swWM/979wh2QMYyuXrNmrF9l+j3HZs= -gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index 715fc629aaba3..f1296371bee46 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -434,7 +434,6 @@ func TestAPIRepoTransfer(t *testing.T) { token = getTokenForLoggedInUser(t, session) req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{ NewOwner: testCase.newOwner, - TeamIDs: testCase.teams, }) session.MakeRequest(t, req, testCase.expectedStatus) } diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index 87b646a533d8b..2344319101fcf 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -14,7 +14,6 @@ type ( notificationService struct { base.NullNotifier issueQueue chan issueNotificationOpts - repoQueue chan repoNotificationOpts } issueNotificationOpts struct { @@ -22,11 +21,6 @@ type ( commentID int64 notificationAuthorID int64 } - - repoNotificationOpts struct { - repo *models.Repository - recipientID, doerID int64 - } ) var ( @@ -37,7 +31,6 @@ var ( func NewNotifier() base.Notifier { return ¬ificationService{ issueQueue: make(chan issueNotificationOpts, 100), - repoQueue: make(chan repoNotificationOpts, 100), } } @@ -99,11 +92,3 @@ func (ns *notificationService) NotifyPullRequestReview(pr *models.PullRequest, r } ns.issueQueue <- opts } - -func (ns *notificationService) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) { - ns.repoQueue <- repoNotificationOpts{ - repo: repo, - // recipientID: user.ID, - doerID: doer.ID, - } -} diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 04cc594f2261a..644939dbfb003 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -163,8 +163,6 @@ type EditRepoOption struct { type TransferRepoOption struct { // required: true NewOwner string `json:"new_owner"` - // ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories. - TeamIDs *[]int64 `json:"team_ids"` } // GitServiceType represents a git service diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index 847028d1067d5..488179fb45927 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -5,12 +5,10 @@ package repo import ( - "fmt" "net/http" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" repo_service "code.gitea.io/gitea/services/repository" @@ -60,31 +58,7 @@ func Transfer(ctx *context.APIContext, opts api.TransferRepoOption) { return } - var teams []*models.Team - if opts.TeamIDs != nil { - if !newOwner.IsOrganization() { - ctx.Error(http.StatusUnprocessableEntity, "repoTransfer", "Teams can only be added to organization-owned repositories") - return - } - - org := convert.ToOrganization(newOwner) - for _, tID := range *opts.TeamIDs { - team, err := models.GetTeamByID(tID) - if err != nil { - ctx.Error(http.StatusUnprocessableEntity, "team", fmt.Errorf("team %d not found", tID)) - return - } - - if team.OrgID != org.ID { - ctx.Error(http.StatusForbidden, "team", fmt.Errorf("team %d belongs not to org %d", tID, org.ID)) - return - } - - teams = append(teams, team) - } - } - - if err = repo_service.TransferOwnership(ctx.User, newOwner, ctx.Repo.Repository, teams); err != nil { + if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, ctx.Repo.Repository); err != nil { ctx.InternalServerError(err) return } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 35c700a765b2d..ffe97eadb65de 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -12645,15 +12645,6 @@ "new_owner": { "type": "string", "x-go-name": "NewOwner" - }, - "team_ids": { - "description": "ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories.", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - }, - "x-go-name": "TeamIDs" } }, "x-go-package": "code.gitea.io/gitea/modules/structs" From dd29d2f0002f1b08523297f889035f4099005b22 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sun, 16 Feb 2020 04:35:55 +0100 Subject: [PATCH 36/89] add support for teams ID --- integrations/api_repo_test.go | 1 + models/repo_transfer.go | 27 +++++++++++++++++++++++++-- modules/structs/repo.go | 3 +++ routers/api/v1/repo/transfer.go | 29 ++++++++++++++++++++++++++++- routers/repo/repo.go | 2 +- routers/repo/setting.go | 2 +- services/repository/transfer.go | 4 ++-- 7 files changed, 61 insertions(+), 7 deletions(-) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index 454ede0d95e6d..ff59fac5cbdf7 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -434,6 +434,7 @@ func TestAPIRepoTransfer(t *testing.T) { token = getTokenForLoggedInUser(t, session) req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/transfer?token=%s", repo.OwnerName, repo.Name, token), &api.TransferRepoOption{ NewOwner: testCase.newOwner, + TeamIDs: testCase.teams, }) session.MakeRequest(t, req, testCase.expectedStatus) } diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 4761384e34d6f..9a8dfd30a0564 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -40,11 +40,13 @@ type RepoTransfer struct { CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` Status TransferStatus + TeamIDs []int64 + Teams []*Team } // LoadAttributes fetches the transfer recipient from the database func (r *RepoTransfer) LoadAttributes() error { - if r.Recipient != nil && r.User != nil { + if r.Recipient != nil && r.User != nil && (len(r.TeamIDs) > 0 && len(r.Teams) == len(r.TeamIDs)) { return nil } @@ -57,6 +59,22 @@ func (r *RepoTransfer) LoadAttributes() error { r.Recipient = u } + if r.Recipient.IsOrganization() && len(r.TeamIDs) != len(r.Teams) { + + for _, v := range r.TeamIDs { + team, err := GetTeamByID(v) + if err != nil { + return err + } + + if team.OrgID != r.Recipient.ID { + return fmt.Errorf("team %d belongs not to org %d", v, r.Recipient.ID) + } + + r.Teams = append(r.Teams, team) + } + } + if r.User == nil { u, err := GetUserByID(r.UserID) if err != nil { @@ -136,7 +154,7 @@ func CancelRepositoryTransfer(repoTransfer *RepoTransfer) error { // StartRepositoryTransfer marks the repository transfer as "pending". It // doesn't actually transfer the repository until the user acks the transfer. -func StartRepositoryTransfer(doer, newOwner *User, repo *Repository) error { +func StartRepositoryTransfer(doer, newOwner *User, repo *Repository, teams []*Team) error { // Make sure the repo isn't being transferred to someone currently // Only one transfer process can be initiated at a time. // It has to be cancelled for a new one to occur @@ -165,6 +183,11 @@ func StartRepositoryTransfer(doer, newOwner *User, repo *Repository) error { CreatedUnix: timeutil.TimeStampNow(), UpdatedUnix: timeutil.TimeStampNow(), UserID: doer.ID, + TeamIDs: []int64{}, + } + + for k := range teams { + transfer.TeamIDs = append(transfer.TeamIDs, teams[k].ID) } _, err = x.Insert(transfer) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 644939dbfb003..0f4e07f2f3394 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -163,6 +163,9 @@ type EditRepoOption struct { type TransferRepoOption struct { // required: true NewOwner string `json:"new_owner"` + // ID of the team or teams to add to the repository. + // Teams can only be added to organization-owned repositories. + TeamIDs *[]int64 `json:"team_ids"` } // GitServiceType represents a git service diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index 488179fb45927..a4ee038786049 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -5,10 +5,12 @@ package repo import ( + "fmt" "net/http" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" repo_service "code.gitea.io/gitea/services/repository" @@ -58,7 +60,32 @@ func Transfer(ctx *context.APIContext, opts api.TransferRepoOption) { return } - if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, ctx.Repo.Repository); err != nil { + var teams []*models.Team + + if opts.TeamIDs != nil { + if !newOwner.IsOrganization() { + ctx.Error(http.StatusUnprocessableEntity, "repoTransfer", "Teams can only be added to organization-owned repositories") + return + } + + org := convert.ToOrganization(newOwner) + for _, tID := range *opts.TeamIDs { + team, err := models.GetTeamByID(tID) + if err != nil { + ctx.Error(http.StatusUnprocessableEntity, "team", fmt.Errorf("team %d not found", tID)) + return + } + + if team.OrgID != org.ID { + ctx.Error(http.StatusForbidden, "team", fmt.Errorf("team %d belongs not to org %d", tID, org.ID)) + return + } + + teams = append(teams, team) + } + } + + if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, ctx.Repo.Repository, teams); err != nil { ctx.InternalServerError(err) return } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 0b7e896d49430..c0e31a8e05a50 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -415,7 +415,7 @@ func Action(ctx *context.Context) { return } - if err := repo_service.TransferOwnership(repoTransfer.User, repoTransfer.Recipient, ctx.Repo.Repository, nil); err != nil { + if err := repo_service.TransferOwnership(repoTransfer.User, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil { ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) return } diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 5dec734e88951..04bbac24ba609 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -392,7 +392,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { ctx.Repo.GitRepo = nil } - if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, repo); err != nil { + if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, repo, nil); err != nil { if models.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) } else if models.IsErrRepoTransferInProgress(err) { diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 211cee91a0c3c..c097c95c07191 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -74,6 +74,6 @@ func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoNam } // StartRepositoryTransfer marks the repository transfer as "pending". -func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repository) error { - return models.StartRepositoryTransfer(doer, newOwner, repo) +func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repository, teams []*models.Team) error { + return models.StartRepositoryTransfer(doer, newOwner, repo, teams) } From f43d9a4929218c31fe6e107f6fc2af1f5dcacdd1 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sun, 16 Feb 2020 04:38:24 +0100 Subject: [PATCH 37/89] Add omit tag --- models/repo_transfer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 9a8dfd30a0564..41f938b7adb47 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -41,7 +41,7 @@ type RepoTransfer struct { UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` Status TransferStatus TeamIDs []int64 - Teams []*Team + Teams []*Team `xorm:"-"` } // LoadAttributes fetches the transfer recipient from the database From b481a5ae1b5c27f75ab8db694baa2a4966aa781e Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sun, 16 Feb 2020 16:46:46 +0100 Subject: [PATCH 38/89] update swagger --- models/repo_test.go | 6 +++--- routers/api/v1/repo/transfer.go | 13 ++++++------- templates/swagger/v1_json.tmpl | 9 +++++++++ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/models/repo_test.go b/models/repo_test.go index 743b119aa7a94..b3f5ef9d4ba27 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -183,7 +183,7 @@ func TestRepositoryTransfer(t *testing.T) { user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - assert.NoError(t, StartRepositoryTransfer(doer, user2, repo)) + assert.NoError(t, StartRepositoryTransfer(doer, user2, repo, nil)) transfer, err = GetPendingRepositoryTransfer(repo) assert.Nil(t, err) @@ -193,12 +193,12 @@ func TestRepositoryTransfer(t *testing.T) { user6 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) // Only transfer can be started at any given time - err = StartRepositoryTransfer(doer, user6, repo) + err = StartRepositoryTransfer(doer, user6, repo, nil) assert.Error(t, err) assert.True(t, IsErrRepoTransferInProgress(err)) // Unknown user - err = StartRepositoryTransfer(doer, &User{ID: 1000, LowerName: "user1000"}, repo) + err = StartRepositoryTransfer(doer, &User{ID: 1000, LowerName: "user1000"}, repo, nil) assert.Error(t, err) // Cancel transfer diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index a4ee038786049..46907cac99537 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -86,16 +86,15 @@ func Transfer(ctx *context.APIContext, opts api.TransferRepoOption) { } if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, ctx.Repo.Repository, teams); err != nil { - ctx.InternalServerError(err) - return - } + if models.IsErrRepoTransferInProgress(err) { + ctx.Error(http.StatusConflict, "StartRepositoryTransfer", err) + return + } - newRepo, err := models.GetRepositoryByName(newOwner.ID, ctx.Repo.Repository.Name) - if err != nil { ctx.InternalServerError(err) return } - log.Trace("Repository transferred: %s -> %s", ctx.Repo.Repository.FullName(), newOwner.Name) - ctx.JSON(http.StatusAccepted, newRepo.APIFormat(models.AccessModeAdmin)) + log.Trace("Repository transfer initiated: %s -> %s", ctx.Repo.Repository.FullName(), newOwner.Name) + ctx.JSON(http.StatusAccepted, ctx.Repo.Repository.APIFormat(models.AccessModeAdmin)) } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 37e63ae1a3974..de00edac31f3c 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13186,6 +13186,15 @@ "new_owner": { "type": "string", "x-go-name": "NewOwner" + }, + "team_ids": { + "description": "ID of the team or teams to add to the repository.\nTeams can only be added to organization-owned repositories.", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "x-go-name": "TeamIDs" } }, "x-go-package": "code.gitea.io/gitea/modules/structs" From 8d8bb35c88d70e5717c3d79225a215caf98c7847 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sun, 16 Feb 2020 22:35:18 +0100 Subject: [PATCH 39/89] fix test --- integrations/api_repo_test.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index ff59fac5cbdf7..2b886e8e08194 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -400,12 +400,17 @@ func TestAPIRepoTransfer(t *testing.T) { teams *[]int64 expectedStatus int }{ + // Transfer to a user with teams in another org should fail + {ctxUserID: 1, newOwner: "user3", teams: &[]int64{5}, expectedStatus: http.StatusForbidden}, + // Transfer to a user with non-existent team IDs should fail + {ctxUserID: 1, newOwner: "user2", teams: &[]int64{2}, expectedStatus: http.StatusUnprocessableEntity}, + // Transfer should go through {ctxUserID: 1, newOwner: "user2", teams: nil, expectedStatus: http.StatusAccepted}, - {ctxUserID: 2, newOwner: "user1", teams: nil, expectedStatus: http.StatusAccepted}, + // Transfer already started.. Cannot start transfer to another + // user again + {ctxUserID: 1, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusConflict}, + // User does not have access to repo {ctxUserID: 2, newOwner: "user6", teams: nil, expectedStatus: http.StatusForbidden}, - {ctxUserID: 1, newOwner: "user2", teams: &[]int64{2}, expectedStatus: http.StatusUnprocessableEntity}, - {ctxUserID: 1, newOwner: "user3", teams: &[]int64{5}, expectedStatus: http.StatusForbidden}, - {ctxUserID: 1, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusAccepted}, } defer prepareTestEnv(t)() From 4084c407b10d9a006aef92c30f0126c3833f5421 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sun, 16 Feb 2020 22:42:03 +0100 Subject: [PATCH 40/89] Fix swagger definition --- modules/structs/repo.go | 3 +-- templates/swagger/v1_json.tmpl | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 0f4e07f2f3394..04cc594f2261a 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -163,8 +163,7 @@ type EditRepoOption struct { type TransferRepoOption struct { // required: true NewOwner string `json:"new_owner"` - // ID of the team or teams to add to the repository. - // Teams can only be added to organization-owned repositories. + // ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories. TeamIDs *[]int64 `json:"team_ids"` } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index de00edac31f3c..b52145a0a9b6f 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13188,7 +13188,7 @@ "x-go-name": "NewOwner" }, "team_ids": { - "description": "ID of the team or teams to add to the repository.\nTeams can only be added to organization-owned repositories.", + "description": "ID of the team or teams to add to the repository. Teams can only be added to organization-owned repositories.", "type": "array", "items": { "type": "integer", From 90578dedf7604b34e0843757fa6b6f0d0506d86b Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Mon, 17 Feb 2020 09:22:55 +0100 Subject: [PATCH 41/89] update migration --- models/migrations/v128.go | 1 + 1 file changed, 1 insertion(+) diff --git a/models/migrations/v128.go b/models/migrations/v128.go index 2b1e3c4d36fed..6d89b8fbb04f0 100644 --- a/models/migrations/v128.go +++ b/models/migrations/v128.go @@ -19,6 +19,7 @@ func addRepoTransfer(x *xorm.Engine) error { CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` Status bool + TeamIDs []int64 } return x.Sync(new(RepoTransfer)) From 140d8a7483f1a3e0be0c2a3da5f9f34467572b4f Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 00:44:10 +0100 Subject: [PATCH 42/89] clean --- modules/notification/base/null.go | 7 ++++--- modules/notification/ui/ui.go | 2 +- routers/api/v1/repo/transfer.go | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go index ec0df0ea9143f..2386f925cec6a 100644 --- a/modules/notification/base/null.go +++ b/modules/notification/base/null.go @@ -21,9 +21,6 @@ var ( func (*NullNotifier) Run() { } -// NotifyTransferRepository places a place holder function -func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, _ string) {} - // NotifyCreateIssueComment places a place holder function func (*NullNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository, issue *models.Issue, comment *models.Comment, mentions []*models.User) { @@ -154,6 +151,10 @@ func (*NullNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, func (*NullNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) { } +// NotifyTransferRepository places a place holder function +func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) { +} + // NotifySyncPushCommits places a place holder function func (*NullNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) { } diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index b8ba4792a229c..25ea4d91c643f 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -27,7 +27,7 @@ type ( ) var ( - _ base.Notifier = (*notificationService)(nil) + _ base.Notifier = ¬ificationService{} ) // NewNotifier create a new notificationService notifier diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index 0e0da6205d436..caa354dcb5b51 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -73,7 +73,6 @@ func Transfer(ctx *context.APIContext) { } var teams []*models.Team - if opts.TeamIDs != nil { if !newOwner.IsOrganization() { ctx.Error(http.StatusUnprocessableEntity, "repoTransfer", "Teams can only be added to organization-owned repositories") From 9595bccf4c0f5b27b232a4108db14df4d6717fb0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 00:48:06 +0100 Subject: [PATCH 43/89] adapt newest changes --- models/repo_transfer.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 41f938b7adb47..9a9066a94df4b 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -8,10 +8,11 @@ import ( "fmt" "os" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" - "xorm.io/xorm" + "code.gitea.io/gitea/modules/util" - "github.com/unknwon/com" + "xorm.io/xorm" ) // TransferStatus determines the current state of a transfer @@ -259,7 +260,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } if newOwner.IsOrganization() { - if err := newOwner.GetTeams(&SearchTeamOptions{}); err != nil { + if err := newOwner.getTeams(sess); err != nil { return fmt.Errorf("GetTeams: %v", err) } for _, t := range newOwner.Teams { @@ -305,7 +306,12 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error // Rename remote wiki repository to new path and delete local copy. wikiPath := WikiPath(oldOwner.Name, repo.Name) - if com.IsExist(wikiPath) { + isExist, err := util.IsExist(wikiPath) + if err != nil { + log.Error("Unable to check if %s exists. Error: %v", wikiPath, err) + return err + } + if isExist { if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { return fmt.Errorf("rename repository wiki: %v", err) } @@ -320,8 +326,8 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error return fmt.Errorf("delete repo redirect: %v", err) } - if err := NewRepoRedirect(DBContext{sess}, oldOwner.ID, repo.ID, repo.Name, repo.Name); err != nil { - return fmt.Errorf("NewRepoRedirect: %v", err) + if err := newRepoRedirect(sess, oldOwner.ID, repo.ID, repo.Name, repo.Name); err != nil { + return fmt.Errorf("newRepoRedirect: %v", err) } return sess.Commit() From 0e3f44ba1eaba406d63339bac849b8621e5be3d4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 02:17:42 +0100 Subject: [PATCH 44/89] Check if Tepo is ready for transfer & transfer directly if allowed --- models/repo.go | 5 +++-- services/repository/transfer.go | 14 +++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/models/repo.go b/models/repo.go index 562b89ca6e28a..001067289fe46 100644 --- a/models/repo.go +++ b/models/repo.go @@ -139,8 +139,9 @@ type RepositoryStatus int // all kinds of RepositoryStatus const ( - RepositoryReady RepositoryStatus = iota // a normal repository - RepositoryBeingMigrated // repository is migrating + RepositoryReady RepositoryStatus = iota // a normal repository + RepositoryBeingMigrated // repository is migrating + RepositoryPendingTransfer // repository pending in ownership transfer state ) // TrustModelType defines the types of trust model for this repository diff --git a/services/repository/transfer.go b/services/repository/transfer.go index e6b1b7d62742d..1398f80ce2186 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -71,7 +71,19 @@ func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoNam return nil } -// StartRepositoryTransfer marks the repository transfer as "pending". +// StartRepositoryTransfer transfer a repo from one owner to a new one. +// it marks the repository transfer as "pending", +// if the new owner is a user or if he dont have access to the new place. func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repository, teams []*models.Team) error { + if err := models.TestRepositoryReadyForTransfer(repo.Status); err != nil { + return err + } + + // if user is allowed to directly transfer, do it + if doer.IsAdmin || newOwner.IsOrganization() && newOwner.HasMemberWithUserID(doer.ID) { + return TransferOwnership(doer, newOwner, repo, teams) + } + + // Make repo as pending for transfer return models.StartRepositoryTransfer(doer, newOwner, repo, teams) } From 36419590d0a7db64bc76a7563e8e3ccfa45c9358 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 02:18:13 +0100 Subject: [PATCH 45/89] exec StartRepositoryTransfer within a db session --- models/repo_transfer.go | 43 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 9a9066a94df4b..43f92a29e20e8 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -153,9 +153,48 @@ func CancelRepositoryTransfer(repoTransfer *RepoTransfer) error { return err } -// StartRepositoryTransfer marks the repository transfer as "pending". It -// doesn't actually transfer the repository until the user acks the transfer. +// TestRepositoryReadyForTransfer make sure repo is ready to transfer +func TestRepositoryReadyForTransfer(status RepositoryStatus) error { + switch status { + case RepositoryBeingMigrated: + return fmt.Errorf("repo is not ready, currently migrating") + case RepositoryPendingTransfer: + return ErrRepoTransferInProgress{} + } + return nil +} + +// StartRepositoryTransfer transfer a repo from one owner to a new one. +// it marks the repository transfer as "pending", +// if the new owner is a user or if he dont have access to the new place. func StartRepositoryTransfer(doer, newOwner *User, repo *Repository, teams []*Team) error { + sess := x.NewSession() + if err := sess.Begin(); err != nil { + return err + } + defer sess.Close() + if err := startRepositoryTransfer(sess, doer, newOwner, repo.ID, teams); err != nil { + return err + } + return sess.Commit() +} + +func startRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams []*Team) error { + repo, err := getRepositoryByID(e, repoID) + if err != nil { + return err + } + + // Make sure repo is ready to transfer + if err = TestRepositoryReadyForTransfer(repo.Status); err != nil { + return err + } + + repo.Status = RepositoryPendingTransfer + if err = updateRepositoryCols(e, repo, "status"); err != nil { + return err + } + // Make sure the repo isn't being transferred to someone currently // Only one transfer process can be initiated at a time. // It has to be cancelled for a new one to occur From 6fa25c0465baff906a4e71f6cd9c96b6278740c0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 02:22:34 +0100 Subject: [PATCH 46/89] just delete RepositoryTransfer entry if done --- models/repo_transfer.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 43f92a29e20e8..f45460510784b 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -11,8 +11,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" - - "xorm.io/xorm" ) // TransferStatus determines the current state of a transfer @@ -136,10 +134,8 @@ func GetPendingRepositoryTransfer(repo *Repository) (*RepoTransfer, error) { return transfer, nil } -func acceptRepositoryTransfer(sess *xorm.Session, repo *Repository) error { - _, err := sess.Where("repo_id = ?", repo.ID).Cols("status").Update(&RepoTransfer{ - Status: Accepted, - }) +func deleteRepositoryTransfer(e Engine, repoID int64) error { + _, err := e.Where("repo_id = ?", repoID).Delete(&RepoTransfer{}) return err } @@ -198,7 +194,7 @@ func startRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams // Make sure the repo isn't being transferred to someone currently // Only one transfer process can be initiated at a time. // It has to be cancelled for a new one to occur - n, err := x.Where("status = ? AND repo_id = ?", Pending, repo.ID). + n, err := e.Where("status = ? AND repo_id = ?", Pending, repo.ID). Count(new(RepoTransfer)) if err != nil { return err @@ -230,7 +226,7 @@ func startRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams transfer.TeamIDs = append(transfer.TeamIDs, teams[k].ID) } - _, err = x.Insert(transfer) + _, err = e.Insert(transfer) return err } @@ -356,7 +352,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } } - if err := acceptRepositoryTransfer(sess, repo); err != nil { + if err = deleteRepositoryTransfer(sess, repo.ID); err != nil { return fmt.Errorf("accept repository transfer: %v", err) } From 3d541d3e4da35893e7dcf8c076429437a82ad4c4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 02:27:49 +0100 Subject: [PATCH 47/89] rm unused & fix migration --- models/migrations/v174.go | 2 +- models/repo_transfer.go | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/models/migrations/v174.go b/models/migrations/v174.go index 6d89b8fbb04f0..62b631d8116e2 100644 --- a/models/migrations/v174.go +++ b/models/migrations/v174.go @@ -18,7 +18,7 @@ func addRepoTransfer(x *xorm.Engine) error { RepoID int64 CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` - Status bool + Status uint8 TeamIDs []int64 } diff --git a/models/repo_transfer.go b/models/repo_transfer.go index f45460510784b..19430756c3e26 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -23,9 +23,6 @@ const ( // Rejected is a status for transfers that get cancelled by either the // recipient or the user who initiated the transfer Rejected - // Accepted is a repo transfer state for repository transfers that have - // been acknowledged by the recipient - Accepted ) // RepoTransfer is used to manage repository transfers From 0dbeb87ff8fab0236c8e4ba1e683176bc66df205 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 02:52:10 +0100 Subject: [PATCH 48/89] API, dates, tests --- integrations/api_repo_test.go | 4 ++- models/repo_test.go | 36 ------------------------- models/repo_transfer.go | 6 ++--- models/repo_transfer_test.go | 47 +++++++++++++++++++++++++++++++++ routers/api/v1/repo/transfer.go | 8 +++++- services/repository/transfer.go | 3 ++- 6 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 models/repo_transfer_test.go diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index 409c808c720eb..fb6c8e42844ff 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -448,8 +448,10 @@ func TestAPIRepoTransfer(t *testing.T) { {ctxUserID: 1, newOwner: "user3", teams: &[]int64{5}, expectedStatus: http.StatusForbidden}, // Transfer to a user with non-existent team IDs should fail {ctxUserID: 1, newOwner: "user2", teams: &[]int64{2}, expectedStatus: http.StatusUnprocessableEntity}, + // Pending transfer is expected + {ctxUserID: 2, newOwner: "user1", teams: nil, expectedStatus: http.StatusCreated}, // Transfer should go through - {ctxUserID: 1, newOwner: "user2", teams: nil, expectedStatus: http.StatusAccepted}, + {ctxUserID: 2, newOwner: "user3", teams: nil, expectedStatus: http.StatusAccepted}, // Transfer already started.. Cannot start transfer to another // user again {ctxUserID: 1, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusConflict}, diff --git a/models/repo_test.go b/models/repo_test.go index 64c5d0a596dcb..a366772d5c257 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -169,42 +169,6 @@ func TestUploadBigAvatar(t *testing.T) { assert.Error(t, err) } -func TestRepositoryTransfer(t *testing.T) { - - assert.NoError(t, PrepareTestDatabase()) - - doer := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) - repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) - - transfer, err := GetPendingRepositoryTransfer(repo) - assert.Error(t, err) - assert.Nil(t, transfer) - assert.True(t, IsErrNoPendingTransfer(err)) - - user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - - assert.NoError(t, StartRepositoryTransfer(doer, user2, repo, nil)) - - transfer, err = GetPendingRepositoryTransfer(repo) - assert.Nil(t, err) - assert.NoError(t, transfer.LoadAttributes()) - assert.Equal(t, "user2", transfer.Recipient.Name) - - user6 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - - // Only transfer can be started at any given time - err = StartRepositoryTransfer(doer, user6, repo, nil) - assert.Error(t, err) - assert.True(t, IsErrRepoTransferInProgress(err)) - - // Unknown user - err = StartRepositoryTransfer(doer, &User{ID: 1000, LowerName: "user1000"}, repo, nil) - assert.Error(t, err) - - // Cancel transfer - assert.NoError(t, CancelRepositoryTransfer(transfer)) -} - func TestDeleteAvatar(t *testing.T) { // Generate image diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 19430756c3e26..fb5f030e71447 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -160,13 +160,13 @@ func TestRepositoryReadyForTransfer(status RepositoryStatus) error { // StartRepositoryTransfer transfer a repo from one owner to a new one. // it marks the repository transfer as "pending", // if the new owner is a user or if he dont have access to the new place. -func StartRepositoryTransfer(doer, newOwner *User, repo *Repository, teams []*Team) error { +func StartRepositoryTransfer(doer, newOwner *User, repoID int64, teams []*Team) error { sess := x.NewSession() if err := sess.Begin(); err != nil { return err } defer sess.Close() - if err := startRepositoryTransfer(sess, doer, newOwner, repo.ID, teams); err != nil { + if err := startRepositoryTransfer(sess, doer, newOwner, repoID, teams); err != nil { return err } return sess.Commit() diff --git a/models/repo_transfer_test.go b/models/repo_transfer_test.go new file mode 100644 index 0000000000000..9e5193bc10201 --- /dev/null +++ b/models/repo_transfer_test.go @@ -0,0 +1,47 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRepositoryTransfer(t *testing.T) { + + assert.NoError(t, PrepareTestDatabase()) + + doer := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) + repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) + + transfer, err := GetPendingRepositoryTransfer(repo) + assert.Error(t, err) + assert.Nil(t, transfer) + assert.True(t, IsErrNoPendingTransfer(err)) + + user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + + assert.NoError(t, StartRepositoryTransfer(doer, user2, repo.ID, nil)) + + transfer, err = GetPendingRepositoryTransfer(repo) + assert.Nil(t, err) + assert.NoError(t, transfer.LoadAttributes()) + assert.Equal(t, "user2", transfer.Recipient.Name) + + user6 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + + // Only transfer can be started at any given time + err = StartRepositoryTransfer(doer, user6, repo.ID, nil) + assert.Error(t, err) + assert.True(t, IsErrRepoTransferInProgress(err)) + + // Unknown user + err = StartRepositoryTransfer(doer, &User{ID: 1000, LowerName: "user1000"}, repo.ID, nil) + assert.Error(t, err) + + // Cancel transfer + assert.NoError(t, CancelRepositoryTransfer(transfer)) +} diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index caa354dcb5b51..d4e8d4bd98848 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -106,6 +106,12 @@ func Transfer(ctx *context.APIContext) { return } - log.Trace("Repository transfer initiated: %s -> %s", ctx.Repo.Repository.FullName(), newOwner.Name) + if ctx.Repo.Repository.Status == models.RepositoryPendingTransfer { + log.Trace("Repository transfer initiated: %s -> %s", ctx.Repo.Repository.FullName(), newOwner.Name) + ctx.JSON(http.StatusCreated, convert.ToRepo(ctx.Repo.Repository, models.AccessModeAdmin)) + return + } + + log.Trace("Repository transferred: %s -> %s", ctx.Repo.Repository.FullName(), newOwner.Name) ctx.JSON(http.StatusAccepted, convert.ToRepo(ctx.Repo.Repository, models.AccessModeAdmin)) } diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 1398f80ce2186..3b2f143a16b05 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -85,5 +85,6 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor } // Make repo as pending for transfer - return models.StartRepositoryTransfer(doer, newOwner, repo, teams) + repo.Status = models.RepositoryPendingTransfer + return models.StartRepositoryTransfer(doer, newOwner, repo.ID, teams) } From 4c409ed392a19f2c3f63d436108bdce0256763ed Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 03:12:27 +0100 Subject: [PATCH 49/89] remove TransferStatus --- models/migrations/v174.go | 1 - models/repo_transfer.go | 53 ++++++++++++------------------------ models/repo_transfer_test.go | 2 +- routers/repo/repo.go | 4 +-- routers/repo/setting.go | 4 +-- 5 files changed, 23 insertions(+), 41 deletions(-) diff --git a/models/migrations/v174.go b/models/migrations/v174.go index 62b631d8116e2..ba34a1e43069d 100644 --- a/models/migrations/v174.go +++ b/models/migrations/v174.go @@ -18,7 +18,6 @@ func addRepoTransfer(x *xorm.Engine) error { RepoID int64 CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` - Status uint8 TeamIDs []int64 } diff --git a/models/repo_transfer.go b/models/repo_transfer.go index fb5f030e71447..4e5bfd985ee7d 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -13,18 +13,6 @@ import ( "code.gitea.io/gitea/modules/util" ) -// TransferStatus determines the current state of a transfer -type TransferStatus uint8 - -const ( - // Pending is the default repo transfer state. All initiated transfers - // automatically get this status. - Pending TransferStatus = iota - // Rejected is a status for transfers that get cancelled by either the - // recipient or the user who initiated the transfer - Rejected -) - // RepoTransfer is used to manage repository transfers type RepoTransfer struct { ID int64 `xorm:"pk autoincr"` @@ -35,7 +23,6 @@ type RepoTransfer struct { RepoID int64 CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` - Status TransferStatus TeamIDs []int64 Teams []*Team `xorm:"-"` } @@ -118,8 +105,7 @@ func (r *RepoTransfer) IsTransferForUser(u *User) bool { func GetPendingRepositoryTransfer(repo *Repository) (*RepoTransfer, error) { var transfer = new(RepoTransfer) - has, err := x.Where("status = ? AND repo_id = ? ", Pending, repo.ID). - Get(transfer) + has, err := x.Where("repo_id = ? ", repo.ID).Get(transfer) if err != nil { return nil, err } @@ -138,12 +124,23 @@ func deleteRepositoryTransfer(e Engine, repoID int64) error { // CancelRepositoryTransfer makes sure to set the transfer process as // "rejected". Thus ending the transfer process -func CancelRepositoryTransfer(repoTransfer *RepoTransfer) error { - repoTransfer.Status = Rejected - repoTransfer.UpdatedUnix = timeutil.TimeStampNow() - _, err := x.ID(repoTransfer.ID).Cols("updated_unix", "status"). - Update(repoTransfer) - return err +func CancelRepositoryTransfer(repo *Repository) error { + sess := x.NewSession() + if err := sess.Begin(); err != nil { + return err + } + defer sess.Close() + + repo.Status = RepositoryReady + if err := updateRepositoryCols(sess, repo, "status"); err != nil { + return err + } + + if err := deleteRepositoryTransfer(sess, repo.ID); err != nil { + return err + } + + return sess.Commit() } // TestRepositoryReadyForTransfer make sure repo is ready to transfer @@ -188,19 +185,6 @@ func startRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams return err } - // Make sure the repo isn't being transferred to someone currently - // Only one transfer process can be initiated at a time. - // It has to be cancelled for a new one to occur - n, err := e.Where("status = ? AND repo_id = ?", Pending, repo.ID). - Count(new(RepoTransfer)) - if err != nil { - return err - } - - if n > 0 { - return ErrRepoTransferInProgress{newOwner.LowerName, repo.Name} - } - // Check if new owner has repository with same name. has, err := IsRepositoryExist(newOwner, repo.Name) if err != nil { @@ -212,7 +196,6 @@ func startRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams transfer := &RepoTransfer{ RepoID: repo.ID, RecipientID: newOwner.ID, - Status: Pending, CreatedUnix: timeutil.TimeStampNow(), UpdatedUnix: timeutil.TimeStampNow(), UserID: doer.ID, diff --git a/models/repo_transfer_test.go b/models/repo_transfer_test.go index 9e5193bc10201..86bff433e2707 100644 --- a/models/repo_transfer_test.go +++ b/models/repo_transfer_test.go @@ -43,5 +43,5 @@ func TestRepositoryTransfer(t *testing.T) { assert.Error(t, err) // Cancel transfer - assert.NoError(t, CancelRepositoryTransfer(transfer)) + assert.NoError(t, CancelRepositoryTransfer(repo)) } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index a8ebecd32ad82..587f5e11b06cc 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -312,7 +312,7 @@ func Action(ctx *context.Context) { return } - if err := repoTransfer.LoadAttributes(); err != nil { + if err = repoTransfer.LoadAttributes(); err != nil { ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) return } @@ -322,7 +322,7 @@ func Action(ctx *context.Context) { return } - if err := models.CancelRepositoryTransfer(repoTransfer); err != nil { + if err = models.CancelRepositoryTransfer(ctx.Repo.Repository); err != nil { if models.IsErrNoPendingTransfer(err) { ctx.Redirect(ctx.Repo.Repository.HTMLURL()) } else { diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 74af715534feb..b7e5afbcefa13 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -524,12 +524,12 @@ func SettingsPost(ctx *context.Context) { return } - if err := repoTransfer.LoadAttributes(); err != nil { + if err = repoTransfer.LoadAttributes(); err != nil { ctx.ServerError("LoadRecipient", err) return } - if err := models.CancelRepositoryTransfer(repoTransfer); err != nil { + if err = models.CancelRepositoryTransfer(ctx.Repo.Repository); err != nil { ctx.ServerError("CancelRepositoryTransfer", err) return } From 0d0fb2627748049618ea89ee1c88709bacae2594 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 03:40:33 +0100 Subject: [PATCH 50/89] ... --- models/migrations/v174.go | 2 +- models/repo_transfer.go | 38 +++++++++++---------------------- routers/repo/repo.go | 6 +++--- services/repository/transfer.go | 15 +++++++++++-- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/models/migrations/v174.go b/models/migrations/v174.go index ba34a1e43069d..d55cb16c6f699 100644 --- a/models/migrations/v174.go +++ b/models/migrations/v174.go @@ -13,7 +13,7 @@ import ( func addRepoTransfer(x *xorm.Engine) error { type RepoTransfer struct { ID int64 `xorm:"pk autoincr"` - UserID int64 + DoerID int64 RecipientID int64 RepoID int64 CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 4e5bfd985ee7d..6d164eb30af7b 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -16,23 +16,20 @@ import ( // RepoTransfer is used to manage repository transfers type RepoTransfer struct { ID int64 `xorm:"pk autoincr"` - UserID int64 - User *User `xorm:"-"` + DoerID int64 + Doer *User `xorm:"-"` RecipientID int64 Recipient *User `xorm:"-"` RepoID int64 - CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` - UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` TeamIDs []int64 Teams []*Team `xorm:"-"` + + CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` } // LoadAttributes fetches the transfer recipient from the database func (r *RepoTransfer) LoadAttributes() error { - if r.Recipient != nil && r.User != nil && (len(r.TeamIDs) > 0 && len(r.Teams) == len(r.TeamIDs)) { - return nil - } - if r.Recipient == nil { u, err := GetUserByID(r.RecipientID) if err != nil { @@ -58,13 +55,13 @@ func (r *RepoTransfer) LoadAttributes() error { } } - if r.User == nil { - u, err := GetUserByID(r.UserID) + if r.Doer == nil { + u, err := GetUserByID(r.DoerID) if err != nil { return err } - r.User = u + r.Doer = u } return nil @@ -72,7 +69,7 @@ func (r *RepoTransfer) LoadAttributes() error { // IsTransferForUser checks if the user has the rights to accept/decline a repo // transfer. -// For organizations, this check if the user is a member of the owners team +// For organizations, it checks if the user is able to create repos func (r *RepoTransfer) IsTransferForUser(u *User) bool { if err := r.LoadAttributes(); err != nil { return false @@ -82,22 +79,13 @@ func (r *RepoTransfer) IsTransferForUser(u *User) bool { return r.RecipientID == u.ID } - t, err := r.Recipient.getOwnerTeam(x) + allowed, err := CanCreateOrgRepo(r.RecipientID, u.ID) if err != nil { + log.Error("CanCreateOrgRepo: %v", err) return false } - if err := t.GetMembers(&SearchMembersOptions{}); err != nil { - return false - } - - for k := range t.Members { - if t.Members[k].ID == u.ID { - return true - } - } - - return false + return allowed } // GetPendingRepositoryTransfer fetches the most recent and ongoing transfer @@ -198,7 +186,7 @@ func startRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams RecipientID: newOwner.ID, CreatedUnix: timeutil.TimeStampNow(), UpdatedUnix: timeutil.TimeStampNow(), - UserID: doer.ID, + DoerID: doer.ID, TeamIDs: []int64{}, } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 587f5e11b06cc..89cdc5e05d11c 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -286,7 +286,7 @@ func Action(ctx *context.Context) { return } - if err := repoTransfer.LoadAttributes(); err != nil { + if err = repoTransfer.LoadAttributes(); err != nil { ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) return } @@ -296,7 +296,7 @@ func Action(ctx *context.Context) { return } - if err := repo_service.TransferOwnership(repoTransfer.User, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil { + if err = repo_service.TransferOwnership(repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil { ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) return } @@ -332,7 +332,7 @@ func Action(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected")) - ctx.Redirect(setting.AppSubURL + "/") + ctx.Redirect(ctx.Repo.Repository.HTMLURL()) return } diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 3b2f143a16b05..4f994b4229c2b 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -79,11 +79,22 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor return err } - // if user is allowed to directly transfer, do it - if doer.IsAdmin || newOwner.IsOrganization() && newOwner.HasMemberWithUserID(doer.ID) { + // Admin is always allowed to transfer + if doer.IsAdmin { return TransferOwnership(doer, newOwner, repo, teams) } + // If new owner is an org and user can create repos he can transfer directly too + if newOwner.IsOrganization() { + allowed, err := models.CanCreateOrgRepo(newOwner.ID, doer.ID) + if err != nil { + return err + } + if allowed { + return TransferOwnership(doer, newOwner, repo, teams) + } + } + // Make repo as pending for transfer repo.Status = models.RepositoryPendingTransfer return models.StartRepositoryTransfer(doer, newOwner, repo.ID, teams) From fc27a9ea2cd0634948ec132bf5593bfd2e5feef6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 03:42:54 +0100 Subject: [PATCH 51/89] rm migration dep --- models/migrations/v174.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/models/migrations/v174.go b/models/migrations/v174.go index d55cb16c6f699..c6e929628ea30 100644 --- a/models/migrations/v174.go +++ b/models/migrations/v174.go @@ -5,8 +5,6 @@ package migrations import ( - "code.gitea.io/gitea/modules/timeutil" - "xorm.io/xorm" ) @@ -16,9 +14,9 @@ func addRepoTransfer(x *xorm.Engine) error { DoerID int64 RecipientID int64 RepoID int64 - CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` - UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"` TeamIDs []int64 + CreatedUnix int64 `xorm:"INDEX NOT NULL created"` + UpdatedUnix int64 `xorm:"INDEX NOT NULL updated"` } return x.Sync(new(RepoTransfer)) From 87e23c994dca6c733b1c90475d2c0a5b610e92f9 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 03:47:16 +0100 Subject: [PATCH 52/89] rm --- routers/repo/setting.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index b7e5afbcefa13..90fd91444333b 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -449,13 +449,6 @@ func SettingsPost(ctx *context.Context) { ctx.Error(404) return } - - if ctx.Repo.Repository.IsArchived { - ctx.Flash.Error(ctx.Tr("repo.archive.repo_transfer")) - ctx.Redirect(setting.AppSubURL + "/" + ctx.Repo.Owner.Name + "/" + repo.Name + "/settings") - return - } - if repo.Name != form.RepoName { ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) return From bb65753166afd8845644c7d82c41f3fd85f378d6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 03:57:08 +0100 Subject: [PATCH 53/89] fix fixtures --- models/fixtures/repo_transfer.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/models/fixtures/repo_transfer.yml b/models/fixtures/repo_transfer.yml index 5d8a5bd79c069..9bf69027f19a5 100644 --- a/models/fixtures/repo_transfer.yml +++ b/models/fixtures/repo_transfer.yml @@ -2,7 +2,6 @@ id: 1 user_id: 3 recipient_id: 1 - status: 1 repo_id: 3 created_unix: 1553610671 updated_unix: 1553610671 From f4b007c0788c638ad7e572dd8060f5bc9863c928 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 04:07:37 +0100 Subject: [PATCH 54/89] fix & co --- models/fixtures/repo_transfer.yml | 2 +- models/repo_transfer.go | 31 +++++++++++++++---------------- models/repo_transfer_test.go | 6 +++--- routers/api/v1/repo/transfer.go | 2 +- services/repository/transfer.go | 4 ++-- 5 files changed, 22 insertions(+), 23 deletions(-) diff --git a/models/fixtures/repo_transfer.yml b/models/fixtures/repo_transfer.yml index 9bf69027f19a5..b841b5e983a1b 100644 --- a/models/fixtures/repo_transfer.yml +++ b/models/fixtures/repo_transfer.yml @@ -1,6 +1,6 @@ - id: 1 - user_id: 3 + doer_id: 3 recipient_id: 1 repo_id: 3 created_unix: 1553610671 diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 6d164eb30af7b..ba272e1243aa2 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -142,22 +142,21 @@ func TestRepositoryReadyForTransfer(status RepositoryStatus) error { return nil } -// StartRepositoryTransfer transfer a repo from one owner to a new one. -// it marks the repository transfer as "pending", -// if the new owner is a user or if he dont have access to the new place. -func StartRepositoryTransfer(doer, newOwner *User, repoID int64, teams []*Team) error { +// CreatePendingRepositoryTransfer transfer a repo from one owner to a new one. +// it marks the repository transfer as "pending" +func CreatePendingRepositoryTransfer(doer, newOwner *User, repoID int64, teams []*Team) error { sess := x.NewSession() + defer sess.Close() if err := sess.Begin(); err != nil { return err } - defer sess.Close() - if err := startRepositoryTransfer(sess, doer, newOwner, repoID, teams); err != nil { + if err := createPendingRepositoryTransfer(sess, doer, newOwner, repoID, teams); err != nil { return err } return sess.Commit() } -func startRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams []*Team) error { +func createPendingRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams []*Team) error { repo, err := getRepositoryByID(e, repoID) if err != nil { return err @@ -200,25 +199,25 @@ func startRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams // TransferOwnership transfers all corresponding setting from old user to new one. func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error { - newOwner, err := GetUserByName(newOwnerName) + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return fmt.Errorf("sess.Begin: %v", err) + } + + newOwner, err := getUserByName(sess, newOwnerName) if err != nil { return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) } // Check if new owner has repository with same name. - has, err := IsRepositoryExist(newOwner, repo.Name) + has, err := isRepositoryExist(sess, newOwner, repo.Name) if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { return ErrRepoAlreadyExist{newOwnerName, repo.Name} } - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return fmt.Errorf("sess.Begin: %v", err) - } - oldOwner := repo.Owner // Note: we have to set value here to make sure recalculate accesses is based on @@ -228,7 +227,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error repo.OwnerName = newOwner.Name // Update repository. - if _, err := sess.ID(repo.ID).Update(repo); err != nil { + if _, err = sess.ID(repo.ID).Update(repo); err != nil { return fmt.Errorf("update owner: %v", err) } diff --git a/models/repo_transfer_test.go b/models/repo_transfer_test.go index 86bff433e2707..b001489e792e8 100644 --- a/models/repo_transfer_test.go +++ b/models/repo_transfer_test.go @@ -24,7 +24,7 @@ func TestRepositoryTransfer(t *testing.T) { user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - assert.NoError(t, StartRepositoryTransfer(doer, user2, repo.ID, nil)) + assert.NoError(t, CreatePendingRepositoryTransfer(doer, user2, repo.ID, nil)) transfer, err = GetPendingRepositoryTransfer(repo) assert.Nil(t, err) @@ -34,12 +34,12 @@ func TestRepositoryTransfer(t *testing.T) { user6 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) // Only transfer can be started at any given time - err = StartRepositoryTransfer(doer, user6, repo.ID, nil) + err = CreatePendingRepositoryTransfer(doer, user6, repo.ID, nil) assert.Error(t, err) assert.True(t, IsErrRepoTransferInProgress(err)) // Unknown user - err = StartRepositoryTransfer(doer, &User{ID: 1000, LowerName: "user1000"}, repo.ID, nil) + err = CreatePendingRepositoryTransfer(doer, &User{ID: 1000, LowerName: "user1000"}, repo.ID, nil) assert.Error(t, err) // Cancel transfer diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index d4e8d4bd98848..1c281f18c395a 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -98,7 +98,7 @@ func Transfer(ctx *context.APIContext) { if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, ctx.Repo.Repository, teams); err != nil { if models.IsErrRepoTransferInProgress(err) { - ctx.Error(http.StatusConflict, "StartRepositoryTransfer", err) + ctx.Error(http.StatusConflict, "CreatePendingRepositoryTransfer", err) return } diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 4f994b4229c2b..23e20fceaa79f 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -41,7 +41,7 @@ func TransferOwnership(doer, newOwner *models.User, repo *models.Repository, tea } for _, team := range teams { - if err := team.AddRepository(newRepo); err != nil { + if err = team.AddRepository(newRepo); err != nil { return err } } @@ -97,5 +97,5 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor // Make repo as pending for transfer repo.Status = models.RepositoryPendingTransfer - return models.StartRepositoryTransfer(doer, newOwner, repo.ID, teams) + return models.CreatePendingRepositoryTransfer(doer, newOwner, repo.ID, teams) } From 31712db75dcf987f8613be46491fcb9d79c2cc7e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 04:15:19 +0100 Subject: [PATCH 55/89] rename & add unrelated rm back --- models/repo_transfer.go | 6 +++--- routers/repo/repo.go | 13 +++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index ba272e1243aa2..8bd71a6f475e7 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -67,10 +67,10 @@ func (r *RepoTransfer) LoadAttributes() error { return nil } -// IsTransferForUser checks if the user has the rights to accept/decline a repo -// transfer. +// CanUserAcceptTransfer checks if the user has the rights to accept/decline a repo transfer. +// For user, it checks if it's himself // For organizations, it checks if the user is able to create repos -func (r *RepoTransfer) IsTransferForUser(u *User) bool { +func (r *RepoTransfer) CanUserAcceptTransfer(u *User) bool { if err := r.LoadAttributes(); err != nil { return false } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 89cdc5e05d11c..e0cfbb39179f7 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -291,8 +291,8 @@ func Action(ctx *context.Context) { return } - if !repoTransfer.IsTransferForUser(ctx.User) { - ctx.NotFound("IsTransferForUser", errors.New("user does not have enough permissions")) + if !repoTransfer.CanUserAcceptTransfer(ctx.User) { + ctx.NotFound("CanUserAcceptTransfer", errors.New("user does not have enough permissions")) return } @@ -317,8 +317,8 @@ func Action(ctx *context.Context) { return } - if !repoTransfer.IsTransferForUser(ctx.User) { - ctx.NotFound("IsTransferForUser", errors.New("user does not have enough permissions")) + if !repoTransfer.CanUserAcceptTransfer(ctx.User) { + ctx.NotFound("CanUserAcceptTransfer", errors.New("user does not have enough permissions")) return } @@ -334,6 +334,11 @@ func Action(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected")) ctx.Redirect(ctx.Repo.Repository.HTMLURL()) return + case "desc": // FIXME: this is not used + if !ctx.Repo.IsOwner() { + ctx.Error(404) + } + return } if err != nil { From f4dd41813e3928e51d19e8c8ae6915a86ba42fed Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 04:49:47 +0100 Subject: [PATCH 56/89] fix deadlock --- models/repo_transfer.go | 21 +++++++++------------ models/repo_transfer_test.go | 7 +++++++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 8bd71a6f475e7..869034bd1725c 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -72,6 +72,7 @@ func (r *RepoTransfer) LoadAttributes() error { // For organizations, it checks if the user is able to create repos func (r *RepoTransfer) CanUserAcceptTransfer(u *User) bool { if err := r.LoadAttributes(); err != nil { + log.Error("LoadAttributes: %v", err) return false } @@ -98,7 +99,7 @@ func GetPendingRepositoryTransfer(repo *Repository) (*RepoTransfer, error) { return nil, err } - if transfer.ID == 0 || !has { + if !has { return nil, ErrNoPendingRepoTransfer{RepoID: repo.ID} } @@ -150,14 +151,8 @@ func CreatePendingRepositoryTransfer(doer, newOwner *User, repoID int64, teams [ if err := sess.Begin(); err != nil { return err } - if err := createPendingRepositoryTransfer(sess, doer, newOwner, repoID, teams); err != nil { - return err - } - return sess.Commit() -} -func createPendingRepositoryTransfer(e Engine, doer, newOwner *User, repoID int64, teams []*Team) error { - repo, err := getRepositoryByID(e, repoID) + repo, err := getRepositoryByID(sess, repoID) if err != nil { return err } @@ -168,12 +163,12 @@ func createPendingRepositoryTransfer(e Engine, doer, newOwner *User, repoID int6 } repo.Status = RepositoryPendingTransfer - if err = updateRepositoryCols(e, repo, "status"); err != nil { + if err = updateRepositoryCols(sess, repo, "status"); err != nil { return err } // Check if new owner has repository with same name. - has, err := IsRepositoryExist(newOwner, repo.Name) + has, err := isRepositoryExist(sess, newOwner, repo.Name) if err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { @@ -193,8 +188,10 @@ func createPendingRepositoryTransfer(e Engine, doer, newOwner *User, repoID int6 transfer.TeamIDs = append(transfer.TeamIDs, teams[k].ID) } - _, err = e.Insert(transfer) - return err + if _, err = sess.Insert(transfer); err != nil { + return err + } + return sess.Commit() } // TransferOwnership transfers all corresponding setting from old user to new one. diff --git a/models/repo_transfer_test.go b/models/repo_transfer_test.go index b001489e792e8..55aedac95d4a1 100644 --- a/models/repo_transfer_test.go +++ b/models/repo_transfer_test.go @@ -18,6 +18,13 @@ func TestRepositoryTransfer(t *testing.T) { repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) transfer, err := GetPendingRepositoryTransfer(repo) + assert.NoError(t, err) + assert.NotNil(t, transfer) + + // Cancel transfer + assert.NoError(t, CancelRepositoryTransfer(repo)) + + transfer, err = GetPendingRepositoryTransfer(repo) assert.Error(t, err) assert.Nil(t, transfer) assert.True(t, IsErrNoPendingTransfer(err)) From 3b181e2d96b606572eed48afcff07683e42227fe Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 05:02:27 +0100 Subject: [PATCH 57/89] Fix Tests & handle RepoExist err --- integrations/api_repo_test.go | 13 +++++-------- routers/api/v1/repo/transfer.go | 5 +++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index fb6c8e42844ff..67368e9311886 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -448,15 +448,12 @@ func TestAPIRepoTransfer(t *testing.T) { {ctxUserID: 1, newOwner: "user3", teams: &[]int64{5}, expectedStatus: http.StatusForbidden}, // Transfer to a user with non-existent team IDs should fail {ctxUserID: 1, newOwner: "user2", teams: &[]int64{2}, expectedStatus: http.StatusUnprocessableEntity}, - // Pending transfer is expected - {ctxUserID: 2, newOwner: "user1", teams: nil, expectedStatus: http.StatusCreated}, // Transfer should go through - {ctxUserID: 2, newOwner: "user3", teams: nil, expectedStatus: http.StatusAccepted}, - // Transfer already started.. Cannot start transfer to another - // user again - {ctxUserID: 1, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusConflict}, - // User does not have access to repo - {ctxUserID: 2, newOwner: "user6", teams: nil, expectedStatus: http.StatusForbidden}, + {ctxUserID: 1, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusAccepted}, + // Cannot start transfer to an existing repo + {ctxUserID: 2, newOwner: "user3", teams: nil, expectedStatus: http.StatusUnprocessableEntity}, + // Start transfer, repo is now in pending transfer mode + {ctxUserID: 2, newOwner: "user6", teams: nil, expectedStatus: http.StatusCreated}, } defer prepareTestEnv(t)() diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index 1c281f18c395a..54104ca2e111d 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -102,6 +102,11 @@ func Transfer(ctx *context.APIContext) { return } + if models.IsErrRepoAlreadyExist(err) { + ctx.Error(http.StatusUnprocessableEntity, "CreatePendingRepositoryTransfer", err) + return + } + ctx.InternalServerError(err) return } From c8019ec94b405c9a66b741cd61ddb4151bbb33bc Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 05:15:07 +0100 Subject: [PATCH 58/89] dont save if RepoTransfer exist in extra var for templates & more --- models/repo_transfer.go | 3 +++ modules/context/repo.go | 12 ++++++++++++ services/repository/transfer.go | 3 +-- templates/repo/settings/options.tmpl | 12 ++++++------ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 869034bd1725c..791083b5cb6bf 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -191,6 +191,9 @@ func CreatePendingRepositoryTransfer(doer, newOwner *User, repoID int64, teams [ if _, err = sess.Insert(transfer); err != nil { return err } + + // TODO: user witch have "create repo"-access to new place + return sess.Commit() } diff --git a/modules/context/repo.go b/modules/context/repo.go index 2832a43aad041..cd3e1c26adecc 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -600,6 +600,18 @@ func RepoAssignment() func(http.Handler) http.Handler { ctx.Data["CanCompareOrPull"] = canCompare ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest + if ctx.Repo.Repository.Status == models.RepositoryPendingTransfer { + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) + if err == nil { + if err := repoTransfer.LoadAttributes(); err != nil { + ctx.ServerError("LoadRecipient", err) + return + } + + ctx.Data["RepoTransfer"] = repoTransfer + } + } + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) if err == nil { if err := repoTransfer.LoadAttributes(); err != nil { diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 23e20fceaa79f..1d9979307cf39 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -72,8 +72,7 @@ func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoNam } // StartRepositoryTransfer transfer a repo from one owner to a new one. -// it marks the repository transfer as "pending", -// if the new owner is a user or if he dont have access to the new place. +// it make repository into pending transfer state, if doer can not create repo for new owner. func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repository, teams []*models.Team) error { if err := models.TestRepositoryReadyForTransfer(repo.Status); err != nil { return err diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index bbc75a3e0aef2..2207c60ce88ee 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -444,19 +444,19 @@ {{end}}

- {{ if .IsRepoTransferInProgress }} + {{if .RepoTransfer}}
{{.CsrfTokenHtml}} -
- {{ else }} - - {{ end }} + + {{ else }} + + {{ end }}
{{.i18n.Tr "repo.settings.transfer"}}
- {{if .IsRepoTransferInProgress }} + {{if .RepoTransfer}}

{{.i18n.Tr "repo.settings.transfer_started" .RepoTransfer.Recipient.Name}}

{{else}}

{{.i18n.Tr "repo.settings.transfer_desc"}}

From 8640c56a5367c9c95a9600f1aa4d0082b10da07c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Feb 2021 05:18:24 +0100 Subject: [PATCH 59/89] rm templates notification_div.tmpl change --- templates/user/notification/notification_div.tmpl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl index b1d34a8f24399..2f25e733877d3 100644 --- a/templates/user/notification/notification_div.tmpl +++ b/templates/user/notification/notification_div.tmpl @@ -67,13 +67,6 @@ {{$repoOwner.Name}}/{{$repo.Name}}
- - {{if eq $notification.Source 4}} - - {{ $.i18n.Tr "mail.repo_transfer_notify"}} - - {{end}} - {{if ne .Status 3}}
From ba96de144a0b46f5e8c828724727e433d562d0ab Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 08:52:06 +0100 Subject: [PATCH 60/89] re-enable transfer repo back from org to user account --- services/repository/transfer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 1d9979307cf39..066883dbaa977 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -78,8 +78,8 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor return err } - // Admin is always allowed to transfer - if doer.IsAdmin { + // Admin is always allowed to transfer || user transfer repo back to his account + if doer.IsAdmin || doer.ID == newOwner.ID { return TransferOwnership(doer, newOwner, repo, teams) } From 390ab2ac4a92b396b04a8d8419750acf684c5ef1 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 09:05:38 +0100 Subject: [PATCH 61/89] nit --- models/repo_transfer.go | 4 +--- modules/context/repo.go | 3 +-- services/repository/transfer.go | 2 ++ 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 791083b5cb6bf..2063e1b360d8a 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -192,12 +192,10 @@ func CreatePendingRepositoryTransfer(doer, newOwner *User, repoID int64, teams [ return err } - // TODO: user witch have "create repo"-access to new place - return sess.Commit() } -// TransferOwnership transfers all corresponding setting from old user to new one. +// TransferOwnership transfers all corresponding repository items from old user to new one. func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error { sess := x.NewSession() defer sess.Close() diff --git a/modules/context/repo.go b/modules/context/repo.go index cd3e1c26adecc..a8342c12239f1 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -614,13 +614,12 @@ func RepoAssignment() func(http.Handler) http.Handler { repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) if err == nil { - if err := repoTransfer.LoadAttributes(); err != nil { + if err = repoTransfer.LoadAttributes(); err != nil { ctx.ServerError("LoadRecipient", err) return } ctx.Data["RepoTransfer"] = repoTransfer - ctx.Data["IsRepoTransferInProgress"] = true } if err != nil && !models.IsErrNoPendingTransfer(err) { diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 066883dbaa977..a3d4eb172e507 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -97,4 +97,6 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor // Make repo as pending for transfer repo.Status = models.RepositoryPendingTransfer return models.CreatePendingRepositoryTransfer(doer, newOwner, repo.ID, teams) + + // TODO notify users who are able to accept / reject transfer } From db2b1eaa0cbc6f95d1b30a9c1aa68db3e1c6c09d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 09:44:32 +0100 Subject: [PATCH 62/89] add test case for latest bugfix --- integrations/api_repo_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index 67368e9311886..3404e050cfe88 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -444,12 +444,18 @@ func TestAPIRepoTransfer(t *testing.T) { teams *[]int64 expectedStatus int }{ + // Disclaimer for test story: "user1" is an admin, "user2" is normal user and part of in owner team of org "user3" + // Transfer to a user with teams in another org should fail {ctxUserID: 1, newOwner: "user3", teams: &[]int64{5}, expectedStatus: http.StatusForbidden}, // Transfer to a user with non-existent team IDs should fail {ctxUserID: 1, newOwner: "user2", teams: &[]int64{2}, expectedStatus: http.StatusUnprocessableEntity}, // Transfer should go through {ctxUserID: 1, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusAccepted}, + // Let user transfer it back to himself + {ctxUserID: 2, newOwner: "user2", expectedStatus: http.StatusAccepted}, + // And revert transfer + {ctxUserID: 2, newOwner: "user3", teams: &[]int64{2}, expectedStatus: http.StatusAccepted}, // Cannot start transfer to an existing repo {ctxUserID: 2, newOwner: "user3", teams: nil, expectedStatus: http.StatusUnprocessableEntity}, // Start transfer, repo is now in pending transfer mode From 9f6c7c1bedf9de9c96225f59bb59ff0b3077a8dd Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 10:07:43 +0100 Subject: [PATCH 63/89] do it the right way! --- modules/context/repo.go | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/modules/context/repo.go b/modules/context/repo.go index a8342c12239f1..234f78076a929 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -602,18 +602,11 @@ func RepoAssignment() func(http.Handler) http.Handler { if ctx.Repo.Repository.Status == models.RepositoryPendingTransfer { repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) - if err == nil { - if err := repoTransfer.LoadAttributes(); err != nil { - ctx.ServerError("LoadRecipient", err) - return - } - - ctx.Data["RepoTransfer"] = repoTransfer + if err != nil { + ctx.ServerError("GetPendingRepositoryTransfer", err) + return } - } - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) - if err == nil { if err = repoTransfer.LoadAttributes(); err != nil { ctx.ServerError("LoadRecipient", err) return @@ -622,11 +615,6 @@ func RepoAssignment() func(http.Handler) http.Handler { ctx.Data["RepoTransfer"] = repoTransfer } - if err != nil && !models.IsErrNoPendingTransfer(err) { - ctx.ServerError("GetPendingRepositoryTransfer", err) - return - } - if ctx.Query("go-get") == "1" { ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name) prefix := setting.AppURL + path.Join(owner.Name, repo.Name, "src", "branch", ctx.Repo.BranchName) From 0604dae1e6e5237ba6b29437dd2108955501a83d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 10:56:27 +0100 Subject: [PATCH 64/89] rework model.CreateRepoTransferNotification() --- models/notification.go | 36 ++++++++++++++++++++++++--------- models/org.go | 11 ++++++++++ models/org_test.go | 4 ++++ services/repository/transfer.go | 6 +++++- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/models/notification.go b/models/notification.go index fadd5c3b6b321..a6b8207023629 100644 --- a/models/notification.go +++ b/models/notification.go @@ -40,8 +40,7 @@ const ( // NotificationSourceCommit is a notification of a commit NotificationSourceCommit // NotificationSourceRepoTransfer is a notification for a repository - // transfer - NotificationSourceRepoTransfer + NotificationSourceRepository ) // Notification represents a notification @@ -123,22 +122,39 @@ func GetNotifications(opts FindNotificationOptions) (NotificationList, error) { } // CreateRepoTransferNotification creates notification for the user a repository was transferred to -func CreateRepoTransferNotification(doerID, recipientID int64, repo *Repository) error { +func CreateRepoTransferNotification(doer, newOwner *User, repo *Repository) error { sess := x.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { return err } + var notify []*Notification - notification := &Notification{ - UserID: recipientID, - RepoID: repo.ID, - Status: NotificationStatusUnread, - UpdatedBy: doerID, - Source: NotificationSourceRepoTransfer, + if newOwner.IsOrganization() { + users, err := GetUsersWhoCanCreateOrgRepo(newOwner.ID) + if err != nil || len(users) == 0 { + return err + } + for i := range users { + notify = append(notify, &Notification{ + UserID: users[i].ID, + RepoID: repo.ID, + Status: NotificationStatusUnread, + UpdatedBy: doer.ID, + Source: NotificationSourceRepository, + }) + } + } else { + notify = []*Notification{{ + UserID: newOwner.ID, + RepoID: repo.ID, + Status: NotificationStatusUnread, + UpdatedBy: doer.ID, + Source: NotificationSourceRepository, + }} } - if _, err := sess.Insert(notification); err != nil { + if _, err := sess.InsertMulti(notify); err != nil { return err } diff --git a/models/org.go b/models/org.go index ee867eec887f4..bb8658cdf90a9 100644 --- a/models/org.go +++ b/models/org.go @@ -391,6 +391,17 @@ func CanCreateOrgRepo(orgID, uid int64) (bool, error) { Exist(new(Team)) } +// GetUsersWhoCanCreateOrgRepo returns users which are able to create repo in organization +func GetUsersWhoCanCreateOrgRepo(orgID int64) ([]*User, error) { + users := make([]*User, 0, 10) + return users, x. + Join("INNER", "`team_user`", "`team_user`.uid=`user`.id"). + Join("INNER", "`team`", "`team`.id=`team_user`.team_id"). + Where(builder.Eq{"team.can_create_org_repo": true}).And("team_user.org_id = ?", orgID). + Asc("`user`.name"). + Find(&users) +} + func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) { orgs := make([]*User, 0, 10) if !showAll { diff --git a/models/org_test.go b/models/org_test.go index 79f8b060e7061..907ddf5edf7b4 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -635,3 +635,7 @@ func TestHasOrgVisibleTypePrivate(t *testing.T) { assert.Equal(t, test2, false) // user not a part of org assert.Equal(t, test3, false) // logged out user } + +func TestGetUsersWhoCanCreateOrgRepo(t *testing.T) { + // TODO write test! +} diff --git a/services/repository/transfer.go b/services/repository/transfer.go index a3d4eb172e507..cc9ac541e0fd2 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -96,7 +96,11 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor // Make repo as pending for transfer repo.Status = models.RepositoryPendingTransfer - return models.CreatePendingRepositoryTransfer(doer, newOwner, repo.ID, teams) + if err := models.CreatePendingRepositoryTransfer(doer, newOwner, repo.ID, teams); err != nil { + return err + } // TODO notify users who are able to accept / reject transfer + // mail & ui -> add new api nto notifier + return models.CreateRepoTransferNotification(doer, newOwner, repo) } From 21f1e42e450c603bc24b5015d7689228e1252af4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 11:28:19 +0100 Subject: [PATCH 65/89] mv SendRepoTransferNotifyMail call into right place & rework SendRepoTransferNotifyMail() --- routers/repo/setting.go | 5 ----- services/mailer/mail_repo.go | 37 +++++++++++++++------------------ services/repository/transfer.go | 15 ++++++++++--- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 90fd91444333b..9e2da2dff0da1 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -490,16 +490,11 @@ func SettingsPost(ctx *context.Context) { return } - if setting.Service.EnableNotifyMail { - mailer.SendRepoTransferNotifyMail(ctx.Locale, ctx.Repo.Owner, ctx.Repo.Repository) - } - log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.LowerName)) ctx.Redirect(setting.AppSubURL + "/" + ctx.Repo.Owner.Name + "/" + repo.Name + "/settings") case "cancel_transfer": - if !ctx.Repo.IsOwner() { ctx.Error(404) return diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go index afd5f037487ca..5b30a565e7ed0 100644 --- a/services/mailer/mail_repo.go +++ b/services/mailer/mail_repo.go @@ -9,14 +9,13 @@ import ( "fmt" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/log" ) -// SendRepoTransferNotifyMail triggers a notification e-mail when a repository -// transfer is initiated -func SendRepoTransferNotifyMail(locale Locale, u *models.User, repo *models.Repository) { +// SendRepoTransferNotifyMail triggers a notification e-mail when a repository transfer is initiated +func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Repository) error { data := map[string]interface{}{ - "Subject": locale.Tr("mail.repo_transfer_notify"), + //"Subject": locale.Tr("mail.repo_transfer_notify"), + "Subject": "mail.repo_transfer_notify", "RepoName": repo.FullName(), "Link": repo.HTMLURL(), "AcceptTransferLink": repo.HTMLURL() + "/action/accept_transfer", @@ -26,30 +25,28 @@ func SendRepoTransferNotifyMail(locale Locale, u *models.User, repo *models.Repo var content bytes.Buffer if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil { - log.Error("Template: %v", err) - return + return err } - var email = u.Email + var emails []string - if u.IsOrganization() && u.Email == "" { - t, err := u.GetOwnerTeam() + if newOwner.IsOrganization() { + users, err := models.GetUsersWhoCanCreateOrgRepo(newOwner.ID) if err != nil { - log.Error("Could not retrieve owners team for organization", err) - return + return err } - if err := t.GetMembers(&models.SearchMembersOptions{}); err != nil { - log.Error("Could not retrieve members of the owners team", err) - return + for i := range users { + emails = append(emails, users[i].Email) } - - // Just use the email address of the first user - email = t.Members[0].Email + } else { + emails = []string{newOwner.Email} } - msg := NewMessage([]string{email}, locale.Tr("mail.repo_transfer_notify"), content.String()) - msg.Info = fmt.Sprintf("UID: %d, repository transfer notification", u.ID) + // msg := NewMessage([]string{email}, locale.Tr("mail.repo_transfer_notify"), content.String()) + msg := NewMessage(emails, "mail.repo_transfer_notify", content.String()) + msg.Info = fmt.Sprintf("UID: %d, repository transfer notification", newOwner.ID) SendAsync(msg) + return nil } diff --git a/services/repository/transfer.go b/services/repository/transfer.go index cc9ac541e0fd2..bb1eda4ba1e03 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -9,7 +9,9 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sync" + "code.gitea.io/gitea/services/mailer" ) // repoWorkingPool represents a working pool to order the parallel changes to the same repository @@ -100,7 +102,14 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor return err } - // TODO notify users who are able to accept / reject transfer - // mail & ui -> add new api nto notifier - return models.CreateRepoTransferNotification(doer, newOwner, repo) + // notify users who are able to accept / reject transfer + // TODO mail & ui -> add new api nto notifier + if err := models.CreateRepoTransferNotification(doer, newOwner, repo); err != nil { + return err + } + if setting.Service.EnableNotifyMail { + return mailer.SendRepoTransferNotifyMail(doer, newOwner, repo) + } + + return nil } From 1c9bdcf3672ad4ae19d24ab2557fb959ba5d1e1c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 11:34:13 +0100 Subject: [PATCH 66/89] lint --- models/notification.go | 2 +- services/repository/transfer.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models/notification.go b/models/notification.go index a6b8207023629..becad6ddaaaab 100644 --- a/models/notification.go +++ b/models/notification.go @@ -39,7 +39,7 @@ const ( NotificationSourcePullRequest // NotificationSourceCommit is a notification of a commit NotificationSourceCommit - // NotificationSourceRepoTransfer is a notification for a repository + // NotificationSourceRepository is a notification for a repository NotificationSourceRepository ) diff --git a/services/repository/transfer.go b/services/repository/transfer.go index bb1eda4ba1e03..9180beee46786 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -103,7 +103,7 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor } // notify users who are able to accept / reject transfer - // TODO mail & ui -> add new api nto notifier + // TODO mail & ui -> add new api to notifier if err := models.CreateRepoTransferNotification(doer, newOwner, repo); err != nil { return err } From de3247a388dcdb6a452264c31d6d7cb7bc6a65ec Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 11:41:34 +0100 Subject: [PATCH 67/89] rm unrelated diff --- routers/repo/repo.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/routers/repo/repo.go b/routers/repo/repo.go index e0cfbb39179f7..7b9032fab3b3a 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -337,8 +337,12 @@ func Action(ctx *context.Context) { case "desc": // FIXME: this is not used if !ctx.Repo.IsOwner() { ctx.Error(404) + return } - return + + ctx.Repo.Repository.Description = ctx.Query("desc") + ctx.Repo.Repository.Website = ctx.Query("site") + err = models.UpdateRepository(ctx.Repo.Repository, false) } if err != nil { From 805c2576fe89c1a6c444306526967979f0f7d68e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 12:05:40 +0100 Subject: [PATCH 68/89] simplify --- routers/repo/repo.go | 91 +++++++++++++----------------------- services/mailer/mail_repo.go | 2 +- 2 files changed, 34 insertions(+), 59 deletions(-) diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 7b9032fab3b3a..560c643218f29 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -276,64 +276,9 @@ func Action(ctx *context.Context) { case "unstar": err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) case "accept_transfer": - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) - if err != nil { - if models.IsErrNoPendingTransfer(err) { - ctx.Redirect(ctx.Repo.Repository.HTMLURL()) - } else { - ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) - } - return - } - - if err = repoTransfer.LoadAttributes(); err != nil { - ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) - return - } - - if !repoTransfer.CanUserAcceptTransfer(ctx.User) { - ctx.NotFound("CanUserAcceptTransfer", errors.New("user does not have enough permissions")) - return - } - - if err = repo_service.TransferOwnership(repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil { - ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) - return - } - - ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success")) - ctx.Redirect(ctx.Repo.Repository.HTMLURL()) - return - - case "decline_transfer": - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) - if err != nil { - ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) - return - } - - if err = repoTransfer.LoadAttributes(); err != nil { - ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err) - return - } - - if !repoTransfer.CanUserAcceptTransfer(ctx.User) { - ctx.NotFound("CanUserAcceptTransfer", errors.New("user does not have enough permissions")) - return - } - - if err = models.CancelRepositoryTransfer(ctx.Repo.Repository); err != nil { - if models.IsErrNoPendingTransfer(err) { - ctx.Redirect(ctx.Repo.Repository.HTMLURL()) - } else { - ctx.ServerError("CancelRepositoryTransfer", err) - } - return - } - - ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected")) - ctx.Redirect(ctx.Repo.Repository.HTMLURL()) - return + err = acceptOrRejectRepoTransfer(ctx, true) + case "reject_transfer": + err = acceptOrRejectRepoTransfer(ctx, false) case "desc": // FIXME: this is not used if !ctx.Repo.IsOwner() { ctx.Error(404) @@ -353,6 +298,36 @@ func Action(ctx *context.Context) { ctx.RedirectToFirst(ctx.Query("redirect_to"), ctx.Repo.RepoLink) } +func acceptOrRejectRepoTransfer(ctx *context.Context, accept bool) error { + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) + if err != nil { + return err + } + + if err = repoTransfer.LoadAttributes(); err != nil { + return err + } + + if !repoTransfer.CanUserAcceptTransfer(ctx.User) { + return errors.New("user does not have enough permissions") + } + + if accept { + if err = repo_service.TransferOwnership(repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil { + return err + } + ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success")) + } else { + if err = models.CancelRepositoryTransfer(ctx.Repo.Repository); err != nil { + return err + } + ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected")) + } + + ctx.Redirect(ctx.Repo.Repository.HTMLURL()) + return nil +} + // RedirectDownload return a file based on the following infos: func RedirectDownload(ctx *context.Context) { var ( diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go index 5b30a565e7ed0..0d4a9a201400e 100644 --- a/services/mailer/mail_repo.go +++ b/services/mailer/mail_repo.go @@ -19,7 +19,7 @@ func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Reposi "RepoName": repo.FullName(), "Link": repo.HTMLURL(), "AcceptTransferLink": repo.HTMLURL() + "/action/accept_transfer", - "DeclineTransferLink": repo.HTMLURL() + "/action/decline_transfer", + "DeclineTransferLink": repo.HTMLURL() + "/action/reject_transfer", } var content bytes.Buffer From 6089ae15c87833d215cba4c9aa125c72a58728df Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 13:11:43 +0100 Subject: [PATCH 69/89] UI test 1 --- models/repo_transfer.go | 4 ++-- modules/context/repo.go | 3 +++ templates/repo/header.tmpl | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 2063e1b360d8a..63575e94bf463 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -111,8 +111,8 @@ func deleteRepositoryTransfer(e Engine, repoID int64) error { return err } -// CancelRepositoryTransfer makes sure to set the transfer process as -// "rejected". Thus ending the transfer process +// CancelRepositoryTransfer marks the repository as ready and remove pending transfer entry, +// thus cancel the transfer process. func CancelRepositoryTransfer(repo *Repository) error { sess := x.NewSession() if err := sess.Begin(); err != nil { diff --git a/modules/context/repo.go b/modules/context/repo.go index 234f78076a929..4e0a4a3f06d09 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -613,6 +613,9 @@ func RepoAssignment() func(http.Handler) http.Handler { } ctx.Data["RepoTransfer"] = repoTransfer + if ctx.User != nil { + ctx.Data["CanUserAcceptTransfer"] = repoTransfer.CanUserAcceptTransfer(ctx.User) + } } if ctx.Query("go-get") == "1" { diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 31bcd5c48a8d7..c5ee08c29f5f0 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -42,6 +42,24 @@
{{if not .IsBeingCreated}}
+ {{if $.RepoTransfer}} + + {{$.CsrfTokenHtml}} +
+ +
+ +
+ {{$.CsrfTokenHtml}} +
+ +
+
+ {{end}}
{{$.CsrfTokenHtml}}
From 3119e89b2ac16c230aaa966a1c2a117109aa414d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 15:51:43 +0100 Subject: [PATCH 70/89] update repoStatus if finaly transfer --- models/repo_transfer.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 63575e94bf463..d2dbf1071911d 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -223,6 +223,8 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error repo.OwnerID = newOwner.ID repo.Owner = newOwner repo.OwnerName = newOwner.Name + // if status was RepositoryPendingTransfer set it back to RepositoryReady + repo.Status = RepositoryReady // Update repository. if _, err = sess.ID(repo.ID).Update(repo); err != nil { @@ -318,7 +320,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } if err = deleteRepositoryTransfer(sess, repo.ID); err != nil { - return fmt.Errorf("accept repository transfer: %v", err) + return fmt.Errorf("deleteRepositoryTransfer: %v", err) } // If there was previously a redirect at this location, remove it. From 2c9ea8ea19855aa7bb173b9027238b3726bd99f1 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 15:58:26 +0100 Subject: [PATCH 71/89] has to be more explicite to work ... --- models/repo_transfer.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index d2dbf1071911d..9fc57e69c1a40 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -223,8 +223,6 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error repo.OwnerID = newOwner.ID repo.Owner = newOwner repo.OwnerName = newOwner.Name - // if status was RepositoryPendingTransfer set it back to RepositoryReady - repo.Status = RepositoryReady // Update repository. if _, err = sess.ID(repo.ID).Update(repo); err != nil { @@ -322,6 +320,10 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error if err = deleteRepositoryTransfer(sess, repo.ID); err != nil { return fmt.Errorf("deleteRepositoryTransfer: %v", err) } + repo.Status = RepositoryReady + if err := updateRepositoryCols(sess, repo, "status"); err != nil { + return err + } // If there was previously a redirect at this location, remove it. if err = deleteRepoRedirect(sess, newOwner.ID, repo.Name); err != nil { From 7b4496727eb7825da2a0862cc1fd4ef4902abf41 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 16:14:53 +0100 Subject: [PATCH 72/89] imprufe UI --- templates/repo/header.tmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index c5ee08c29f5f0..5755a1e8eb165 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -45,15 +45,15 @@ {{if $.RepoTransfer}} {{$.CsrfTokenHtml}} -
+
{{$.CsrfTokenHtml}} -
+
From e3b3258a880c71bedce3518eb349149168d98841 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 16:48:20 +0100 Subject: [PATCH 73/89] localize --- options/locale/locale_en-US.ini | 6 ++++-- routers/repo/setting.go | 2 +- templates/repo/header.tmpl | 6 +++--- templates/repo/settings/options.tmpl | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5ef0b71aa3d50..d8df4ed15e1ee 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -319,7 +319,6 @@ activate_email = Verify your email address reset_password = Recover your account register_success = Registration successful register_notify = Welcome to Gitea -repo_transfer_notify = A repository has been transferred to you [modal] yes = Yes @@ -736,6 +735,10 @@ delete_preexisting = Delete pre-existing files delete_preexisting_content = Delete files in %s delete_preexisting_success = Deleted unadopted files in %s +transfer.accept = Accept Transfer +transfer.accept_desc = Transfer to "%s" +transfer.reject = Reject Transfer + desc.private = Private desc.public = Public desc.private_template = Private template @@ -758,7 +761,6 @@ template.invalid = Must select a template repository archive.title = This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests. archive.issue.nocomment = This repo is archived. You cannot comment on issues. archive.pull.nocomment = This repo is archived. You cannot comment on pull requests. -archive.repo_transfer = This repo is archived. You cannot transfer this repo until it is un-archived form.reach_limit_of_creation_1 = You have already reached your limit of %d repository. form.reach_limit_of_creation_n = You have already reached your limit of %d repositories. diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 9e2da2dff0da1..4280c65861204 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -491,7 +491,7 @@ func SettingsPost(ctx *context.Context) { } log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) - ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.LowerName)) + ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName())) ctx.Redirect(setting.AppSubURL + "/" + ctx.Repo.Owner.Name + "/" + repo.Name + "/settings") case "cancel_transfer": diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 5755a1e8eb165..ba8b236464ffe 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -45,9 +45,9 @@ {{if $.RepoTransfer}} {{$.CsrfTokenHtml}} -
+
@@ -55,7 +55,7 @@ {{$.CsrfTokenHtml}}
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 2207c60ce88ee..e15d1f1e7347c 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -457,7 +457,7 @@
{{.i18n.Tr "repo.settings.transfer"}}
{{if .RepoTransfer}} -

{{.i18n.Tr "repo.settings.transfer_started" .RepoTransfer.Recipient.Name}}

+

{{.i18n.Tr "repo.settings.transfer_started" .RepoTransfer.Recipient.DisplayName}}

{{else}}

{{.i18n.Tr "repo.settings.transfer_desc"}}

{{end}} From e2c1aea4e61651b8683b64047d023fd7a1fdae36 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 16:54:27 +0100 Subject: [PATCH 74/89] finish UI --- options/locale/locale_en-US.ini | 1 + templates/repo/header.tmpl | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d8df4ed15e1ee..fc68792011def 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -738,6 +738,7 @@ delete_preexisting_success = Deleted unadopted files in %s transfer.accept = Accept Transfer transfer.accept_desc = Transfer to "%s" transfer.reject = Reject Transfer +transfer.reject_desc = Cancel transfer to "%s" desc.private = Private desc.public = Public diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index ba8b236464ffe..9c64edd8a8fee 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -45,16 +45,16 @@ {{if $.RepoTransfer}}
{{$.CsrfTokenHtml}} -
-
{{$.CsrfTokenHtml}} -
-
From 97e447e218bb573020e5676c66dca23e23bc9a71 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 17:01:02 +0100 Subject: [PATCH 75/89] Tell user the reason, the button do not work --- options/locale/locale_en-US.ini | 2 ++ templates/repo/header.tmpl | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index fc68792011def..873e8e7de9ee8 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -739,6 +739,8 @@ transfer.accept = Accept Transfer transfer.accept_desc = Transfer to "%s" transfer.reject = Reject Transfer transfer.reject_desc = Cancel transfer to "%s" +transfer.no_permission_to_accept = You do not have permission to Accept +transfer.no_permission_to_reject = You do not have permission to Reject desc.private = Private desc.public = Public diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 9c64edd8a8fee..21af8e04f1583 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -45,16 +45,16 @@ {{if $.RepoTransfer}} {{$.CsrfTokenHtml}} -
-
{{$.CsrfTokenHtml}} -
-
From 97f5b136c5388f1b02d0f288037fb1936487d3d1 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 17:07:24 +0100 Subject: [PATCH 76/89] sort locale --- options/locale/locale_en-US.ini | 22 +++++++++++----------- routers/repo/setting.go | 4 ++-- templates/repo/settings/options.tmpl | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 873e8e7de9ee8..da4d3aec08874 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1561,13 +1561,21 @@ settings.convert_fork_notices_1 = This operation will convert the fork into a re settings.convert_fork_confirm = Convert Repository settings.convert_fork_succeed = The fork has been converted into a regular repository. settings.transfer = Transfer Ownership +settings.transfer.rejected = Repository transfer was rejected. +settings.transfer.success = Repository transfer was successful. +settings.transfer_abort = Cancel transfer +settings.transfer_abort_invalid = You cannot cancel a non existent repository transfer. +settings.transfer_abort_success = The repository transfer to %s was successfully cancelled. settings.transfer_desc = Transfer this repository to a user or to an organization for which you have administrator rights. +settings.transfer_form_title = Enter the repository name as confirmation: +settings.transfer_in_progress = There is currently an ongoing transfer. Please cancel it if you will like to transfer this repository to another user. settings.transfer_notices_1 = - You will lose access to the repository if you transfer it to an individual user. settings.transfer_notices_2 = - You will keep access to the repository if you transfer it to an organization that you (co-)own. -settings.transfer_form_title = Enter the repository name as confirmation: +settings.transfer_owner = New Owner +settings.transfer_perform = Perform Transfer +settings.transfer_started = This repository has been marked for transfer and awaits confirmation from "%s" +settings.transfer_succeed = The repository has been transferred. settings.signing_settings = Signing Verification Settings -settings.transfer.success = Repository transfer was successful. -settings.transfer.rejected = Repository transfer was rejected. settings.trust_model = Signature Trust Model settings.trust_model.default = Default Trust Model settings.trust_model.default.desc= Use the default repository trust model for this installation. @@ -1592,14 +1600,6 @@ settings.delete_notices_2 = - This operation will permanently delete the {{.CsrfTokenHtml}} - + {{ else }} @@ -611,7 +611,7 @@
{{.i18n.Tr "settings.cancel"}}
- +
From a10c15bad0934b22f14c5b82fee388a8ad2c4670 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 17:34:11 +0100 Subject: [PATCH 77/89] finish GetUsersWhoCanCreateOrgRepo() --- models/org.go | 5 ++--- models/org_test.go | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/models/org.go b/models/org.go index bb8658cdf90a9..9028e1d61d520 100644 --- a/models/org.go +++ b/models/org.go @@ -397,9 +397,8 @@ func GetUsersWhoCanCreateOrgRepo(orgID int64) ([]*User, error) { return users, x. Join("INNER", "`team_user`", "`team_user`.uid=`user`.id"). Join("INNER", "`team`", "`team`.id=`team_user`.team_id"). - Where(builder.Eq{"team.can_create_org_repo": true}).And("team_user.org_id = ?", orgID). - Asc("`user`.name"). - Find(&users) + Where(builder.Eq{"team.can_create_org_repo": true}.Or(builder.Eq{"team.authorize": AccessModeOwner})). + And("team_user.org_id = ?", orgID).Asc("`user`.name").Find(&users) } func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) { diff --git a/models/org_test.go b/models/org_test.go index 907ddf5edf7b4..45268f0f231dc 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -637,5 +637,19 @@ func TestHasOrgVisibleTypePrivate(t *testing.T) { } func TestGetUsersWhoCanCreateOrgRepo(t *testing.T) { - // TODO write test! + assert.NoError(t, PrepareTestDatabase()) + + users, err := GetUsersWhoCanCreateOrgRepo(3) + assert.NoError(t, err) + assert.Len(t, users, 2) + var ids []int64 + for i := range users { + ids = append(ids, users[i].ID) + } + assert.ElementsMatch(t, ids, []int64{2, 28}) + + users, err = GetUsersWhoCanCreateOrgRepo(7) + assert.NoError(t, err) + assert.Len(t, users, 1) + assert.EqualValues(t, 5, users[0].ID) } From ac0d28c4cc3b88997d1c426eeb1964539b7a4a0c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 17:48:12 +0100 Subject: [PATCH 78/89] use notification module --- modules/notification/base/notifier.go | 2 ++ modules/notification/base/null.go | 4 ++++ modules/notification/mail/mail.go | 6 ++++++ modules/notification/notification.go | 7 +++++++ modules/notification/ui/ui.go | 7 +++++++ services/repository/transfer.go | 10 +--------- 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go index 5bb833d275170..8f8aa659b45d9 100644 --- a/modules/notification/base/notifier.go +++ b/modules/notification/base/notifier.go @@ -57,4 +57,6 @@ type Notifier interface { NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) + + NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) } diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go index 2386f925cec6a..e61b37a943090 100644 --- a/modules/notification/base/null.go +++ b/modules/notification/base/null.go @@ -166,3 +166,7 @@ func (*NullNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Reposit // NotifySyncDeleteRef places a place holder function func (*NullNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) { } + +// NotifyRepoPendingTransfer places a place holder function +func (*NullNotifier) NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) { +} diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go index f984ea7661ae6..f7192f5a52eff 100644 --- a/modules/notification/mail/mail.go +++ b/modules/notification/mail/mail.go @@ -170,3 +170,9 @@ func (m *mailNotifier) NotifyNewRelease(rel *models.Release) { mailer.MailNewRelease(rel) } + +func (m *mailNotifier) NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) { + if err := mailer.SendRepoTransferNotifyMail(doer, newOwner, repo); err != nil { + log.Error("NotifyRepoPendingTransfer: %v", err) + } +} diff --git a/modules/notification/notification.go b/modules/notification/notification.go index d22d157bec18e..b574f3ccda225 100644 --- a/modules/notification/notification.go +++ b/modules/notification/notification.go @@ -290,3 +290,10 @@ func NotifySyncDeleteRef(pusher *models.User, repo *models.Repository, refType, notifier.NotifySyncDeleteRef(pusher, repo, refType, refFullName) } } + +// NotifyRepoPendingTransfer notifies creation of pending transfer to notifiers +func NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) { + for _, notifier := range notifiers { + notifier.NotifyRepoPendingTransfer(doer, newOwner, repo) + } +} diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index 25ea4d91c643f..b6cdbce5927c8 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -201,3 +201,10 @@ func (ns *notificationService) NotifyPullReviewRequest(doer *models.User, issue _ = ns.issueQueue.Push(opts) } } + +func (ns *notificationService) NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) { + // TODO: use Queue + if err := models.CreateRepoTransferNotification(doer, newOwner, repo); err != nil { + log.Error("NotifyRepoPendingTransfer: %v", err) + } +} diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 9180beee46786..5937667bb9415 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -9,9 +9,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/notification" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sync" - "code.gitea.io/gitea/services/mailer" ) // repoWorkingPool represents a working pool to order the parallel changes to the same repository @@ -103,13 +101,7 @@ func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repositor } // notify users who are able to accept / reject transfer - // TODO mail & ui -> add new api to notifier - if err := models.CreateRepoTransferNotification(doer, newOwner, repo); err != nil { - return err - } - if setting.Service.EnableNotifyMail { - return mailer.SendRepoTransferNotifyMail(doer, newOwner, repo) - } + notification.NotifyRepoPendingTransfer(doer, newOwner, repo) return nil } From f17bfa84961a32416e8c57dbd65ca5ddbb7a7f2e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 19:18:02 +0100 Subject: [PATCH 79/89] finish mail --- services/mailer/mail_repo.go | 37 ++++++++++++++---------- templates/mail/notify/repo_transfer.tmpl | 5 ++-- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go index 0d4a9a201400e..5c8ce6524346d 100644 --- a/services/mailer/mail_repo.go +++ b/services/mailer/mail_repo.go @@ -13,22 +13,11 @@ import ( // SendRepoTransferNotifyMail triggers a notification e-mail when a repository transfer is initiated func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Repository) error { - data := map[string]interface{}{ - //"Subject": locale.Tr("mail.repo_transfer_notify"), - "Subject": "mail.repo_transfer_notify", - "RepoName": repo.FullName(), - "Link": repo.HTMLURL(), - "AcceptTransferLink": repo.HTMLURL() + "/action/accept_transfer", - "DeclineTransferLink": repo.HTMLURL() + "/action/reject_transfer", - } - - var content bytes.Buffer - - if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil { - return err - } - - var emails []string + var ( + emails []string + destination string + content bytes.Buffer + ) if newOwner.IsOrganization() { users, err := models.GetUsersWhoCanCreateOrgRepo(newOwner.ID) @@ -39,8 +28,24 @@ func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Reposi for i := range users { emails = append(emails, users[i].Email) } + destination = newOwner.DisplayName() } else { emails = []string{newOwner.Email} + destination = "you" + } + + data := map[string]interface{}{ + "Doer": doer, + "User": repo.Owner, + "Repo": repo.FullName(), + "Link": repo.HTMLURL(), + "Subject": fmt.Sprintf("%s like to transfer \"%s\" to %s", doer.DisplayName(), repo.FullName(), destination), + + "Destination": destination, + } + + if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil { + return err } // msg := NewMessage([]string{email}, locale.Tr("mail.repo_transfer_notify"), content.String()) diff --git a/templates/mail/notify/repo_transfer.tmpl b/templates/mail/notify/repo_transfer.tmpl index 038afe2377823..68ceded116738 100644 --- a/templates/mail/notify/repo_transfer.tmpl +++ b/templates/mail/notify/repo_transfer.tmpl @@ -6,9 +6,8 @@ -

You have been invited as an owner of the repository, {{.RepoName}}. - Please click here to accept the invitation or - here to reject the invitation

+

{{.Subject}}. + To accept or reject it visit {{.Repo}} or just ignore it.

---
From 17f475003de80577801dfd5be18c03c05c6dcc08 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 19:20:04 +0100 Subject: [PATCH 80/89] ... now --- services/mailer/mail_repo.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go index 5c8ce6524346d..d12e745bf40c2 100644 --- a/services/mailer/mail_repo.go +++ b/services/mailer/mail_repo.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/models" ) -// SendRepoTransferNotifyMail triggers a notification e-mail when a repository transfer is initiated +// SendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Repository) error { var ( emails []string @@ -34,12 +34,13 @@ func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Reposi destination = "you" } + subject := fmt.Sprintf("%s like to transfer \"%s\" to %s", doer.DisplayName(), repo.FullName(), destination) data := map[string]interface{}{ "Doer": doer, "User": repo.Owner, "Repo": repo.FullName(), "Link": repo.HTMLURL(), - "Subject": fmt.Sprintf("%s like to transfer \"%s\" to %s", doer.DisplayName(), repo.FullName(), destination), + "Subject": subject, "Destination": destination, } @@ -48,9 +49,8 @@ func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Reposi return err } - // msg := NewMessage([]string{email}, locale.Tr("mail.repo_transfer_notify"), content.String()) - msg := NewMessage(emails, "mail.repo_transfer_notify", content.String()) - msg.Info = fmt.Sprintf("UID: %d, repository transfer notification", newOwner.ID) + msg := NewMessage(emails, subject, content.String()) + msg.Info = fmt.Sprintf("UID: %d, repository pending transfer notification", newOwner.ID) SendAsync(msg) return nil From 93d4c0ac26f97ce80cf439ab4d2926b87dfbf5ae Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 19:24:00 +0100 Subject: [PATCH 81/89] update Copyright dates --- models/migrations/v174.go | 2 +- services/mailer/mail_repo.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models/migrations/v174.go b/models/migrations/v174.go index c6e929628ea30..ce337df2b30d6 100644 --- a/models/migrations/v174.go +++ b/models/migrations/v174.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. +// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go index d12e745bf40c2..d5efdd6deefda 100644 --- a/services/mailer/mail_repo.go +++ b/services/mailer/mail_repo.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. +// Copyright 2021 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. From 6dc23d978fe05fa0cb025913432f12843543ef63 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 20:59:49 +0100 Subject: [PATCH 82/89] Make UI work for NotificationSourceRepository --- models/notification.go | 24 +++++++++++++------ modules/convert/notification.go | 10 ++++++-- modules/notification/ui/ui.go | 1 - .../user/notification/notification_div.tmpl | 8 ++++++- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/models/notification.go b/models/notification.go index becad6ddaaaab..c58a26a546d04 100644 --- a/models/notification.go +++ b/models/notification.go @@ -405,7 +405,7 @@ func (n *Notification) loadRepo(e Engine) (err error) { } func (n *Notification) loadIssue(e Engine) (err error) { - if n.Issue == nil { + if n.Issue == nil && n.IssueID != 0 { n.Issue, err = getIssueByID(e, n.IssueID) if err != nil { return fmt.Errorf("getIssueByID [%d]: %v", n.IssueID, err) @@ -416,7 +416,7 @@ func (n *Notification) loadIssue(e Engine) (err error) { } func (n *Notification) loadComment(e Engine) (err error) { - if n.Comment == nil && n.CommentID > 0 { + if n.Comment == nil && n.CommentID != 0 { n.Comment, err = getCommentByID(e, n.CommentID) if err != nil { return fmt.Errorf("GetCommentByID [%d] for issue ID [%d]: %v", n.CommentID, n.IssueID, err) @@ -447,10 +447,18 @@ func (n *Notification) GetIssue() (*Issue, error) { // HTMLURL formats a URL-string to the notification func (n *Notification) HTMLURL() string { - if n.Comment != nil { - return n.Comment.HTMLURL() + switch n.Source { + case NotificationSourceIssue, NotificationSourcePullRequest: + if n.Comment != nil { + return n.Comment.HTMLURL() + } + return n.Issue.HTMLURL() + case NotificationSourceCommit: + return n.Repository.HTMLURL() + "/commit/" + n.CommitID + case NotificationSourceRepository: + return n.Repository.HTMLURL() } - return n.Issue.HTMLURL() + return "" } // APIURL formats a URL-string to the notification @@ -604,8 +612,10 @@ func (nl NotificationList) LoadIssues() ([]int, error) { if notification.Issue == nil { notification.Issue = issues[notification.IssueID] if notification.Issue == nil { - log.Error("Notification[%d]: IssueID: %d Not Found", notification.ID, notification.IssueID) - failures = append(failures, i) + if notification.IssueID != 0 { + log.Error("Notification[%d]: IssueID: %d Not Found", notification.ID, notification.IssueID) + failures = append(failures, i) + } continue } notification.Issue.Repo = notification.Repository diff --git a/modules/convert/notification.go b/modules/convert/notification.go index fff891b15dffd..86536ccae83fc 100644 --- a/modules/convert/notification.go +++ b/modules/convert/notification.go @@ -48,12 +48,18 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread { result.Subject.LatestCommentURL = comment.APIURL() } } - case models.NotificationSourceCommit: + case models.NotificationSourceCommit: //unused until now result.Subject = &api.NotificationSubject{ Type: "Commit", Title: n.CommitID, + URL: n.Repository.HTMLURL() + "/commit/" + n.CommitID, + } + case models.NotificationSourceRepository: + result.Subject = &api.NotificationSubject{ + Type: "Repository", + Title: n.Repository.FullName(), + URL: n.Repository.Link(), } - //unused until now } return result diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index b6cdbce5927c8..b1374f5608fd4 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -203,7 +203,6 @@ func (ns *notificationService) NotifyPullReviewRequest(doer *models.User, issue } func (ns *notificationService) NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) { - // TODO: use Queue if err := models.CreateRepoTransferNotification(doer, newOwner, repo); err != nil { log.Error("NotifyRepoPendingTransfer: %v", err) } diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl index 2f25e733877d3..2158dcb000f1f 100644 --- a/templates/user/notification/notification_div.tmpl +++ b/templates/user/notification/notification_div.tmpl @@ -39,6 +39,8 @@ {{if eq .Status 3}} {{svg "octicon-pin"}} + {{else if not $issue}} + {{svg "octicon-repo"}} {{else if $issue.IsPull}} {{if $issue.IsClosed}} {{if $issue.GetPullRequest.HasMerged}} @@ -59,7 +61,11 @@ - #{{$issue.Index}} - {{$issue.Title}} + {{if $issue}} + #{{$issue.Index}} - {{$issue.Title}} + {{else}} + {{$repo.FullName}} + {{end}} From 150c36df6eda69dcd8ccd069d471cde99f4273e0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 21:18:59 +0100 Subject: [PATCH 83/89] Set repo notification-status read if unread --- models/issue.go | 2 +- models/notification.go | 9 ++++++++- models/repo.go | 5 +++++ routers/repo/view.go | 8 ++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/models/issue.go b/models/issue.go index b903e82ad7d40..c0bafb54e45a5 100644 --- a/models/issue.go +++ b/models/issue.go @@ -563,7 +563,7 @@ func (issue *Issue) ReadBy(userID int64) error { return err } - return setNotificationStatusReadIfUnread(x, userID, issue.ID) + return setIssueNotificationStatusReadIfUnread(x, userID, issue.ID) } func updateIssueCols(e Engine, issue *Issue, cols ...string) error { diff --git a/models/notification.go b/models/notification.go index c58a26a546d04..086d9b3980f01 100644 --- a/models/notification.go +++ b/models/notification.go @@ -735,7 +735,7 @@ func GetUIDsAndNotificationCounts(since, until timeutil.TimeStamp) ([]UserIDCoun return res, x.SQL(sql, since, until, NotificationStatusUnread).Find(&res) } -func setNotificationStatusReadIfUnread(e Engine, userID, issueID int64) error { +func setIssueNotificationStatusReadIfUnread(e Engine, userID, issueID int64) error { notification, err := getIssueNotification(e, userID, issueID) // ignore if not exists if err != nil { @@ -752,6 +752,13 @@ func setNotificationStatusReadIfUnread(e Engine, userID, issueID int64) error { return err } +func setRepoNotificationStatusReadIfUnread(e Engine, userID, repoID int64) error { + _, err := e.Where(builder.Eq{"user_id": userID}.And(builder.Eq{"status": NotificationStatusUnread}).And( + builder.Eq{"source": NotificationSourceRepository})).Cols("status"). + Update(&Notification{Status: NotificationStatusRead}) + return err +} + // SetNotificationStatus change the notification status func SetNotificationStatus(notificationID int64, user *User, status NotificationStatus) error { notification, err := getNotificationByID(x, notificationID) diff --git a/models/repo.go b/models/repo.go index 001067289fe46..34517bae62183 100644 --- a/models/repo.go +++ b/models/repo.go @@ -869,6 +869,11 @@ func (repo *Repository) DescriptionHTML() template.HTML { return template.HTML(markup.Sanitize(string(desc))) } +// ReadBy sets repo to be visited by given user. +func (repo *Repository) ReadBy(userID int64) error { + return setRepoNotificationStatusReadIfUnread(x, userID, repo.ID) +} + func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) { has, err := e.Get(&Repository{ OwnerID: u.ID, diff --git a/routers/repo/view.go b/routers/repo/view.go index a5e3cbe3e4323..39f16d183c3b1 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -586,6 +586,14 @@ func Home(ctx *context.Context) { return } + if ctx.IsSigned { + // Set repo notification-status read if unread + if err := ctx.Repo.Repository.ReadBy(ctx.User.ID); err != nil { + ctx.ServerError("ReadBy", err) + return + } + } + var firstUnit *models.Unit for _, repoUnit := range ctx.Repo.Units { if repoUnit.Type == models.UnitTypeCode { From 8ef58b80ec700b72adb57e2000e25121647d16f0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 26 Feb 2021 21:37:36 +0100 Subject: [PATCH 84/89] fix setRepoNotificationStatusReadIfUnread query --- models/notification.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/notification.go b/models/notification.go index 086d9b3980f01..efd1c323daafd 100644 --- a/models/notification.go +++ b/models/notification.go @@ -754,8 +754,8 @@ func setIssueNotificationStatusReadIfUnread(e Engine, userID, issueID int64) err func setRepoNotificationStatusReadIfUnread(e Engine, userID, repoID int64) error { _, err := e.Where(builder.Eq{"user_id": userID}.And(builder.Eq{"status": NotificationStatusUnread}).And( - builder.Eq{"source": NotificationSourceRepository})).Cols("status"). - Update(&Notification{Status: NotificationStatusRead}) + builder.Eq{"source": NotificationSourceRepository}).And(builder.Eq{"repo_id": repoID})). + Cols("status").Update(&Notification{Status: NotificationStatusRead}) return err } From b3056a026a77cda9b20182ddecea9da254b97e9a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 27 Feb 2021 22:09:47 +0100 Subject: [PATCH 85/89] Apply suggestions from code review Co-authored-by: zeripath --- models/notification.go | 9 ++++++--- models/repo_transfer.go | 2 +- modules/convert/notification.go | 2 +- services/mailer/mail_repo.go | 2 +- services/repository/transfer.go | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/models/notification.go b/models/notification.go index efd1c323daafd..f6e4a5ccc4f37 100644 --- a/models/notification.go +++ b/models/notification.go @@ -753,9 +753,12 @@ func setIssueNotificationStatusReadIfUnread(e Engine, userID, issueID int64) err } func setRepoNotificationStatusReadIfUnread(e Engine, userID, repoID int64) error { - _, err := e.Where(builder.Eq{"user_id": userID}.And(builder.Eq{"status": NotificationStatusUnread}).And( - builder.Eq{"source": NotificationSourceRepository}).And(builder.Eq{"repo_id": repoID})). - Cols("status").Update(&Notification{Status: NotificationStatusRead}) + _, err := e.Where(builder.Eq{ + "user_id": userID, + "status": NotificationStatusUnread, + "source": NotificationSourceRepository, + "repo_id": repoID, + }).Cols("status").Update(&Notification{Status: NotificationStatusRead}) return err } diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 9fc57e69c1a40..e879b23134a8b 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -181,7 +181,7 @@ func CreatePendingRepositoryTransfer(doer, newOwner *User, repoID int64, teams [ CreatedUnix: timeutil.TimeStampNow(), UpdatedUnix: timeutil.TimeStampNow(), DoerID: doer.ID, - TeamIDs: []int64{}, + TeamIDs: make([]int64, 0, len(teams)), } for k := range teams { diff --git a/modules/convert/notification.go b/modules/convert/notification.go index 86536ccae83fc..49abe01253246 100644 --- a/modules/convert/notification.go +++ b/modules/convert/notification.go @@ -48,7 +48,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread { result.Subject.LatestCommentURL = comment.APIURL() } } - case models.NotificationSourceCommit: //unused until now + case models.NotificationSourceCommit: result.Subject = &api.NotificationSubject{ Type: "Commit", Title: n.CommitID, diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go index d5efdd6deefda..b9d24f4334d92 100644 --- a/services/mailer/mail_repo.go +++ b/services/mailer/mail_repo.go @@ -34,7 +34,7 @@ func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Reposi destination = "you" } - subject := fmt.Sprintf("%s like to transfer \"%s\" to %s", doer.DisplayName(), repo.FullName(), destination) + subject := fmt.Sprintf("%s would like to transfer \"%s\" to %s", doer.DisplayName(), repo.FullName(), destination) data := map[string]interface{}{ "Doer": doer, "User": repo.Owner, diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 5937667bb9415..ec769190bdb49 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -41,7 +41,7 @@ func TransferOwnership(doer, newOwner *models.User, repo *models.Repository, tea } for _, team := range teams { - if err = team.AddRepository(newRepo); err != nil { + if err := team.AddRepository(newRepo); err != nil { return err } } From 1387a7446e3111029902a640d06a64c2ef4f01e5 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 27 Feb 2021 22:24:42 +0100 Subject: [PATCH 86/89] limit var scope if posible --- models/repo_transfer.go | 43 +++++++++++++++------------------ modules/context/repo.go | 2 +- routers/api/v1/repo/transfer.go | 2 +- routers/repo/repo.go | 6 ++--- routers/repo/setting.go | 6 ++--- 5 files changed, 28 insertions(+), 31 deletions(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index e879b23134a8b..43e254588e8b8 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -158,18 +158,17 @@ func CreatePendingRepositoryTransfer(doer, newOwner *User, repoID int64, teams [ } // Make sure repo is ready to transfer - if err = TestRepositoryReadyForTransfer(repo.Status); err != nil { + if err := TestRepositoryReadyForTransfer(repo.Status); err != nil { return err } repo.Status = RepositoryPendingTransfer - if err = updateRepositoryCols(sess, repo, "status"); err != nil { + if err := updateRepositoryCols(sess, repo, "status"); err != nil { return err } // Check if new owner has repository with same name. - has, err := isRepositoryExist(sess, newOwner, repo.Name) - if err != nil { + if has, err := isRepositoryExist(sess, newOwner, repo.Name); err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { return ErrRepoAlreadyExist{newOwner.LowerName, repo.Name} @@ -188,7 +187,7 @@ func CreatePendingRepositoryTransfer(doer, newOwner *User, repoID int64, teams [ transfer.TeamIDs = append(transfer.TeamIDs, teams[k].ID) } - if _, err = sess.Insert(transfer); err != nil { + if _, err := sess.Insert(transfer); err != nil { return err } @@ -209,8 +208,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } // Check if new owner has repository with same name. - has, err := isRepositoryExist(sess, newOwner, repo.Name) - if err != nil { + if has, err := isRepositoryExist(sess, newOwner, repo.Name); err != nil { return fmt.Errorf("IsRepositoryExist: %v", err) } else if has { return ErrRepoAlreadyExist{newOwnerName, repo.Name} @@ -225,7 +223,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error repo.OwnerName = newOwner.Name // Update repository. - if _, err = sess.ID(repo.ID).Update(repo); err != nil { + if _, err := sess.ID(repo.ID).Update(repo); err != nil { return fmt.Errorf("update owner: %v", err) } @@ -247,14 +245,14 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } } collaboration.UserID = c.ID - if _, err = sess.Delete(collaboration); err != nil { + if _, err := sess.Delete(collaboration); err != nil { return fmt.Errorf("remove collaborator '%d': %v", c.ID, err) } } // Remove old team-repository relations. if oldOwner.IsOrganization() { - if err = oldOwner.removeOrgRepo(sess, repo.ID); err != nil { + if err := oldOwner.removeOrgRepo(sess, repo.ID); err != nil { return fmt.Errorf("removeOrgRepo: %v", err) } } @@ -270,25 +268,25 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } } } - } else if err = repo.recalculateAccesses(sess); err != nil { + } else if err := repo.recalculateAccesses(sess); err != nil { // Organization called this in addRepository method. return fmt.Errorf("recalculateAccesses: %v", err) } // Update repository count. - if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos+1 WHERE id=?", newOwner.ID); err != nil { + if _, err := sess.Exec("UPDATE `user` SET num_repos=num_repos+1 WHERE id=?", newOwner.ID); err != nil { return fmt.Errorf("increase new owner repository count: %v", err) - } else if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", oldOwner.ID); err != nil { + } else if _, err := sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", oldOwner.ID); err != nil { return fmt.Errorf("decrease old owner repository count: %v", err) } - if err = watchRepo(sess, doer.ID, repo.ID, true); err != nil { + if err := watchRepo(sess, doer.ID, repo.ID, true); err != nil { return fmt.Errorf("watchRepo: %v", err) } // Remove watch for organization. if oldOwner.IsOrganization() { - if err = watchRepo(sess, oldOwner.ID, repo.ID, false); err != nil { + if err := watchRepo(sess, oldOwner.ID, repo.ID, false); err != nil { return fmt.Errorf("watchRepo [false]: %v", err) } } @@ -300,24 +298,23 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error return fmt.Errorf("Failed to create dir %s: %v", dir, err) } - if err = os.Rename(RepoPath(oldOwner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { + if err := os.Rename(RepoPath(oldOwner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { return fmt.Errorf("rename repository directory: %v", err) } // Rename remote wiki repository to new path and delete local copy. wikiPath := WikiPath(oldOwner.Name, repo.Name) - isExist, err := util.IsExist(wikiPath) - if err != nil { + + if isExist, err := util.IsExist(wikiPath); err != nil { log.Error("Unable to check if %s exists. Error: %v", wikiPath, err) return err - } - if isExist { - if err = os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { + } else if isExist { + if err := os.Rename(wikiPath, WikiPath(newOwner.Name, repo.Name)); err != nil { return fmt.Errorf("rename repository wiki: %v", err) } } - if err = deleteRepositoryTransfer(sess, repo.ID); err != nil { + if err := deleteRepositoryTransfer(sess, repo.ID); err != nil { return fmt.Errorf("deleteRepositoryTransfer: %v", err) } repo.Status = RepositoryReady @@ -326,7 +323,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error } // If there was previously a redirect at this location, remove it. - if err = deleteRepoRedirect(sess, newOwner.ID, repo.Name); err != nil { + if err := deleteRepoRedirect(sess, newOwner.ID, repo.Name); err != nil { return fmt.Errorf("delete repo redirect: %v", err) } diff --git a/modules/context/repo.go b/modules/context/repo.go index 4e0a4a3f06d09..ba3cfe9bf256a 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -607,7 +607,7 @@ func RepoAssignment() func(http.Handler) http.Handler { return } - if err = repoTransfer.LoadAttributes(); err != nil { + if err := repoTransfer.LoadAttributes(); err != nil { ctx.ServerError("LoadRecipient", err) return } diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index 54104ca2e111d..2e052aa4ff23a 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -96,7 +96,7 @@ func Transfer(ctx *context.APIContext) { } } - if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, ctx.Repo.Repository, teams); err != nil { + if err := repo_service.StartRepositoryTransfer(ctx.User, newOwner, ctx.Repo.Repository, teams); err != nil { if models.IsErrRepoTransferInProgress(err) { ctx.Error(http.StatusConflict, "CreatePendingRepositoryTransfer", err) return diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 560c643218f29..6fa566e7d6f43 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -304,7 +304,7 @@ func acceptOrRejectRepoTransfer(ctx *context.Context, accept bool) error { return err } - if err = repoTransfer.LoadAttributes(); err != nil { + if err := repoTransfer.LoadAttributes(); err != nil { return err } @@ -313,12 +313,12 @@ func acceptOrRejectRepoTransfer(ctx *context.Context, accept bool) error { } if accept { - if err = repo_service.TransferOwnership(repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil { + if err := repo_service.TransferOwnership(repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil { return err } ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success")) } else { - if err = models.CancelRepositoryTransfer(ctx.Repo.Repository); err != nil { + if err := models.CancelRepositoryTransfer(ctx.Repo.Repository); err != nil { return err } ctx.Flash.Success(ctx.Tr("repo.settings.transfer.rejected")) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 451c6dff3e4cf..b35828d7b1d2f 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -478,7 +478,7 @@ func SettingsPost(ctx *context.Context) { ctx.Repo.GitRepo = nil } - if err = repo_service.StartRepositoryTransfer(ctx.User, newOwner, repo, nil); err != nil { + if err := repo_service.StartRepositoryTransfer(ctx.User, newOwner, repo, nil); err != nil { if models.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) } else if models.IsErrRepoTransferInProgress(err) { @@ -512,12 +512,12 @@ func SettingsPost(ctx *context.Context) { return } - if err = repoTransfer.LoadAttributes(); err != nil { + if err := repoTransfer.LoadAttributes(); err != nil { ctx.ServerError("LoadRecipient", err) return } - if err = models.CancelRepositoryTransfer(ctx.Repo.Repository); err != nil { + if err := models.CancelRepositoryTransfer(ctx.Repo.Repository); err != nil { ctx.ServerError("CancelRepositoryTransfer", err) return } From 1bed3c5455d7b6f6920947ba86d286adbe41f2b3 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 27 Feb 2021 22:39:52 +0100 Subject: [PATCH 87/89] line indenting --- templates/repo/header.tmpl | 32 ++++++++++++++-------------- templates/repo/settings/options.tmpl | 6 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 21af8e04f1583..2f593567d5d75 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -43,22 +43,22 @@ {{if not .IsBeingCreated}}

{{if $.RepoTransfer}} -
- {{$.CsrfTokenHtml}} -
- -
-
-
- {{$.CsrfTokenHtml}} -
- -
-
+
+ {{$.CsrfTokenHtml}} +
+ +
+
+
+ {{$.CsrfTokenHtml}} +
+ +
+
{{end}}
{{$.CsrfTokenHtml}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 9aca40d3fe729..b69f90f9c5849 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -457,9 +457,9 @@
{{.i18n.Tr "repo.settings.transfer"}}
{{if .RepoTransfer}} -

{{.i18n.Tr "repo.settings.transfer_started" .RepoTransfer.Recipient.DisplayName}}

- {{else}} -

{{.i18n.Tr "repo.settings.transfer_desc"}}

+

{{.i18n.Tr "repo.settings.transfer_started" .RepoTransfer.Recipient.DisplayName}}

+ {{else}} +

{{.i18n.Tr "repo.settings.transfer_desc"}}

{{end}}
From 39f5cd596e230e25dbdfe519b61e2d522d27ff78 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 28 Feb 2021 14:49:49 +0100 Subject: [PATCH 88/89] engine version of GetUsersWhoCanCreateOrgRepo --- models/notification.go | 2 +- models/org.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/models/notification.go b/models/notification.go index f6e4a5ccc4f37..a9178b97defd9 100644 --- a/models/notification.go +++ b/models/notification.go @@ -131,7 +131,7 @@ func CreateRepoTransferNotification(doer, newOwner *User, repo *Repository) erro var notify []*Notification if newOwner.IsOrganization() { - users, err := GetUsersWhoCanCreateOrgRepo(newOwner.ID) + users, err := getUsersWhoCanCreateOrgRepo(sess, newOwner.ID) if err != nil || len(users) == 0 { return err } diff --git a/models/org.go b/models/org.go index 9028e1d61d520..437d57d0561cb 100644 --- a/models/org.go +++ b/models/org.go @@ -393,6 +393,10 @@ func CanCreateOrgRepo(orgID, uid int64) (bool, error) { // GetUsersWhoCanCreateOrgRepo returns users which are able to create repo in organization func GetUsersWhoCanCreateOrgRepo(orgID int64) ([]*User, error) { + return getUsersWhoCanCreateOrgRepo(x, orgID) +} + +func getUsersWhoCanCreateOrgRepo(e Engine, orgID int64) ([]*User, error) { users := make([]*User, 0, 10) return users, x. Join("INNER", "`team_user`", "`team_user`.uid=`user`.id"). From c78a4096bb1c640e5ea1bad75167746d3c3efebe Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 1 Mar 2021 01:10:59 +0100 Subject: [PATCH 89/89] fix defer sess.Close() --- models/repo_transfer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 43e254588e8b8..cf1e16b9c3858 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -115,10 +115,10 @@ func deleteRepositoryTransfer(e Engine, repoID int64) error { // thus cancel the transfer process. func CancelRepositoryTransfer(repo *Repository) error { sess := x.NewSession() + defer sess.Close() if err := sess.Begin(); err != nil { return err } - defer sess.Close() repo.Status = RepositoryReady if err := updateRepositoryCols(sess, repo, "status"); err != nil {