From 86bd92cd9649c8584214c234f9eec89cfb1ae7a7 Mon Sep 17 00:00:00 2001 From: Lanre Adelowo Date: Sat, 12 Jan 2019 22:36:25 +0100 Subject: [PATCH 01/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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))