mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-04 08:26:20 +00:00
Merge branch 'main' into lunny/refactor_org_setting
This commit is contained in:
commit
a3d88b14f6
@ -282,77 +282,72 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
|
|||||||
// InsertRun inserts a run
|
// InsertRun inserts a run
|
||||||
// The title will be cut off at 255 characters if it's longer than 255 characters.
|
// The title will be cut off at 255 characters if it's longer than 255 characters.
|
||||||
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
|
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
run.Index = index
|
|
||||||
run.Title = util.EllipsisDisplayString(run.Title, 255)
|
|
||||||
|
|
||||||
if err := db.Insert(ctx, run); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if run.Repo == nil {
|
|
||||||
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
run.Repo = repo
|
run.Index = index
|
||||||
}
|
run.Title = util.EllipsisDisplayString(run.Title, 255)
|
||||||
|
|
||||||
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
if err := db.Insert(ctx, run); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
runJobs := make([]*ActionRunJob, 0, len(jobs))
|
|
||||||
var hasWaiting bool
|
|
||||||
for _, v := range jobs {
|
|
||||||
id, job := v.Job()
|
|
||||||
needs := job.Needs()
|
|
||||||
if err := v.SetJob(id, job.EraseNeeds()); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
payload, _ := v.Marshal()
|
|
||||||
status := StatusWaiting
|
|
||||||
if len(needs) > 0 || run.NeedApproval {
|
|
||||||
status = StatusBlocked
|
|
||||||
} else {
|
|
||||||
hasWaiting = true
|
|
||||||
}
|
|
||||||
job.Name = util.EllipsisDisplayString(job.Name, 255)
|
|
||||||
runJobs = append(runJobs, &ActionRunJob{
|
|
||||||
RunID: run.ID,
|
|
||||||
RepoID: run.RepoID,
|
|
||||||
OwnerID: run.OwnerID,
|
|
||||||
CommitSHA: run.CommitSHA,
|
|
||||||
IsForkPullRequest: run.IsForkPullRequest,
|
|
||||||
Name: job.Name,
|
|
||||||
WorkflowPayload: payload,
|
|
||||||
JobID: id,
|
|
||||||
Needs: needs,
|
|
||||||
RunsOn: job.RunsOn(),
|
|
||||||
Status: status,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := db.Insert(ctx, runJobs); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is a job in the waiting status, increase tasks version.
|
if run.Repo == nil {
|
||||||
if hasWaiting {
|
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
|
||||||
if err := IncreaseTaskVersion(ctx, run.OwnerID, run.RepoID); err != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
run.Repo = repo
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateRepoRunsNumbers(ctx, run.Repo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
runJobs := make([]*ActionRunJob, 0, len(jobs))
|
||||||
|
var hasWaiting bool
|
||||||
|
for _, v := range jobs {
|
||||||
|
id, job := v.Job()
|
||||||
|
needs := job.Needs()
|
||||||
|
if err := v.SetJob(id, job.EraseNeeds()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
payload, _ := v.Marshal()
|
||||||
|
status := StatusWaiting
|
||||||
|
if len(needs) > 0 || run.NeedApproval {
|
||||||
|
status = StatusBlocked
|
||||||
|
} else {
|
||||||
|
hasWaiting = true
|
||||||
|
}
|
||||||
|
job.Name = util.EllipsisDisplayString(job.Name, 255)
|
||||||
|
runJobs = append(runJobs, &ActionRunJob{
|
||||||
|
RunID: run.ID,
|
||||||
|
RepoID: run.RepoID,
|
||||||
|
OwnerID: run.OwnerID,
|
||||||
|
CommitSHA: run.CommitSHA,
|
||||||
|
IsForkPullRequest: run.IsForkPullRequest,
|
||||||
|
Name: job.Name,
|
||||||
|
WorkflowPayload: payload,
|
||||||
|
JobID: id,
|
||||||
|
Needs: needs,
|
||||||
|
RunsOn: job.RunsOn(),
|
||||||
|
Status: status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := db.Insert(ctx, runJobs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is a job in the waiting status, increase tasks version.
|
||||||
|
if hasWaiting {
|
||||||
|
if err := IncreaseTaskVersion(ctx, run.OwnerID, run.RepoID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
|
func GetRunByRepoAndID(ctx context.Context, repoID, runID int64) (*ActionRun, error) {
|
||||||
|
@ -56,65 +56,54 @@ func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin transaction
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
// Loop through each schedule row
|
||||||
if err != nil {
|
for _, row := range rows {
|
||||||
return err
|
row.Title = util.EllipsisDisplayString(row.Title, 255)
|
||||||
}
|
// Create new schedule row
|
||||||
defer committer.Close()
|
if err := db.Insert(ctx, row); err != nil {
|
||||||
|
|
||||||
// Loop through each schedule row
|
|
||||||
for _, row := range rows {
|
|
||||||
row.Title = util.EllipsisDisplayString(row.Title, 255)
|
|
||||||
// Create new schedule row
|
|
||||||
if err = db.Insert(ctx, row); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through each schedule spec and create a new spec row
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
for _, spec := range row.Specs {
|
|
||||||
specRow := &ActionScheduleSpec{
|
|
||||||
RepoID: row.RepoID,
|
|
||||||
ScheduleID: row.ID,
|
|
||||||
Spec: spec,
|
|
||||||
}
|
|
||||||
// Parse the spec and check for errors
|
|
||||||
schedule, err := specRow.Parse()
|
|
||||||
if err != nil {
|
|
||||||
continue // skip to the next spec if there's an error
|
|
||||||
}
|
|
||||||
|
|
||||||
specRow.Next = timeutil.TimeStamp(schedule.Next(now).Unix())
|
|
||||||
|
|
||||||
// Insert the new schedule spec row
|
|
||||||
if err = db.Insert(ctx, specRow); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit transaction
|
// Loop through each schedule spec and create a new spec row
|
||||||
return committer.Commit()
|
now := time.Now()
|
||||||
|
|
||||||
|
for _, spec := range row.Specs {
|
||||||
|
specRow := &ActionScheduleSpec{
|
||||||
|
RepoID: row.RepoID,
|
||||||
|
ScheduleID: row.ID,
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
// Parse the spec and check for errors
|
||||||
|
schedule, err := specRow.Parse()
|
||||||
|
if err != nil {
|
||||||
|
continue // skip to the next spec if there's an error
|
||||||
|
}
|
||||||
|
|
||||||
|
specRow.Next = timeutil.TimeStamp(schedule.Next(now).Unix())
|
||||||
|
|
||||||
|
// Insert the new schedule spec row
|
||||||
|
if err = db.Insert(ctx, specRow); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
|
func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil {
|
if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil {
|
return nil
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {
|
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {
|
||||||
|
@ -352,78 +352,70 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task
|
|||||||
stepStates[v.Id] = v
|
stepStates[v.Id] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*ActionTask, error) {
|
||||||
if err != nil {
|
e := db.GetEngine(ctx)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
e := db.GetEngine(ctx)
|
task := &ActionTask{}
|
||||||
|
if has, err := e.ID(state.Id).Get(task); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, util.ErrNotExist
|
||||||
|
} else if runnerID != task.RunnerID {
|
||||||
|
return nil, errors.New("invalid runner for task")
|
||||||
|
}
|
||||||
|
|
||||||
task := &ActionTask{}
|
if task.Status.IsDone() {
|
||||||
if has, err := e.ID(state.Id).Get(task); err != nil {
|
// the state is final, do nothing
|
||||||
return nil, err
|
return task, nil
|
||||||
} else if !has {
|
}
|
||||||
return nil, util.ErrNotExist
|
|
||||||
} else if runnerID != task.RunnerID {
|
// state.Result is not unspecified means the task is finished
|
||||||
return nil, errors.New("invalid runner for task")
|
if state.Result != runnerv1.Result_RESULT_UNSPECIFIED {
|
||||||
}
|
task.Status = Status(state.Result)
|
||||||
|
task.Stopped = timeutil.TimeStamp(state.StoppedAt.AsTime().Unix())
|
||||||
|
if err := UpdateTask(ctx, task, "status", "stopped"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := UpdateRunJob(ctx, &ActionRunJob{
|
||||||
|
ID: task.JobID,
|
||||||
|
Status: task.Status,
|
||||||
|
Stopped: task.Stopped,
|
||||||
|
}, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
|
||||||
|
task.Updated = timeutil.TimeStampNow()
|
||||||
|
if err := UpdateTask(ctx, task, "updated"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := task.LoadAttributes(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, step := range task.Steps {
|
||||||
|
var result runnerv1.Result
|
||||||
|
if v, ok := stepStates[step.Index]; ok {
|
||||||
|
result = v.Result
|
||||||
|
step.LogIndex = v.LogIndex
|
||||||
|
step.LogLength = v.LogLength
|
||||||
|
step.Started = convertTimestamp(v.StartedAt)
|
||||||
|
step.Stopped = convertTimestamp(v.StoppedAt)
|
||||||
|
}
|
||||||
|
if result != runnerv1.Result_RESULT_UNSPECIFIED {
|
||||||
|
step.Status = Status(result)
|
||||||
|
} else if step.Started != 0 {
|
||||||
|
step.Status = StatusRunning
|
||||||
|
}
|
||||||
|
if _, err := e.ID(step.ID).Update(step); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if task.Status.IsDone() {
|
|
||||||
// the state is final, do nothing
|
|
||||||
return task, nil
|
return task, nil
|
||||||
}
|
})
|
||||||
|
|
||||||
// state.Result is not unspecified means the task is finished
|
|
||||||
if state.Result != runnerv1.Result_RESULT_UNSPECIFIED {
|
|
||||||
task.Status = Status(state.Result)
|
|
||||||
task.Stopped = timeutil.TimeStamp(state.StoppedAt.AsTime().Unix())
|
|
||||||
if err := UpdateTask(ctx, task, "status", "stopped"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := UpdateRunJob(ctx, &ActionRunJob{
|
|
||||||
ID: task.JobID,
|
|
||||||
Status: task.Status,
|
|
||||||
Stopped: task.Stopped,
|
|
||||||
}, nil); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
|
|
||||||
task.Updated = timeutil.TimeStampNow()
|
|
||||||
if err := UpdateTask(ctx, task, "updated"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := task.LoadAttributes(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, step := range task.Steps {
|
|
||||||
var result runnerv1.Result
|
|
||||||
if v, ok := stepStates[step.Index]; ok {
|
|
||||||
result = v.Result
|
|
||||||
step.LogIndex = v.LogIndex
|
|
||||||
step.LogLength = v.LogLength
|
|
||||||
step.Started = convertTimestamp(v.StartedAt)
|
|
||||||
step.Stopped = convertTimestamp(v.StoppedAt)
|
|
||||||
}
|
|
||||||
if result != runnerv1.Result_RESULT_UNSPECIFIED {
|
|
||||||
step.Status = Status(result)
|
|
||||||
} else if step.Started != 0 {
|
|
||||||
step.Status = StatusRunning
|
|
||||||
}
|
|
||||||
if _, err := e.ID(step.ID).Update(step); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return task, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func StopTask(ctx context.Context, taskID int64, status Status) error {
|
func StopTask(ctx context.Context, taskID int64, status Status) error {
|
||||||
|
@ -73,33 +73,29 @@ func increaseTasksVersionByScope(ctx context.Context, ownerID, repoID int64) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
|
func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// 1. increase global
|
||||||
return err
|
if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil {
|
||||||
}
|
log.Error("IncreaseTasksVersionByScope(Global): %v", err)
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
// 1. increase global
|
|
||||||
if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil {
|
|
||||||
log.Error("IncreaseTasksVersionByScope(Global): %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. increase owner
|
|
||||||
if ownerID > 0 {
|
|
||||||
if err := increaseTasksVersionByScope(ctx, ownerID, 0); err != nil {
|
|
||||||
log.Error("IncreaseTasksVersionByScope(Owner): %v", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 3. increase repo
|
// 2. increase owner
|
||||||
if repoID > 0 {
|
if ownerID > 0 {
|
||||||
if err := increaseTasksVersionByScope(ctx, 0, repoID); err != nil {
|
if err := increaseTasksVersionByScope(ctx, ownerID, 0); err != nil {
|
||||||
log.Error("IncreaseTasksVersionByScope(Repo): %v", err)
|
log.Error("IncreaseTasksVersionByScope(Owner): %v", err)
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
// 3. increase repo
|
||||||
|
if repoID > 0 {
|
||||||
|
if err := increaseTasksVersionByScope(ctx, 0, repoID); err != nil {
|
||||||
|
log.Error("IncreaseTasksVersionByScope(Repo): %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -70,17 +70,9 @@ func (opts FindNotificationOptions) ToOrders() string {
|
|||||||
// for each watcher, or updates it if already exists
|
// for each watcher, or updates it if already exists
|
||||||
// receiverID > 0 just send to receiver, else send to all watcher
|
// receiverID > 0 just send to receiver, else send to all watcher
|
||||||
func CreateOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
|
func CreateOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return createOrUpdateIssueNotifications(ctx, issueID, commentID, notificationAuthorID, receiverID)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := createOrUpdateIssueNotifications(ctx, issueID, commentID, notificationAuthorID, receiverID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
|
func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
|
||||||
|
@ -228,17 +228,10 @@ func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err err
|
|||||||
return fmt.Errorf("GetPublicKeyByID: %w", err)
|
return fmt.Errorf("GetPublicKeyByID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
_, err = deleteGPGKey(ctx, key.KeyID)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err = deleteGPGKey(ctx, key.KeyID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {
|
func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {
|
||||||
|
@ -14,97 +14,76 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// __________________ ________ ____ __.
|
|
||||||
// / _____/\______ \/ _____/ | |/ _|____ ___.__.
|
|
||||||
// / \ ___ | ___/ \ ___ | <_/ __ < | |
|
|
||||||
// \ \_\ \| | \ \_\ \ | | \ ___/\___ |
|
|
||||||
// \______ /|____| \______ / |____|__ \___ > ____|
|
|
||||||
// \/ \/ \/ \/\/
|
|
||||||
// ____ ____ .__ _____
|
|
||||||
// \ \ / /___________|__|/ ____\__.__.
|
|
||||||
// \ Y // __ \_ __ \ \ __< | |
|
|
||||||
// \ /\ ___/| | \/ || | \___ |
|
|
||||||
// \___/ \___ >__| |__||__| / ____|
|
|
||||||
// \/ \/
|
|
||||||
|
|
||||||
// This file provides functions relating verifying gpg keys
|
// This file provides functions relating verifying gpg keys
|
||||||
|
|
||||||
// VerifyGPGKey marks a GPG key as verified
|
// VerifyGPGKey marks a GPG key as verified
|
||||||
func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature string) (string, error) {
|
func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature string) (string, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (string, error) {
|
||||||
if err != nil {
|
key := new(GPGKey)
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
key := new(GPGKey)
|
has, err := db.GetEngine(ctx).Where("owner_id = ? AND key_id = ?", ownerID, keyID).Get(key)
|
||||||
|
if err != nil {
|
||||||
has, err := db.GetEngine(ctx).Where("owner_id = ? AND key_id = ?", ownerID, keyID).Get(key)
|
return "", err
|
||||||
if err != nil {
|
} else if !has {
|
||||||
return "", err
|
return "", ErrGPGKeyNotExist{}
|
||||||
} else if !has {
|
|
||||||
return "", ErrGPGKeyNotExist{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := key.LoadSubKeys(ctx); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := ExtractSignature(signature)
|
|
||||||
if err != nil {
|
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
|
||||||
ID: key.KeyID,
|
|
||||||
Wrapped: err,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
signer, err := hashAndVerifyWithSubKeys(sig, token, key)
|
if err := key.LoadSubKeys(ctx); err != nil {
|
||||||
if err != nil {
|
return "", err
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
|
||||||
ID: key.KeyID,
|
|
||||||
Wrapped: err,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if signer == nil {
|
sig, err := ExtractSignature(signature)
|
||||||
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n", key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
ID: key.KeyID,
|
ID: key.KeyID,
|
||||||
Wrapped: err,
|
Wrapped: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if signer == nil {
|
signer, err := hashAndVerifyWithSubKeys(sig, token, key)
|
||||||
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n\n", key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
ID: key.KeyID,
|
ID: key.KeyID,
|
||||||
Wrapped: err,
|
Wrapped: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if signer == nil {
|
||||||
|
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n", key)
|
||||||
if signer == nil {
|
if err != nil {
|
||||||
log.Debug("VerifyGPGKey failed: no signer")
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
return "", ErrGPGInvalidTokenSignature{
|
ID: key.KeyID,
|
||||||
ID: key.KeyID,
|
Wrapped: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if signer == nil {
|
||||||
|
signer, err = hashAndVerifyWithSubKeys(sig, token+"\n\n", key)
|
||||||
|
if err != nil {
|
||||||
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
|
ID: key.KeyID,
|
||||||
|
Wrapped: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if signer.PrimaryKeyID != key.KeyID && signer.KeyID != key.KeyID {
|
if signer == nil {
|
||||||
return "", ErrGPGKeyNotExist{}
|
log.Debug("VerifyGPGKey failed: no signer")
|
||||||
}
|
return "", ErrGPGInvalidTokenSignature{
|
||||||
|
ID: key.KeyID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
key.Verified = true
|
if signer.PrimaryKeyID != key.KeyID && signer.KeyID != key.KeyID {
|
||||||
if _, err := db.GetEngine(ctx).ID(key.ID).SetExpr("verified", true).Update(new(GPGKey)); err != nil {
|
return "", ErrGPGKeyNotExist{}
|
||||||
return "", err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
key.Verified = true
|
||||||
return "", err
|
if _, err := db.GetEngine(ctx).ID(key.ID).SetExpr("verified", true).Update(new(GPGKey)); err != nil {
|
||||||
}
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
return key.KeyID, nil
|
return key.KeyID, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerificationToken returns token for the user that will be valid in minutes (time)
|
// VerificationToken returns token for the user that will be valid in minutes (time)
|
||||||
|
@ -99,40 +99,36 @@ func AddPublicKey(ctx context.Context, ownerID int64, name, content string, auth
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*PublicKey, error) {
|
||||||
if err != nil {
|
if err := checkKeyFingerprint(ctx, fingerprint); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := checkKeyFingerprint(ctx, fingerprint); err != nil {
|
// Key name of same user cannot be duplicated.
|
||||||
return nil, err
|
has, err := db.GetEngine(ctx).
|
||||||
}
|
Where("owner_id = ? AND name = ?", ownerID, name).
|
||||||
|
Get(new(PublicKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if has {
|
||||||
|
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
|
||||||
|
}
|
||||||
|
|
||||||
// Key name of same user cannot be duplicated.
|
key := &PublicKey{
|
||||||
has, err := db.GetEngine(ctx).
|
OwnerID: ownerID,
|
||||||
Where("owner_id = ? AND name = ?", ownerID, name).
|
Name: name,
|
||||||
Get(new(PublicKey))
|
Fingerprint: fingerprint,
|
||||||
if err != nil {
|
Content: content,
|
||||||
return nil, err
|
Mode: perm.AccessModeWrite,
|
||||||
} else if has {
|
Type: KeyTypeUser,
|
||||||
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
|
LoginSourceID: authSourceID,
|
||||||
}
|
}
|
||||||
|
if err = addKey(ctx, key); err != nil {
|
||||||
|
return nil, fmt.Errorf("addKey: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
key := &PublicKey{
|
return key, nil
|
||||||
OwnerID: ownerID,
|
})
|
||||||
Name: name,
|
|
||||||
Fingerprint: fingerprint,
|
|
||||||
Content: content,
|
|
||||||
Mode: perm.AccessModeWrite,
|
|
||||||
Type: KeyTypeUser,
|
|
||||||
LoginSourceID: authSourceID,
|
|
||||||
}
|
|
||||||
if err = addKey(ctx, key); err != nil {
|
|
||||||
return nil, fmt.Errorf("addKey: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return key, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPublicKeyByID returns public key by given ID.
|
// GetPublicKeyByID returns public key by given ID.
|
||||||
@ -288,33 +284,24 @@ func PublicKeyIsExternallyManaged(ctx context.Context, id int64) (bool, error) {
|
|||||||
|
|
||||||
// deleteKeysMarkedForDeletion returns true if ssh keys needs update
|
// deleteKeysMarkedForDeletion returns true if ssh keys needs update
|
||||||
func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, error) {
|
func deleteKeysMarkedForDeletion(ctx context.Context, keys []string) (bool, error) {
|
||||||
// Start session
|
return db.WithTx2(ctx, func(ctx context.Context) (bool, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
// Delete keys marked for deletion
|
||||||
if err != nil {
|
var sshKeysNeedUpdate bool
|
||||||
return false, err
|
for _, KeyToDelete := range keys {
|
||||||
}
|
key, err := SearchPublicKeyByContent(ctx, KeyToDelete)
|
||||||
defer committer.Close()
|
if err != nil {
|
||||||
|
log.Error("SearchPublicKeyByContent: %v", err)
|
||||||
// Delete keys marked for deletion
|
continue
|
||||||
var sshKeysNeedUpdate bool
|
}
|
||||||
for _, KeyToDelete := range keys {
|
if _, err = db.DeleteByID[PublicKey](ctx, key.ID); err != nil {
|
||||||
key, err := SearchPublicKeyByContent(ctx, KeyToDelete)
|
log.Error("DeleteByID[PublicKey]: %v", err)
|
||||||
if err != nil {
|
continue
|
||||||
log.Error("SearchPublicKeyByContent: %v", err)
|
}
|
||||||
continue
|
sshKeysNeedUpdate = true
|
||||||
}
|
}
|
||||||
if _, err = db.DeleteByID[PublicKey](ctx, key.ID); err != nil {
|
|
||||||
log.Error("DeleteByID[PublicKey]: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sshKeysNeedUpdate = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
return sshKeysNeedUpdate, nil
|
||||||
return false, err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return sshKeysNeedUpdate, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPublicKeysBySource add a users public keys. Returns true if there are changes.
|
// AddPublicKeysBySource add a users public keys. Returns true if there are changes.
|
||||||
|
@ -125,39 +125,35 @@ func AddDeployKey(ctx context.Context, repoID int64, name, content string, readO
|
|||||||
accessMode = perm.AccessModeWrite
|
accessMode = perm.AccessModeWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*DeployKey, error) {
|
||||||
if err != nil {
|
pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
} else if exist {
|
||||||
|
if pkey.Type != KeyTypeDeploy {
|
||||||
pkey, exist, err := db.Get[PublicKey](ctx, builder.Eq{"fingerprint": fingerprint})
|
return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
} else {
|
||||||
} else if exist {
|
// First time use this deploy key.
|
||||||
if pkey.Type != KeyTypeDeploy {
|
pkey = &PublicKey{
|
||||||
return nil, ErrKeyAlreadyExist{0, fingerprint, ""}
|
Fingerprint: fingerprint,
|
||||||
|
Mode: accessMode,
|
||||||
|
Type: KeyTypeDeploy,
|
||||||
|
Content: content,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
if err = addKey(ctx, pkey); err != nil {
|
||||||
|
return nil, fmt.Errorf("addKey: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// First time use this deploy key.
|
|
||||||
pkey = &PublicKey{
|
|
||||||
Fingerprint: fingerprint,
|
|
||||||
Mode: accessMode,
|
|
||||||
Type: KeyTypeDeploy,
|
|
||||||
Content: content,
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
if err = addKey(ctx, pkey); err != nil {
|
|
||||||
return nil, fmt.Errorf("addKey: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := addDeployKey(ctx, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
|
key, err := addDeployKey(ctx, pkey.ID, repoID, name, pkey.Fingerprint, accessMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, committer.Commit()
|
return key, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDeployKeyByID returns deploy key by given ID.
|
// GetDeployKeyByID returns deploy key by given ID.
|
||||||
|
@ -15,41 +15,33 @@ import (
|
|||||||
|
|
||||||
// VerifySSHKey marks a SSH key as verified
|
// VerifySSHKey marks a SSH key as verified
|
||||||
func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signature string) (string, error) {
|
func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signature string) (string, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (string, error) {
|
||||||
if err != nil {
|
key := new(PublicKey)
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
key := new(PublicKey)
|
has, err := db.GetEngine(ctx).Where("owner_id = ? AND fingerprint = ?", ownerID, fingerprint).Get(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if !has {
|
||||||
|
return "", ErrKeyNotExist{}
|
||||||
|
}
|
||||||
|
|
||||||
has, err := db.GetEngine(ctx).Where("owner_id = ? AND fingerprint = ?", ownerID, fingerprint).Get(key)
|
err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
|
||||||
} else if !has {
|
// see https://github.com/PowerShell/PowerShell/issues/5974
|
||||||
return "", ErrKeyNotExist{}
|
if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
|
||||||
}
|
log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
|
||||||
|
return "", ErrSSHInvalidTokenSignature{
|
||||||
err = sshsig.Verify(strings.NewReader(token), []byte(signature), []byte(key.Content), "gitea")
|
Fingerprint: key.Fingerprint,
|
||||||
if err != nil {
|
}
|
||||||
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
|
|
||||||
// see https://github.com/PowerShell/PowerShell/issues/5974
|
|
||||||
if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
|
|
||||||
log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
|
|
||||||
return "", ErrSSHInvalidTokenSignature{
|
|
||||||
Fingerprint: key.Fingerprint,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
key.Verified = true
|
key.Verified = true
|
||||||
if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil {
|
if _, err := db.GetEngine(ctx).ID(key.ID).Cols("verified").Update(key); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
return key.Fingerprint, nil
|
||||||
return "", err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return key.Fingerprint, nil
|
|
||||||
}
|
}
|
||||||
|
@ -289,35 +289,31 @@ type UpdateOAuth2ApplicationOptions struct {
|
|||||||
|
|
||||||
// UpdateOAuth2Application updates an oauth2 application
|
// UpdateOAuth2Application updates an oauth2 application
|
||||||
func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOptions) (*OAuth2Application, error) {
|
func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOptions) (*OAuth2Application, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*OAuth2Application, error) {
|
||||||
if err != nil {
|
app, err := GetOAuth2ApplicationByID(ctx, opts.ID)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
}
|
||||||
|
if app.UID != opts.UserID {
|
||||||
|
return nil, errors.New("UID mismatch")
|
||||||
|
}
|
||||||
|
builtinApps := BuiltinApplications()
|
||||||
|
if _, builtin := builtinApps[app.ClientID]; builtin {
|
||||||
|
return nil, fmt.Errorf("failed to edit OAuth2 application: application is locked: %s", app.ClientID)
|
||||||
|
}
|
||||||
|
|
||||||
app, err := GetOAuth2ApplicationByID(ctx, opts.ID)
|
app.Name = opts.Name
|
||||||
if err != nil {
|
app.RedirectURIs = opts.RedirectURIs
|
||||||
return nil, err
|
app.ConfidentialClient = opts.ConfidentialClient
|
||||||
}
|
app.SkipSecondaryAuthorization = opts.SkipSecondaryAuthorization
|
||||||
if app.UID != opts.UserID {
|
|
||||||
return nil, errors.New("UID mismatch")
|
|
||||||
}
|
|
||||||
builtinApps := BuiltinApplications()
|
|
||||||
if _, builtin := builtinApps[app.ClientID]; builtin {
|
|
||||||
return nil, fmt.Errorf("failed to edit OAuth2 application: application is locked: %s", app.ClientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Name = opts.Name
|
if err = updateOAuth2Application(ctx, app); err != nil {
|
||||||
app.RedirectURIs = opts.RedirectURIs
|
return nil, err
|
||||||
app.ConfidentialClient = opts.ConfidentialClient
|
}
|
||||||
app.SkipSecondaryAuthorization = opts.SkipSecondaryAuthorization
|
app.ClientSecret = ""
|
||||||
|
|
||||||
if err = updateOAuth2Application(ctx, app); err != nil {
|
return app, nil
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
app.ClientSecret = ""
|
|
||||||
|
|
||||||
return app, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error {
|
func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error {
|
||||||
@ -358,23 +354,17 @@ func deleteOAuth2Application(ctx context.Context, id, userid int64) error {
|
|||||||
|
|
||||||
// DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app.
|
// DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app.
|
||||||
func DeleteOAuth2Application(ctx context.Context, id, userid int64) error {
|
func DeleteOAuth2Application(ctx context.Context, id, userid int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
app, err := GetOAuth2ApplicationByID(ctx, id)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
defer committer.Close()
|
}
|
||||||
app, err := GetOAuth2ApplicationByID(ctx, id)
|
builtinApps := BuiltinApplications()
|
||||||
if err != nil {
|
if _, builtin := builtinApps[app.ClientID]; builtin {
|
||||||
return err
|
return fmt.Errorf("failed to delete OAuth2 application: application is locked: %s", app.ClientID)
|
||||||
}
|
}
|
||||||
builtinApps := BuiltinApplications()
|
return deleteOAuth2Application(ctx, id, userid)
|
||||||
if _, builtin := builtinApps[app.ClientID]; builtin {
|
})
|
||||||
return fmt.Errorf("failed to delete OAuth2 application: application is locked: %s", app.ClientID)
|
|
||||||
}
|
|
||||||
if err := deleteOAuth2Application(ctx, id, userid); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
|
@ -35,26 +35,22 @@ func UpdateSession(ctx context.Context, key string, data []byte) error {
|
|||||||
|
|
||||||
// ReadSession reads the data for the provided session
|
// ReadSession reads the data for the provided session
|
||||||
func ReadSession(ctx context.Context, key string) (*Session, error) {
|
func ReadSession(ctx context.Context, key string) (*Session, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Session, error) {
|
||||||
if err != nil {
|
session, exist, err := db.Get[Session](ctx, builder.Eq{"`key`": key})
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
session, exist, err := db.Get[Session](ctx, builder.Eq{"`key`": key})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if !exist {
|
|
||||||
session = &Session{
|
|
||||||
Key: key,
|
|
||||||
Expiry: timeutil.TimeStampNow(),
|
|
||||||
}
|
|
||||||
if err := db.Insert(ctx, session); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
|
} else if !exist {
|
||||||
|
session = &Session{
|
||||||
|
Key: key,
|
||||||
|
Expiry: timeutil.TimeStampNow(),
|
||||||
|
}
|
||||||
|
if err := db.Insert(ctx, session); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return session, committer.Commit()
|
return session, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExistSession checks if a session exists
|
// ExistSession checks if a session exists
|
||||||
@ -72,40 +68,36 @@ func DestroySession(ctx context.Context, key string) error {
|
|||||||
|
|
||||||
// RegenerateSession regenerates a session from the old id
|
// RegenerateSession regenerates a session from the old id
|
||||||
func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, error) {
|
func RegenerateSession(ctx context.Context, oldKey, newKey string) (*Session, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Session, error) {
|
||||||
if err != nil {
|
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": newKey}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
} else if has {
|
||||||
defer committer.Close()
|
return nil, fmt.Errorf("session Key: %s already exists", newKey)
|
||||||
|
}
|
||||||
|
|
||||||
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": newKey}); err != nil {
|
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": oldKey}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if has {
|
} else if !has {
|
||||||
return nil, fmt.Errorf("session Key: %s already exists", newKey)
|
if err := db.Insert(ctx, &Session{
|
||||||
}
|
Key: oldKey,
|
||||||
|
Expiry: timeutil.TimeStampNow(),
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if has, err := db.Exist[Session](ctx, builder.Eq{"`key`": oldKey}); err != nil {
|
if _, err := db.Exec(ctx, "UPDATE "+db.TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
|
||||||
return nil, err
|
|
||||||
} else if !has {
|
|
||||||
if err := db.Insert(ctx, &Session{
|
|
||||||
Key: oldKey,
|
|
||||||
Expiry: timeutil.TimeStampNow(),
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE "+db.TableName(&Session{})+" SET `key` = ? WHERE `key`=?", newKey, oldKey); err != nil {
|
s, _, err := db.Get[Session](ctx, builder.Eq{"`key`": newKey})
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
// is not exist, it should be impossible
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
s, _, err := db.Get[Session](ctx, builder.Eq{"`key`": newKey})
|
return s, nil
|
||||||
if err != nil {
|
})
|
||||||
// is not exist, it should be impossible
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountSessions returns the number of sessions
|
// CountSessions returns the number of sessions
|
||||||
|
@ -470,35 +470,31 @@ func NewCommitStatus(ctx context.Context, opts NewCommitStatusOptions) error {
|
|||||||
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", opts.Repo.FullName(), opts.SHA)
|
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", opts.Repo.FullName(), opts.SHA)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// Get the next Status Index
|
||||||
return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", opts.Repo.ID, opts.Creator.ID, opts.SHA, err)
|
idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA.String())
|
||||||
}
|
if err != nil {
|
||||||
defer committer.Close()
|
return fmt.Errorf("generate commit status index failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the next Status Index
|
opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description)
|
||||||
idx, err := GetNextCommitStatusIndex(ctx, opts.Repo.ID, opts.SHA.String())
|
opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
|
||||||
if err != nil {
|
opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
|
||||||
return fmt.Errorf("generate commit status index failed: %w", err)
|
opts.CommitStatus.SHA = opts.SHA.String()
|
||||||
}
|
opts.CommitStatus.CreatorID = opts.Creator.ID
|
||||||
|
opts.CommitStatus.RepoID = opts.Repo.ID
|
||||||
|
opts.CommitStatus.Index = idx
|
||||||
|
log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
|
||||||
|
|
||||||
opts.CommitStatus.Description = strings.TrimSpace(opts.CommitStatus.Description)
|
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
|
||||||
opts.CommitStatus.Context = strings.TrimSpace(opts.CommitStatus.Context)
|
|
||||||
opts.CommitStatus.TargetURL = strings.TrimSpace(opts.CommitStatus.TargetURL)
|
|
||||||
opts.CommitStatus.SHA = opts.SHA.String()
|
|
||||||
opts.CommitStatus.CreatorID = opts.Creator.ID
|
|
||||||
opts.CommitStatus.RepoID = opts.Repo.ID
|
|
||||||
opts.CommitStatus.Index = idx
|
|
||||||
log.Debug("NewCommitStatus[%s, %s]: %d", opts.Repo.FullName(), opts.SHA, opts.CommitStatus.Index)
|
|
||||||
|
|
||||||
opts.CommitStatus.ContextHash = hashCommitStatusContext(opts.CommitStatus.Context)
|
// Insert new CommitStatus
|
||||||
|
if err = db.Insert(ctx, opts.CommitStatus); err != nil {
|
||||||
|
return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Insert new CommitStatus
|
return nil
|
||||||
if _, err = db.GetEngine(ctx).Insert(opts.CommitStatus); err != nil {
|
})
|
||||||
return fmt.Errorf("insert CommitStatus[%s, %s]: %w", opts.Repo.FullName(), opts.SHA, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignCommitWithStatuses represents a commit with validation of signature and status state.
|
// SignCommitWithStatuses represents a commit with validation of signature and status state.
|
||||||
|
@ -135,25 +135,18 @@ var ErrLFSObjectNotExist = db.ErrNotExist{Resource: "LFS Meta object"}
|
|||||||
// NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
|
// NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
|
||||||
// if it is not already present.
|
// if it is not already present.
|
||||||
func NewLFSMetaObject(ctx context.Context, repoID int64, p lfs.Pointer) (*LFSMetaObject, error) {
|
func NewLFSMetaObject(ctx context.Context, repoID int64, p lfs.Pointer) (*LFSMetaObject, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
m, exist, err := db.Get[LFSMetaObject](ctx, builder.Eq{"repository_id": repoID, "oid": p.Oid})
|
m, exist, err := db.Get[LFSMetaObject](ctx, builder.Eq{"repository_id": repoID, "oid": p.Oid})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if exist {
|
} else if exist {
|
||||||
return m, committer.Commit()
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m = &LFSMetaObject{Pointer: p, RepositoryID: repoID}
|
m = &LFSMetaObject{Pointer: p, RepositoryID: repoID}
|
||||||
if err = db.Insert(ctx, m); err != nil {
|
if err = db.Insert(ctx, m); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return m, nil
|
||||||
return m, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLFSMetaObjectByOid selects a LFSMetaObject entry from database by its OID.
|
// GetLFSMetaObjectByOid selects a LFSMetaObject entry from database by its OID.
|
||||||
|
@ -70,32 +70,28 @@ func (l *LFSLock) LoadOwner(ctx context.Context) error {
|
|||||||
|
|
||||||
// CreateLFSLock creates a new lock.
|
// CreateLFSLock creates a new lock.
|
||||||
func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
|
func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*LFSLock, error) {
|
||||||
if err != nil {
|
if err := CheckLFSAccessForRepo(ctx, lock.OwnerID, repo, perm.AccessModeWrite); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := CheckLFSAccessForRepo(dbCtx, lock.OwnerID, repo, perm.AccessModeWrite); err != nil {
|
lock.Path = util.PathJoinRel(lock.Path)
|
||||||
return nil, err
|
lock.RepoID = repo.ID
|
||||||
}
|
|
||||||
|
|
||||||
lock.Path = util.PathJoinRel(lock.Path)
|
l, err := GetLFSLock(ctx, repo, lock.Path)
|
||||||
lock.RepoID = repo.ID
|
if err == nil {
|
||||||
|
return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
|
||||||
|
}
|
||||||
|
if !IsErrLFSLockNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
l, err := GetLFSLock(dbCtx, repo, lock.Path)
|
if err := db.Insert(ctx, lock); err != nil {
|
||||||
if err == nil {
|
return nil, err
|
||||||
return l, ErrLFSLockAlreadyExist{lock.RepoID, lock.Path}
|
}
|
||||||
}
|
|
||||||
if !IsErrLFSLockNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Insert(dbCtx, lock); err != nil {
|
return lock, nil
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return lock, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLFSLock returns release by given path.
|
// GetLFSLock returns release by given path.
|
||||||
@ -163,30 +159,26 @@ func CountLFSLockByRepoID(ctx context.Context, repoID int64) (int64, error) {
|
|||||||
|
|
||||||
// DeleteLFSLockByID deletes a lock by given ID.
|
// DeleteLFSLockByID deletes a lock by given ID.
|
||||||
func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
|
func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*LFSLock, error) {
|
||||||
if err != nil {
|
lock, err := GetLFSLockByID(ctx, id)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
lock, err := GetLFSLockByID(dbCtx, id)
|
if err := CheckLFSAccessForRepo(ctx, u.ID, repo, perm.AccessModeWrite); err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := CheckLFSAccessForRepo(dbCtx, u.ID, repo, perm.AccessModeWrite); err != nil {
|
if !force && u.ID != lock.OwnerID {
|
||||||
return nil, err
|
return nil, errors.New("user doesn't own lock and force flag is not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !force && u.ID != lock.OwnerID {
|
if _, err := db.GetEngine(ctx).ID(id).Delete(new(LFSLock)); err != nil {
|
||||||
return nil, errors.New("user doesn't own lock and force flag is not set")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil {
|
return lock, nil
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return lock, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckLFSAccessForRepo check needed access mode base on action
|
// CheckLFSAccessForRepo check needed access mode base on action
|
||||||
|
@ -766,81 +766,73 @@ func (c *Comment) CodeCommentLink(ctx context.Context) string {
|
|||||||
|
|
||||||
// CreateComment creates comment with context
|
// CreateComment creates comment with context
|
||||||
func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) {
|
func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
var LabelID int64
|
||||||
return nil, err
|
if opts.Label != nil {
|
||||||
}
|
LabelID = opts.Label.ID
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
e := db.GetEngine(ctx)
|
|
||||||
var LabelID int64
|
|
||||||
if opts.Label != nil {
|
|
||||||
LabelID = opts.Label.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
var commentMetaData *CommentMetaData
|
|
||||||
if opts.ProjectColumnTitle != "" {
|
|
||||||
commentMetaData = &CommentMetaData{
|
|
||||||
ProjectColumnID: opts.ProjectColumnID,
|
|
||||||
ProjectColumnTitle: opts.ProjectColumnTitle,
|
|
||||||
ProjectTitle: opts.ProjectTitle,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
comment := &Comment{
|
var commentMetaData *CommentMetaData
|
||||||
Type: opts.Type,
|
if opts.ProjectColumnTitle != "" {
|
||||||
PosterID: opts.Doer.ID,
|
commentMetaData = &CommentMetaData{
|
||||||
Poster: opts.Doer,
|
ProjectColumnID: opts.ProjectColumnID,
|
||||||
IssueID: opts.Issue.ID,
|
ProjectColumnTitle: opts.ProjectColumnTitle,
|
||||||
LabelID: LabelID,
|
ProjectTitle: opts.ProjectTitle,
|
||||||
OldMilestoneID: opts.OldMilestoneID,
|
}
|
||||||
MilestoneID: opts.MilestoneID,
|
}
|
||||||
OldProjectID: opts.OldProjectID,
|
|
||||||
ProjectID: opts.ProjectID,
|
|
||||||
TimeID: opts.TimeID,
|
|
||||||
RemovedAssignee: opts.RemovedAssignee,
|
|
||||||
AssigneeID: opts.AssigneeID,
|
|
||||||
AssigneeTeamID: opts.AssigneeTeamID,
|
|
||||||
CommitID: opts.CommitID,
|
|
||||||
CommitSHA: opts.CommitSHA,
|
|
||||||
Line: opts.LineNum,
|
|
||||||
Content: opts.Content,
|
|
||||||
OldTitle: opts.OldTitle,
|
|
||||||
NewTitle: opts.NewTitle,
|
|
||||||
OldRef: opts.OldRef,
|
|
||||||
NewRef: opts.NewRef,
|
|
||||||
DependentIssueID: opts.DependentIssueID,
|
|
||||||
TreePath: opts.TreePath,
|
|
||||||
ReviewID: opts.ReviewID,
|
|
||||||
Patch: opts.Patch,
|
|
||||||
RefRepoID: opts.RefRepoID,
|
|
||||||
RefIssueID: opts.RefIssueID,
|
|
||||||
RefCommentID: opts.RefCommentID,
|
|
||||||
RefAction: opts.RefAction,
|
|
||||||
RefIsPull: opts.RefIsPull,
|
|
||||||
IsForcePush: opts.IsForcePush,
|
|
||||||
Invalidated: opts.Invalidated,
|
|
||||||
CommentMetaData: commentMetaData,
|
|
||||||
}
|
|
||||||
if _, err = e.Insert(comment); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = opts.Repo.LoadOwner(ctx); err != nil {
|
comment := &Comment{
|
||||||
return nil, err
|
Type: opts.Type,
|
||||||
}
|
PosterID: opts.Doer.ID,
|
||||||
|
Poster: opts.Doer,
|
||||||
|
IssueID: opts.Issue.ID,
|
||||||
|
LabelID: LabelID,
|
||||||
|
OldMilestoneID: opts.OldMilestoneID,
|
||||||
|
MilestoneID: opts.MilestoneID,
|
||||||
|
OldProjectID: opts.OldProjectID,
|
||||||
|
ProjectID: opts.ProjectID,
|
||||||
|
TimeID: opts.TimeID,
|
||||||
|
RemovedAssignee: opts.RemovedAssignee,
|
||||||
|
AssigneeID: opts.AssigneeID,
|
||||||
|
AssigneeTeamID: opts.AssigneeTeamID,
|
||||||
|
CommitID: opts.CommitID,
|
||||||
|
CommitSHA: opts.CommitSHA,
|
||||||
|
Line: opts.LineNum,
|
||||||
|
Content: opts.Content,
|
||||||
|
OldTitle: opts.OldTitle,
|
||||||
|
NewTitle: opts.NewTitle,
|
||||||
|
OldRef: opts.OldRef,
|
||||||
|
NewRef: opts.NewRef,
|
||||||
|
DependentIssueID: opts.DependentIssueID,
|
||||||
|
TreePath: opts.TreePath,
|
||||||
|
ReviewID: opts.ReviewID,
|
||||||
|
Patch: opts.Patch,
|
||||||
|
RefRepoID: opts.RefRepoID,
|
||||||
|
RefIssueID: opts.RefIssueID,
|
||||||
|
RefCommentID: opts.RefCommentID,
|
||||||
|
RefAction: opts.RefAction,
|
||||||
|
RefIsPull: opts.RefIsPull,
|
||||||
|
IsForcePush: opts.IsForcePush,
|
||||||
|
Invalidated: opts.Invalidated,
|
||||||
|
CommentMetaData: commentMetaData,
|
||||||
|
}
|
||||||
|
if err = db.Insert(ctx, comment); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err = updateCommentInfos(ctx, opts, comment); err != nil {
|
if err = opts.Repo.LoadOwner(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil {
|
if err = updateCommentInfos(ctx, opts, comment); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = committer.Commit(); err != nil {
|
|
||||||
return nil, err
|
if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil {
|
||||||
}
|
return nil, err
|
||||||
return comment, nil
|
}
|
||||||
|
return comment, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment *Comment) (err error) {
|
func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment *Comment) (err error) {
|
||||||
@ -1092,33 +1084,21 @@ func UpdateCommentInvalidate(ctx context.Context, c *Comment) error {
|
|||||||
|
|
||||||
// UpdateComment updates information of comment.
|
// UpdateComment updates information of comment.
|
||||||
func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *user_model.User) error {
|
func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *user_model.User) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
c.ContentVersion = contentVersion + 1
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
c.ContentVersion = contentVersion + 1
|
affected, err := db.GetEngine(ctx).ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c)
|
||||||
|
if err != nil {
|
||||||
affected, err := sess.ID(c.ID).AllCols().Where("content_version = ?", contentVersion).Update(c)
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return err
|
if affected == 0 {
|
||||||
}
|
return ErrCommentAlreadyChanged
|
||||||
if affected == 0 {
|
}
|
||||||
return ErrCommentAlreadyChanged
|
if err := c.LoadIssue(ctx); err != nil {
|
||||||
}
|
return err
|
||||||
if err := c.LoadIssue(ctx); err != nil {
|
}
|
||||||
return err
|
return c.AddCrossReferences(ctx, doer, true)
|
||||||
}
|
})
|
||||||
if err := c.AddCrossReferences(ctx, doer, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return fmt.Errorf("Commit: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteComment deletes the comment
|
// DeleteComment deletes the comment
|
||||||
@ -1277,31 +1257,28 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error {
|
|||||||
return comment.IssueID, true
|
return comment.IssueID, true
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, comment := range comments {
|
||||||
return err
|
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(comment); err != nil {
|
||||||
}
|
return err
|
||||||
defer committer.Close()
|
}
|
||||||
for _, comment := range comments {
|
|
||||||
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(comment); err != nil {
|
for _, reaction := range comment.Reactions {
|
||||||
return err
|
reaction.IssueID = comment.IssueID
|
||||||
|
reaction.CommentID = comment.ID
|
||||||
|
}
|
||||||
|
if len(comment.Reactions) > 0 {
|
||||||
|
if err := db.Insert(ctx, comment.Reactions); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, reaction := range comment.Reactions {
|
for _, issueID := range issueIDs {
|
||||||
reaction.IssueID = comment.IssueID
|
if err := UpdateIssueNumComments(ctx, issueID); err != nil {
|
||||||
reaction.CommentID = comment.ID
|
|
||||||
}
|
|
||||||
if len(comment.Reactions) > 0 {
|
|
||||||
if err := db.Insert(ctx, comment.Reactions); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
for _, issueID := range issueIDs {
|
|
||||||
if err := UpdateIssueNumComments(ctx, issueID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@ -128,79 +128,64 @@ const (
|
|||||||
|
|
||||||
// CreateIssueDependency creates a new dependency for an issue
|
// CreateIssueDependency creates a new dependency for an issue
|
||||||
func CreateIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue) error {
|
func CreateIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// Check if it already exists
|
||||||
return err
|
exists, err := issueDepExists(ctx, issue.ID, dep.ID)
|
||||||
}
|
if err != nil {
|
||||||
defer committer.Close()
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return ErrDependencyExists{issue.ID, dep.ID}
|
||||||
|
}
|
||||||
|
// And if it would be circular
|
||||||
|
circular, err := issueDepExists(ctx, dep.ID, issue.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if circular {
|
||||||
|
return ErrCircularDependency{issue.ID, dep.ID}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if it already exists
|
if err := db.Insert(ctx, &IssueDependency{
|
||||||
exists, err := issueDepExists(ctx, issue.ID, dep.ID)
|
UserID: user.ID,
|
||||||
if err != nil {
|
IssueID: issue.ID,
|
||||||
return err
|
DependencyID: dep.ID,
|
||||||
}
|
}); err != nil {
|
||||||
if exists {
|
return err
|
||||||
return ErrDependencyExists{issue.ID, dep.ID}
|
}
|
||||||
}
|
|
||||||
// And if it would be circular
|
|
||||||
circular, err := issueDepExists(ctx, dep.ID, issue.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if circular {
|
|
||||||
return ErrCircularDependency{issue.ID, dep.ID}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Insert(ctx, &IssueDependency{
|
// Add comment referencing the new dependency
|
||||||
UserID: user.ID,
|
return createIssueDependencyComment(ctx, user, issue, dep, true)
|
||||||
IssueID: issue.ID,
|
})
|
||||||
DependencyID: dep.ID,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add comment referencing the new dependency
|
|
||||||
if err = createIssueDependencyComment(ctx, user, issue, dep, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveIssueDependency removes a dependency from an issue
|
// RemoveIssueDependency removes a dependency from an issue
|
||||||
func RemoveIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) {
|
func RemoveIssueDependency(ctx context.Context, user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
var issueDepToDelete IssueDependency
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
var issueDepToDelete IssueDependency
|
switch depType {
|
||||||
|
case DependencyTypeBlockedBy:
|
||||||
|
issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID}
|
||||||
|
case DependencyTypeBlocking:
|
||||||
|
issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID}
|
||||||
|
default:
|
||||||
|
return ErrUnknownDependencyType{depType}
|
||||||
|
}
|
||||||
|
|
||||||
switch depType {
|
affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete)
|
||||||
case DependencyTypeBlockedBy:
|
if err != nil {
|
||||||
issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID}
|
return err
|
||||||
case DependencyTypeBlocking:
|
}
|
||||||
issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID}
|
|
||||||
default:
|
|
||||||
return ErrUnknownDependencyType{depType}
|
|
||||||
}
|
|
||||||
|
|
||||||
affected, err := db.GetEngine(ctx).Delete(&issueDepToDelete)
|
// If we deleted nothing, the dependency did not exist
|
||||||
if err != nil {
|
if affected <= 0 {
|
||||||
return err
|
return ErrDependencyNotExists{issue.ID, dep.ID}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we deleted nothing, the dependency did not exist
|
// Add comment referencing the removed dependency
|
||||||
if affected <= 0 {
|
return createIssueDependencyComment(ctx, user, issue, dep, false)
|
||||||
return ErrDependencyNotExists{issue.ID, dep.ID}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// Add comment referencing the removed dependency
|
|
||||||
if err = createIssueDependencyComment(ctx, user, issue, dep, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the dependency already exists
|
// Check if the dependency already exists
|
||||||
|
@ -755,18 +755,14 @@ func (issue *Issue) HasOriginalAuthor() bool {
|
|||||||
|
|
||||||
// InsertIssues insert issues to database
|
// InsertIssues insert issues to database
|
||||||
func InsertIssues(ctx context.Context, issues ...*Issue) error {
|
func InsertIssues(ctx context.Context, issues ...*Issue) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, issue := range issues {
|
||||||
return err
|
if err := insertIssue(ctx, issue); err != nil {
|
||||||
}
|
return err
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
for _, issue := range issues {
|
|
||||||
if err := insertIssue(ctx, issue); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
return committer.Commit()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func insertIssue(ctx context.Context, issue *Issue) error {
|
func insertIssue(ctx context.Context, issue *Issue) error {
|
||||||
|
@ -12,20 +12,12 @@ import (
|
|||||||
// RecalculateIssueIndexForRepo create issue_index for repo if not exist and
|
// RecalculateIssueIndexForRepo create issue_index for repo if not exist and
|
||||||
// update it based on highest index of existing issues assigned to a repo
|
// update it based on highest index of existing issues assigned to a repo
|
||||||
func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error {
|
func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
var maxIndex int64
|
||||||
return err
|
if _, err := db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&maxIndex); err != nil {
|
||||||
}
|
return err
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
var maxIndex int64
|
return db.SyncMaxResourceIndex(ctx, "issue_index", repoID, maxIndex)
|
||||||
if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&maxIndex); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = db.SyncMaxResourceIndex(ctx, "issue_index", repoID, maxIndex); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@ -88,36 +88,28 @@ func NewIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_m
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = issue.LoadRepo(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = issue.LoadRepo(ctx); err != nil {
|
// Do NOT add invalid labels
|
||||||
return err
|
if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID {
|
||||||
}
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Do NOT add invalid labels
|
if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil {
|
||||||
if issue.RepoID != label.RepoID && issue.Repo.OwnerID != label.OrgID {
|
return nil
|
||||||
return nil
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err = RemoveDuplicateExclusiveIssueLabels(ctx, issue, label, doer); err != nil {
|
if err = newIssueLabel(ctx, issue, label, doer); err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = newIssueLabel(ctx, issue, label, doer); err != nil {
|
issue.isLabelsLoaded = false
|
||||||
return err
|
issue.Labels = nil
|
||||||
}
|
return issue.LoadLabels(ctx)
|
||||||
|
})
|
||||||
issue.isLabelsLoaded = false
|
|
||||||
issue.Labels = nil
|
|
||||||
if err = issue.LoadLabels(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIssueLabels add labels to an issue. It will check if the labels are valid for the issue
|
// newIssueLabels add labels to an issue. It will check if the labels are valid for the issue
|
||||||
@ -151,24 +143,16 @@ func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *us
|
|||||||
|
|
||||||
// NewIssueLabels creates a list of issue-label relations.
|
// NewIssueLabels creates a list of issue-label relations.
|
||||||
func NewIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
|
func NewIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = newIssueLabels(ctx, issue, labels, doer); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = newIssueLabels(ctx, issue, labels, doer); err != nil {
|
// reload all labels
|
||||||
return err
|
issue.isLabelsLoaded = false
|
||||||
}
|
issue.Labels = nil
|
||||||
|
return issue.LoadLabels(ctx)
|
||||||
// reload all labels
|
})
|
||||||
issue.isLabelsLoaded = false
|
|
||||||
issue.Labels = nil
|
|
||||||
if err = issue.LoadLabels(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) {
|
func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) {
|
||||||
@ -365,35 +349,23 @@ func clearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User)
|
|||||||
// ClearIssueLabels removes all issue labels as the given user.
|
// ClearIssueLabels removes all issue labels as the given user.
|
||||||
// Triggers appropriate WebHooks, if any.
|
// Triggers appropriate WebHooks, if any.
|
||||||
func ClearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) (err error) {
|
func ClearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err := issue.LoadRepo(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
} else if err = issue.LoadPullRequest(ctx); err != nil {
|
||||||
defer committer.Close()
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := issue.LoadRepo(ctx); err != nil {
|
perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
|
||||||
return err
|
if err != nil {
|
||||||
} else if err = issue.LoadPullRequest(ctx); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||||
|
return ErrRepoLabelNotExist{}
|
||||||
|
}
|
||||||
|
|
||||||
perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
|
return clearIssueLabels(ctx, issue, doer)
|
||||||
if err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
|
|
||||||
return ErrRepoLabelNotExist{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = clearIssueLabels(ctx, issue, doer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = committer.Commit(); err != nil {
|
|
||||||
return fmt.Errorf("Commit: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type labelSorter []*Label
|
type labelSorter []*Label
|
||||||
@ -438,69 +410,61 @@ func RemoveDuplicateExclusiveLabels(labels []*Label) []*Label {
|
|||||||
// ReplaceIssueLabels removes all current labels and add new labels to the issue.
|
// ReplaceIssueLabels removes all current labels and add new labels to the issue.
|
||||||
// Triggers appropriate WebHooks, if any.
|
// Triggers appropriate WebHooks, if any.
|
||||||
func ReplaceIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
|
func ReplaceIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = issue.LoadRepo(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = issue.LoadRepo(ctx); err != nil {
|
if err = issue.LoadLabels(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = issue.LoadLabels(ctx); err != nil {
|
labels = RemoveDuplicateExclusiveLabels(labels)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
labels = RemoveDuplicateExclusiveLabels(labels)
|
sort.Sort(labelSorter(labels))
|
||||||
|
sort.Sort(labelSorter(issue.Labels))
|
||||||
|
|
||||||
sort.Sort(labelSorter(labels))
|
var toAdd, toRemove []*Label
|
||||||
sort.Sort(labelSorter(issue.Labels))
|
|
||||||
|
|
||||||
var toAdd, toRemove []*Label
|
addIndex, removeIndex := 0, 0
|
||||||
|
for addIndex < len(labels) && removeIndex < len(issue.Labels) {
|
||||||
|
addLabel := labels[addIndex]
|
||||||
|
removeLabel := issue.Labels[removeIndex]
|
||||||
|
if addLabel.ID == removeLabel.ID {
|
||||||
|
// Silently drop invalid labels
|
||||||
|
if removeLabel.RepoID != issue.RepoID && removeLabel.OrgID != issue.Repo.OwnerID {
|
||||||
|
toRemove = append(toRemove, removeLabel)
|
||||||
|
}
|
||||||
|
|
||||||
addIndex, removeIndex := 0, 0
|
addIndex++
|
||||||
for addIndex < len(labels) && removeIndex < len(issue.Labels) {
|
removeIndex++
|
||||||
addLabel := labels[addIndex]
|
} else if addLabel.ID < removeLabel.ID {
|
||||||
removeLabel := issue.Labels[removeIndex]
|
// Only add if the label is valid
|
||||||
if addLabel.ID == removeLabel.ID {
|
if addLabel.RepoID == issue.RepoID || addLabel.OrgID == issue.Repo.OwnerID {
|
||||||
// Silently drop invalid labels
|
toAdd = append(toAdd, addLabel)
|
||||||
if removeLabel.RepoID != issue.RepoID && removeLabel.OrgID != issue.Repo.OwnerID {
|
}
|
||||||
|
addIndex++
|
||||||
|
} else {
|
||||||
toRemove = append(toRemove, removeLabel)
|
toRemove = append(toRemove, removeLabel)
|
||||||
|
removeIndex++
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
toAdd = append(toAdd, labels[addIndex:]...)
|
||||||
|
toRemove = append(toRemove, issue.Labels[removeIndex:]...)
|
||||||
|
|
||||||
addIndex++
|
if len(toAdd) > 0 {
|
||||||
removeIndex++
|
if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil {
|
||||||
} else if addLabel.ID < removeLabel.ID {
|
return fmt.Errorf("addLabels: %w", err)
|
||||||
// Only add if the label is valid
|
|
||||||
if addLabel.RepoID == issue.RepoID || addLabel.OrgID == issue.Repo.OwnerID {
|
|
||||||
toAdd = append(toAdd, addLabel)
|
|
||||||
}
|
}
|
||||||
addIndex++
|
|
||||||
} else {
|
|
||||||
toRemove = append(toRemove, removeLabel)
|
|
||||||
removeIndex++
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
toAdd = append(toAdd, labels[addIndex:]...)
|
|
||||||
toRemove = append(toRemove, issue.Labels[removeIndex:]...)
|
|
||||||
|
|
||||||
if len(toAdd) > 0 {
|
for _, l := range toRemove {
|
||||||
if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil {
|
if err = deleteIssueLabel(ctx, issue, l, doer); err != nil {
|
||||||
return fmt.Errorf("addLabels: %w", err)
|
return fmt.Errorf("removeLabel: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for _, l := range toRemove {
|
issue.Labels = nil
|
||||||
if err = deleteIssueLabel(ctx, issue, l, doer); err != nil {
|
return issue.LoadLabels(ctx)
|
||||||
return fmt.Errorf("removeLabel: %w", err)
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
issue.Labels = nil
|
|
||||||
if err = issue.LoadLabels(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@ -47,26 +47,19 @@ func updateIssueLock(ctx context.Context, opts *IssueLockOptions, lock bool) err
|
|||||||
commentType = CommentTypeUnlock
|
commentType = CommentTypeUnlock
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := UpdateIssueCols(ctx, opts.Issue, "is_locked"); err != nil {
|
opt := &CreateCommentOptions{
|
||||||
|
Doer: opts.Doer,
|
||||||
|
Issue: opts.Issue,
|
||||||
|
Repo: opts.Issue.Repo,
|
||||||
|
Type: commentType,
|
||||||
|
Content: opts.Reason,
|
||||||
|
}
|
||||||
|
_, err := CreateComment(ctx, opt)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
|
|
||||||
opt := &CreateCommentOptions{
|
|
||||||
Doer: opts.Doer,
|
|
||||||
Issue: opts.Issue,
|
|
||||||
Repo: opts.Issue.Repo,
|
|
||||||
Type: commentType,
|
|
||||||
Content: opts.Reason,
|
|
||||||
}
|
|
||||||
if _, err := CreateComment(ctx, opt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@ -167,20 +167,9 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
return SetIssueAsClosed(ctx, issue, doer, false)
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
comment, err := SetIssueAsClosed(ctx, issue, doer, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return comment, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReopenIssue changes issue status to open.
|
// ReopenIssue changes issue status to open.
|
||||||
@ -192,88 +181,64 @@ func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Com
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
return setIssueAsReopen(ctx, issue, doer)
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
comment, err := setIssueAsReopen(ctx, issue, doer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return comment, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeIssueTitle changes the title of this issue, as the given user.
|
// ChangeIssueTitle changes the title of this issue, as the given user.
|
||||||
func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User, oldTitle string) (err error) {
|
func ChangeIssueTitle(ctx context.Context, issue *Issue, doer *user_model.User, oldTitle string) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
||||||
return err
|
if err = UpdateIssueCols(ctx, issue, "name"); err != nil {
|
||||||
}
|
return fmt.Errorf("updateIssueCols: %w", err)
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
if err = issue.LoadRepo(ctx); err != nil {
|
||||||
if err = UpdateIssueCols(ctx, issue, "name"); err != nil {
|
return fmt.Errorf("loadRepo: %w", err)
|
||||||
return fmt.Errorf("updateIssueCols: %w", err)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err = issue.LoadRepo(ctx); err != nil {
|
opts := &CreateCommentOptions{
|
||||||
return fmt.Errorf("loadRepo: %w", err)
|
Type: CommentTypeChangeTitle,
|
||||||
}
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
opts := &CreateCommentOptions{
|
Issue: issue,
|
||||||
Type: CommentTypeChangeTitle,
|
OldTitle: oldTitle,
|
||||||
Doer: doer,
|
NewTitle: issue.Title,
|
||||||
Repo: issue.Repo,
|
}
|
||||||
Issue: issue,
|
if _, err = CreateComment(ctx, opts); err != nil {
|
||||||
OldTitle: oldTitle,
|
return fmt.Errorf("createComment: %w", err)
|
||||||
NewTitle: issue.Title,
|
}
|
||||||
}
|
return issue.AddCrossReferences(ctx, doer, true)
|
||||||
if _, err = CreateComment(ctx, opts); err != nil {
|
})
|
||||||
return fmt.Errorf("createComment: %w", err)
|
|
||||||
}
|
|
||||||
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeIssueRef changes the branch of this issue, as the given user.
|
// ChangeIssueRef changes the branch of this issue, as the given user.
|
||||||
func ChangeIssueRef(ctx context.Context, issue *Issue, doer *user_model.User, oldRef string) (err error) {
|
func ChangeIssueRef(ctx context.Context, issue *Issue, doer *user_model.User, oldRef string) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = UpdateIssueCols(ctx, issue, "ref"); err != nil {
|
||||||
return err
|
return fmt.Errorf("updateIssueCols: %w", err)
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = UpdateIssueCols(ctx, issue, "ref"); err != nil {
|
if err = issue.LoadRepo(ctx); err != nil {
|
||||||
return fmt.Errorf("updateIssueCols: %w", err)
|
return fmt.Errorf("loadRepo: %w", err)
|
||||||
}
|
}
|
||||||
|
oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix)
|
||||||
|
newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix)
|
||||||
|
|
||||||
if err = issue.LoadRepo(ctx); err != nil {
|
opts := &CreateCommentOptions{
|
||||||
return fmt.Errorf("loadRepo: %w", err)
|
Type: CommentTypeChangeIssueRef,
|
||||||
}
|
Doer: doer,
|
||||||
oldRefFriendly := strings.TrimPrefix(oldRef, git.BranchPrefix)
|
Repo: issue.Repo,
|
||||||
newRefFriendly := strings.TrimPrefix(issue.Ref, git.BranchPrefix)
|
Issue: issue,
|
||||||
|
OldRef: oldRefFriendly,
|
||||||
opts := &CreateCommentOptions{
|
NewRef: newRefFriendly,
|
||||||
Type: CommentTypeChangeIssueRef,
|
}
|
||||||
Doer: doer,
|
if _, err = CreateComment(ctx, opts); err != nil {
|
||||||
Repo: issue.Repo,
|
return fmt.Errorf("createComment: %w", err)
|
||||||
Issue: issue,
|
}
|
||||||
OldRef: oldRefFriendly,
|
return nil
|
||||||
NewRef: newRefFriendly,
|
})
|
||||||
}
|
|
||||||
if _, err = CreateComment(ctx, opts); err != nil {
|
|
||||||
return fmt.Errorf("createComment: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDeletePRBranchComment adds delete branch comment for pull request issue
|
// AddDeletePRBranchComment adds delete branch comment for pull request issue
|
||||||
@ -295,64 +260,56 @@ func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo *
|
|||||||
|
|
||||||
// UpdateIssueAttachments update attachments by UUIDs for the issue
|
// UpdateIssueAttachments update attachments by UUIDs for the issue
|
||||||
func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string) (err error) {
|
func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
|
||||||
defer committer.Close()
|
|
||||||
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, uuids)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %w", uuids, err)
|
|
||||||
}
|
|
||||||
for i := range attachments {
|
|
||||||
attachments[i].IssueID = issueID
|
|
||||||
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
|
|
||||||
return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
|
|
||||||
}
|
}
|
||||||
}
|
for i := range attachments {
|
||||||
return committer.Commit()
|
attachments[i].IssueID = issueID
|
||||||
|
if err := repo_model.UpdateAttachment(ctx, attachments[i]); err != nil {
|
||||||
|
return fmt.Errorf("update attachment [id: %d]: %w", attachments[i].ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeIssueContent changes issue content, as the given user.
|
// ChangeIssueContent changes issue content, as the given user.
|
||||||
func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) {
|
func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("HasIssueContentHistory: %w", err)
|
||||||
defer committer.Close()
|
}
|
||||||
|
if !hasContentHistory {
|
||||||
|
if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0,
|
||||||
|
issue.CreatedUnix, issue.Content, true); err != nil {
|
||||||
|
return fmt.Errorf("SaveIssueContentHistory: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0)
|
issue.Content = content
|
||||||
if err != nil {
|
issue.ContentVersion = contentVersion + 1
|
||||||
return fmt.Errorf("HasIssueContentHistory: %w", err)
|
|
||||||
}
|
affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue)
|
||||||
if !hasContentHistory {
|
if err != nil {
|
||||||
if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0,
|
return err
|
||||||
issue.CreatedUnix, issue.Content, true); err != nil {
|
}
|
||||||
|
if affected == 0 {
|
||||||
|
return ErrIssueAlreadyChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
|
||||||
|
timeutil.TimeStampNow(), issue.Content, false); err != nil {
|
||||||
return fmt.Errorf("SaveIssueContentHistory: %w", err)
|
return fmt.Errorf("SaveIssueContentHistory: %w", err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
issue.Content = content
|
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
|
||||||
issue.ContentVersion = contentVersion + 1
|
return fmt.Errorf("addCrossReferences: %w", err)
|
||||||
|
}
|
||||||
affected, err := db.GetEngine(ctx).ID(issue.ID).Cols("content", "content_version").Where("content_version = ?", contentVersion).Update(issue)
|
return nil
|
||||||
if err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
if affected == 0 {
|
|
||||||
return ErrIssueAlreadyChanged
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0,
|
|
||||||
timeutil.TimeStampNow(), issue.Content, false); err != nil {
|
|
||||||
return fmt.Errorf("SaveIssueContentHistory: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = issue.AddCrossReferences(ctx, doer, true); err != nil {
|
|
||||||
return fmt.Errorf("addCrossReferences: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIssueOptions represents the options of a new issue.
|
// NewIssueOptions represents the options of a new issue.
|
||||||
@ -512,23 +469,19 @@ func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeuti
|
|||||||
if issue.DeadlineUnix == deadlineUnix {
|
if issue.DeadlineUnix == deadlineUnix {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
// Update the deadline
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil {
|
// Update the deadline
|
||||||
return err
|
if err = UpdateIssueCols(ctx, &Issue{ID: issue.ID, DeadlineUnix: deadlineUnix}, "deadline_unix"); err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Make the comment
|
// Make the comment
|
||||||
if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil {
|
if _, err = createDeadlineComment(ctx, doer, issue, deadlineUnix); err != nil {
|
||||||
return fmt.Errorf("createRemovedDueDateComment: %w", err)
|
return fmt.Errorf("createRemovedDueDateComment: %w", err)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return committer.Commit()
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database.
|
// FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database.
|
||||||
|
@ -209,24 +209,20 @@ func NewLabel(ctx context.Context, l *Label) error {
|
|||||||
|
|
||||||
// NewLabels creates new labels
|
// NewLabels creates new labels
|
||||||
func NewLabels(ctx context.Context, labels ...*Label) error {
|
func NewLabels(ctx context.Context, labels ...*Label) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, l := range labels {
|
||||||
return err
|
color, err := label.NormalizeColor(l.Color)
|
||||||
}
|
if err != nil {
|
||||||
defer committer.Close()
|
return err
|
||||||
|
}
|
||||||
|
l.Color = color
|
||||||
|
|
||||||
for _, l := range labels {
|
if err := db.Insert(ctx, l); err != nil {
|
||||||
color, err := label.NormalizeColor(l.Color)
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
l.Color = color
|
return nil
|
||||||
|
})
|
||||||
if err := db.Insert(ctx, l); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateLabel updates label information.
|
// UpdateLabel updates label information.
|
||||||
@ -250,35 +246,26 @@ func DeleteLabel(ctx context.Context, id, labelID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if l.BelongsToOrg() && l.OrgID != id {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if l.BelongsToRepo() && l.RepoID != id {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = db.DeleteByID[Label](ctx, labelID); err != nil {
|
||||||
|
return err
|
||||||
|
} else if _, err = db.GetEngine(ctx).
|
||||||
|
Where("label_id = ?", labelID).
|
||||||
|
Delete(new(IssueLabel)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete comments about now deleted label_id
|
||||||
|
_, err = db.GetEngine(ctx).Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{})
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
if l.BelongsToOrg() && l.OrgID != id {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if l.BelongsToRepo() && l.RepoID != id {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.DeleteByID[Label](ctx, labelID); err != nil {
|
|
||||||
return err
|
|
||||||
} else if _, err = sess.
|
|
||||||
Where("label_id = ?", labelID).
|
|
||||||
Delete(new(IssueLabel)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete comments about now deleted label_id
|
|
||||||
if _, err = sess.Where("label_id = ?", labelID).Cols("label_id").Delete(&Comment{}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLabelByID returns a label by given ID.
|
// GetLabelByID returns a label by given ID.
|
||||||
|
@ -105,22 +105,16 @@ func (m *Milestone) State() api.StateType {
|
|||||||
|
|
||||||
// NewMilestone creates new milestone of repository.
|
// NewMilestone creates new milestone of repository.
|
||||||
func NewMilestone(ctx context.Context, m *Milestone) (err error) {
|
func NewMilestone(ctx context.Context, m *Milestone) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
m.Name = strings.TrimSpace(m.Name)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
m.Name = strings.TrimSpace(m.Name)
|
if err = db.Insert(ctx, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err = db.Insert(ctx, m); err != nil {
|
_, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasMilestoneByRepoID returns if the milestone exists in the repository.
|
// HasMilestoneByRepoID returns if the milestone exists in the repository.
|
||||||
@ -155,28 +149,23 @@ func GetMilestoneByRepoIDANDName(ctx context.Context, repoID int64, name string)
|
|||||||
|
|
||||||
// UpdateMilestone updates information of given milestone.
|
// UpdateMilestone updates information of given milestone.
|
||||||
func UpdateMilestone(ctx context.Context, m *Milestone, oldIsClosed bool) error {
|
func UpdateMilestone(ctx context.Context, m *Milestone, oldIsClosed bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if m.IsClosed && !oldIsClosed {
|
||||||
return err
|
m.ClosedDateUnix = timeutil.TimeStampNow()
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if m.IsClosed && !oldIsClosed {
|
if err := updateMilestone(ctx, m); err != nil {
|
||||||
m.ClosedDateUnix = timeutil.TimeStampNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := updateMilestone(ctx, m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if IsClosed changed, update milestone numbers of repository
|
|
||||||
if oldIsClosed != m.IsClosed {
|
|
||||||
if err := updateRepoMilestoneNum(ctx, m.RepoID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
// if IsClosed changed, update milestone numbers of repository
|
||||||
|
if oldIsClosed != m.IsClosed {
|
||||||
|
if err := updateRepoMilestoneNum(ctx, m.RepoID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMilestone(ctx context.Context, m *Milestone) error {
|
func updateMilestone(ctx context.Context, m *Milestone) error {
|
||||||
@ -213,44 +202,28 @@ func UpdateMilestoneCounters(ctx context.Context, id int64) error {
|
|||||||
|
|
||||||
// ChangeMilestoneStatusByRepoIDAndID changes a milestone open/closed status if the milestone ID is in the repo.
|
// ChangeMilestoneStatusByRepoIDAndID changes a milestone open/closed status if the milestone ID is in the repo.
|
||||||
func ChangeMilestoneStatusByRepoIDAndID(ctx context.Context, repoID, milestoneID int64, isClosed bool) error {
|
func ChangeMilestoneStatusByRepoIDAndID(ctx context.Context, repoID, milestoneID int64, isClosed bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
m := &Milestone{
|
||||||
return err
|
ID: milestoneID,
|
||||||
}
|
RepoID: repoID,
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
m := &Milestone{
|
has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m)
|
||||||
ID: milestoneID,
|
if err != nil {
|
||||||
RepoID: repoID,
|
return err
|
||||||
}
|
} else if !has {
|
||||||
|
return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID}
|
||||||
|
}
|
||||||
|
|
||||||
has, err := db.GetEngine(ctx).ID(milestoneID).Where("repo_id = ?", repoID).Get(m)
|
return changeMilestoneStatus(ctx, m, isClosed)
|
||||||
if err != nil {
|
})
|
||||||
return err
|
|
||||||
} else if !has {
|
|
||||||
return ErrMilestoneNotExist{ID: milestoneID, RepoID: repoID}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := changeMilestoneStatus(ctx, m, isClosed); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeMilestoneStatus changes the milestone open/closed status.
|
// ChangeMilestoneStatus changes the milestone open/closed status.
|
||||||
func ChangeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) (err error) {
|
func ChangeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return changeMilestoneStatus(ctx, m, isClosed)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := changeMilestoneStatus(ctx, m, isClosed); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) error {
|
func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) error {
|
||||||
@ -284,40 +257,34 @@ func DeleteMilestoneByRepoID(ctx context.Context, repoID, id int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if _, err = db.DeleteByID[Milestone](ctx, m.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err = db.DeleteByID[Milestone](ctx, m.ID); err != nil {
|
numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
|
||||||
return err
|
RepoID: repo.ID,
|
||||||
}
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
IsClosed: optional.Some(true),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
repo.NumMilestones = int(numMilestones)
|
||||||
|
repo.NumClosedMilestones = int(numClosedMilestones)
|
||||||
|
|
||||||
numMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
|
if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
|
||||||
RepoID: repo.ID,
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID)
|
||||||
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
numClosedMilestones, err := db.Count[Milestone](ctx, FindMilestoneOptions{
|
|
||||||
RepoID: repo.ID,
|
|
||||||
IsClosed: optional.Some(true),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
repo.NumMilestones = int(numMilestones)
|
|
||||||
repo.NumClosedMilestones = int(numClosedMilestones)
|
|
||||||
|
|
||||||
if _, err = db.GetEngine(ctx).ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateRepoMilestoneNum(ctx context.Context, repoID int64) error {
|
func updateRepoMilestoneNum(ctx context.Context, repoID int64) error {
|
||||||
@ -360,22 +327,15 @@ func InsertMilestones(ctx context.Context, ms ...*Milestone) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// to return the id, so we should not use batch insert
|
||||||
return err
|
for _, m := range ms {
|
||||||
}
|
if _, err = db.GetEngine(ctx).NoAutoTime().Insert(m); err != nil {
|
||||||
defer committer.Close()
|
return err
|
||||||
sess := db.GetEngine(ctx)
|
}
|
||||||
|
|
||||||
// to return the id, so we should not use batch insert
|
|
||||||
for _, m := range ms {
|
|
||||||
if _, err = sess.NoAutoTime().Insert(m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + ? WHERE id = ?", len(ms), ms[0].RepoID); err != nil {
|
_, err = db.Exec(ctx, "UPDATE `repository` SET num_milestones = num_milestones + ? WHERE id = ?", len(ms), ms[0].RepoID)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@ -364,17 +364,10 @@ func (pr *PullRequest) GetApprovers(ctx context.Context) string {
|
|||||||
|
|
||||||
func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer) error {
|
func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer) error {
|
||||||
maxReviewers := setting.Repository.PullRequest.DefaultMergeMessageMaxApprovers
|
maxReviewers := setting.Repository.PullRequest.DefaultMergeMessageMaxApprovers
|
||||||
|
|
||||||
if maxReviewers == 0 {
|
if maxReviewers == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
// Note: This doesn't page as we only expect a very limited number of reviews
|
// Note: This doesn't page as we only expect a very limited number of reviews
|
||||||
reviews, err := FindLatestReviews(ctx, FindReviewOptions{
|
reviews, err := FindLatestReviews(ctx, FindReviewOptions{
|
||||||
Types: []ReviewType{ReviewTypeApprove},
|
Types: []ReviewType{ReviewTypeApprove},
|
||||||
@ -410,7 +403,7 @@ func (pr *PullRequest) getReviewedByLines(ctx context.Context, writer io.Writer)
|
|||||||
}
|
}
|
||||||
reviewersWritten++
|
reviewersWritten++
|
||||||
}
|
}
|
||||||
return committer.Commit()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGitHeadRefName returns git ref for hidden pull request branch
|
// GetGitHeadRefName returns git ref for hidden pull request branch
|
||||||
@ -464,45 +457,36 @@ func (pr *PullRequest) IsFromFork() bool {
|
|||||||
|
|
||||||
// NewPullRequest creates new pull request with labels for repository.
|
// NewPullRequest creates new pull request with labels for repository.
|
||||||
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
|
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("generate pull request index failed: %w", err)
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("generate pull request index failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
issue.Index = idx
|
|
||||||
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
|
||||||
|
|
||||||
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
|
|
||||||
Repo: repo,
|
|
||||||
Issue: issue,
|
|
||||||
LabelIDs: labelIDs,
|
|
||||||
Attachments: uuids,
|
|
||||||
IsPull: true,
|
|
||||||
}); err != nil {
|
|
||||||
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf("newIssue: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pr.Index = issue.Index
|
issue.Index = idx
|
||||||
pr.BaseRepo = repo
|
issue.Title = util.EllipsisDisplayString(issue.Title, 255)
|
||||||
pr.IssueID = issue.ID
|
|
||||||
if err = db.Insert(ctx, pr); err != nil {
|
|
||||||
return fmt.Errorf("insert pull repo: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = committer.Commit(); err != nil {
|
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
|
||||||
return fmt.Errorf("Commit: %w", err)
|
Repo: repo,
|
||||||
}
|
Issue: issue,
|
||||||
|
LabelIDs: labelIDs,
|
||||||
|
Attachments: uuids,
|
||||||
|
IsPull: true,
|
||||||
|
}); err != nil {
|
||||||
|
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fmt.Errorf("newIssue: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
pr.Index = issue.Index
|
||||||
|
pr.BaseRepo = repo
|
||||||
|
pr.IssueID = issue.ID
|
||||||
|
if err = db.Insert(ctx, pr); err != nil {
|
||||||
|
return fmt.Errorf("insert pull repo: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUserMustCollaborator represents an error that the user must be a collaborator to a given repo.
|
// ErrUserMustCollaborator represents an error that the user must be a collaborator to a given repo.
|
||||||
@ -977,22 +961,18 @@ func TokenizeCodeOwnersLine(line string) []string {
|
|||||||
|
|
||||||
// InsertPullRequests inserted pull requests
|
// InsertPullRequests inserted pull requests
|
||||||
func InsertPullRequests(ctx context.Context, prs ...*PullRequest) error {
|
func InsertPullRequests(ctx context.Context, prs ...*PullRequest) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, pr := range prs {
|
||||||
return err
|
if err := insertIssue(ctx, pr.Issue); err != nil {
|
||||||
}
|
return err
|
||||||
defer committer.Close()
|
}
|
||||||
sess := db.GetEngine(ctx)
|
pr.IssueID = pr.Issue.ID
|
||||||
for _, pr := range prs {
|
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(pr); err != nil {
|
||||||
if err := insertIssue(ctx, pr.Issue); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
}
|
||||||
pr.IssueID = pr.Issue.ID
|
return nil
|
||||||
if _, err := sess.NoAutoTime().Insert(pr); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequestByMergedCommit returns a merged pull request by the given commit
|
// GetPullRequestByMergedCommit returns a merged pull request by the given commit
|
||||||
|
@ -224,21 +224,9 @@ func CreateReaction(ctx context.Context, opts *ReactionOptions) (*Reaction, erro
|
|||||||
return nil, ErrForbiddenIssueReaction{opts.Type}
|
return nil, ErrForbiddenIssueReaction{opts.Type}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Reaction, error) {
|
||||||
if err != nil {
|
return createReaction(ctx, opts)
|
||||||
return nil, err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
reaction, err := createReaction(ctx, opts)
|
|
||||||
if err != nil {
|
|
||||||
return reaction, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return reaction, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteReaction deletes reaction for issue or comment.
|
// DeleteReaction deletes reaction for issue or comment.
|
||||||
|
@ -334,54 +334,51 @@ func IsOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organizatio
|
|||||||
|
|
||||||
// CreateReview creates a new review based on opts
|
// CreateReview creates a new review based on opts
|
||||||
func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error) {
|
func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Review, error) {
|
||||||
if err != nil {
|
sess := db.GetEngine(ctx)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
review := &Review{
|
review := &Review{
|
||||||
Issue: opts.Issue,
|
Issue: opts.Issue,
|
||||||
IssueID: opts.Issue.ID,
|
IssueID: opts.Issue.ID,
|
||||||
Reviewer: opts.Reviewer,
|
Reviewer: opts.Reviewer,
|
||||||
ReviewerTeam: opts.ReviewerTeam,
|
ReviewerTeam: opts.ReviewerTeam,
|
||||||
Content: opts.Content,
|
Content: opts.Content,
|
||||||
Official: opts.Official,
|
Official: opts.Official,
|
||||||
CommitID: opts.CommitID,
|
CommitID: opts.CommitID,
|
||||||
Stale: opts.Stale,
|
Stale: opts.Stale,
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Reviewer != nil {
|
|
||||||
review.Type = opts.Type
|
|
||||||
review.ReviewerID = opts.Reviewer.ID
|
|
||||||
|
|
||||||
reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID}
|
|
||||||
// make sure user review requests are cleared
|
|
||||||
if opts.Type != ReviewTypePending {
|
|
||||||
if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// make sure if the created review gets dismissed no old review surface
|
|
||||||
// other types can be ignored, as they don't affect branch protection
|
|
||||||
if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject {
|
|
||||||
if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))).
|
|
||||||
Cols("dismissed").Update(&Review{Dismissed: true}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if opts.ReviewerTeam != nil {
|
|
||||||
review.Type = ReviewTypeRequest
|
|
||||||
review.ReviewerTeamID = opts.ReviewerTeam.ID
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("provide either reviewer or reviewer team")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := sess.Insert(review); err != nil {
|
if opts.Reviewer != nil {
|
||||||
return nil, err
|
review.Type = opts.Type
|
||||||
}
|
review.ReviewerID = opts.Reviewer.ID
|
||||||
return review, committer.Commit()
|
|
||||||
|
reviewCond := builder.Eq{"reviewer_id": opts.Reviewer.ID, "issue_id": opts.Issue.ID}
|
||||||
|
// make sure user review requests are cleared
|
||||||
|
if opts.Type != ReviewTypePending {
|
||||||
|
if _, err := sess.Where(reviewCond.And(builder.Eq{"type": ReviewTypeRequest})).Delete(new(Review)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make sure if the created review gets dismissed no old review surface
|
||||||
|
// other types can be ignored, as they don't affect branch protection
|
||||||
|
if opts.Type == ReviewTypeApprove || opts.Type == ReviewTypeReject {
|
||||||
|
if _, err := sess.Where(reviewCond.And(builder.In("type", ReviewTypeApprove, ReviewTypeReject))).
|
||||||
|
Cols("dismissed").Update(&Review{Dismissed: true}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if opts.ReviewerTeam != nil {
|
||||||
|
review.Type = ReviewTypeRequest
|
||||||
|
review.ReviewerTeamID = opts.ReviewerTeam.ID
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("provide either reviewer or reviewer team")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := sess.Insert(review); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return review, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentReview returns the current pending review of reviewer for given issue
|
// GetCurrentReview returns the current pending review of reviewer for given issue
|
||||||
@ -605,168 +602,152 @@ func DismissReview(ctx context.Context, review *Review, isDismiss bool) (err err
|
|||||||
|
|
||||||
// InsertReviews inserts review and review comments
|
// InsertReviews inserts review and review comments
|
||||||
func InsertReviews(ctx context.Context, reviews []*Review) error {
|
func InsertReviews(ctx context.Context, reviews []*Review) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
sess := db.GetEngine(ctx)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
for _, review := range reviews {
|
for _, review := range reviews {
|
||||||
if _, err := sess.NoAutoTime().Insert(review); err != nil {
|
if _, err := sess.NoAutoTime().Insert(review); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := sess.NoAutoTime().Insert(&Comment{
|
if _, err := sess.NoAutoTime().Insert(&Comment{
|
||||||
Type: CommentTypeReview,
|
Type: CommentTypeReview,
|
||||||
Content: review.Content,
|
Content: review.Content,
|
||||||
PosterID: review.ReviewerID,
|
PosterID: review.ReviewerID,
|
||||||
OriginalAuthor: review.OriginalAuthor,
|
OriginalAuthor: review.OriginalAuthor,
|
||||||
OriginalAuthorID: review.OriginalAuthorID,
|
OriginalAuthorID: review.OriginalAuthorID,
|
||||||
IssueID: review.IssueID,
|
IssueID: review.IssueID,
|
||||||
ReviewID: review.ID,
|
ReviewID: review.ID,
|
||||||
CreatedUnix: review.CreatedUnix,
|
CreatedUnix: review.CreatedUnix,
|
||||||
UpdatedUnix: review.UpdatedUnix,
|
UpdatedUnix: review.UpdatedUnix,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range review.Comments {
|
for _, c := range review.Comments {
|
||||||
c.ReviewID = review.ID
|
c.ReviewID = review.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(review.Comments) > 0 {
|
if len(review.Comments) > 0 {
|
||||||
if _, err := sess.NoAutoTime().Insert(review.Comments); err != nil {
|
if _, err := sess.NoAutoTime().Insert(review.Comments); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
if err := UpdateIssueNumComments(ctx, review.IssueID); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddReviewRequest add a review request from one reviewer
|
// AddReviewRequest add a review request from one reviewer
|
||||||
func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
|
func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
sess := db.GetEngine(ctx)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
|
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if review != nil {
|
|
||||||
// skip it when reviewer has been request to review
|
|
||||||
if review.Type == ReviewTypeRequest {
|
|
||||||
return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
|
|
||||||
}
|
|
||||||
|
|
||||||
if issue.IsClosed {
|
|
||||||
return nil, ErrReviewRequestOnClosedPR{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if issue.IsPull {
|
|
||||||
if err := issue.LoadPullRequest(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if issue.PullRequest.HasMerged {
|
|
||||||
return nil, ErrReviewRequestOnClosedPR{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the reviewer is an official reviewer,
|
|
||||||
// remove the official flag in the all previous reviews
|
|
||||||
official, err := IsOfficialReviewer(ctx, issue, reviewer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if official {
|
|
||||||
if _, err := sess.Exec("UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_id=?", false, issue.ID, reviewer.ID); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
review, err = CreateReview(ctx, CreateReviewOptions{
|
if review != nil {
|
||||||
Type: ReviewTypeRequest,
|
// skip it when reviewer has been request to review
|
||||||
Issue: issue,
|
if review.Type == ReviewTypeRequest {
|
||||||
Reviewer: reviewer,
|
return nil, nil // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
|
||||||
Official: official,
|
}
|
||||||
Stale: false,
|
|
||||||
|
if issue.IsClosed {
|
||||||
|
return nil, ErrReviewRequestOnClosedPR{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if issue.IsPull {
|
||||||
|
if err := issue.LoadPullRequest(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if issue.PullRequest.HasMerged {
|
||||||
|
return nil, ErrReviewRequestOnClosedPR{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the reviewer is an official reviewer,
|
||||||
|
// remove the official flag in the all previous reviews
|
||||||
|
official, err := IsOfficialReviewer(ctx, issue, reviewer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if official {
|
||||||
|
if _, err := sess.Exec("UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_id=?", false, issue.ID, reviewer.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
review, err = CreateReview(ctx, CreateReviewOptions{
|
||||||
|
Type: ReviewTypeRequest,
|
||||||
|
Issue: issue,
|
||||||
|
Reviewer: reviewer,
|
||||||
|
Official: official,
|
||||||
|
Stale: false,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Type: CommentTypeReviewRequest,
|
||||||
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Issue: issue,
|
||||||
|
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
||||||
|
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
|
||||||
|
ReviewID: review.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// func caller use the created comment to retrieve created review too.
|
||||||
|
comment.Review = review
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
|
||||||
Type: CommentTypeReviewRequest,
|
|
||||||
Doer: doer,
|
|
||||||
Repo: issue.Repo,
|
|
||||||
Issue: issue,
|
|
||||||
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
|
||||||
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
|
|
||||||
ReviewID: review.ID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// func caller use the created comment to retrieve created review too.
|
|
||||||
comment.Review = review
|
|
||||||
|
|
||||||
return comment, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveReviewRequest remove a review request from one reviewer
|
// RemoveReviewRequest remove a review request from one reviewer
|
||||||
func RemoveReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
|
func RemoveReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
|
||||||
return nil, err
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, reviewer.ID)
|
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if review == nil || review.Type != ReviewTypeRequest {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.DeleteByBean(ctx, review); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
official, err := IsOfficialReviewer(ctx, issue, reviewer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if official {
|
|
||||||
if err := restoreLatestOfficialReview(ctx, issue.ID, reviewer.ID); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
if review == nil || review.Type != ReviewTypeRequest {
|
||||||
Type: CommentTypeReviewRequest,
|
return nil, nil
|
||||||
Doer: doer,
|
}
|
||||||
Repo: issue.Repo,
|
|
||||||
Issue: issue,
|
if _, err = db.DeleteByBean(ctx, review); err != nil {
|
||||||
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
|
return nil, err
|
||||||
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
|
}
|
||||||
|
|
||||||
|
official, err := IsOfficialReviewer(ctx, issue, reviewer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if official {
|
||||||
|
if err := restoreLatestOfficialReview(ctx, issue.ID, reviewer.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Type: CommentTypeReviewRequest,
|
||||||
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Issue: issue,
|
||||||
|
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
|
||||||
|
AssigneeID: reviewer.ID, // Use AssigneeID as reviewer ID
|
||||||
|
})
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return comment, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recalculate the latest official review for reviewer
|
// Recalculate the latest official review for reviewer
|
||||||
@ -787,120 +768,112 @@ func restoreLatestOfficialReview(ctx context.Context, issueID, reviewerID int64)
|
|||||||
|
|
||||||
// AddTeamReviewRequest add a review request from one team
|
// AddTeamReviewRequest add a review request from one team
|
||||||
func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
|
func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
if err != nil {
|
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
|
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// This team already has been requested to review - therefore skip this.
|
|
||||||
if review != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
|
|
||||||
} else if !official {
|
|
||||||
if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil {
|
|
||||||
return nil, fmt.Errorf("isOfficialReviewer(): %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if review, err = CreateReview(ctx, CreateReviewOptions{
|
|
||||||
Type: ReviewTypeRequest,
|
|
||||||
Issue: issue,
|
|
||||||
ReviewerTeam: reviewer,
|
|
||||||
Official: official,
|
|
||||||
Stale: false,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if official {
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
|
||||||
Type: CommentTypeReviewRequest,
|
|
||||||
Doer: doer,
|
|
||||||
Repo: issue.Repo,
|
|
||||||
Issue: issue,
|
|
||||||
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
|
||||||
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
|
|
||||||
ReviewID: review.ID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("CreateComment(): %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return comment, committer.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveTeamReviewRequest remove a review request from one team
|
|
||||||
func RemoveTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
|
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if review == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.DeleteByBean(ctx, review); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if official {
|
|
||||||
// recalculate which is the latest official review from that team
|
|
||||||
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, -reviewer.ID)
|
|
||||||
if err != nil && !IsErrReviewNotExist(err) {
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This team already has been requested to review - therefore skip this.
|
||||||
if review != nil {
|
if review != nil {
|
||||||
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE id=?", true, review.ID); err != nil {
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
|
||||||
|
} else if !official {
|
||||||
|
if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil {
|
||||||
|
return nil, fmt.Errorf("isOfficialReviewer(): %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if review, err = CreateReview(ctx, CreateReviewOptions{
|
||||||
|
Type: ReviewTypeRequest,
|
||||||
|
Issue: issue,
|
||||||
|
ReviewerTeam: reviewer,
|
||||||
|
Official: official,
|
||||||
|
Stale: false,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if official {
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if doer == nil {
|
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
return nil, committer.Commit()
|
Type: CommentTypeReviewRequest,
|
||||||
}
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Issue: issue,
|
||||||
|
RemovedAssignee: false, // Use RemovedAssignee as !isRequest
|
||||||
|
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
|
||||||
|
ReviewID: review.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("CreateComment(): %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
return comment, nil
|
||||||
Type: CommentTypeReviewRequest,
|
|
||||||
Doer: doer,
|
|
||||||
Repo: issue.Repo,
|
|
||||||
Issue: issue,
|
|
||||||
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
|
|
||||||
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
}
|
||||||
return nil, fmt.Errorf("CreateComment(): %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return comment, committer.Commit()
|
// RemoveTeamReviewRequest remove a review request from one team
|
||||||
|
func RemoveTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
|
||||||
|
return db.WithTx2(ctx, func(ctx context.Context) (*Comment, error) {
|
||||||
|
review, err := GetTeamReviewerByIssueIDAndTeamID(ctx, issue.ID, reviewer.ID)
|
||||||
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if review == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = db.DeleteByBean(ctx, review); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if official {
|
||||||
|
// recalculate which is the latest official review from that team
|
||||||
|
review, err := GetReviewByIssueIDAndUserID(ctx, issue.ID, -reviewer.ID)
|
||||||
|
if err != nil && !IsErrReviewNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if review != nil {
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE id=?", true, review.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if doer == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Type: CommentTypeReviewRequest,
|
||||||
|
Doer: doer,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Issue: issue,
|
||||||
|
RemovedAssignee: true, // Use RemovedAssignee as !isRequest
|
||||||
|
AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("CreateComment(): %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return comment, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkConversation Add or remove Conversation mark for a code comment
|
// MarkConversation Add or remove Conversation mark for a code comment
|
||||||
@ -966,61 +939,56 @@ func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.Use
|
|||||||
|
|
||||||
// DeleteReview delete a review and it's code comments
|
// DeleteReview delete a review and it's code comments
|
||||||
func DeleteReview(ctx context.Context, r *Review) error {
|
func DeleteReview(ctx context.Context, r *Review) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if r.ID == 0 {
|
||||||
return err
|
return errors.New("review is not allowed to be 0")
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if r.ID == 0 {
|
if r.Type == ReviewTypeRequest {
|
||||||
return errors.New("review is not allowed to be 0")
|
return errors.New("review request can not be deleted using this method")
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Type == ReviewTypeRequest {
|
opts := FindCommentsOptions{
|
||||||
return errors.New("review request can not be deleted using this method")
|
Type: CommentTypeCode,
|
||||||
}
|
IssueID: r.IssueID,
|
||||||
|
ReviewID: r.ID,
|
||||||
|
}
|
||||||
|
|
||||||
opts := FindCommentsOptions{
|
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
||||||
Type: CommentTypeCode,
|
|
||||||
IssueID: r.IssueID,
|
|
||||||
ReviewID: r.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
opts = FindCommentsOptions{
|
|
||||||
Type: CommentTypeReview,
|
|
||||||
IssueID: r.IssueID,
|
|
||||||
ReviewID: r.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
opts = FindCommentsOptions{
|
|
||||||
Type: CommentTypeDismissReview,
|
|
||||||
IssueID: r.IssueID,
|
|
||||||
ReviewID: r.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Official {
|
|
||||||
if err := restoreLatestOfficialReview(ctx, r.IssueID, r.ReviewerID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
opts = FindCommentsOptions{
|
||||||
|
Type: CommentTypeReview,
|
||||||
|
IssueID: r.IssueID,
|
||||||
|
ReviewID: r.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = FindCommentsOptions{
|
||||||
|
Type: CommentTypeDismissReview,
|
||||||
|
IssueID: r.IssueID,
|
||||||
|
ReviewID: r.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.Delete[Comment](ctx, opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.DeleteByID[Review](ctx, r.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Official {
|
||||||
|
if err := restoreLatestOfficialReview(ctx, r.IssueID, r.ReviewerID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCodeCommentsCount return count of CodeComments a Review has
|
// GetCodeCommentsCount return count of CodeComments a Review has
|
||||||
|
@ -168,35 +168,31 @@ func GetTrackedSeconds(ctx context.Context, opts FindTrackedTimesOptions) (track
|
|||||||
|
|
||||||
// AddTime will add the given time (in seconds) to the issue
|
// AddTime will add the given time (in seconds) to the issue
|
||||||
func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
|
func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*TrackedTime, error) {
|
||||||
if err != nil {
|
t, err := addTime(ctx, user, issue, amount, created)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
t, err := addTime(ctx, user, issue, amount, created)
|
if err := issue.LoadRepo(ctx); err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := issue.LoadRepo(ctx); err != nil {
|
if _, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
return nil, err
|
Issue: issue,
|
||||||
}
|
Repo: issue.Repo,
|
||||||
|
Doer: user,
|
||||||
|
// Content before v1.21 did store the formatted string instead of seconds,
|
||||||
|
// so use "|" as delimiter to mark the new format
|
||||||
|
Content: fmt.Sprintf("|%d", amount),
|
||||||
|
Type: CommentTypeAddTimeManual,
|
||||||
|
TimeID: t.ID,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := CreateComment(ctx, &CreateCommentOptions{
|
return t, nil
|
||||||
Issue: issue,
|
})
|
||||||
Repo: issue.Repo,
|
|
||||||
Doer: user,
|
|
||||||
// Content before v1.21 did store the formatted string instead of seconds,
|
|
||||||
// so use "|" as delimiter to mark the new format
|
|
||||||
Content: fmt.Sprintf("|%d", amount),
|
|
||||||
Type: CommentTypeAddTimeManual,
|
|
||||||
TimeID: t.ID,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
|
func addTime(ctx context.Context, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
|
||||||
@ -241,72 +237,58 @@ func TotalTimesForEachUser(ctx context.Context, options *FindTrackedTimesOptions
|
|||||||
|
|
||||||
// DeleteIssueUserTimes deletes times for issue
|
// DeleteIssueUserTimes deletes times for issue
|
||||||
func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.User) error {
|
func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.User) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
opts := FindTrackedTimesOptions{
|
||||||
return err
|
IssueID: issue.ID,
|
||||||
}
|
UserID: user.ID,
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
opts := FindTrackedTimesOptions{
|
removedTime, err := deleteTimes(ctx, opts)
|
||||||
IssueID: issue.ID,
|
if err != nil {
|
||||||
UserID: user.ID,
|
return err
|
||||||
}
|
}
|
||||||
|
if removedTime == 0 {
|
||||||
|
return db.ErrNotExist{Resource: "tracked_time"}
|
||||||
|
}
|
||||||
|
|
||||||
removedTime, err := deleteTimes(ctx, opts)
|
if err := issue.LoadRepo(ctx); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
|
}
|
||||||
|
_, err = CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Issue: issue,
|
||||||
|
Repo: issue.Repo,
|
||||||
|
Doer: user,
|
||||||
|
// Content before v1.21 did store the formatted string instead of seconds,
|
||||||
|
// so use "|" as delimiter to mark the new format
|
||||||
|
Content: fmt.Sprintf("|%d", removedTime),
|
||||||
|
Type: CommentTypeDeleteTimeManual,
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
if removedTime == 0 {
|
|
||||||
return db.ErrNotExist{Resource: "tracked_time"}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := issue.LoadRepo(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := CreateComment(ctx, &CreateCommentOptions{
|
|
||||||
Issue: issue,
|
|
||||||
Repo: issue.Repo,
|
|
||||||
Doer: user,
|
|
||||||
// Content before v1.21 did store the formatted string instead of seconds,
|
|
||||||
// so use "|" as delimiter to mark the new format
|
|
||||||
Content: fmt.Sprintf("|%d", removedTime),
|
|
||||||
Type: CommentTypeDeleteTimeManual,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTime delete a specific Time
|
// DeleteTime delete a specific Time
|
||||||
func DeleteTime(ctx context.Context, t *TrackedTime) error {
|
func DeleteTime(ctx context.Context, t *TrackedTime) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err := t.LoadAttributes(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := t.LoadAttributes(ctx); err != nil {
|
if err := deleteTime(ctx, t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deleteTime(ctx, t); err != nil {
|
_, err := CreateComment(ctx, &CreateCommentOptions{
|
||||||
|
Issue: t.Issue,
|
||||||
|
Repo: t.Issue.Repo,
|
||||||
|
Doer: t.User,
|
||||||
|
// Content before v1.21 did store the formatted string instead of seconds,
|
||||||
|
// so use "|" as delimiter to mark the new format
|
||||||
|
Content: fmt.Sprintf("|%d", t.Time),
|
||||||
|
Type: CommentTypeDeleteTimeManual,
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
|
|
||||||
if _, err := CreateComment(ctx, &CreateCommentOptions{
|
|
||||||
Issue: t.Issue,
|
|
||||||
Repo: t.Issue.Repo,
|
|
||||||
Doer: t.User,
|
|
||||||
// Content before v1.21 did store the formatted string instead of seconds,
|
|
||||||
// so use "|" as delimiter to mark the new format
|
|
||||||
Content: fmt.Sprintf("|%d", t.Time),
|
|
||||||
Type: CommentTypeDeleteTimeManual,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime int64, err error) {
|
func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime int64, err error) {
|
||||||
|
@ -310,74 +310,69 @@ func CreateOrganization(ctx context.Context, org *Organization, owner *user_mode
|
|||||||
org.NumMembers = 1
|
org.NumMembers = 1
|
||||||
org.Type = user_model.UserTypeOrganization
|
org.Type = user_model.UserTypeOrganization
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = db.Insert(ctx, org); err != nil {
|
|
||||||
return fmt.Errorf("insert organization: %w", err)
|
|
||||||
}
|
|
||||||
if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil {
|
|
||||||
return fmt.Errorf("generate random avatar: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add initial creator to organization and owner team.
|
|
||||||
if err = db.Insert(ctx, &OrgUser{
|
|
||||||
UID: owner.ID,
|
|
||||||
OrgID: org.ID,
|
|
||||||
IsPublic: setting.Service.DefaultOrgMemberVisible,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("insert org-user relation: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create default owner team.
|
|
||||||
t := &Team{
|
|
||||||
OrgID: org.ID,
|
|
||||||
LowerName: strings.ToLower(OwnerTeamName),
|
|
||||||
Name: OwnerTeamName,
|
|
||||||
AccessMode: perm.AccessModeOwner,
|
|
||||||
NumMembers: 1,
|
|
||||||
IncludesAllRepositories: true,
|
|
||||||
CanCreateOrgRepo: true,
|
|
||||||
}
|
|
||||||
if err = db.Insert(ctx, t); err != nil {
|
|
||||||
return fmt.Errorf("insert owner team: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert units for team
|
|
||||||
units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
|
|
||||||
for _, tp := range unit.AllRepoUnitTypes {
|
|
||||||
up := perm.AccessModeOwner
|
|
||||||
if tp == unit.TypeExternalTracker || tp == unit.TypeExternalWiki {
|
|
||||||
up = perm.AccessModeRead
|
|
||||||
}
|
}
|
||||||
units = append(units, TeamUnit{
|
|
||||||
OrgID: org.ID,
|
|
||||||
TeamID: t.ID,
|
|
||||||
Type: tp,
|
|
||||||
AccessMode: up,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = db.Insert(ctx, &units); err != nil {
|
if err = db.Insert(ctx, org); err != nil {
|
||||||
return err
|
return fmt.Errorf("insert organization: %w", err)
|
||||||
}
|
}
|
||||||
|
if err = user_model.GenerateRandomAvatar(ctx, org.AsUser()); err != nil {
|
||||||
|
return fmt.Errorf("generate random avatar: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err = db.Insert(ctx, &TeamUser{
|
// Add initial creator to organization and owner team.
|
||||||
UID: owner.ID,
|
if err = db.Insert(ctx, &OrgUser{
|
||||||
OrgID: org.ID,
|
UID: owner.ID,
|
||||||
TeamID: t.ID,
|
OrgID: org.ID,
|
||||||
}); err != nil {
|
IsPublic: setting.Service.DefaultOrgMemberVisible,
|
||||||
return fmt.Errorf("insert team-user relation: %w", err)
|
}); err != nil {
|
||||||
}
|
return fmt.Errorf("insert org-user relation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return committer.Commit()
|
// Create default owner team.
|
||||||
|
t := &Team{
|
||||||
|
OrgID: org.ID,
|
||||||
|
LowerName: strings.ToLower(OwnerTeamName),
|
||||||
|
Name: OwnerTeamName,
|
||||||
|
AccessMode: perm.AccessModeOwner,
|
||||||
|
NumMembers: 1,
|
||||||
|
IncludesAllRepositories: true,
|
||||||
|
CanCreateOrgRepo: true,
|
||||||
|
}
|
||||||
|
if err = db.Insert(ctx, t); err != nil {
|
||||||
|
return fmt.Errorf("insert owner team: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert units for team
|
||||||
|
units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
|
||||||
|
for _, tp := range unit.AllRepoUnitTypes {
|
||||||
|
up := perm.AccessModeOwner
|
||||||
|
if tp == unit.TypeExternalTracker || tp == unit.TypeExternalWiki {
|
||||||
|
up = perm.AccessModeRead
|
||||||
|
}
|
||||||
|
units = append(units, TeamUnit{
|
||||||
|
OrgID: org.ID,
|
||||||
|
TeamID: t.ID,
|
||||||
|
Type: tp,
|
||||||
|
AccessMode: up,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = db.Insert(ctx, &units); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = db.Insert(ctx, &TeamUser{
|
||||||
|
UID: owner.ID,
|
||||||
|
OrgID: org.ID,
|
||||||
|
TeamID: t.ID,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("insert team-user relation: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgByName returns organization by given name.
|
// GetOrgByName returns organization by given name.
|
||||||
@ -499,31 +494,26 @@ func AddOrgUser(ctx context.Context, orgID, uid int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// check in transaction
|
||||||
return err
|
isAlreadyMember, err = IsOrganizationMember(ctx, orgID, uid)
|
||||||
}
|
if err != nil || isAlreadyMember {
|
||||||
defer committer.Close()
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// check in transaction
|
ou := &OrgUser{
|
||||||
isAlreadyMember, err = IsOrganizationMember(ctx, orgID, uid)
|
UID: uid,
|
||||||
if err != nil || isAlreadyMember {
|
OrgID: orgID,
|
||||||
return err
|
IsPublic: setting.Service.DefaultOrgMemberVisible,
|
||||||
}
|
}
|
||||||
|
|
||||||
ou := &OrgUser{
|
if err := db.Insert(ctx, ou); err != nil {
|
||||||
UID: uid,
|
return err
|
||||||
OrgID: orgID,
|
} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
|
||||||
IsPublic: setting.Service.DefaultOrgMemberVisible,
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
if err := db.Insert(ctx, ou); err != nil {
|
})
|
||||||
return err
|
|
||||||
} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgByID returns the user object by given ID if exists.
|
// GetOrgByID returns the user object by given ID if exists.
|
||||||
|
@ -31,21 +31,16 @@ func getUnitsByTeamID(ctx context.Context, teamID int64) (units []*TeamUnit, err
|
|||||||
|
|
||||||
// UpdateTeamUnits updates a teams's units
|
// UpdateTeamUnits updates a teams's units
|
||||||
func UpdateTeamUnits(ctx context.Context, team *Team, units []TeamUnit) (err error) {
|
func UpdateTeamUnits(ctx context.Context, team *Team, units []TeamUnit) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if _, err = db.GetEngine(ctx).Where("team_id = ?", team.ID).Delete(new(TeamUnit)); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err = db.GetEngine(ctx).Where("team_id = ?", team.ID).Delete(new(TeamUnit)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(units) > 0 {
|
|
||||||
if err = db.Insert(ctx, units); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
if len(units) > 0 {
|
||||||
|
if err = db.Insert(ctx, units); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -359,41 +359,25 @@ func updateRepositoryProjectCount(ctx context.Context, repoID int64) error {
|
|||||||
|
|
||||||
// ChangeProjectStatusByRepoIDAndID toggles a project between opened and closed
|
// ChangeProjectStatusByRepoIDAndID toggles a project between opened and closed
|
||||||
func ChangeProjectStatusByRepoIDAndID(ctx context.Context, repoID, projectID int64, isClosed bool) error {
|
func ChangeProjectStatusByRepoIDAndID(ctx context.Context, repoID, projectID int64, isClosed bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
p := new(Project)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
p := new(Project)
|
has, err := db.GetEngine(ctx).ID(projectID).Where("repo_id = ?", repoID).Get(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if !has {
|
||||||
|
return ErrProjectNotExist{ID: projectID, RepoID: repoID}
|
||||||
|
}
|
||||||
|
|
||||||
has, err := db.GetEngine(ctx).ID(projectID).Where("repo_id = ?", repoID).Get(p)
|
return changeProjectStatus(ctx, p, isClosed)
|
||||||
if err != nil {
|
})
|
||||||
return err
|
|
||||||
} else if !has {
|
|
||||||
return ErrProjectNotExist{ID: projectID, RepoID: repoID}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := changeProjectStatus(ctx, p, isClosed); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeProjectStatus toggle a project between opened and closed
|
// ChangeProjectStatus toggle a project between opened and closed
|
||||||
func ChangeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
|
func ChangeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return changeProjectStatus(ctx, p, isClosed)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := changeProjectStatus(ctx, p, isClosed); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
|
func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
|
||||||
|
@ -290,19 +290,14 @@ func UpdateRepoStats(ctx context.Context, id int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateUserStarNumbers(ctx context.Context, users []user_model.User) error {
|
func updateUserStarNumbers(ctx context.Context, users []user_model.User) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, user := range users {
|
||||||
return err
|
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=?) WHERE id=?", user.ID, user.ID); err != nil {
|
||||||
}
|
return err
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
for _, user := range users {
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=?) WHERE id=?", user.ID, user.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoctorUserStarNum recalculate Stars number for all user
|
// DoctorUserStarNum recalculate Stars number for all user
|
||||||
|
@ -141,101 +141,90 @@ func GetTopLanguageStats(ctx context.Context, repo *Repository, limit int) (Lang
|
|||||||
|
|
||||||
// UpdateLanguageStats updates the language statistics for repository
|
// UpdateLanguageStats updates the language statistics for repository
|
||||||
func UpdateLanguageStats(ctx context.Context, repo *Repository, commitID string, stats map[string]int64) error {
|
func UpdateLanguageStats(ctx context.Context, repo *Repository, commitID string, stats map[string]int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
sess := db.GetEngine(ctx)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
oldstats, err := GetLanguageStats(ctx, repo)
|
oldstats, err := GetLanguageStats(ctx, repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
var topLang string
|
|
||||||
var s int64
|
|
||||||
for lang, size := range stats {
|
|
||||||
if size > s {
|
|
||||||
s = size
|
|
||||||
topLang = lang
|
|
||||||
}
|
}
|
||||||
}
|
var topLang string
|
||||||
|
var s int64
|
||||||
for lang, size := range stats {
|
for lang, size := range stats {
|
||||||
upd := false
|
if size > s {
|
||||||
for _, s := range oldstats {
|
s = size
|
||||||
// Update already existing language
|
topLang = lang
|
||||||
if strings.EqualFold(s.Language, lang) {
|
|
||||||
s.CommitID = commitID
|
|
||||||
s.IsPrimary = lang == topLang
|
|
||||||
s.Size = size
|
|
||||||
if _, err := sess.ID(s.ID).Cols("`commit_id`", "`size`", "`is_primary`").Update(s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
upd = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Insert new language
|
|
||||||
if !upd {
|
for lang, size := range stats {
|
||||||
if err := db.Insert(ctx, &LanguageStat{
|
upd := false
|
||||||
RepoID: repo.ID,
|
for _, s := range oldstats {
|
||||||
CommitID: commitID,
|
// Update already existing language
|
||||||
IsPrimary: lang == topLang,
|
if strings.EqualFold(s.Language, lang) {
|
||||||
Language: lang,
|
s.CommitID = commitID
|
||||||
Size: size,
|
s.IsPrimary = lang == topLang
|
||||||
}); err != nil {
|
s.Size = size
|
||||||
|
if _, err := sess.ID(s.ID).Cols("`commit_id`", "`size`", "`is_primary`").Update(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
upd = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Insert new language
|
||||||
|
if !upd {
|
||||||
|
if err := db.Insert(ctx, &LanguageStat{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
CommitID: commitID,
|
||||||
|
IsPrimary: lang == topLang,
|
||||||
|
Language: lang,
|
||||||
|
Size: size,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Delete old languages
|
||||||
|
statsToDelete := make([]int64, 0, len(oldstats))
|
||||||
|
for _, s := range oldstats {
|
||||||
|
if s.CommitID != commitID {
|
||||||
|
statsToDelete = append(statsToDelete, s.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(statsToDelete) > 0 {
|
||||||
|
if _, err := sess.In("`id`", statsToDelete).Delete(&LanguageStat{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Delete old languages
|
|
||||||
statsToDelete := make([]int64, 0, len(oldstats))
|
|
||||||
for _, s := range oldstats {
|
|
||||||
if s.CommitID != commitID {
|
|
||||||
statsToDelete = append(statsToDelete, s.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(statsToDelete) > 0 {
|
|
||||||
if _, err := sess.In("`id`", statsToDelete).Delete(&LanguageStat{}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update indexer status
|
// Update indexer status
|
||||||
if err = UpdateIndexerStatus(ctx, repo, RepoIndexerTypeStats, commitID); err != nil {
|
return UpdateIndexerStatus(ctx, repo, RepoIndexerTypeStats, commitID)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyLanguageStat Copy originalRepo language stat information to destRepo (use for forked repo)
|
// CopyLanguageStat Copy originalRepo language stat information to destRepo (use for forked repo)
|
||||||
func CopyLanguageStat(ctx context.Context, originalRepo, destRepo *Repository) error {
|
func CopyLanguageStat(ctx context.Context, originalRepo, destRepo *Repository) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
RepoLang := make(LanguageStatList, 0, 6)
|
||||||
return err
|
if err := db.GetEngine(ctx).Where("`repo_id` = ?", originalRepo.ID).Desc("`size`").Find(&RepoLang); err != nil {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
RepoLang := make(LanguageStatList, 0, 6)
|
|
||||||
if err := db.GetEngine(ctx).Where("`repo_id` = ?", originalRepo.ID).Desc("`size`").Find(&RepoLang); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(RepoLang) > 0 {
|
|
||||||
for i := range RepoLang {
|
|
||||||
RepoLang[i].ID = 0
|
|
||||||
RepoLang[i].RepoID = destRepo.ID
|
|
||||||
RepoLang[i].CreatedUnix = timeutil.TimeStampNow()
|
|
||||||
}
|
|
||||||
// update destRepo's indexer status
|
|
||||||
tmpCommitID := RepoLang[0].CommitID
|
|
||||||
if err := UpdateIndexerStatus(ctx, destRepo, RepoIndexerTypeStats, tmpCommitID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := db.Insert(ctx, &RepoLang); err != nil {
|
if len(RepoLang) > 0 {
|
||||||
return err
|
for i := range RepoLang {
|
||||||
|
RepoLang[i].ID = 0
|
||||||
|
RepoLang[i].RepoID = destRepo.ID
|
||||||
|
RepoLang[i].CreatedUnix = timeutil.TimeStampNow()
|
||||||
|
}
|
||||||
|
// update destRepo's indexer status
|
||||||
|
tmpCommitID := RepoLang[0].CommitID
|
||||||
|
if err := UpdateIndexerStatus(ctx, destRepo, RepoIndexerTypeStats, tmpCommitID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := db.Insert(ctx, &RepoLang); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
return committer.Commit()
|
})
|
||||||
}
|
}
|
||||||
|
@ -472,30 +472,24 @@ func (r *Release) GetExternalID() int64 { return r.OriginalAuthorID }
|
|||||||
|
|
||||||
// InsertReleases migrates release
|
// InsertReleases migrates release
|
||||||
func InsertReleases(ctx context.Context, rels ...*Release) error {
|
func InsertReleases(ctx context.Context, rels ...*Release) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
for _, rel := range rels {
|
||||||
return err
|
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(rel); err != nil {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
for _, rel := range rels {
|
|
||||||
if _, err := sess.NoAutoTime().Insert(rel); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rel.Attachments) > 0 {
|
|
||||||
for i := range rel.Attachments {
|
|
||||||
rel.Attachments[i].ReleaseID = rel.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := sess.NoAutoTime().Insert(rel.Attachments); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
if len(rel.Attachments) > 0 {
|
||||||
|
for i := range rel.Attachments {
|
||||||
|
rel.Attachments[i].ReleaseID = rel.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.GetEngine(ctx).NoAutoTime().Insert(rel.Attachments); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string) (map[string][]*Release, error) {
|
func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string) (map[string][]*Release, error) {
|
||||||
|
@ -25,48 +25,45 @@ func init() {
|
|||||||
|
|
||||||
// StarRepo or unstar repository.
|
// StarRepo or unstar repository.
|
||||||
func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star bool) error {
|
func StarRepo(ctx context.Context, doer *user_model.User, repo *Repository, star bool) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
staring := IsStaring(ctx, doer.ID, repo.ID)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
staring := IsStaring(ctx, doer.ID, repo.ID)
|
|
||||||
|
|
||||||
if star {
|
if star {
|
||||||
if user_model.IsUserBlockedBy(ctx, doer, repo.OwnerID) {
|
if user_model.IsUserBlockedBy(ctx, doer, repo.OwnerID) {
|
||||||
return user_model.ErrBlockedUser
|
return user_model.ErrBlockedUser
|
||||||
|
}
|
||||||
|
|
||||||
|
if staring {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Insert(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", doer.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !staring {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.DeleteByBean(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repo.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", doer.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if staring {
|
return nil
|
||||||
return nil
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Insert(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?", repo.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?", doer.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !staring {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := db.DeleteByBean(ctx, &Star{UID: doer.ID, RepoID: repo.ID}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?", repo.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?", doer.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsStaring checks if user has starred given repository.
|
// IsStaring checks if user has starred given repository.
|
||||||
|
@ -227,32 +227,26 @@ func GetRepoTopicByName(ctx context.Context, repoID int64, topicName string) (*T
|
|||||||
|
|
||||||
// AddTopic adds a topic name to a repository (if it does not already have it)
|
// AddTopic adds a topic name to a repository (if it does not already have it)
|
||||||
func AddTopic(ctx context.Context, repoID int64, topicName string) (*Topic, error) {
|
func AddTopic(ctx context.Context, repoID int64, topicName string) (*Topic, error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Topic, error) {
|
||||||
if err != nil {
|
topic, err := GetRepoTopicByName(ctx, repoID, topicName)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
defer committer.Close()
|
}
|
||||||
sess := db.GetEngine(ctx)
|
if topic != nil {
|
||||||
|
// Repo already have topic
|
||||||
|
return topic, nil
|
||||||
|
}
|
||||||
|
|
||||||
topic, err := GetRepoTopicByName(ctx, repoID, topicName)
|
topic, err = addTopicByNameToRepo(ctx, repoID, topicName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if topic != nil {
|
|
||||||
// Repo already have topic
|
if err = syncTopicsInRepository(ctx, repoID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return topic, nil
|
return topic, nil
|
||||||
}
|
})
|
||||||
|
|
||||||
topic, err = addTopicByNameToRepo(ctx, repoID, topicName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = syncTopicsInRepository(sess, repoID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return topic, committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTopic removes a topic name from a repository (if it has it)
|
// DeleteTopic removes a topic name from a repository (if it has it)
|
||||||
@ -266,14 +260,15 @@ func DeleteTopic(ctx context.Context, repoID int64, topicName string) (*Topic, e
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = removeTopicFromRepo(ctx, repoID, topic)
|
return db.WithTx2(ctx, func(ctx context.Context) (*Topic, error) {
|
||||||
if err != nil {
|
if err = removeTopicFromRepo(ctx, repoID, topic); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err = syncTopicsInRepository(ctx, repoID); err != nil {
|
||||||
err = syncTopicsInRepository(db.GetEngine(ctx), repoID)
|
return nil, err
|
||||||
|
}
|
||||||
return topic, err
|
return topic, nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveTopics save topics to a repository
|
// SaveTopics save topics to a repository
|
||||||
@ -285,64 +280,55 @@ func SaveTopics(ctx context.Context, repoID int64, topicNames ...string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
var addedTopicNames []string
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
var addedTopicNames []string
|
|
||||||
for _, topicName := range topicNames {
|
|
||||||
if strings.TrimSpace(topicName) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var found bool
|
|
||||||
for _, t := range topics {
|
|
||||||
if strings.EqualFold(topicName, t.Name) {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
addedTopicNames = append(addedTopicNames, topicName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var removeTopics []*Topic
|
|
||||||
for _, t := range topics {
|
|
||||||
var found bool
|
|
||||||
for _, topicName := range topicNames {
|
for _, topicName := range topicNames {
|
||||||
if strings.EqualFold(topicName, t.Name) {
|
if strings.TrimSpace(topicName) == "" {
|
||||||
found = true
|
continue
|
||||||
break
|
}
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
for _, t := range topics {
|
||||||
|
if strings.EqualFold(topicName, t.Name) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
addedTopicNames = append(addedTopicNames, topicName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
|
||||||
removeTopics = append(removeTopics, t)
|
var removeTopics []*Topic
|
||||||
|
for _, t := range topics {
|
||||||
|
var found bool
|
||||||
|
for _, topicName := range topicNames {
|
||||||
|
if strings.EqualFold(topicName, t.Name) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
removeTopics = append(removeTopics, t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for _, topicName := range addedTopicNames {
|
for _, topicName := range addedTopicNames {
|
||||||
_, err := addTopicByNameToRepo(ctx, repoID, topicName)
|
_, err := addTopicByNameToRepo(ctx, repoID, topicName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for _, topic := range removeTopics {
|
for _, topic := range removeTopics {
|
||||||
err := removeTopicFromRepo(ctx, repoID, topic)
|
err := removeTopicFromRepo(ctx, repoID, topic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := syncTopicsInRepository(sess, repoID); err != nil {
|
return syncTopicsInRepository(ctx, repoID)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateTopics generates topics from a template repository
|
// GenerateTopics generates topics from a template repository
|
||||||
@ -353,19 +339,19 @@ func GenerateTopics(ctx context.Context, templateRepo, generateRepo *Repository)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return syncTopicsInRepository(db.GetEngine(ctx), generateRepo.ID)
|
return syncTopicsInRepository(ctx, generateRepo.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// syncTopicsInRepository makes sure topics in the topics table are copied into the topics field of the repository
|
// syncTopicsInRepository makes sure topics in the topics table are copied into the topics field of the repository
|
||||||
func syncTopicsInRepository(sess db.Engine, repoID int64) error {
|
func syncTopicsInRepository(ctx context.Context, repoID int64) error {
|
||||||
topicNames := make([]string, 0, 25)
|
topicNames := make([]string, 0, 25)
|
||||||
if err := sess.Table("topic").Cols("name").
|
if err := db.GetEngine(ctx).Table("topic").Cols("name").
|
||||||
Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id").
|
Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id").
|
||||||
Where("repo_topic.repo_id = ?", repoID).Asc("topic.name").Find(&topicNames); err != nil {
|
Where("repo_topic.repo_id = ?", repoID).Asc("topic.name").Find(&topicNames); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{
|
if _, err := db.GetEngine(ctx).ID(repoID).Cols("topics").Update(&Repository{
|
||||||
Topics: topicNames,
|
Topics: topicNames,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -19,11 +19,6 @@ func UpdateRepositoryOwnerNames(ctx context.Context, ownerID int64, ownerName st
|
|||||||
if ownerID == 0 {
|
if ownerID == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").NoAutoTime().Update(&Repository{
|
if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").NoAutoTime().Update(&Repository{
|
||||||
OwnerName: ownerName,
|
OwnerName: ownerName,
|
||||||
@ -31,7 +26,7 @@ func UpdateRepositoryOwnerNames(ctx context.Context, ownerID int64, ownerName st
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return committer.Commit()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRepositoryUpdatedTime updates a repository's updated time
|
// UpdateRepositoryUpdatedTime updates a repository's updated time
|
||||||
|
@ -117,12 +117,6 @@ func DeleteUploads(ctx context.Context, uploads ...*Upload) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
ids := make([]int64, len(uploads))
|
ids := make([]int64, len(uploads))
|
||||||
for i := range uploads {
|
for i := range uploads {
|
||||||
ids[i] = uploads[i].ID
|
ids[i] = uploads[i].ID
|
||||||
@ -131,10 +125,6 @@ func DeleteUploads(ctx context.Context, uploads ...*Upload) (err error) {
|
|||||||
return fmt.Errorf("delete uploads: %w", err)
|
return fmt.Errorf("delete uploads: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = committer.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, upload := range uploads {
|
for _, upload := range uploads {
|
||||||
localPath := upload.LocalPath()
|
localPath := upload.LocalPath()
|
||||||
isFile, err := util.IsFile(localPath)
|
isFile, err := util.IsFile(localPath)
|
||||||
|
@ -256,15 +256,9 @@ func IsEmailUsed(ctx context.Context, email string) (bool, error) {
|
|||||||
|
|
||||||
// ActivateEmail activates the email address to given user.
|
// ActivateEmail activates the email address to given user.
|
||||||
func ActivateEmail(ctx context.Context, email *EmailAddress) error {
|
func ActivateEmail(ctx context.Context, email *EmailAddress) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return updateActivation(ctx, email, true)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
if err := updateActivation(ctx, email, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateActivation(ctx context.Context, email *EmailAddress, activate bool) error {
|
func updateActivation(ctx context.Context, email *EmailAddress, activate bool) error {
|
||||||
@ -305,33 +299,30 @@ func makeEmailPrimaryInternal(ctx context.Context, emailID int64, isActive bool)
|
|||||||
return ErrUserNotExist{UID: email.UID}
|
return ErrUserNotExist{UID: email.UID}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
sess := db.GetEngine(ctx)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
|
|
||||||
// 1. Update user table
|
// 1. Update user table
|
||||||
user.Email = email.Email
|
user.Email = email.Email
|
||||||
if _, err = sess.ID(user.ID).Cols("email").Update(user); err != nil {
|
if _, err := sess.ID(user.ID).Cols("email").Update(user); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Update old primary email
|
// 2. Update old primary email
|
||||||
if _, err = sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&EmailAddress{
|
if _, err := sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&EmailAddress{
|
||||||
IsPrimary: false,
|
IsPrimary: false,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. update new primary email
|
// 3. update new primary email
|
||||||
email.IsPrimary = true
|
email.IsPrimary = true
|
||||||
if _, err = sess.ID(email.ID).Cols("is_primary").Update(email); err != nil {
|
if _, err := sess.ID(email.ID).Cols("is_primary").Update(email); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return committer.Commit()
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeInactivePrimaryEmail replaces the inactive primary email of a given user
|
// ChangeInactivePrimaryEmail replaces the inactive primary email of a given user
|
||||||
|
@ -38,24 +38,20 @@ func FollowUser(ctx context.Context, user, follow *User) (err error) {
|
|||||||
return ErrBlockedUser
|
return ErrBlockedUser
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = db.Insert(ctx, &Follow{UserID: user.ID, FollowID: follow.ID}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = db.Insert(ctx, &Follow{UserID: user.ID, FollowID: follow.ID}); err != nil {
|
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", follow.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", follow.ID); err != nil {
|
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", user.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", user.ID); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnfollowUser unmarks someone as another's follower.
|
// UnfollowUser unmarks someone as another's follower.
|
||||||
@ -64,22 +60,18 @@ func UnfollowUser(ctx context.Context, userID, followID int64) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil {
|
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil {
|
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil {
|
})
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@ -319,21 +319,16 @@ func UpdateWebhookLastStatus(ctx context.Context, w *Webhook) error {
|
|||||||
// DeleteWebhookByID uses argument bean as query condition,
|
// DeleteWebhookByID uses argument bean as query condition,
|
||||||
// ID must be specified and do not assign unnecessary fields.
|
// ID must be specified and do not assign unnecessary fields.
|
||||||
func DeleteWebhookByID(ctx context.Context, id int64) (err error) {
|
func DeleteWebhookByID(ctx context.Context, id int64) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if count, err := db.DeleteByID[Webhook](ctx, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
} else if count == 0 {
|
||||||
defer committer.Close()
|
return ErrWebhookNotExist{ID: id}
|
||||||
|
} else if _, err = db.DeleteByBean(ctx, &HookTask{HookID: id}); err != nil {
|
||||||
if count, err := db.DeleteByID[Webhook](ctx, id); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
} else if count == 0 {
|
return nil
|
||||||
return ErrWebhookNotExist{ID: id}
|
})
|
||||||
} else if _, err = db.DeleteByBean(ctx, &HookTask{HookID: id}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteWebhookByRepoID deletes webhook of repository by given ID.
|
// DeleteWebhookByRepoID deletes webhook of repository by given ID.
|
||||||
|
@ -262,7 +262,8 @@ func (it IssueTemplate) Type() IssueTemplateType {
|
|||||||
// IssueMeta basic issue information
|
// IssueMeta basic issue information
|
||||||
// swagger:model
|
// swagger:model
|
||||||
type IssueMeta struct {
|
type IssueMeta struct {
|
||||||
Index int64 `json:"index"`
|
Index int64 `json:"index"`
|
||||||
|
// owner of the issue's repo
|
||||||
Owner string `json:"owner"`
|
Owner string `json:"owner"`
|
||||||
Name string `json:"repo"`
|
Name string `json:"repo"`
|
||||||
}
|
}
|
||||||
|
@ -48,15 +48,16 @@ type ExternalWiki struct {
|
|||||||
|
|
||||||
// Repository represents a repository
|
// Repository represents a repository
|
||||||
type Repository struct {
|
type Repository struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Owner *User `json:"owner"`
|
Owner *User `json:"owner"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
FullName string `json:"full_name"`
|
FullName string `json:"full_name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Empty bool `json:"empty"`
|
Empty bool `json:"empty"`
|
||||||
Private bool `json:"private"`
|
Private bool `json:"private"`
|
||||||
Fork bool `json:"fork"`
|
Fork bool `json:"fork"`
|
||||||
Template bool `json:"template"`
|
Template bool `json:"template"`
|
||||||
|
// the original repository if this repository is a fork, otherwise null
|
||||||
Parent *Repository `json:"parent,omitempty"`
|
Parent *Repository `json:"parent,omitempty"`
|
||||||
Mirror bool `json:"mirror"`
|
Mirror bool `json:"mirror"`
|
||||||
Size int `json:"size"`
|
Size int `json:"size"`
|
||||||
@ -225,15 +226,13 @@ type EditRepoOption struct {
|
|||||||
EnablePrune *bool `json:"enable_prune,omitempty"`
|
EnablePrune *bool `json:"enable_prune,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateRepoOption options when creating repository using a template
|
// GenerateRepoOption options when creating a repository using a template
|
||||||
// swagger:model
|
// swagger:model
|
||||||
type GenerateRepoOption struct {
|
type GenerateRepoOption struct {
|
||||||
// The organization or person who will own the new repository
|
// the organization's name or individual user's name who will own the new repository
|
||||||
//
|
//
|
||||||
// required: true
|
// required: true
|
||||||
Owner string `json:"owner"`
|
Owner string `json:"owner"`
|
||||||
// Name of the repository to create
|
|
||||||
//
|
|
||||||
// required: true
|
// required: true
|
||||||
// unique: true
|
// unique: true
|
||||||
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||||
@ -352,9 +351,9 @@ func (gt GitServiceType) Title() string {
|
|||||||
type MigrateRepoOptions struct {
|
type MigrateRepoOptions struct {
|
||||||
// required: true
|
// required: true
|
||||||
CloneAddr string `json:"clone_addr" binding:"Required"`
|
CloneAddr string `json:"clone_addr" binding:"Required"`
|
||||||
// deprecated (only for backwards compatibility)
|
// deprecated (only for backwards compatibility, use repo_owner instead)
|
||||||
RepoOwnerID int64 `json:"uid"`
|
RepoOwnerID int64 `json:"uid"`
|
||||||
// Name of User or Organisation who will own Repo after migration
|
// the organization's name or individual user's name who will own the migrated repository
|
||||||
RepoOwner string `json:"repo_owner"`
|
RepoOwner string `json:"repo_owner"`
|
||||||
// required: true
|
// required: true
|
||||||
RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||||
|
@ -44,7 +44,7 @@ webauthn_use_twofa = Use a two-factor code from your phone
|
|||||||
webauthn_error = Could not read your security key.
|
webauthn_error = Could not read your security key.
|
||||||
webauthn_unsupported_browser = Your browser does not currently support WebAuthn.
|
webauthn_unsupported_browser = Your browser does not currently support WebAuthn.
|
||||||
webauthn_error_unknown = An unknown error occurred. Please retry.
|
webauthn_error_unknown = An unknown error occurred. Please retry.
|
||||||
webauthn_error_insecure = WebAuthn only supports secure connections. For testing over HTTP, you can use the origin "localhost" or "127.0.0.1"
|
webauthn_error_insecure = WebAuthn only supports secure connections. For testing over HTTP, you can use the origin "localhost" or "127.0.0.1".
|
||||||
webauthn_error_unable_to_process = The server could not process your request.
|
webauthn_error_unable_to_process = The server could not process your request.
|
||||||
webauthn_error_duplicated = The security key is not permitted for this request. Please make sure that the key is not already registered.
|
webauthn_error_duplicated = The security key is not permitted for this request. Please make sure that the key is not already registered.
|
||||||
webauthn_error_empty = You must set a name for this key.
|
webauthn_error_empty = You must set a name for this key.
|
||||||
@ -169,7 +169,7 @@ no_results_found = No results found.
|
|||||||
internal_error_skipped = Internal error occurred but is skipped: %s
|
internal_error_skipped = Internal error occurred but is skipped: %s
|
||||||
|
|
||||||
[search]
|
[search]
|
||||||
search = Search...
|
search = Search…
|
||||||
type_tooltip = Search type
|
type_tooltip = Search type
|
||||||
fuzzy = Fuzzy
|
fuzzy = Fuzzy
|
||||||
fuzzy_tooltip = Include results that closely match the search term
|
fuzzy_tooltip = Include results that closely match the search term
|
||||||
@ -179,23 +179,23 @@ regexp = Regexp
|
|||||||
regexp_tooltip = Include only results that match the regexp search term
|
regexp_tooltip = Include only results that match the regexp search term
|
||||||
exact = Exact
|
exact = Exact
|
||||||
exact_tooltip = Include only results that match the exact search term
|
exact_tooltip = Include only results that match the exact search term
|
||||||
repo_kind = Search repos...
|
repo_kind = Search repos…
|
||||||
user_kind = Search users...
|
user_kind = Search users…
|
||||||
org_kind = Search orgs...
|
org_kind = Search orgs…
|
||||||
team_kind = Search teams...
|
team_kind = Search teams…
|
||||||
code_kind = Search code...
|
code_kind = Search code…
|
||||||
code_search_unavailable = Code search is currently not available. Please contact the site administrator.
|
code_search_unavailable = Code search is currently not available. Please contact the site administrator.
|
||||||
code_search_by_git_grep = Current code search results are provided by "git grep". There might be better results if site administrator enables Repository Indexer.
|
code_search_by_git_grep = Current code search results are provided by "git grep". There might be better results if site administrator enables Repository Indexer.
|
||||||
package_kind = Search packages...
|
package_kind = Search packages…
|
||||||
project_kind = Search projects...
|
project_kind = Search projects…
|
||||||
branch_kind = Search branches...
|
branch_kind = Search branches…
|
||||||
tag_kind = Search tags...
|
tag_kind = Search tags…
|
||||||
tag_tooltip = Search for matching tags. Use '%' to match any sequence of numbers.
|
tag_tooltip = Search for matching tags. Use '%' to match any sequence of numbers.
|
||||||
commit_kind = Search commits...
|
commit_kind = Search commits…
|
||||||
runner_kind = Search runners...
|
runner_kind = Search runners…
|
||||||
no_results = No matching results found.
|
no_results = No matching results found.
|
||||||
issue_kind = Search issues...
|
issue_kind = Search issues…
|
||||||
pull_kind = Search pulls...
|
pull_kind = Search pull requests…
|
||||||
keyword_search_unavailable = Searching by keyword is currently not available. Please contact the site administrator.
|
keyword_search_unavailable = Searching by keyword is currently not available. Please contact the site administrator.
|
||||||
|
|
||||||
[aria]
|
[aria]
|
||||||
@ -231,8 +231,8 @@ buttons.enable_monospace_font = Enable monospace font
|
|||||||
buttons.disable_monospace_font = Disable monospace font
|
buttons.disable_monospace_font = Disable monospace font
|
||||||
|
|
||||||
[filter]
|
[filter]
|
||||||
string.asc = A - Z
|
string.asc = A–Z
|
||||||
string.desc = Z - A
|
string.desc = Z–A
|
||||||
|
|
||||||
[error]
|
[error]
|
||||||
occurred = An error occurred
|
occurred = An error occurred
|
||||||
@ -253,7 +253,7 @@ license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="%[1]s">
|
|||||||
|
|
||||||
[install]
|
[install]
|
||||||
install = Installation
|
install = Installation
|
||||||
installing_desc = Installing now, please wait...
|
installing_desc = Installing now, please wait…
|
||||||
title = Initial Configuration
|
title = Initial Configuration
|
||||||
docker_helper = If you run Gitea inside Docker, please read the <a target="_blank" rel="noopener noreferrer" href="%s">documentation</a> before changing any settings.
|
docker_helper = If you run Gitea inside Docker, please read the <a target="_blank" rel="noopener noreferrer" href="%s">documentation</a> before changing any settings.
|
||||||
require_db_desc = Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
|
require_db_desc = Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
|
||||||
@ -270,16 +270,16 @@ path = Path
|
|||||||
sqlite_helper = File path for the SQLite3 database.<br>Enter an absolute path if you run Gitea as a service.
|
sqlite_helper = File path for the SQLite3 database.<br>Enter an absolute path if you run Gitea as a service.
|
||||||
reinstall_error = You are trying to install into an existing Gitea database
|
reinstall_error = You are trying to install into an existing Gitea database
|
||||||
reinstall_confirm_message = Re-installing with an existing Gitea database can cause multiple problems. In most cases, you should use your existing "app.ini" to run Gitea. If you know what you are doing, confirm the following:
|
reinstall_confirm_message = Re-installing with an existing Gitea database can cause multiple problems. In most cases, you should use your existing "app.ini" to run Gitea. If you know what you are doing, confirm the following:
|
||||||
reinstall_confirm_check_1 = The data encrypted by the SECRET_KEY in app.ini may be lost: users may not be able to log in with 2FA/OTP & mirrors may not function correctly. By checking this box you confirm that the current app.ini file contains the correct the SECRET_KEY.
|
reinstall_confirm_check_1 = The data encrypted by the SECRET_KEY in app.ini may be lost: users may not be able to log in with 2FA/OTP and mirrors may not function correctly. By checking this box, you confirm that the current app.ini file contains the correct the SECRET_KEY.
|
||||||
reinstall_confirm_check_2 = The repositories and settings may need to be re-synchronized. By checking this box you confirm that you will resynchronize the hooks for the repositories and authorized_keys file manually. You confirm that you will ensure that repository and mirror settings are correct.
|
reinstall_confirm_check_2 = The repositories and settings may need to be resynchronized. By checking this box, you confirm that you will resynchronize the hooks for the repositories and authorized_keys file manually. You confirm that you will ensure that repository and mirror settings are correct.
|
||||||
reinstall_confirm_check_3 = You confirm that you are absolutely sure that this Gitea is running with the correct app.ini location and that you are sure that you have to re-install. You confirm that you acknowledge the above risks.
|
reinstall_confirm_check_3 = You confirm that you are absolutely sure that this Gitea is running with the correct app.ini location and that you are sure that you have to re-install. You confirm that you acknowledge the above risks.
|
||||||
err_empty_db_path = The SQLite3 database path cannot be empty.
|
err_empty_db_path = The SQLite3 database path cannot be empty.
|
||||||
no_admin_and_disable_registration = You cannot disable user self-registration without creating an administrator account.
|
no_admin_and_disable_registration = You cannot disable user self-registration without creating an administrator account.
|
||||||
err_empty_admin_password = The administrator password cannot be empty.
|
err_empty_admin_password = The administrator password cannot be empty.
|
||||||
err_empty_admin_email = The administrator email cannot be empty.
|
err_empty_admin_email = The administrator email address cannot be empty.
|
||||||
err_admin_name_is_reserved = Administrator Username is invalid, username is reserved
|
err_admin_name_is_reserved = Administrator username is invalid. Username is reserved.
|
||||||
err_admin_name_pattern_not_allowed = Administrator username is invalid, the username matches a reserved pattern
|
err_admin_name_pattern_not_allowed = Administrator username is invalid. The username matches a reserved pattern.
|
||||||
err_admin_name_is_invalid = Administrator Username is invalid
|
err_admin_name_is_invalid = Administrator username is invalid
|
||||||
|
|
||||||
general_title = General Settings
|
general_title = General Settings
|
||||||
app_name = Site Title
|
app_name = Site Title
|
||||||
@ -295,7 +295,7 @@ domain_helper = Domain or host address for the server.
|
|||||||
ssh_port = SSH Server Port
|
ssh_port = SSH Server Port
|
||||||
ssh_port_helper = Port number your SSH server listens on. Leave empty to disable.
|
ssh_port_helper = Port number your SSH server listens on. Leave empty to disable.
|
||||||
http_port = Gitea HTTP Listen Port
|
http_port = Gitea HTTP Listen Port
|
||||||
http_port_helper = Port number the Giteas web server will listen on.
|
http_port_helper = Port number the Gitea web server will listen on.
|
||||||
app_url = Gitea Base URL
|
app_url = Gitea Base URL
|
||||||
app_url_helper = Base address for HTTP(S) clone URLs and email notifications.
|
app_url_helper = Base address for HTTP(S) clone URLs and email notifications.
|
||||||
log_root_path = Log Path
|
log_root_path = Log Path
|
||||||
@ -431,8 +431,8 @@ allow_password_change = Require user to change password (recommended)
|
|||||||
reset_password_mail_sent_prompt = A confirmation email has been sent to <b>%s</b>. Please check your inbox within the next %s to complete the account recovery process.
|
reset_password_mail_sent_prompt = A confirmation email has been sent to <b>%s</b>. Please check your inbox within the next %s to complete the account recovery process.
|
||||||
active_your_account = Activate Your Account
|
active_your_account = Activate Your Account
|
||||||
account_activated = Account has been activated
|
account_activated = Account has been activated
|
||||||
prohibit_login = Sign In Prohibited
|
prohibit_login = Sign-In Prohibited
|
||||||
prohibit_login_desc = Your account is prohibited from signing in, please contact your site administrator.
|
prohibit_login_desc = Your account is prohibited from signing in. Please contact your site administrator.
|
||||||
resent_limit_prompt = You have already requested an activation email recently. Please wait 3 minutes and try again.
|
resent_limit_prompt = You have already requested an activation email recently. Please wait 3 minutes and try again.
|
||||||
has_unconfirmed_mail = Hi %s, you have an unconfirmed email address (<b>%s</b>). If you haven't received a confirmation email or need to resend a new one, please click on the button below.
|
has_unconfirmed_mail = Hi %s, you have an unconfirmed email address (<b>%s</b>). If you haven't received a confirmation email or need to resend a new one, please click on the button below.
|
||||||
change_unconfirmed_mail_address = If your registration email address is incorrect, you can change it here and resend a new confirmation email.
|
change_unconfirmed_mail_address = If your registration email address is incorrect, you can change it here and resend a new confirmation email.
|
||||||
@ -453,7 +453,7 @@ use_scratch_code = Use a scratch code
|
|||||||
twofa_scratch_used = You have used your scratch code. You have been redirected to the two-factor settings page so you may remove your device enrollment or generate a new scratch code.
|
twofa_scratch_used = You have used your scratch code. You have been redirected to the two-factor settings page so you may remove your device enrollment or generate a new scratch code.
|
||||||
twofa_passcode_incorrect = Your passcode is incorrect. If you misplaced your device, use your scratch code to sign in.
|
twofa_passcode_incorrect = Your passcode is incorrect. If you misplaced your device, use your scratch code to sign in.
|
||||||
twofa_scratch_token_incorrect = Your scratch code is incorrect.
|
twofa_scratch_token_incorrect = Your scratch code is incorrect.
|
||||||
twofa_required = You must set up Two-Factor Authentication to get access to repositories, or try to log in again.
|
twofa_required = You must set up two-factor authentication to get access to repositories, or try to log in again.
|
||||||
login_userpass = Sign In
|
login_userpass = Sign In
|
||||||
login_openid = OpenID
|
login_openid = OpenID
|
||||||
oauth_signup_tab = Register New Account
|
oauth_signup_tab = Register New Account
|
||||||
@ -472,17 +472,17 @@ openid_connect_desc = The chosen OpenID URI is unknown. Associate it with a new
|
|||||||
openid_register_title = Create new account
|
openid_register_title = Create new account
|
||||||
openid_register_desc = The chosen OpenID URI is unknown. Associate it with a new account here.
|
openid_register_desc = The chosen OpenID URI is unknown. Associate it with a new account here.
|
||||||
openid_signin_desc = Enter your OpenID URI. For example: alice.openid.example.org or https://openid.example.org/alice.
|
openid_signin_desc = Enter your OpenID URI. For example: alice.openid.example.org or https://openid.example.org/alice.
|
||||||
disable_forgot_password_mail = Account recovery is disabled because no email is set up. Please contact your site administrator.
|
disable_forgot_password_mail = Account recovery is disabled because no email address is set up. Please contact your site administrator.
|
||||||
disable_forgot_password_mail_admin = Account recovery is only available when email is set up. Please set up email to enable account recovery.
|
disable_forgot_password_mail_admin = Account recovery is only available when an email address is set up.
|
||||||
email_domain_blacklisted = You cannot register with your email address.
|
email_domain_blacklisted = You cannot register with your email address.
|
||||||
authorize_application = Authorize Application
|
authorize_application = Authorize Application
|
||||||
authorize_redirect_notice = You will be redirected to %s if you authorize this application.
|
authorize_redirect_notice = You will be redirected to %s if you authorize this application.
|
||||||
authorize_application_created_by = This application was created by %s.
|
authorize_application_created_by = This application was created by %s.
|
||||||
authorize_application_description = If you grant access, it will be able to access and write to all your account information, including private repos and organisations.
|
authorize_application_description = If you grant access, it will be able to access and write to all your account information, including private repos and organizations.
|
||||||
authorize_application_with_scopes = With scopes: %s
|
authorize_application_with_scopes = With scopes: %s
|
||||||
authorize_title = Authorize "%s" to access your account?
|
authorize_title = Authorize "%s" to access your account?
|
||||||
authorization_failed = Authorization failed
|
authorization_failed = Authorization failed
|
||||||
authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you have tried to authorize.
|
authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you tried to authorize.
|
||||||
sspi_auth_failed = SSPI authentication failed
|
sspi_auth_failed = SSPI authentication failed
|
||||||
password_pwned = The password you chose is on a <a target="_blank" rel="noopener noreferrer" href="%s">list of stolen passwords</a> previously exposed in public data breaches. Please try again with a different password and consider changing this password elsewhere too.
|
password_pwned = The password you chose is on a <a target="_blank" rel="noopener noreferrer" href="%s">list of stolen passwords</a> previously exposed in public data breaches. Please try again with a different password and consider changing this password elsewhere too.
|
||||||
password_pwned_err = Could not complete request to HaveIBeenPwned
|
password_pwned_err = Could not complete request to HaveIBeenPwned
|
||||||
@ -546,7 +546,7 @@ release.download.targz = Source Code (TAR.GZ)
|
|||||||
repo.transfer.subject_to = %s would like to transfer "%s" to %s
|
repo.transfer.subject_to = %s would like to transfer "%s" to %s
|
||||||
repo.transfer.subject_to_you = %s would like to transfer "%s" to you
|
repo.transfer.subject_to_you = %s would like to transfer "%s" to you
|
||||||
repo.transfer.to_you = you
|
repo.transfer.to_you = you
|
||||||
repo.transfer.body = To accept or reject it visit %s or just ignore it.
|
repo.transfer.body = To accept or reject it, visit %s or just ignore it.
|
||||||
|
|
||||||
repo.collaborator.added.subject = %s added you to %s
|
repo.collaborator.added.subject = %s added you to %s
|
||||||
repo.collaborator.added.text = You have been added as a collaborator of repository:
|
repo.collaborator.added.text = You have been added as a collaborator of repository:
|
||||||
@ -598,7 +598,7 @@ url_error = `"%s" is not a valid URL.`
|
|||||||
include_error = ` must contain substring "%s".`
|
include_error = ` must contain substring "%s".`
|
||||||
glob_pattern_error = ` glob pattern is invalid: %s.`
|
glob_pattern_error = ` glob pattern is invalid: %s.`
|
||||||
regex_pattern_error = ` regex pattern is invalid: %s.`
|
regex_pattern_error = ` regex pattern is invalid: %s.`
|
||||||
username_error = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.`
|
username_error = ` can only contain alphanumeric characters ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric characters, and consecutive non-alphanumeric characters are also forbidden.`
|
||||||
invalid_group_team_map_error = ` mapping is invalid: %s`
|
invalid_group_team_map_error = ` mapping is invalid: %s`
|
||||||
unknown_error = Unknown error:
|
unknown_error = Unknown error:
|
||||||
captcha_incorrect = The CAPTCHA code is incorrect.
|
captcha_incorrect = The CAPTCHA code is incorrect.
|
||||||
@ -613,17 +613,17 @@ username_has_not_been_changed = Username has not been changed
|
|||||||
repo_name_been_taken = The repository name is already used.
|
repo_name_been_taken = The repository name is already used.
|
||||||
repository_force_private = Force Private is enabled: private repositories cannot be made public.
|
repository_force_private = Force Private is enabled: private repositories cannot be made public.
|
||||||
repository_files_already_exist = Files already exist for this repository. Contact the system administrator.
|
repository_files_already_exist = Files already exist for this repository. Contact the system administrator.
|
||||||
repository_files_already_exist.adopt = Files already exist for this repository and can only be Adopted.
|
repository_files_already_exist.adopt = Files already exist for this repository and can only be adopted.
|
||||||
repository_files_already_exist.delete = Files already exist for this repository. You must delete them.
|
repository_files_already_exist.delete = Files already exist for this repository. You must delete them.
|
||||||
repository_files_already_exist.adopt_or_delete = Files already exist for this repository. Either adopt them or delete them.
|
repository_files_already_exist.adopt_or_delete = Files already exist for this repository. Either adopt them or delete them.
|
||||||
visit_rate_limit = Remote visit addressed rate limitation.
|
visit_rate_limit = Remote visit addressed rate limitation.
|
||||||
2fa_auth_required = Remote visit required two factors authentication.
|
2fa_auth_required = Remote visit required two-factor authentication.
|
||||||
org_name_been_taken = The organization name is already taken.
|
org_name_been_taken = The organization name is already taken.
|
||||||
team_name_been_taken = The team name is already taken.
|
team_name_been_taken = The team name is already taken.
|
||||||
team_no_units_error = Allow access to at least one repository section.
|
team_no_units_error = Allow access to at least one repository section.
|
||||||
email_been_used = The email address is already used.
|
email_been_used = The email address is already used.
|
||||||
email_invalid = The email address is invalid.
|
email_invalid = The email address is invalid.
|
||||||
email_domain_is_not_allowed = The domain of user email <b>%s</b> conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST. Please ensure your operation is expected.
|
email_domain_is_not_allowed = The domain of user email address <b>%s</b> conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST. Please ensure your operation is expected.
|
||||||
openid_been_used = The OpenID address "%s" is already used.
|
openid_been_used = The OpenID address "%s" is already used.
|
||||||
username_password_incorrect = Username or password is incorrect.
|
username_password_incorrect = Username or password is incorrect.
|
||||||
password_complexity = Password does not pass complexity requirements:
|
password_complexity = Password does not pass complexity requirements:
|
||||||
@ -648,14 +648,14 @@ invalid_ssh_key = Cannot verify your SSH key: %s
|
|||||||
invalid_gpg_key = Cannot verify your GPG key: %s
|
invalid_gpg_key = Cannot verify your GPG key: %s
|
||||||
invalid_ssh_principal = Invalid principal: %s
|
invalid_ssh_principal = Invalid principal: %s
|
||||||
must_use_public_key = The key you provided is a private key. Please do not upload your private key anywhere. Use your public key instead.
|
must_use_public_key = The key you provided is a private key. Please do not upload your private key anywhere. Use your public key instead.
|
||||||
unable_verify_ssh_key = "Cannot verify the SSH key, double-check it for mistakes."
|
unable_verify_ssh_key = "Cannot verify the SSH key. Double-check it for mistakes."
|
||||||
auth_failed = Authentication failed: %v
|
auth_failed = Authentication failed: %v
|
||||||
|
|
||||||
still_own_repo = "Your account owns one or more repositories, delete or transfer them first."
|
still_own_repo = "Your account owns one or more repositories. Delete or transfer them first."
|
||||||
still_has_org = "Your account is a member of one or more organizations, leave them first."
|
still_has_org = "Your account is a member of one or more organizations. Leave them first."
|
||||||
still_own_packages = "Your account owns one or more packages, delete them first."
|
still_own_packages = "Your account owns one or more packages. Delete them first."
|
||||||
org_still_own_repo = "This organization still owns one or more repositories, delete or transfer them first."
|
org_still_own_repo = "This organization still owns one or more repositories. Delete or transfer them first."
|
||||||
org_still_own_packages = "This organization still owns one or more packages, delete them first."
|
org_still_own_packages = "This organization still owns one or more packages. Delete them first."
|
||||||
|
|
||||||
target_branch_not_exist = Target branch does not exist.
|
target_branch_not_exist = Target branch does not exist.
|
||||||
target_ref_not_exist = Target ref does not exist %s
|
target_ref_not_exist = Target ref does not exist %s
|
||||||
@ -686,7 +686,7 @@ settings = User Settings
|
|||||||
|
|
||||||
form.name_reserved = The username "%s" is reserved.
|
form.name_reserved = The username "%s" is reserved.
|
||||||
form.name_pattern_not_allowed = The pattern "%s" is not allowed in a username.
|
form.name_pattern_not_allowed = The pattern "%s" is not allowed in a username.
|
||||||
form.name_chars_not_allowed = User name "%s" contains invalid characters.
|
form.name_chars_not_allowed = Username "%s" contains invalid characters.
|
||||||
|
|
||||||
block.block = Block
|
block.block = Block
|
||||||
block.block.user = Block user
|
block.block.user = Block user
|
||||||
@ -703,7 +703,7 @@ block.info_3 = send you notifications by @mentioning your username
|
|||||||
block.info_4 = inviting you as a collaborator to their repositories
|
block.info_4 = inviting you as a collaborator to their repositories
|
||||||
block.info_5 = starring, forking or watching on repositories
|
block.info_5 = starring, forking or watching on repositories
|
||||||
block.info_6 = opening and commenting on issues or pull requests
|
block.info_6 = opening and commenting on issues or pull requests
|
||||||
block.info_7 = reacting on your comments in issues or pull requests
|
block.info_7 = reacting to your comments in issues or pull requests
|
||||||
block.user_to_block = User to block
|
block.user_to_block = User to block
|
||||||
block.note = Note
|
block.note = Note
|
||||||
block.note.title = Optional note:
|
block.note.title = Optional note:
|
||||||
@ -754,7 +754,7 @@ cancel = Cancel
|
|||||||
language = Language
|
language = Language
|
||||||
ui = Theme
|
ui = Theme
|
||||||
hidden_comment_types = Hidden comment types
|
hidden_comment_types = Hidden comment types
|
||||||
hidden_comment_types_description = Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "{user} added/removed {label}" comments.
|
hidden_comment_types_description = Comment types checked here will not be shown on issue pages. Checking "Label", for example, removes all "{user} added/removed {label}" comments.
|
||||||
hidden_comment_types.ref_tooltip = Comments where this issue was referenced from another issue/commit/…
|
hidden_comment_types.ref_tooltip = Comments where this issue was referenced from another issue/commit/…
|
||||||
hidden_comment_types.issue_ref_tooltip = Comments where the user changes the branch/tag associated with the issue
|
hidden_comment_types.issue_ref_tooltip = Comments where the user changes the branch/tag associated with the issue
|
||||||
comment_type_group_reference = Reference
|
comment_type_group_reference = Reference
|
||||||
@ -802,15 +802,15 @@ manage_themes = Select default theme
|
|||||||
manage_openid = Manage OpenID Addresses
|
manage_openid = Manage OpenID Addresses
|
||||||
email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.
|
email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.
|
||||||
theme_desc = This will be your default theme across the site.
|
theme_desc = This will be your default theme across the site.
|
||||||
theme_colorblindness_help = Colorblindness Theme Support
|
theme_colorblindness_help = Color blindness Theme Support
|
||||||
theme_colorblindness_prompt = Gitea just gets some themes with basic colorblindness support, which only have a few colors defined. The work is still in progress. More improvements could be done by defining more colors in the theme CSS files.
|
theme_colorblindness_prompt = Gitea only has a few themes with basic color blindness support, which only have a few colors defined. The work is still in progress. More improvements could be made by defining more colors in the theme CSS files.
|
||||||
primary = Primary
|
primary = Primary
|
||||||
activated = Activated
|
activated = Activated
|
||||||
requires_activation = Requires activation
|
requires_activation = Requires activation
|
||||||
primary_email = Make Primary
|
primary_email = Make Primary
|
||||||
activate_email = Send Activation
|
activate_email = Send Activation
|
||||||
activations_pending = Activations Pending
|
activations_pending = Activations Pending
|
||||||
can_not_add_email_activations_pending = There is a pending activation, try again in a few minutes if you want to add a new email.
|
can_not_add_email_activations_pending = There is a pending activation. Try again in a few minutes if you want to add a new email address.
|
||||||
delete_email = Remove
|
delete_email = Remove
|
||||||
email_deletion = Remove Email Address
|
email_deletion = Remove Email Address
|
||||||
email_deletion_desc = This email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue?
|
email_deletion_desc = This email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue?
|
||||||
@ -856,7 +856,7 @@ gpg_key_matched_identities_long=The embedded identities in this key match the fo
|
|||||||
gpg_key_verified=Verified Key
|
gpg_key_verified=Verified Key
|
||||||
gpg_key_verified_long=Key has been verified with a token and can be used to verify commits matching any activated email addresses for this user in addition to any matched identities for this key.
|
gpg_key_verified_long=Key has been verified with a token and can be used to verify commits matching any activated email addresses for this user in addition to any matched identities for this key.
|
||||||
gpg_key_verify=Verify
|
gpg_key_verify=Verify
|
||||||
gpg_invalid_token_signature = The provided GPG key, signature and token do not match or token is out-of-date.
|
gpg_invalid_token_signature = The provided GPG key, signature and token do not match, or the token is out-of-date.
|
||||||
gpg_token_required = You must provide a signature for the below token
|
gpg_token_required = You must provide a signature for the below token
|
||||||
gpg_token = Token
|
gpg_token = Token
|
||||||
gpg_token_help = You can generate a signature using:
|
gpg_token_help = You can generate a signature using:
|
||||||
@ -866,7 +866,7 @@ verify_gpg_key_success = GPG key "%s" has been verified.
|
|||||||
ssh_key_verified=Verified Key
|
ssh_key_verified=Verified Key
|
||||||
ssh_key_verified_long=Key has been verified with a token and can be used to verify commits matching any activated email addresses for this user.
|
ssh_key_verified_long=Key has been verified with a token and can be used to verify commits matching any activated email addresses for this user.
|
||||||
ssh_key_verify=Verify
|
ssh_key_verify=Verify
|
||||||
ssh_invalid_token_signature = The provided SSH key, signature or token do not match or token is out-of-date.
|
ssh_invalid_token_signature = The provided SSH key, signature or token do not match, or the token is out-of-date.
|
||||||
ssh_token_required = You must provide a signature for the below token
|
ssh_token_required = You must provide a signature for the below token
|
||||||
ssh_token = Token
|
ssh_token = Token
|
||||||
ssh_token_help = You can generate a signature using:
|
ssh_token_help = You can generate a signature using:
|
||||||
@ -887,7 +887,7 @@ gpg_key_deletion = Remove GPG Key
|
|||||||
ssh_principal_deletion = Remove SSH Certificate Principal
|
ssh_principal_deletion = Remove SSH Certificate Principal
|
||||||
ssh_key_deletion_desc = Removing an SSH key revokes its access to your account. Continue?
|
ssh_key_deletion_desc = Removing an SSH key revokes its access to your account. Continue?
|
||||||
gpg_key_deletion_desc = Removing a GPG key un-verifies commits signed by it. Continue?
|
gpg_key_deletion_desc = Removing a GPG key un-verifies commits signed by it. Continue?
|
||||||
ssh_principal_deletion_desc = Removing a SSH Certificate Principal revokes its access to your account. Continue?
|
ssh_principal_deletion_desc = Removing an SSH Certificate Principal revokes its access to your account. Continue?
|
||||||
ssh_key_deletion_success = The SSH key has been removed.
|
ssh_key_deletion_success = The SSH key has been removed.
|
||||||
gpg_key_deletion_success = The GPG key has been removed.
|
gpg_key_deletion_success = The GPG key has been removed.
|
||||||
ssh_principal_deletion_success = The principal has been removed.
|
ssh_principal_deletion_success = The principal has been removed.
|
||||||
@ -949,7 +949,7 @@ create_oauth2_application_button = Create Application
|
|||||||
create_oauth2_application_success = You have successfully created a new OAuth2 application.
|
create_oauth2_application_success = You have successfully created a new OAuth2 application.
|
||||||
update_oauth2_application_success = You have successfully updated the OAuth2 application.
|
update_oauth2_application_success = You have successfully updated the OAuth2 application.
|
||||||
oauth2_application_name = Application Name
|
oauth2_application_name = Application Name
|
||||||
oauth2_confidential_client = Confidential Client. Select for apps that keep the secret confidential, such as web apps. Do not select for native apps including desktop and mobile apps.
|
oauth2_confidential_client = Confidential Client. Select for apps that keep the secret confidential, such as web apps. Do not select for native apps, including desktop and mobile apps.
|
||||||
oauth2_skip_secondary_authorization = Skip authorization for public clients after granting access once. <strong>May pose a security risk.</strong>
|
oauth2_skip_secondary_authorization = Skip authorization for public clients after granting access once. <strong>May pose a security risk.</strong>
|
||||||
oauth2_redirect_uris = Redirect URIs. Please use a new line for every URI.
|
oauth2_redirect_uris = Redirect URIs. Please use a new line for every URI.
|
||||||
save_application = Save
|
save_application = Save
|
||||||
@ -964,10 +964,10 @@ oauth2_application_remove_description = Removing an OAuth2 application will prev
|
|||||||
oauth2_application_locked = Gitea pre-registers some OAuth2 applications on startup if enabled in config. To prevent unexpected behavior, these can neither be edited nor removed. Please refer to the OAuth2 documentation for more information.
|
oauth2_application_locked = Gitea pre-registers some OAuth2 applications on startup if enabled in config. To prevent unexpected behavior, these can neither be edited nor removed. Please refer to the OAuth2 documentation for more information.
|
||||||
|
|
||||||
authorized_oauth2_applications = Authorized OAuth2 Applications
|
authorized_oauth2_applications = Authorized OAuth2 Applications
|
||||||
authorized_oauth2_applications_description = You have granted access to your personal Gitea account to these third party applications. Please revoke access for applications you no longer need.
|
authorized_oauth2_applications_description = You have granted access to your personal Gitea account to these third-party applications. Please revoke access for applications you no longer need.
|
||||||
revoke_key = Revoke
|
revoke_key = Revoke
|
||||||
revoke_oauth2_grant = Revoke Access
|
revoke_oauth2_grant = Revoke Access
|
||||||
revoke_oauth2_grant_description = Revoking access for this third party application will prevent this application from accessing your data. Are you sure?
|
revoke_oauth2_grant_description = Revoking access for this third-party application will prevent this application from accessing your data. Are you sure?
|
||||||
revoke_oauth2_grant_success = Access revoked successfully.
|
revoke_oauth2_grant_success = Access revoked successfully.
|
||||||
|
|
||||||
twofa_desc = To protect your account against password theft, you can use a smartphone or another device for receiving time-based one-time passwords ("TOTP").
|
twofa_desc = To protect your account against password theft, you can use a smartphone or another device for receiving time-based one-time passwords ("TOTP").
|
||||||
@ -977,7 +977,7 @@ twofa_not_enrolled = Your account is not currently enrolled in two-factor authen
|
|||||||
twofa_disable = Disable Two-Factor Authentication
|
twofa_disable = Disable Two-Factor Authentication
|
||||||
twofa_scratch_token_regenerate = Regenerate Single-Use Recovery Key
|
twofa_scratch_token_regenerate = Regenerate Single-Use Recovery Key
|
||||||
twofa_scratch_token_regenerated = Your single-use recovery key is now %s. Store it in a safe place, as it will not be shown again.
|
twofa_scratch_token_regenerated = Your single-use recovery key is now %s. Store it in a safe place, as it will not be shown again.
|
||||||
twofa_enroll = Enroll into Two-Factor Authentication
|
twofa_enroll = Enroll in Two-Factor Authentication
|
||||||
twofa_disable_note = You can disable two-factor authentication if needed.
|
twofa_disable_note = You can disable two-factor authentication if needed.
|
||||||
twofa_disable_desc = Disabling two-factor authentication will make your account less secure. Continue?
|
twofa_disable_desc = Disabling two-factor authentication will make your account less secure. Continue?
|
||||||
regenerate_scratch_token_desc = If you misplaced your recovery key or have already used it to sign in, you can reset it here.
|
regenerate_scratch_token_desc = If you misplaced your recovery key or have already used it to sign in, you can reset it here.
|
||||||
@ -993,7 +993,7 @@ webauthn_desc = Security keys are hardware devices containing cryptographic keys
|
|||||||
webauthn_register_key = Add Security Key
|
webauthn_register_key = Add Security Key
|
||||||
webauthn_nickname = Nickname
|
webauthn_nickname = Nickname
|
||||||
webauthn_delete_key = Remove Security Key
|
webauthn_delete_key = Remove Security Key
|
||||||
webauthn_delete_key_desc = If you remove a security key you can no longer sign in with it. Continue?
|
webauthn_delete_key_desc = If you remove a security key, you can no longer sign in with it. Continue?
|
||||||
webauthn_key_loss_warning = If you lose your security keys, you will lose access to your account.
|
webauthn_key_loss_warning = If you lose your security keys, you will lose access to your account.
|
||||||
webauthn_alternative_tip = You may want to configure an additional authentication method.
|
webauthn_alternative_tip = You may want to configure an additional authentication method.
|
||||||
|
|
||||||
@ -1110,7 +1110,7 @@ mirror_address_protocol_invalid = The provided URL is invalid. Only http(s):// o
|
|||||||
mirror_lfs = Large File Storage (LFS)
|
mirror_lfs = Large File Storage (LFS)
|
||||||
mirror_lfs_desc = Activate mirroring of LFS data.
|
mirror_lfs_desc = Activate mirroring of LFS data.
|
||||||
mirror_lfs_endpoint = LFS Endpoint
|
mirror_lfs_endpoint = LFS Endpoint
|
||||||
mirror_lfs_endpoint_desc = Sync will attempt to use the clone url to <a target="_blank" rel="noopener noreferrer" href="%s">determine the LFS server</a>. You can also specify a custom endpoint if the repository LFS data is stored somewhere else.
|
mirror_lfs_endpoint_desc = Sync will attempt to use the clone URL to <a target="_blank" rel="noopener noreferrer" href="%s">determine the LFS server</a>. You can also specify a custom endpoint if the repository LFS data is stored somewhere else.
|
||||||
mirror_last_synced = Last Synchronized
|
mirror_last_synced = Last Synchronized
|
||||||
mirror_password_placeholder = (Unchanged)
|
mirror_password_placeholder = (Unchanged)
|
||||||
mirror_password_blank_placeholder = (Unset)
|
mirror_password_blank_placeholder = (Unset)
|
||||||
@ -1123,7 +1123,7 @@ stars = Stars
|
|||||||
reactions_more = and %d more
|
reactions_more = and %d more
|
||||||
unit_disabled = The site administrator has disabled this repository section.
|
unit_disabled = The site administrator has disabled this repository section.
|
||||||
language_other = Other
|
language_other = Other
|
||||||
adopt_search = Enter username to search for unadopted repositories... (leave blank to find all)
|
adopt_search = Enter username to search for unadopted repositories… (leave blank to find all)
|
||||||
adopt_preexisting_label = Adopt Files
|
adopt_preexisting_label = Adopt Files
|
||||||
adopt_preexisting = Adopt pre-existing files
|
adopt_preexisting = Adopt pre-existing files
|
||||||
adopt_preexisting_content = Create repository from %s
|
adopt_preexisting_content = Create repository from %s
|
||||||
@ -1165,8 +1165,8 @@ template.issue_labels = Issue Labels
|
|||||||
template.one_item = Must select at least one template item
|
template.one_item = Must select at least one template item
|
||||||
template.invalid = Must select a template repository
|
template.invalid = Must select a template repository
|
||||||
|
|
||||||
archive.title = This repo is archived. You can view files and clone it. You cannot open issues, pull requests or push a commit.
|
archive.title = This repo is archived. You can view files and clone it. You cannot open issues or pull requests or push a commit.
|
||||||
archive.title_date = This repository has been archived on %s. You can view files and clone it. You cannot open issues, pull requests or push a commit.
|
archive.title_date = This repository has been archived on %s. You can view files and clone it. You cannot open issues or pull requests or push a commit.
|
||||||
archive.issue.nocomment = This repo is archived. You cannot comment on issues.
|
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.pull.nocomment = This repo is archived. You cannot comment on pull requests.
|
||||||
|
|
||||||
@ -1183,7 +1183,7 @@ migrate_options_lfs = Migrate LFS files
|
|||||||
migrate_options_lfs_endpoint.label = LFS Endpoint
|
migrate_options_lfs_endpoint.label = LFS Endpoint
|
||||||
migrate_options_lfs_endpoint.description = Migration will attempt to use your Git remote to <a target="_blank" rel="noopener noreferrer" href="%s">determine the LFS server</a>. You can also specify a custom endpoint if the repository LFS data is stored somewhere else.
|
migrate_options_lfs_endpoint.description = Migration will attempt to use your Git remote to <a target="_blank" rel="noopener noreferrer" href="%s">determine the LFS server</a>. You can also specify a custom endpoint if the repository LFS data is stored somewhere else.
|
||||||
migrate_options_lfs_endpoint.description.local = A local server path is supported too.
|
migrate_options_lfs_endpoint.description.local = A local server path is supported too.
|
||||||
migrate_options_lfs_endpoint.placeholder = If left blank, the endpoint will be derived from the clone URL
|
migrate_options_lfs_endpoint.placeholder = If left blank, the endpoint will be derived from the clone URL.
|
||||||
migrate_items = Migration Items
|
migrate_items = Migration Items
|
||||||
migrate_items_wiki = Wiki
|
migrate_items_wiki = Wiki
|
||||||
migrate_items_milestones = Milestones
|
migrate_items_milestones = Milestones
|
||||||
@ -1195,10 +1195,10 @@ migrate_items_releases = Releases
|
|||||||
migrate_repo = Migrate Repository
|
migrate_repo = Migrate Repository
|
||||||
migrate.clone_address = Migrate / Clone From URL
|
migrate.clone_address = Migrate / Clone From URL
|
||||||
migrate.clone_address_desc = The HTTP(S) or Git 'clone' URL of an existing repository
|
migrate.clone_address_desc = The HTTP(S) or Git 'clone' URL of an existing repository
|
||||||
migrate.github_token_desc = You can put one or more tokens here separated by commas to make migrating faster by circumventing the GitHub API rate limit. WARNING: Abusing this feature may violate the service provider's policy and may lead to getting your account(s) blocked.
|
migrate.github_token_desc = You can put one or more tokens here, separated by commas, to make migrating faster by circumventing the GitHub API rate limit. WARNING: Abusing this feature may violate the service provider's policy and may lead to getting your account(s) blocked.
|
||||||
migrate.clone_local_path = or a local server path
|
migrate.clone_local_path = or a local server path
|
||||||
migrate.permission_denied = You are not allowed to import local repositories.
|
migrate.permission_denied = You are not allowed to import local repositories.
|
||||||
migrate.permission_denied_blocked = You cannot import from disallowed hosts, please ask the admin to check ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS settings.
|
migrate.permission_denied_blocked = You cannot import from disallowed hosts. Please ask the admin to check ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS settings.
|
||||||
migrate.invalid_local_path = "The local path is invalid. It doesn't exist or is not a directory."
|
migrate.invalid_local_path = "The local path is invalid. It doesn't exist or is not a directory."
|
||||||
migrate.invalid_lfs_endpoint = The LFS endpoint is not valid.
|
migrate.invalid_lfs_endpoint = The LFS endpoint is not valid.
|
||||||
migrate.failed = Migration failed: %v
|
migrate.failed = Migration failed: %v
|
||||||
@ -1206,7 +1206,7 @@ migrate.migrate_items_options = Access Token is required to migrate additional i
|
|||||||
migrated_from = Migrated from <a href="%[1]s">%[2]s</a>
|
migrated_from = Migrated from <a href="%[1]s">%[2]s</a>
|
||||||
migrated_from_fake = Migrated From %[1]s
|
migrated_from_fake = Migrated From %[1]s
|
||||||
migrate.migrate = Migrate From %s
|
migrate.migrate = Migrate From %s
|
||||||
migrate.migrating = Migrating from <b>%s</b> ...
|
migrate.migrating = Migrating from <b>%s</b>…
|
||||||
migrate.migrating_failed = Migrating from <b>%s</b> failed.
|
migrate.migrating_failed = Migrating from <b>%s</b> failed.
|
||||||
migrate.migrating_failed.error = Failed to migrate: %s
|
migrate.migrating_failed.error = Failed to migrate: %s
|
||||||
migrate.migrating_failed_no_addr = Migration failed.
|
migrate.migrating_failed_no_addr = Migration failed.
|
||||||
@ -1255,7 +1255,7 @@ clone_this_repo = Clone this repository
|
|||||||
cite_this_repo = Cite this repository
|
cite_this_repo = Cite this repository
|
||||||
create_new_repo_command = Creating a new repository on the command line
|
create_new_repo_command = Creating a new repository on the command line
|
||||||
push_exist_repo = Pushing an existing repository from the command line
|
push_exist_repo = Pushing an existing repository from the command line
|
||||||
empty_message = This repository does not contain any content.
|
empty_message = This repository does not have any content.
|
||||||
broken_message = The Git data underlying this repository cannot be read. Contact the administrator of this instance or delete this repository.
|
broken_message = The Git data underlying this repository cannot be read. Contact the administrator of this instance or delete this repository.
|
||||||
no_branch = This repository doesn't have any branches.
|
no_branch = This repository doesn't have any branches.
|
||||||
|
|
||||||
@ -1274,7 +1274,7 @@ projects = Projects
|
|||||||
packages = Packages
|
packages = Packages
|
||||||
actions = Actions
|
actions = Actions
|
||||||
labels = Labels
|
labels = Labels
|
||||||
org_labels_desc = Organization level labels that can be used with <strong>all repositories</strong> under this organization
|
org_labels_desc = Organization-level labels that can be used with <strong>all repositories</strong> under this organization
|
||||||
org_labels_desc_manage = manage
|
org_labels_desc_manage = manage
|
||||||
|
|
||||||
milestone = Milestone
|
milestone = Milestone
|
||||||
@ -1371,17 +1371,17 @@ editor.new_branch_name_desc = New branch name…
|
|||||||
editor.cancel = Cancel
|
editor.cancel = Cancel
|
||||||
editor.filename_cannot_be_empty = The filename cannot be empty.
|
editor.filename_cannot_be_empty = The filename cannot be empty.
|
||||||
editor.filename_is_invalid = The filename is invalid: "%s".
|
editor.filename_is_invalid = The filename is invalid: "%s".
|
||||||
editor.commit_email = Commit email
|
editor.commit_email = Commit email address
|
||||||
editor.invalid_commit_email = The email for the commit is invalid.
|
editor.invalid_commit_email = The email address for the commit is invalid.
|
||||||
editor.branch_does_not_exist = Branch "%s" does not exist in this repository.
|
editor.branch_does_not_exist = Branch "%s" does not exist in this repository.
|
||||||
editor.branch_already_exists = Branch "%s" already exists in this repository.
|
editor.branch_already_exists = Branch "%s" already exists in this repository.
|
||||||
editor.directory_is_a_file = Directory name "%s" is already used as a filename in this repository.
|
editor.directory_is_a_file = Directory name "%s" is already used as a filename in this repository.
|
||||||
editor.file_is_a_symlink = `"%s" is a symbolic link. Symbolic links cannot be edited in the web editor`
|
editor.file_is_a_symlink = `"%s" is a symbolic link. Symbolic links cannot be edited in the web editor.`
|
||||||
editor.filename_is_a_directory = Filename "%s" is already used as a directory name in this repository.
|
editor.filename_is_a_directory = Filename "%s" is already used as a directory name in this repository.
|
||||||
editor.file_modifying_no_longer_exists = The file being modified, "%s", no longer exists in this repository.
|
editor.file_modifying_no_longer_exists = The file being modified, "%s", no longer exists in this repository.
|
||||||
editor.file_changed_while_editing = The file contents have changed since you started editing. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit Changes again</strong> to overwrite them.
|
editor.file_changed_while_editing = The file contents have changed since you started editing. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit Changes again</strong> to overwrite them.
|
||||||
editor.file_already_exists = A file named "%s" already exists in this repository.
|
editor.file_already_exists = A file named "%s" already exists in this repository.
|
||||||
editor.commit_id_not_matching = The Commit ID does not match the ID when you began editing. Commit into a patch branch and then merge.
|
editor.commit_id_not_matching = The Commit ID does not match the ID when you began editing. Commit into a patch branch and then merge.
|
||||||
editor.push_out_of_date = The push appears to be out of date.
|
editor.push_out_of_date = The push appears to be out of date.
|
||||||
editor.commit_empty_file_header = Commit an empty file
|
editor.commit_empty_file_header = Commit an empty file
|
||||||
editor.commit_empty_file_text = The file you're about to commit is empty. Proceed?
|
editor.commit_empty_file_text = The file you're about to commit is empty. Proceed?
|
||||||
@ -1407,7 +1407,7 @@ editor.fork_create_description = You cannot edit this repository directly. Inste
|
|||||||
editor.fork_edit_description = You cannot edit this repository directly. The changes will be written to your fork <b>%s</b>, so you can create a pull request.
|
editor.fork_edit_description = You cannot edit this repository directly. The changes will be written to your fork <b>%s</b>, so you can create a pull request.
|
||||||
editor.fork_not_editable = You have forked this repository but your fork is not editable.
|
editor.fork_not_editable = You have forked this repository but your fork is not editable.
|
||||||
editor.fork_failed_to_push_branch = Failed to push branch %s to your repository.
|
editor.fork_failed_to_push_branch = Failed to push branch %s to your repository.
|
||||||
editor.fork_branch_exists = Branch "%s" already exists in your fork, please choose a new branch name.
|
editor.fork_branch_exists = Branch "%s" already exists in your fork. Please choose a new branch name.
|
||||||
|
|
||||||
commits.desc = Browse source code change history.
|
commits.desc = Browse source code change history.
|
||||||
commits.commits = Commits
|
commits.commits = Commits
|
||||||
@ -1509,7 +1509,7 @@ issues.new.clear_assignees = Clear assignees
|
|||||||
issues.new.no_assignees = No Assignees
|
issues.new.no_assignees = No Assignees
|
||||||
issues.new.no_reviewers = No Reviewers
|
issues.new.no_reviewers = No Reviewers
|
||||||
issues.new.blocked_user = Cannot create issue because you are blocked by the repository owner.
|
issues.new.blocked_user = Cannot create issue because you are blocked by the repository owner.
|
||||||
issues.edit.already_changed = Unable to save changes to the issue. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes
|
issues.edit.already_changed = Unable to save changes to the issue. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes.
|
||||||
issues.edit.blocked_user = Cannot edit content because you are blocked by the poster or repository owner.
|
issues.edit.blocked_user = Cannot edit content because you are blocked by the poster or repository owner.
|
||||||
issues.choose.get_started = Get Started
|
issues.choose.get_started = Get Started
|
||||||
issues.choose.open_external_link = Open
|
issues.choose.open_external_link = Open
|
||||||
@ -1581,7 +1581,7 @@ issues.filter_type.reviewed_by_you = Reviewed by you
|
|||||||
issues.filter_sort = Sort
|
issues.filter_sort = Sort
|
||||||
issues.filter_sort.latest = Newest
|
issues.filter_sort.latest = Newest
|
||||||
issues.filter_sort.oldest = Oldest
|
issues.filter_sort.oldest = Oldest
|
||||||
issues.filter_sort.recentupdate = Recently updated
|
issues.filter_sort.recentupdate = Most recently updated
|
||||||
issues.filter_sort.leastupdate = Least recently updated
|
issues.filter_sort.leastupdate = Least recently updated
|
||||||
issues.filter_sort.mostcomment = Most commented
|
issues.filter_sort.mostcomment = Most commented
|
||||||
issues.filter_sort.leastcomment = Least commented
|
issues.filter_sort.leastcomment = Least commented
|
||||||
@ -1781,9 +1781,9 @@ issues.dependency.pr_closing_blockedby = Closing this pull request is blocked by
|
|||||||
issues.dependency.issue_closing_blockedby = Closing this issue is blocked by the following issues
|
issues.dependency.issue_closing_blockedby = Closing this issue is blocked by the following issues
|
||||||
issues.dependency.issue_close_blocks = This issue blocks closing of the following issues
|
issues.dependency.issue_close_blocks = This issue blocks closing of the following issues
|
||||||
issues.dependency.pr_close_blocks = This pull request blocks closing of the following issues
|
issues.dependency.pr_close_blocks = This pull request blocks closing of the following issues
|
||||||
issues.dependency.issue_close_blocked = You need to close all issues blocking this issue before you can close it.
|
issues.dependency.issue_close_blocked = You need to close all issues that are blocking this issue before you can close it.
|
||||||
issues.dependency.issue_batch_close_blocked = "Cannot batch close issues that you choose, because issue #%d still has open dependencies"
|
issues.dependency.issue_batch_close_blocked = "Cannot batch close issues that you choose, because issue #%d still has open dependencies"
|
||||||
issues.dependency.pr_close_blocked = You need to close all issues blocking this pull request before you can merge it.
|
issues.dependency.pr_close_blocked = You need to close all issues that are blocking this pull request before you can merge it.
|
||||||
issues.dependency.blocks_short = Blocks
|
issues.dependency.blocks_short = Blocks
|
||||||
issues.dependency.blocked_by_short = Depends on
|
issues.dependency.blocked_by_short = Depends on
|
||||||
issues.dependency.remove_header = Remove Dependency
|
issues.dependency.remove_header = Remove Dependency
|
||||||
@ -1794,13 +1794,13 @@ issues.dependency.add_error_same_issue = You cannot make an issue depend on itse
|
|||||||
issues.dependency.add_error_dep_issue_not_exist = Dependent issue does not exist.
|
issues.dependency.add_error_dep_issue_not_exist = Dependent issue does not exist.
|
||||||
issues.dependency.add_error_dep_not_exist = Dependency does not exist.
|
issues.dependency.add_error_dep_not_exist = Dependency does not exist.
|
||||||
issues.dependency.add_error_dep_exists = Dependency already exists.
|
issues.dependency.add_error_dep_exists = Dependency already exists.
|
||||||
issues.dependency.add_error_cannot_create_circular = You cannot create a dependency with two issues blocking each other.
|
issues.dependency.add_error_cannot_create_circular = You cannot create a dependency with two issues that block each other.
|
||||||
issues.dependency.add_error_dep_not_same_repo = Both issues must be in the same repository.
|
issues.dependency.add_error_dep_not_same_repo = Both issues must be in the same repository.
|
||||||
issues.review.self.approval = You cannot approve your own pull request.
|
issues.review.self.approval = You cannot approve your own pull request.
|
||||||
issues.review.self.rejection = You cannot request changes on your own pull request.
|
issues.review.self.rejection = You cannot request changes on your own pull request.
|
||||||
issues.review.approve = "approved these changes %s"
|
issues.review.approve = "approved these changes %s"
|
||||||
issues.review.comment = "reviewed %s"
|
issues.review.comment = "reviewed %s"
|
||||||
issues.review.dismissed = "dismissed %s’s review %s"
|
issues.review.dismissed = "dismissed %s's review %s"
|
||||||
issues.review.dismissed_label = Dismissed
|
issues.review.dismissed_label = Dismissed
|
||||||
issues.review.left_comment = left a comment
|
issues.review.left_comment = left a comment
|
||||||
issues.review.content.empty = You need to leave a comment indicating the requested change(s).
|
issues.review.content.empty = You need to leave a comment indicating the requested change(s).
|
||||||
@ -1808,7 +1808,7 @@ issues.review.reject = "requested changes %s"
|
|||||||
issues.review.wait = "was requested for review %s"
|
issues.review.wait = "was requested for review %s"
|
||||||
issues.review.add_review_request = "requested review from %s %s"
|
issues.review.add_review_request = "requested review from %s %s"
|
||||||
issues.review.remove_review_request = "removed review request for %s %s"
|
issues.review.remove_review_request = "removed review request for %s %s"
|
||||||
issues.review.remove_review_request_self = "refused to review %s"
|
issues.review.remove_review_request_self = "declined to review %s"
|
||||||
issues.review.pending = Pending
|
issues.review.pending = Pending
|
||||||
issues.review.pending.tooltip = This comment is not currently visible to other users. To submit your pending comments, select "%s" -> "%s/%s/%s" at the top of the page.
|
issues.review.pending.tooltip = This comment is not currently visible to other users. To submit your pending comments, select "%s" -> "%s/%s/%s" at the top of the page.
|
||||||
issues.review.review = Review
|
issues.review.review = Review
|
||||||
@ -1830,7 +1830,7 @@ issues.review.requested = Review pending
|
|||||||
issues.review.rejected = Changes requested
|
issues.review.rejected = Changes requested
|
||||||
issues.review.stale = Updated since approval
|
issues.review.stale = Updated since approval
|
||||||
issues.review.unofficial = Uncounted approval
|
issues.review.unofficial = Uncounted approval
|
||||||
issues.assignee.error = Not all assignees was added due to an unexpected error.
|
issues.assignee.error = Not all assignees were added, due to an unexpected error.
|
||||||
issues.reference_issue.body = Body
|
issues.reference_issue.body = Body
|
||||||
issues.content_history.deleted = deleted
|
issues.content_history.deleted = deleted
|
||||||
issues.content_history.edited = edited
|
issues.content_history.edited = edited
|
||||||
@ -1847,7 +1847,7 @@ pulls.desc = Enable pull requests and code reviews.
|
|||||||
pulls.new = New Pull Request
|
pulls.new = New Pull Request
|
||||||
pulls.new.blocked_user = Cannot create pull request because you are blocked by the repository owner.
|
pulls.new.blocked_user = Cannot create pull request because you are blocked by the repository owner.
|
||||||
pulls.new.must_collaborator = You must be a collaborator to create pull request.
|
pulls.new.must_collaborator = You must be a collaborator to create pull request.
|
||||||
pulls.edit.already_changed = Unable to save changes to the pull request. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes
|
pulls.edit.already_changed = Unable to save changes to the pull request. It appears the content has already been changed by another user. Please refresh the page and try editing again to avoid overwriting their changes.
|
||||||
pulls.view = View Pull Request
|
pulls.view = View Pull Request
|
||||||
pulls.compare_changes = New Pull Request
|
pulls.compare_changes = New Pull Request
|
||||||
pulls.allow_edits_from_maintainers = Allow edits from maintainers
|
pulls.allow_edits_from_maintainers = Allow edits from maintainers
|
||||||
@ -1868,7 +1868,7 @@ pulls.show_all_commits = Show all commits
|
|||||||
pulls.show_changes_since_your_last_review = Show changes since your last review
|
pulls.show_changes_since_your_last_review = Show changes since your last review
|
||||||
pulls.showing_only_single_commit = Showing only changes of commit %[1]s
|
pulls.showing_only_single_commit = Showing only changes of commit %[1]s
|
||||||
pulls.showing_specified_commit_range = Showing only changes between %[1]s..%[2]s
|
pulls.showing_specified_commit_range = Showing only changes between %[1]s..%[2]s
|
||||||
pulls.select_commit_hold_shift_for_range = Select commit. Hold shift + click to select a range
|
pulls.select_commit_hold_shift_for_range = Select commit. Hold Shift and click to select a range.
|
||||||
pulls.review_only_possible_for_full_diff = Review is only possible when viewing the full diff
|
pulls.review_only_possible_for_full_diff = Review is only possible when viewing the full diff
|
||||||
pulls.filter_changes_by_commit = Filter by commit
|
pulls.filter_changes_by_commit = Filter by commit
|
||||||
pulls.nothing_to_compare = These branches are equal. There is no need to create a pull request.
|
pulls.nothing_to_compare = These branches are equal. There is no need to create a pull request.
|
||||||
@ -1897,7 +1897,7 @@ pulls.add_prefix = Add <strong>%s</strong> prefix
|
|||||||
pulls.remove_prefix = Remove <strong>%s</strong> prefix
|
pulls.remove_prefix = Remove <strong>%s</strong> prefix
|
||||||
pulls.data_broken = This pull request is broken due to missing fork information.
|
pulls.data_broken = This pull request is broken due to missing fork information.
|
||||||
pulls.files_conflicted = This pull request has changes conflicting with the target branch.
|
pulls.files_conflicted = This pull request has changes conflicting with the target branch.
|
||||||
pulls.is_checking = Checking for merge conflicts ...
|
pulls.is_checking = Checking for merge conflicts…
|
||||||
pulls.is_ancestor = "This branch is already included in the target branch. There is nothing to merge."
|
pulls.is_ancestor = "This branch is already included in the target branch. There is nothing to merge."
|
||||||
pulls.is_empty = "The changes on this branch are already on the target branch. This will be an empty commit."
|
pulls.is_empty = "The changes on this branch are already on the target branch. This will be an empty commit."
|
||||||
pulls.required_status_check_failed = Some required checks were not successful.
|
pulls.required_status_check_failed = Some required checks were not successful.
|
||||||
@ -1921,16 +1921,16 @@ pulls.reject_count_1 = "%d change request"
|
|||||||
pulls.reject_count_n = "%d change requests"
|
pulls.reject_count_n = "%d change requests"
|
||||||
pulls.waiting_count_1 = "%d waiting review"
|
pulls.waiting_count_1 = "%d waiting review"
|
||||||
pulls.waiting_count_n = "%d waiting reviews"
|
pulls.waiting_count_n = "%d waiting reviews"
|
||||||
pulls.wrong_commit_id = "commit id must be a commit id on the target branch"
|
pulls.wrong_commit_id = "commit ID must be a commit ID on the target branch"
|
||||||
|
|
||||||
pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled.
|
pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled.
|
||||||
pulls.no_merge_helper = Enable merge options in the repository settings or merge the pull request manually.
|
pulls.no_merge_helper = Enable merge options in the repository settings or merge the pull request manually.
|
||||||
pulls.no_merge_wip = This pull request cannot be merged because it is marked as being a work in progress.
|
pulls.no_merge_wip = This pull request cannot be merged because it is marked as being a work in progress.
|
||||||
pulls.no_merge_not_ready = This pull request is not ready to be merged, check review status and status checks.
|
pulls.no_merge_not_ready = This pull request is not ready to be merged. Check review status and status checks.
|
||||||
pulls.no_merge_access = You are not authorized to merge this pull request.
|
pulls.no_merge_access = You are not authorized to merge this pull request.
|
||||||
pulls.merge_pull_request = Create merge commit
|
pulls.merge_pull_request = Create merge commit
|
||||||
pulls.rebase_merge_pull_request = Rebase then fast-forward
|
pulls.rebase_merge_pull_request = Rebase, then fast-forward
|
||||||
pulls.rebase_merge_commit_pull_request = Rebase then create merge commit
|
pulls.rebase_merge_commit_pull_request = Rebase, then create merge commit
|
||||||
pulls.squash_merge_pull_request = Create squash commit
|
pulls.squash_merge_pull_request = Create squash commit
|
||||||
pulls.fast_forward_only_merge_pull_request = Fast-forward only
|
pulls.fast_forward_only_merge_pull_request = Fast-forward only
|
||||||
pulls.merge_manually = Manually merged
|
pulls.merge_manually = Manually merged
|
||||||
@ -1938,17 +1938,17 @@ pulls.merge_commit_id = The merge commit ID
|
|||||||
pulls.require_signed_wont_sign = The branch requires signed commits but this merge will not be signed
|
pulls.require_signed_wont_sign = The branch requires signed commits but this merge will not be signed
|
||||||
|
|
||||||
pulls.invalid_merge_option = You cannot use this merge option for this pull request.
|
pulls.invalid_merge_option = You cannot use this merge option for this pull request.
|
||||||
pulls.merge_conflict = Merge Failed: There was a conflict whilst merging. Hint: Try a different strategy
|
pulls.merge_conflict = Merge Failed: There was a conflict while merging. Hint: Try a different strategy.
|
||||||
pulls.merge_conflict_summary = Error Message
|
pulls.merge_conflict_summary = Error Message
|
||||||
pulls.rebase_conflict = Merge Failed: There was a conflict whilst rebasing commit: %[1]s. Hint: Try a different strategy
|
pulls.rebase_conflict = Merge Failed: There was a conflict while rebasing commit: %[1]s. Hint: Try a different strategy.
|
||||||
pulls.rebase_conflict_summary = Error Message
|
pulls.rebase_conflict_summary = Error Message
|
||||||
pulls.unrelated_histories = Merge Failed: The merge head and base do not share a common history. Hint: Try a different strategy
|
pulls.unrelated_histories = Merge Failed: The merge head and base do not share a common history. Hint: Try a different strategy.
|
||||||
pulls.merge_out_of_date = Merge Failed: Whilst generating the merge, the base was updated. Hint: Try again.
|
pulls.merge_out_of_date = Merge Failed: While generating the merge, the base was updated. Hint: Try again.
|
||||||
pulls.head_out_of_date = Merge Failed: Whilst generating the merge, the head was updated. Hint: Try again.
|
pulls.head_out_of_date = Merge Failed: While generating the merge, the head was updated. Hint: Try again.
|
||||||
pulls.has_merged = Failed: The pull request has been merged, you cannot merge again or change the target branch.
|
pulls.has_merged = Failed: The pull request has been merged. You cannot merge again or change the target branch.
|
||||||
pulls.push_rejected = Push Failed: The push was rejected. Review the Git Hooks for this repository.
|
pulls.push_rejected = Push Failed: The push was rejected. Review the Git Hooks for this repository.
|
||||||
pulls.push_rejected_summary = Full Rejection Message
|
pulls.push_rejected_summary = Full Rejection Message
|
||||||
pulls.push_rejected_no_message = Push Failed: The push was rejected but there was no remote message. Review the Git Hooks for this repository
|
pulls.push_rejected_no_message = Push Failed: The push was rejected but there was no remote message. Review the Git Hooks for this repository.
|
||||||
pulls.open_unmerged_pull_exists = `You cannot perform a reopen operation because there is a pending pull request (#%d) with identical properties.`
|
pulls.open_unmerged_pull_exists = `You cannot perform a reopen operation because there is a pending pull request (#%d) with identical properties.`
|
||||||
pulls.status_checking = Some checks are pending
|
pulls.status_checking = Some checks are pending
|
||||||
pulls.status_checks_success = All checks were successful
|
pulls.status_checks_success = All checks were successful
|
||||||
@ -1974,7 +1974,7 @@ pulls.cmd_instruction_merge_title = Merge
|
|||||||
pulls.cmd_instruction_merge_desc = Merge the changes and update on Gitea.
|
pulls.cmd_instruction_merge_desc = Merge the changes and update on Gitea.
|
||||||
pulls.cmd_instruction_merge_warning = Warning: This operation cannot merge pull request because "autodetect manual merge" is not enabled.
|
pulls.cmd_instruction_merge_warning = Warning: This operation cannot merge pull request because "autodetect manual merge" is not enabled.
|
||||||
pulls.clear_merge_message = Clear merge message
|
pulls.clear_merge_message = Clear merge message
|
||||||
pulls.clear_merge_message_hint = Clearing the merge message will only remove the commit message content and keep generated git trailers such as "Co-Authored-By …".
|
pulls.clear_merge_message_hint = Clearing the merge message will only remove the commit message content and keep generated git trailers such as "Co-Authored-By…".
|
||||||
|
|
||||||
pulls.auto_merge_button_when_succeed = (When checks succeed)
|
pulls.auto_merge_button_when_succeed = (When checks succeed)
|
||||||
pulls.auto_merge_when_succeed = Auto merge when all checks succeed
|
pulls.auto_merge_when_succeed = Auto merge when all checks succeed
|
||||||
@ -2040,7 +2040,7 @@ signing.wont_sign.nokey = There is no key available to sign this commit.
|
|||||||
signing.wont_sign.never = Commits are never signed.
|
signing.wont_sign.never = Commits are never signed.
|
||||||
signing.wont_sign.always = Commits are always signed.
|
signing.wont_sign.always = Commits are always signed.
|
||||||
signing.wont_sign.pubkey = The commit will not be signed because you do not have a public key associated with your account.
|
signing.wont_sign.pubkey = The commit will not be signed because you do not have a public key associated with your account.
|
||||||
signing.wont_sign.twofa = You must have two factor authentication enabled to have commits signed.
|
signing.wont_sign.twofa = You must have two-factor authentication enabled to have commits signed.
|
||||||
signing.wont_sign.parentsigned = The commit will not be signed as the parent commit is not signed.
|
signing.wont_sign.parentsigned = The commit will not be signed as the parent commit is not signed.
|
||||||
signing.wont_sign.basesigned = The merge will not be signed as the base commit is not signed.
|
signing.wont_sign.basesigned = The merge will not be signed as the base commit is not signed.
|
||||||
signing.wont_sign.headsigned = The merge will not be signed as the head commit is not signed.
|
signing.wont_sign.headsigned = The merge will not be signed as the head commit is not signed.
|
||||||
@ -2154,13 +2154,13 @@ contributors.contribution_type.additions = Additions
|
|||||||
contributors.contribution_type.deletions = Deletions
|
contributors.contribution_type.deletions = Deletions
|
||||||
|
|
||||||
settings = Settings
|
settings = Settings
|
||||||
settings.desc = Settings is where you can manage the settings for the repository
|
settings.desc = Settings is where you can manage the settings for the repository.
|
||||||
settings.options = Repository
|
settings.options = Repository
|
||||||
settings.public_access = Public Access
|
settings.public_access = Public Access
|
||||||
settings.public_access_desc = Configure public visitor's access permissions to override the defaults of this repository.
|
settings.public_access_desc = Configure public visitor's access permissions to override the defaults of this repository.
|
||||||
settings.public_access.docs.not_set = Not Set: no extra public access permission. The visitor's permission follows the repository's visibility and member permissions.
|
settings.public_access.docs.not_set = Not Set: no extra public access permission. The visitor's permission follows the repository's visibility and member permissions.
|
||||||
settings.public_access.docs.anonymous_read = Anonymous Read: users who are not logged in can access the unit with read permission.
|
settings.public_access.docs.anonymous_read = Anonymous Read: users who are not logged in can access the unit with read permission.
|
||||||
settings.public_access.docs.everyone_read = Everyone Read: all logged-in users can access the unit with read permission. Read permission of issues/pull-requests units also means users can create new issues/pull-requests.
|
settings.public_access.docs.everyone_read = Everyone Read: all logged-in users can access the unit with read permission. Read permission of issue/pull-request units also means users can create new issues/pull requests.
|
||||||
settings.public_access.docs.everyone_write = Everyone Write: all logged-in users have write permission to the unit. Only Wiki unit supports this permission.
|
settings.public_access.docs.everyone_write = Everyone Write: all logged-in users have write permission to the unit. Only Wiki unit supports this permission.
|
||||||
settings.collaboration = Collaborators
|
settings.collaboration = Collaborators
|
||||||
settings.collaboration.admin = Administrator
|
settings.collaboration.admin = Administrator
|
||||||
@ -2178,7 +2178,7 @@ settings.mirror_settings.docs.disabled_pull_mirror.instructions = Set up your pr
|
|||||||
settings.mirror_settings.docs.disabled_push_mirror.instructions = Set up your project to automatically pull commits, tags and branches from another repository.
|
settings.mirror_settings.docs.disabled_push_mirror.instructions = Set up your project to automatically pull commits, tags and branches from another repository.
|
||||||
settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Right now, this can only be done in the "New Migration" menu. For more information, please consult:
|
settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Right now, this can only be done in the "New Migration" menu. For more information, please consult:
|
||||||
settings.mirror_settings.docs.disabled_push_mirror.info = Push mirrors have been disabled by your site administrator.
|
settings.mirror_settings.docs.disabled_push_mirror.info = Push mirrors have been disabled by your site administrator.
|
||||||
settings.mirror_settings.docs.no_new_mirrors = Your repository is mirroring changes to or from another repository. Please keep in mind that you can't create any new mirrors at this time.
|
settings.mirror_settings.docs.no_new_mirrors = Your repository is mirroring changes to or from another repository. Please keep in mind that you currently can't create any new mirrors.
|
||||||
settings.mirror_settings.docs.can_still_use = Although you can't modify existing mirrors or create new ones, you may still use your existing mirror.
|
settings.mirror_settings.docs.can_still_use = Although you can't modify existing mirrors or create new ones, you may still use your existing mirror.
|
||||||
settings.mirror_settings.docs.pull_mirror_instructions = To set up a pull mirror, please consult:
|
settings.mirror_settings.docs.pull_mirror_instructions = To set up a pull mirror, please consult:
|
||||||
settings.mirror_settings.docs.more_information_if_disabled = You can find out more about push and pull mirrors here:
|
settings.mirror_settings.docs.more_information_if_disabled = You can find out more about push and pull mirrors here:
|
||||||
@ -2253,7 +2253,7 @@ settings.admin_indexer_commit_sha = Last Indexed SHA
|
|||||||
settings.admin_indexer_unindexed = Unindexed
|
settings.admin_indexer_unindexed = Unindexed
|
||||||
settings.reindex_button = Add to Reindex Queue
|
settings.reindex_button = Add to Reindex Queue
|
||||||
settings.reindex_requested=Reindex Requested
|
settings.reindex_requested=Reindex Requested
|
||||||
settings.admin_enable_close_issues_via_commit_in_any_branch = Close an issue via a commit made in a non default branch
|
settings.admin_enable_close_issues_via_commit_in_any_branch = Close an issue via a commit made in a non-default branch
|
||||||
settings.danger_zone = Danger Zone
|
settings.danger_zone = Danger Zone
|
||||||
settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name.
|
settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name.
|
||||||
settings.convert = Convert to Regular Repository
|
settings.convert = Convert to Regular Repository
|
||||||
@ -2275,7 +2275,7 @@ settings.transfer_abort_invalid = You cannot cancel a non existent repository tr
|
|||||||
settings.transfer_abort_success = The repository transfer to %s was successfully canceled.
|
settings.transfer_abort_success = The repository transfer to %s was successfully canceled.
|
||||||
settings.transfer_desc = Transfer this repository to a user or to an organization for which you have administrator rights.
|
settings.transfer_desc = Transfer this repository to a user or to an organization for which you have administrator rights.
|
||||||
settings.transfer_form_title = Enter the repository name as confirmation:
|
settings.transfer_form_title = Enter the repository name as confirmation:
|
||||||
settings.transfer_in_progress = There is currently an ongoing transfer. Please cancel it if you will like to transfer this repository to another user.
|
settings.transfer_in_progress = There is currently an ongoing transfer. Please cancel it if you would like to transfer this repository to another user.
|
||||||
settings.transfer_notices_1 = - You will lose access to the repository if you transfer it to an individual user.
|
settings.transfer_notices_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_notices_2 = - You will keep access to the repository if you transfer it to an organization that you (co-)own.
|
||||||
settings.transfer_notices_3 = - If the repository is private and is transferred to an individual user, this action makes sure that the user does have at least read permission (and changes permissions if necessary).
|
settings.transfer_notices_3 = - If the repository is private and is transferred to an individual user, this action makes sure that the user does have at least read permission (and changes permissions if necessary).
|
||||||
@ -2290,13 +2290,13 @@ settings.trust_model.default = Default Trust Model
|
|||||||
settings.trust_model.default.desc= Use the default repository trust model for this installation.
|
settings.trust_model.default.desc= Use the default repository trust model for this installation.
|
||||||
settings.trust_model.collaborator = Collaborator
|
settings.trust_model.collaborator = Collaborator
|
||||||
settings.trust_model.collaborator.long = Collaborator: Trust signatures by collaborators
|
settings.trust_model.collaborator.long = Collaborator: Trust signatures by collaborators
|
||||||
settings.trust_model.collaborator.desc = Valid signatures by collaborators of this repository will be marked "trusted" - (whether they match the committer or not). Otherwise, valid signatures will be marked "untrusted" if the signature matches the committer and "unmatched" if not.
|
settings.trust_model.collaborator.desc = Valid signatures by collaborators of this repository will be marked "trusted", whether they match the committer or not. Otherwise, valid signatures will be marked "untrusted" if the signature matches the committer and "unmatched" if not.
|
||||||
settings.trust_model.committer = Committer
|
settings.trust_model.committer = Committer
|
||||||
settings.trust_model.committer.long = Committer: Trust signatures that match committers (This matches GitHub and will force Gitea signed commits to have Gitea as the committer)
|
settings.trust_model.committer.long = Committer: Trust signatures that match committers. This matches GitHub's behavior and will force commits signed by Gitea to have Gitea as the committer.
|
||||||
settings.trust_model.committer.desc = Valid signatures will only be marked "trusted" if they match the committer, otherwise they will be marked "unmatched". This forces Gitea to be the committer on signed commits with the actual committer marked as Co-authored-by: and Co-committed-by: trailer in the commit. The default Gitea key must match a User in the database.
|
settings.trust_model.committer.desc = Valid signatures will only be marked "trusted" if they match the committer, otherwise they will be marked "unmatched". This forces Gitea to be the committer on signed commits, with the actual committer marked as Co-authored-by: and Co-committed-by: trailer in the commit. The default Gitea key must match a user in the database.
|
||||||
settings.trust_model.collaboratorcommitter = Collaborator+Committer
|
settings.trust_model.collaboratorcommitter = Collaborator+Committer
|
||||||
settings.trust_model.collaboratorcommitter.long = Collaborator+Committer: Trust signatures by collaborators which match the committer
|
settings.trust_model.collaboratorcommitter.long = Collaborator+Committer: Trust signatures by collaborators which match the committer
|
||||||
settings.trust_model.collaboratorcommitter.desc = Valid signatures by collaborators of this repository will be marked "trusted" if they match the committer. Otherwise, valid signatures will be marked "untrusted" if the signature matches the committer and "unmatched" otherwise. This will force Gitea to be marked as the committer on signed commits with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default Gitea key must match a User in the database.
|
settings.trust_model.collaboratorcommitter.desc = Valid signatures by collaborators of this repository will be marked "trusted" if they match the committer. Otherwise, valid signatures will be marked "untrusted" if the signature matches the committer and "unmatched" otherwise. This will force Gitea to be marked as the committer on signed commits, with the actual committer marked as Co-Authored-By: and Co-Committed-By: trailer in the commit. The default Gitea key must match a user in the database.
|
||||||
settings.wiki_delete = Delete Wiki Data
|
settings.wiki_delete = Delete Wiki Data
|
||||||
settings.wiki_delete_desc = Deleting repository wiki data is permanent and cannot be undone.
|
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.
|
settings.wiki_delete_notices_1 = - This will permanently delete and disable the repository wiki for %s.
|
||||||
@ -2305,7 +2305,7 @@ settings.wiki_deletion_success = The repository wiki data has been deleted.
|
|||||||
settings.delete = Delete This Repository
|
settings.delete = Delete This Repository
|
||||||
settings.delete_desc = Deleting a repository is permanent and cannot be undone.
|
settings.delete_desc = Deleting a repository is permanent and cannot be undone.
|
||||||
settings.delete_notices_1 = - This operation <strong>CANNOT</strong> be undone.
|
settings.delete_notices_1 = - This operation <strong>CANNOT</strong> be undone.
|
||||||
settings.delete_notices_2 = - This operation will permanently delete the <strong>%s</strong> repository including code, issues, comments, wiki data and collaborator settings.
|
settings.delete_notices_2 = - This operation will permanently delete the <strong>%s</strong> repository, including code, issues, comments, wiki data and collaborator settings.
|
||||||
settings.delete_notices_fork_1 = - Forks of this repository will become independent after deletion.
|
settings.delete_notices_fork_1 = - Forks of this repository will become independent after deletion.
|
||||||
settings.deletion_success = The repository has been deleted.
|
settings.deletion_success = The repository has been deleted.
|
||||||
settings.update_settings_success = The repository settings have been updated.
|
settings.update_settings_success = The repository settings have been updated.
|
||||||
@ -2327,8 +2327,8 @@ settings.team_not_in_organization = The team is not in the same organization as
|
|||||||
settings.teams = Teams
|
settings.teams = Teams
|
||||||
settings.add_team = Add Team
|
settings.add_team = Add Team
|
||||||
settings.add_team_duplicate = Team already has the repository
|
settings.add_team_duplicate = Team already has the repository
|
||||||
settings.add_team_success = The team now have access to the repository.
|
settings.add_team_success = The team now has access to the repository.
|
||||||
settings.change_team_permission_tip = Team's permission is set on the team setting page and can't be changed per repository
|
settings.change_team_permission_tip = Team's permission is set on the team settings page and can't be changed per repository
|
||||||
settings.delete_team_tip = This team has access to all repositories and can't be removed
|
settings.delete_team_tip = This team has access to all repositories and can't be removed
|
||||||
settings.remove_team_success = The team's access to the repository has been removed.
|
settings.remove_team_success = The team's access to the repository has been removed.
|
||||||
settings.add_webhook = Add Webhook
|
settings.add_webhook = Add Webhook
|
||||||
@ -2593,7 +2593,7 @@ settings.archive.tagsettings_unavailable = Tag settings are not available if the
|
|||||||
settings.archive.mirrors_unavailable = Mirrors are not available if the repo is archived.
|
settings.archive.mirrors_unavailable = Mirrors are not available if the repo is archived.
|
||||||
settings.unarchive.button = Unarchive repo
|
settings.unarchive.button = Unarchive repo
|
||||||
settings.unarchive.header = Unarchive this repo
|
settings.unarchive.header = Unarchive this repo
|
||||||
settings.unarchive.text = Unarchiving the repo will restore its ability to receive commits and pushes, as well as new issues and pull-requests.
|
settings.unarchive.text = Unarchiving the repo will restore its ability to receive commits and pushes, as well as new issues and pull requests.
|
||||||
settings.unarchive.success = The repo was successfully unarchived.
|
settings.unarchive.success = The repo was successfully unarchived.
|
||||||
settings.unarchive.error = An error occurred while trying to unarchive the repo. See the log for more details.
|
settings.unarchive.error = An error occurred while trying to unarchive the repo. See the log for more details.
|
||||||
settings.update_avatar_success = The repository avatar has been updated.
|
settings.update_avatar_success = The repository avatar has been updated.
|
||||||
@ -2611,11 +2611,11 @@ settings.lfs_invalid_locking_path=Invalid path: %s
|
|||||||
settings.lfs_invalid_lock_directory=Cannot lock directory: %s
|
settings.lfs_invalid_lock_directory=Cannot lock directory: %s
|
||||||
settings.lfs_lock_already_exists=Lock already exists: %s
|
settings.lfs_lock_already_exists=Lock already exists: %s
|
||||||
settings.lfs_lock=Lock
|
settings.lfs_lock=Lock
|
||||||
settings.lfs_lock_path=Filepath to lock...
|
settings.lfs_lock_path=Filepath to lock…
|
||||||
settings.lfs_locks_no_locks=No Locks
|
settings.lfs_locks_no_locks=No Locks
|
||||||
settings.lfs_lock_file_no_exist=Locked file does not exist in default branch
|
settings.lfs_lock_file_no_exist=Locked file does not exist in default branch
|
||||||
settings.lfs_force_unlock=Force Unlock
|
settings.lfs_force_unlock=Force Unlock
|
||||||
settings.lfs_pointers.found=Found %d blob pointer(s) - %d associated, %d unassociated (%d missing from store)
|
settings.lfs_pointers.found=Found %d blob pointer(s) — %d associated, %d unassociated (%d missing from store)
|
||||||
settings.lfs_pointers.sha=Blob SHA
|
settings.lfs_pointers.sha=Blob SHA
|
||||||
settings.lfs_pointers.oid=OID
|
settings.lfs_pointers.oid=OID
|
||||||
settings.lfs_pointers.inRepo=In Repo
|
settings.lfs_pointers.inRepo=In Repo
|
||||||
@ -2773,7 +2773,7 @@ branch.new_branch_from = Create new branch from "%s"
|
|||||||
branch.renamed = Branch %s was renamed to %s.
|
branch.renamed = Branch %s was renamed to %s.
|
||||||
branch.rename_default_or_protected_branch_error = Only admins can rename default or protected branches.
|
branch.rename_default_or_protected_branch_error = Only admins can rename default or protected branches.
|
||||||
branch.rename_protected_branch_failed = This branch is protected by glob-based protection rules.
|
branch.rename_protected_branch_failed = This branch is protected by glob-based protection rules.
|
||||||
branch.commits_divergence_from = Commits divergence: %[1]d behind and %[2]d ahead of %[3]s
|
branch.commits_divergence_from = Commit divergence: %[1]d behind and %[2]d ahead of %[3]s
|
||||||
branch.commits_no_divergence = The same as branch %[1]s
|
branch.commits_no_divergence = The same as branch %[1]s
|
||||||
|
|
||||||
tag.create_tag = Create tag %s
|
tag.create_tag = Create tag %s
|
||||||
@ -2798,7 +2798,7 @@ error.csv.invalid_field_count = Can't render this file because it has a wrong nu
|
|||||||
error.broken_git_hook = Git hooks of this repository seem to be broken. Please follow the <a target="_blank" rel="noreferrer" href="%s">documentation</a> to fix them, then push some commits to refresh the status.
|
error.broken_git_hook = Git hooks of this repository seem to be broken. Please follow the <a target="_blank" rel="noreferrer" href="%s">documentation</a> to fix them, then push some commits to refresh the status.
|
||||||
|
|
||||||
[graphs]
|
[graphs]
|
||||||
component_loading = Loading %s...
|
component_loading = Loading %s…
|
||||||
component_loading_failed = Could not load %s
|
component_loading_failed = Could not load %s
|
||||||
component_loading_info = This might take a bit…
|
component_loading_info = This might take a bit…
|
||||||
component_failed_to_load = An unexpected error happened.
|
component_failed_to_load = An unexpected error happened.
|
||||||
@ -2837,7 +2837,7 @@ form.create_org_not_allowed = You are not allowed to create an organization.
|
|||||||
settings = Settings
|
settings = Settings
|
||||||
settings.options = Organization
|
settings.options = Organization
|
||||||
settings.full_name = Full Name
|
settings.full_name = Full Name
|
||||||
settings.email = Contact Email
|
settings.email = Contact Email Address
|
||||||
settings.website = Website
|
settings.website = Website
|
||||||
settings.location = Location
|
settings.location = Location
|
||||||
settings.permission = Permissions
|
settings.permission = Permissions
|
||||||
@ -2859,10 +2859,10 @@ settings.update_setting_success = Organization settings have been updated.
|
|||||||
|
|
||||||
settings.rename = Rename Organization
|
settings.rename = Rename Organization
|
||||||
settings.rename_desc = Changing the organization name will also change your organization's URL and free the old name.
|
settings.rename_desc = Changing the organization name will also change your organization's URL and free the old name.
|
||||||
settings.rename_success = Organization %[1]s have been renamed to %[2]s successfully.
|
settings.rename_success = Organization %[1]s has been renamed to %[2]s successfully.
|
||||||
settings.rename_no_change = Organization name is no change.
|
settings.rename_no_change = Organization name is not changed.
|
||||||
settings.rename_new_org_name = New Organization Name
|
settings.rename_new_org_name = New Organization Name
|
||||||
settings.rename_failed = Rename Organization failed because of internal error
|
settings.rename_failed = Renaming organization failed because of an internal error
|
||||||
settings.rename_notices_1 = This operation <strong>CANNOT</strong> be undone.
|
settings.rename_notices_1 = This operation <strong>CANNOT</strong> be undone.
|
||||||
settings.rename_notices_2 = The old name will redirect until it is claimed.
|
settings.rename_notices_2 = The old name will redirect until it is claimed.
|
||||||
|
|
||||||
@ -2872,11 +2872,11 @@ settings.delete_account = Delete This Organization
|
|||||||
settings.delete_prompt = The organization will be permanently removed. This <strong>CANNOT</strong> be undone!
|
settings.delete_prompt = The organization will be permanently removed. This <strong>CANNOT</strong> be undone!
|
||||||
settings.name_confirm = Enter the organization name as confirmation:
|
settings.name_confirm = Enter the organization name as confirmation:
|
||||||
settings.delete_notices_1 = This operation <strong>CANNOT</strong> be undone.
|
settings.delete_notices_1 = This operation <strong>CANNOT</strong> be undone.
|
||||||
settings.delete_notices_2 = This operation will permanently delete all the <strong>repositories</strong> of <strong>%s</strong> including code, issues, comments, wiki data and collaborator settings.
|
settings.delete_notices_2 = This operation will permanently delete all the <strong>repositories</strong> of <strong>%s</strong>, including code, issues, comments, wiki data and collaborator settings.
|
||||||
settings.delete_notices_3 = This operation will permanently delete all the <strong>packages</strong> of <strong>%s</strong>.
|
settings.delete_notices_3 = This operation will permanently delete all the <strong>packages</strong> of <strong>%s</strong>.
|
||||||
settings.delete_notices_4 = This operation will permanently delete all the <strong>projects</strong> of <strong>%s</strong>.
|
settings.delete_notices_4 = This operation will permanently delete all the <strong>projects</strong> of <strong>%s</strong>.
|
||||||
settings.confirm_delete_account = Confirm Deletion
|
settings.confirm_delete_account = Confirm Deletion
|
||||||
settings.delete_failed = Delete Organization failed due to an internal error
|
settings.delete_failed = Deleting organization failed due to an internal error
|
||||||
settings.delete_successful = Organization <b>%s</b> has been deleted successfully.
|
settings.delete_successful = Organization <b>%s</b> has been deleted successfully.
|
||||||
settings.hooks_desc = Add webhooks which will be triggered for <strong>all repositories</strong> under this organization.
|
settings.hooks_desc = Add webhooks which will be triggered for <strong>all repositories</strong> under this organization.
|
||||||
|
|
||||||
@ -2933,7 +2933,7 @@ teams.remove_all_repos_title = Remove all team repositories
|
|||||||
teams.remove_all_repos_desc = This will remove all repositories from the team.
|
teams.remove_all_repos_desc = This will remove all repositories from the team.
|
||||||
teams.add_all_repos_title = Add all repositories
|
teams.add_all_repos_title = Add all repositories
|
||||||
teams.add_all_repos_desc = This will add all the organization's repositories to the team.
|
teams.add_all_repos_desc = This will add all the organization's repositories to the team.
|
||||||
teams.add_nonexistent_repo = "The repository you're trying to add doesn't exist, please create it first."
|
teams.add_nonexistent_repo = "The repository you're trying to add doesn't exist. Please create it first."
|
||||||
teams.add_duplicate_users = User is already a team member.
|
teams.add_duplicate_users = User is already a team member.
|
||||||
teams.repos.none = No repositories could be accessed by this team.
|
teams.repos.none = No repositories could be accessed by this team.
|
||||||
teams.members.none = No members on this team.
|
teams.members.none = No members on this team.
|
||||||
@ -2974,7 +2974,7 @@ repositories = Repositories
|
|||||||
hooks = Webhooks
|
hooks = Webhooks
|
||||||
integrations = Integrations
|
integrations = Integrations
|
||||||
authentication = Authentication Sources
|
authentication = Authentication Sources
|
||||||
emails = User Emails
|
emails = User Email Addresses
|
||||||
config = Configuration
|
config = Configuration
|
||||||
config_summary = Summary
|
config_summary = Summary
|
||||||
config_settings = Settings
|
config_settings = Settings
|
||||||
@ -3006,11 +3006,11 @@ dashboard.cron.cancelled=Cron: %[1]s canceled: %[3]s
|
|||||||
dashboard.cron.error=Error in Cron: %s: %[3]s
|
dashboard.cron.error=Error in Cron: %s: %[3]s
|
||||||
dashboard.cron.finished=Cron: %[1]s has finished
|
dashboard.cron.finished=Cron: %[1]s has finished
|
||||||
dashboard.delete_inactive_accounts = Delete all unactivated accounts
|
dashboard.delete_inactive_accounts = Delete all unactivated accounts
|
||||||
dashboard.delete_inactive_accounts.started = Delete all unactivated accounts task started.
|
dashboard.delete_inactive_accounts.started = Task to delete all unactivated accounts started
|
||||||
dashboard.delete_repo_archives = "Delete all repositories' archives (ZIP, TAR.GZ, etc..)"
|
dashboard.delete_repo_archives = "Delete all repositories' archives (ZIP, TAR.GZ, etc..)"
|
||||||
dashboard.delete_repo_archives.started = Delete all repository archives task started.
|
dashboard.delete_repo_archives.started = Task to delete all repository archives started
|
||||||
dashboard.delete_missing_repos = Delete all repositories missing their Git files
|
dashboard.delete_missing_repos = Delete all repositories missing their Git files
|
||||||
dashboard.delete_missing_repos.started = Delete all repositories missing their Git files task started.
|
dashboard.delete_missing_repos.started = Task to delete all repositories missing their Git files started
|
||||||
dashboard.delete_generated_repository_avatars = Delete generated repository avatars
|
dashboard.delete_generated_repository_avatars = Delete generated repository avatars
|
||||||
dashboard.sync_repo_branches = Sync missed branches from git data to databases
|
dashboard.sync_repo_branches = Sync missed branches from git data to databases
|
||||||
dashboard.sync_repo_tags = Sync tags from git data to database
|
dashboard.sync_repo_tags = Sync tags from git data to database
|
||||||
@ -3018,12 +3018,12 @@ dashboard.update_mirrors = Update Mirrors
|
|||||||
dashboard.repo_health_check = Health check all repositories
|
dashboard.repo_health_check = Health check all repositories
|
||||||
dashboard.check_repo_stats = Check all repository statistics
|
dashboard.check_repo_stats = Check all repository statistics
|
||||||
dashboard.archive_cleanup = Delete old repository archives
|
dashboard.archive_cleanup = Delete old repository archives
|
||||||
dashboard.deleted_branches_cleanup = Clean-up deleted branches
|
dashboard.deleted_branches_cleanup = Clean up deleted branches
|
||||||
dashboard.update_migration_poster_id = Update migration poster IDs
|
dashboard.update_migration_poster_id = Update migration poster IDs
|
||||||
dashboard.git_gc_repos = Garbage collect all repositories
|
dashboard.git_gc_repos = Garbage-collect all repositories
|
||||||
dashboard.resync_all_sshkeys = Update the '.ssh/authorized_keys' file with Gitea SSH keys.
|
dashboard.resync_all_sshkeys = Update the '.ssh/authorized_keys' file with Gitea SSH keys
|
||||||
dashboard.resync_all_sshprincipals = Update the '.ssh/authorized_principals' file with Gitea SSH principals.
|
dashboard.resync_all_sshprincipals = Update the '.ssh/authorized_principals' file with Gitea SSH principals
|
||||||
dashboard.resync_all_hooks = Resynchronize pre-receive, update and post-receive hooks of all repositories.
|
dashboard.resync_all_hooks = Resynchronize pre-receive, update and post-receive hooks of all repositories
|
||||||
dashboard.reinit_missing_repos = Reinitialize all missing Git repositories for which records exist
|
dashboard.reinit_missing_repos = Reinitialize all missing Git repositories for which records exist
|
||||||
dashboard.sync_external_users = Synchronize external user data
|
dashboard.sync_external_users = Synchronize external user data
|
||||||
dashboard.cleanup_hook_task_table = Clean up hook_task table
|
dashboard.cleanup_hook_task_table = Clean up hook_task table
|
||||||
@ -3059,10 +3059,10 @@ dashboard.total_gc_pause = Total GC Pause
|
|||||||
dashboard.last_gc_pause = Last GC Pause
|
dashboard.last_gc_pause = Last GC Pause
|
||||||
dashboard.gc_times = GC Times
|
dashboard.gc_times = GC Times
|
||||||
dashboard.delete_old_actions = Delete all old activities from database
|
dashboard.delete_old_actions = Delete all old activities from database
|
||||||
dashboard.delete_old_actions.started = Delete all old activities from database started.
|
dashboard.delete_old_actions.started = Deletion of all old activities from database started
|
||||||
dashboard.update_checker = Update checker
|
dashboard.update_checker = Update checker
|
||||||
dashboard.delete_old_system_notices = Delete all old system notices from database
|
dashboard.delete_old_system_notices = Delete all old system notices from database
|
||||||
dashboard.gc_lfs = Garbage collect LFS meta objects
|
dashboard.gc_lfs = Garbage-collect LFS meta objects
|
||||||
dashboard.stop_zombie_tasks = Stop actions zombie tasks
|
dashboard.stop_zombie_tasks = Stop actions zombie tasks
|
||||||
dashboard.stop_endless_tasks = Stop actions endless tasks
|
dashboard.stop_endless_tasks = Stop actions endless tasks
|
||||||
dashboard.cancel_abandoned_jobs = Cancel actions abandoned jobs
|
dashboard.cancel_abandoned_jobs = Cancel actions abandoned jobs
|
||||||
@ -3086,7 +3086,7 @@ users.2fa = 2FA
|
|||||||
users.repos = Repos
|
users.repos = Repos
|
||||||
users.created = Created
|
users.created = Created
|
||||||
users.last_login = Last Sign-In
|
users.last_login = Last Sign-In
|
||||||
users.never_login = Never Signed-In
|
users.never_login = Never Signed In
|
||||||
users.send_register_notify = Send User Registration Notification
|
users.send_register_notify = Send User Registration Notification
|
||||||
users.new_success = The user account "%s" has been created.
|
users.new_success = The user account "%s" has been created.
|
||||||
users.edit = Edit
|
users.edit = Edit
|
||||||
@ -3113,7 +3113,7 @@ users.still_own_repo = This user still owns one or more repositories. Delete or
|
|||||||
users.still_has_org = This user is a member of an organization. Remove the user from any organizations first.
|
users.still_has_org = This user is a member of an organization. Remove the user from any organizations first.
|
||||||
users.purge = Purge User
|
users.purge = Purge User
|
||||||
users.purge_help = Forcibly delete user and any repositories, organizations, and packages owned by the user. All comments will be deleted too.
|
users.purge_help = Forcibly delete user and any repositories, organizations, and packages owned by the user. All comments will be deleted too.
|
||||||
users.still_own_packages = This user still owns one or more packages, delete these packages first.
|
users.still_own_packages = This user still owns one or more packages. Delete these packages first.
|
||||||
users.deletion_success = The user account has been deleted.
|
users.deletion_success = The user account has been deleted.
|
||||||
users.reset_2fa = Reset 2FA
|
users.reset_2fa = Reset 2FA
|
||||||
users.list_status_filter.menu_text = Filter
|
users.list_status_filter.menu_text = Filter
|
||||||
@ -3133,11 +3133,11 @@ users.details = User Details
|
|||||||
emails.email_manage_panel = User Email Management
|
emails.email_manage_panel = User Email Management
|
||||||
emails.primary = Primary
|
emails.primary = Primary
|
||||||
emails.activated = Activated
|
emails.activated = Activated
|
||||||
emails.filter_sort.email = Email
|
emails.filter_sort.email = Email address
|
||||||
emails.filter_sort.email_reverse = Email (reverse)
|
emails.filter_sort.email_reverse = Email address (reverse)
|
||||||
emails.filter_sort.name = User Name
|
emails.filter_sort.name = Username
|
||||||
emails.filter_sort.name_reverse = User Name (reverse)
|
emails.filter_sort.name_reverse = Username (reverse)
|
||||||
emails.updated = Email updated
|
emails.updated = Email address updated
|
||||||
emails.not_updated = Failed to update the requested email address: %v
|
emails.not_updated = Failed to update the requested email address: %v
|
||||||
emails.duplicate_active = This email address is already active for a different user.
|
emails.duplicate_active = This email address is already active for a different user.
|
||||||
emails.change_email_header = Update Email Properties
|
emails.change_email_header = Update Email Properties
|
||||||
@ -3145,7 +3145,7 @@ emails.change_email_text = Are you sure you want to update this email address?
|
|||||||
emails.delete = Delete Email
|
emails.delete = Delete Email
|
||||||
emails.delete_desc = Are you sure you want to delete this email address?
|
emails.delete_desc = Are you sure you want to delete this email address?
|
||||||
emails.deletion_success = The email address has been deleted.
|
emails.deletion_success = The email address has been deleted.
|
||||||
emails.delete_primary_email_error = You can not delete the primary email.
|
emails.delete_primary_email_error = You cannot delete the primary email address.
|
||||||
|
|
||||||
orgs.org_manage_panel = Organization Management
|
orgs.org_manage_panel = Organization Management
|
||||||
orgs.name = Name
|
orgs.name = Name
|
||||||
@ -3259,29 +3259,29 @@ auths.oauth2_required_claim_name_helper = Set this name to restrict login from t
|
|||||||
auths.oauth2_required_claim_value = Required Claim Value
|
auths.oauth2_required_claim_value = Required Claim Value
|
||||||
auths.oauth2_required_claim_value_helper = Set this value to restrict login from this source to users with a claim with this name and value
|
auths.oauth2_required_claim_value_helper = Set this value to restrict login from this source to users with a claim with this name and value
|
||||||
auths.oauth2_group_claim_name = Claim name providing group names for this source. (Optional)
|
auths.oauth2_group_claim_name = Claim name providing group names for this source. (Optional)
|
||||||
auths.oauth2_full_name_claim_name = Full Name Claim Name. (Optional, if set, the user's full name will always be synchronized with this claim)
|
auths.oauth2_full_name_claim_name = Full Name Claim Name. (Optional; if set, the user's full name will always be synchronized with this claim)
|
||||||
auths.oauth2_ssh_public_key_claim_name = SSH Public Key Claim Name
|
auths.oauth2_ssh_public_key_claim_name = SSH Public Key Claim Name
|
||||||
auths.oauth2_admin_group = Group Claim value for administrator users. (Optional - requires claim name above)
|
auths.oauth2_admin_group = Group Claim value for administrator users. (Optional — requires claim name above)
|
||||||
auths.oauth2_restricted_group = Group Claim value for restricted users. (Optional - requires claim name above)
|
auths.oauth2_restricted_group = Group Claim value for restricted users. (Optional — requires claim name above)
|
||||||
auths.oauth2_map_group_to_team = Map claimed groups to Organization teams. (Optional - requires claim name above)
|
auths.oauth2_map_group_to_team = Map claimed groups to Organization teams. (Optional — requires claim name above)
|
||||||
auths.oauth2_map_group_to_team_removal = Remove users from synchronized teams if user does not belong to corresponding group.
|
auths.oauth2_map_group_to_team_removal = Remove users from synchronized teams if user does not belong to corresponding group.
|
||||||
auths.enable_auto_register = Enable Auto Registration
|
auths.enable_auto_register = Enable Auto Registration
|
||||||
auths.sspi_auto_create_users = Automatically create users
|
auths.sspi_auto_create_users = Automatically create users
|
||||||
auths.sspi_auto_create_users_helper = Allow SSPI auth method to automatically create new accounts for users that login for the first time
|
auths.sspi_auto_create_users_helper = Allow SSPI auth method to automatically create new accounts for users that log in for the first time
|
||||||
auths.sspi_auto_activate_users = Automatically activate users
|
auths.sspi_auto_activate_users = Automatically activate users
|
||||||
auths.sspi_auto_activate_users_helper = Allow SSPI auth method to automatically activate new users
|
auths.sspi_auto_activate_users_helper = Allow SSPI auth method to automatically activate new users
|
||||||
auths.sspi_strip_domain_names = Remove domain names from usernames
|
auths.sspi_strip_domain_names = Remove domain names from usernames
|
||||||
auths.sspi_strip_domain_names_helper = If checked, domain names will be removed from logon names (eg. "DOMAIN\user" and "user@example.org" both will become just "user").
|
auths.sspi_strip_domain_names_helper = If checked, domain names will be removed from logon names (e.g. "DOMAIN\user" and "user@example.org" both will become just "user").
|
||||||
auths.sspi_separator_replacement = Separator to use instead of \, / and @
|
auths.sspi_separator_replacement = Separator to use instead of \, / and @
|
||||||
auths.sspi_separator_replacement_helper = The character to use to replace the separators of down-level logon names (eg. the \ in "DOMAIN\user") and user principal names (eg. the @ in "user@example.org").
|
auths.sspi_separator_replacement_helper = The character to use to replace the separators of down-level logon names (e.g. the \ in "DOMAIN\user") and user principal names (e.g. the @ in "user@example.org").
|
||||||
auths.sspi_default_language = Default user language
|
auths.sspi_default_language = Default user language
|
||||||
auths.sspi_default_language_helper = Default language for users automatically created by SSPI auth method. Leave empty if you prefer language to be automatically detected.
|
auths.sspi_default_language_helper = Default language for users automatically created by SSPI auth method. Leave empty if you prefer the language to be automatically detected.
|
||||||
auths.tips = Tips
|
auths.tips = Tips
|
||||||
auths.tips.oauth2.general = OAuth2 Authentication
|
auths.tips.oauth2.general = OAuth2 Authentication
|
||||||
auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be:
|
auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be:
|
||||||
auths.tip.oauth2_provider = OAuth2 Provider
|
auths.tip.oauth2_provider = OAuth2 Provider
|
||||||
auths.tip.bitbucket = Register a new OAuth consumer on %s and add the permission 'Account' - 'Read'
|
auths.tip.bitbucket = Register a new OAuth consumer on %s and add the permission 'Account' - 'Read'
|
||||||
auths.tip.nextcloud = Register a new OAuth consumer on your instance using the following menu "Settings -> Security -> OAuth 2.0 client"
|
auths.tip.nextcloud = Register a new OAuth consumer on your instance by selecting "Settings -> Security -> OAuth 2.0 client" in the menu
|
||||||
auths.tip.dropbox = Create a new application at %s
|
auths.tip.dropbox = Create a new application at %s
|
||||||
auths.tip.facebook = Register a new application at %s and add the product "Facebook Login"
|
auths.tip.facebook = Register a new application at %s and add the product "Facebook Login"
|
||||||
auths.tip.github = Register a new OAuth application on %s
|
auths.tip.github = Register a new OAuth application on %s
|
||||||
@ -3391,7 +3391,7 @@ config.mailer_sendmail_path = Sendmail Path
|
|||||||
config.mailer_sendmail_args = Extra Arguments to Sendmail
|
config.mailer_sendmail_args = Extra Arguments to Sendmail
|
||||||
config.mailer_sendmail_timeout = Sendmail Timeout
|
config.mailer_sendmail_timeout = Sendmail Timeout
|
||||||
config.mailer_use_dummy = Dummy
|
config.mailer_use_dummy = Dummy
|
||||||
config.test_email_placeholder = Email (e.g. test@example.com)
|
config.test_email_placeholder = Email Address (e.g. test@example.com)
|
||||||
config.send_test_mail = Send Testing Email
|
config.send_test_mail = Send Testing Email
|
||||||
config.send_test_mail_submit = Send
|
config.send_test_mail_submit = Send
|
||||||
config.test_mail_failed = Failed to send a testing email to "%s": %v
|
config.test_mail_failed = Failed to send a testing email to "%s": %v
|
||||||
@ -3480,7 +3480,7 @@ monitor.queue.numberinqueue = Number in Queue
|
|||||||
monitor.queue.review_add = Review / Add Workers
|
monitor.queue.review_add = Review / Add Workers
|
||||||
monitor.queue.settings.title = Pool Settings
|
monitor.queue.settings.title = Pool Settings
|
||||||
monitor.queue.settings.desc = Pools dynamically grow in response to their worker queue blocking.
|
monitor.queue.settings.desc = Pools dynamically grow in response to their worker queue blocking.
|
||||||
monitor.queue.settings.maxnumberworkers = Max Number of workers
|
monitor.queue.settings.maxnumberworkers = Max number of workers
|
||||||
monitor.queue.settings.maxnumberworkers.placeholder = Currently %[1]d
|
monitor.queue.settings.maxnumberworkers.placeholder = Currently %[1]d
|
||||||
monitor.queue.settings.maxnumberworkers.error = Max number of workers must be a number
|
monitor.queue.settings.maxnumberworkers.error = Max number of workers must be a number
|
||||||
monitor.queue.settings.submit = Update Settings
|
monitor.queue.settings.submit = Update Settings
|
||||||
@ -3506,10 +3506,10 @@ notices.delete_success = The system notices have been deleted.
|
|||||||
self_check.no_problem_found = No problem found yet.
|
self_check.no_problem_found = No problem found yet.
|
||||||
self_check.startup_warnings = Startup warnings:
|
self_check.startup_warnings = Startup warnings:
|
||||||
self_check.database_collation_mismatch = Expect database to use collation: %s
|
self_check.database_collation_mismatch = Expect database to use collation: %s
|
||||||
self_check.database_collation_case_insensitive = Database is using a collation %s, which is an insensitive collation. Although Gitea could work with it, there might be some rare cases which don't work as expected.
|
self_check.database_collation_case_insensitive = Database is using collation %s, which is a case-insensitive collation. Although Gitea could work with it, there might be some rare cases which don't work as expected.
|
||||||
self_check.database_inconsistent_collation_columns = Database is using collation %s, but these columns are using mismatched collations. It might cause some unexpected problems.
|
self_check.database_inconsistent_collation_columns = Database is using collation %s, but these columns are using mismatched collations. This might cause some unexpected problems.
|
||||||
self_check.database_fix_mysql = For MySQL/MariaDB users, you could use the "gitea doctor convert" command to fix the collation problems, or you could also fix the problem by "ALTER ... COLLATE ..." SQLs manually.
|
self_check.database_fix_mysql = For MySQL/MariaDB users, you could use the "gitea doctor convert" command to fix the collation problems, or you could also fix the problem manually with "ALTER ... COLLATE ..." SQL queries.
|
||||||
self_check.database_fix_mssql = For MSSQL users, you could only fix the problem by "ALTER ... COLLATE ..." SQLs manually at the moment.
|
self_check.database_fix_mssql = For MSSQL users, you could only fix the problem manually with "ALTER ... COLLATE ..." SQL queries at the moment.
|
||||||
self_check.location_origin_mismatch = Current URL (%[1]s) doesn't match the URL seen by Gitea (%[2]s). If you are using a reverse proxy, please make sure the "Host" and "X-Forwarded-Proto" headers are set correctly.
|
self_check.location_origin_mismatch = Current URL (%[1]s) doesn't match the URL seen by Gitea (%[2]s). If you are using a reverse proxy, please make sure the "Host" and "X-Forwarded-Proto" headers are set correctly.
|
||||||
|
|
||||||
[action]
|
[action]
|
||||||
@ -3593,8 +3593,8 @@ error.no_committer_account = No account linked to committer's email address
|
|||||||
error.no_gpg_keys_found = "No known key found for this signature in database"
|
error.no_gpg_keys_found = "No known key found for this signature in database"
|
||||||
error.not_signed_commit = "Not a signed commit"
|
error.not_signed_commit = "Not a signed commit"
|
||||||
error.failed_retrieval_gpg_keys = "Failed to retrieve any key attached to the committer's account"
|
error.failed_retrieval_gpg_keys = "Failed to retrieve any key attached to the committer's account"
|
||||||
error.probable_bad_signature = "WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS."
|
error.probable_bad_signature = "WARNING! Although there is a key with this ID in the database, it does not verify this commit! This commit is SUSPICIOUS."
|
||||||
error.probable_bad_default_signature = "WARNING! Although the default key has this ID it does not verify this commit! This commit is SUSPICIOUS."
|
error.probable_bad_default_signature = "WARNING! Although the default key has this ID, it does not verify this commit! This commit is SUSPICIOUS."
|
||||||
|
|
||||||
[units]
|
[units]
|
||||||
unit = Unit
|
unit = Unit
|
||||||
@ -3633,7 +3633,7 @@ versions.view_all = View all
|
|||||||
dependency.id = ID
|
dependency.id = ID
|
||||||
dependency.version = Version
|
dependency.version = Version
|
||||||
search_in_external_registry = Search in %s
|
search_in_external_registry = Search in %s
|
||||||
alpine.registry = Setup this registry by adding the url in your <code>/etc/apk/repositories</code> file:
|
alpine.registry = Set up this registry by adding the URL in your <code>/etc/apk/repositories</code> file:
|
||||||
alpine.registry.key = Download the registry public RSA key into the <code>/etc/apk/keys/</code> folder to verify the index signature:
|
alpine.registry.key = Download the registry public RSA key into the <code>/etc/apk/keys/</code> folder to verify the index signature:
|
||||||
alpine.registry.info = Choose $branch and $repository from the list below.
|
alpine.registry.info = Choose $branch and $repository from the list below.
|
||||||
alpine.install = To install the package, run the following command:
|
alpine.install = To install the package, run the following command:
|
||||||
@ -3646,18 +3646,18 @@ arch.install = Sync package with pacman:
|
|||||||
arch.repository = Repository Info
|
arch.repository = Repository Info
|
||||||
arch.repository.repositories = Repositories
|
arch.repository.repositories = Repositories
|
||||||
arch.repository.architectures = Architectures
|
arch.repository.architectures = Architectures
|
||||||
cargo.registry = Setup this registry in the Cargo configuration file (for example <code>~/.cargo/config.toml</code>):
|
cargo.registry = Set up this registry in the Cargo configuration file (for example <code>~/.cargo/config.toml</code>):
|
||||||
cargo.install = To install the package using Cargo, run the following command:
|
cargo.install = To install the package using Cargo, run the following command:
|
||||||
chef.registry = Setup this registry in your <code>~/.chef/config.rb</code> file:
|
chef.registry = Set up this registry in your <code>~/.chef/config.rb</code> file:
|
||||||
chef.install = To install the package, run the following command:
|
chef.install = To install the package, run the following command:
|
||||||
composer.registry = Setup this registry in your <code>~/.composer/config.json</code> file:
|
composer.registry = Set up this registry in your <code>~/.composer/config.json</code> file:
|
||||||
composer.install = To install the package using Composer, run the following command:
|
composer.install = To install the package using Composer, run the following command:
|
||||||
composer.dependencies = Dependencies
|
composer.dependencies = Dependencies
|
||||||
composer.dependencies.development = Development Dependencies
|
composer.dependencies.development = Development Dependencies
|
||||||
conan.details.repository = Repository
|
conan.details.repository = Repository
|
||||||
conan.registry = Setup this registry from the command line:
|
conan.registry = Set up this registry from the command line:
|
||||||
conan.install = To install the package using Conan, run the following command:
|
conan.install = To install the package using Conan, run the following command:
|
||||||
conda.registry = Setup this registry as a Conda repository in your <code>.condarc</code> file:
|
conda.registry = Set up this registry as a Conda repository in your <code>.condarc</code> file:
|
||||||
conda.install = To install the package using Conda, run the following command:
|
conda.install = To install the package using Conda, run the following command:
|
||||||
container.details.type = Image Type
|
container.details.type = Image Type
|
||||||
container.details.platform = Platform
|
container.details.platform = Platform
|
||||||
@ -3669,9 +3669,9 @@ container.layers = Image Layers
|
|||||||
container.labels = Labels
|
container.labels = Labels
|
||||||
container.labels.key = Key
|
container.labels.key = Key
|
||||||
container.labels.value = Value
|
container.labels.value = Value
|
||||||
cran.registry = Setup this registry in your <code>Rprofile.site</code> file:
|
cran.registry = Set up this registry in your <code>Rprofile.site</code> file:
|
||||||
cran.install = To install the package, run the following command:
|
cran.install = To install the package, run the following command:
|
||||||
debian.registry = Setup this registry from the command line:
|
debian.registry = Set up this registry from the command line:
|
||||||
debian.registry.info = Choose $distribution and $component from the list below.
|
debian.registry.info = Choose $distribution and $component from the list below.
|
||||||
debian.install = To install the package, run the following command:
|
debian.install = To install the package, run the following command:
|
||||||
debian.repository = Repository Info
|
debian.repository = Repository Info
|
||||||
@ -3680,16 +3680,16 @@ debian.repository.components = Components
|
|||||||
debian.repository.architectures = Architectures
|
debian.repository.architectures = Architectures
|
||||||
generic.download = Download package from the command line:
|
generic.download = Download package from the command line:
|
||||||
go.install = Install the package from the command line:
|
go.install = Install the package from the command line:
|
||||||
helm.registry = Setup this registry from the command line:
|
helm.registry = Set up this registry from the command line:
|
||||||
helm.install = To install the package, run the following command:
|
helm.install = To install the package, run the following command:
|
||||||
maven.registry = Setup this registry in your project <code>pom.xml</code> file:
|
maven.registry = Set up this registry in your project <code>pom.xml</code> file:
|
||||||
maven.install = To use the package include the following in the <code>dependencies</code> block in the <code>pom.xml</code> file:
|
maven.install = To use the package, include the following in the <code>dependencies</code> block in the <code>pom.xml</code> file:
|
||||||
maven.install2 = Run via command line:
|
maven.install2 = Run via command line:
|
||||||
maven.download = To download the dependency, run via command line:
|
maven.download = To download the dependency, run via command line:
|
||||||
nuget.registry = Setup this registry from the command line:
|
nuget.registry = Set up this registry from the command line:
|
||||||
nuget.install = To install the package using NuGet, run the following command:
|
nuget.install = To install the package using NuGet, run the following command:
|
||||||
nuget.dependency.framework = Target Framework
|
nuget.dependency.framework = Target Framework
|
||||||
npm.registry = Setup this registry in your project <code>.npmrc</code> file:
|
npm.registry = Set up this registry in your project <code>.npmrc</code> file:
|
||||||
npm.install = To install the package using npm, run the following command:
|
npm.install = To install the package using npm, run the following command:
|
||||||
npm.install2 = or add it to the package.json file:
|
npm.install2 = or add it to the package.json file:
|
||||||
npm.dependencies = Dependencies
|
npm.dependencies = Dependencies
|
||||||
@ -3701,7 +3701,7 @@ npm.details.tag = Tag
|
|||||||
pub.install = To install the package using Dart, run the following command:
|
pub.install = To install the package using Dart, run the following command:
|
||||||
pypi.requires = Requires Python
|
pypi.requires = Requires Python
|
||||||
pypi.install = To install the package using pip, run the following command:
|
pypi.install = To install the package using pip, run the following command:
|
||||||
rpm.registry = Setup this registry from the command line:
|
rpm.registry = Set up this registry from the command line:
|
||||||
rpm.distros.redhat = on RedHat based distributions
|
rpm.distros.redhat = on RedHat based distributions
|
||||||
rpm.distros.suse = on SUSE based distributions
|
rpm.distros.suse = on SUSE based distributions
|
||||||
rpm.install = To install the package, run the following command:
|
rpm.install = To install the package, run the following command:
|
||||||
@ -3714,7 +3714,7 @@ rubygems.dependencies.runtime = Runtime Dependencies
|
|||||||
rubygems.dependencies.development = Development Dependencies
|
rubygems.dependencies.development = Development Dependencies
|
||||||
rubygems.required.ruby = Requires Ruby version
|
rubygems.required.ruby = Requires Ruby version
|
||||||
rubygems.required.rubygems = Requires RubyGem version
|
rubygems.required.rubygems = Requires RubyGem version
|
||||||
swift.registry = Setup this registry from the command line:
|
swift.registry = Set up this registry from the command line:
|
||||||
swift.install = Add the package in your <code>Package.swift</code> file:
|
swift.install = Add the package in your <code>Package.swift</code> file:
|
||||||
swift.install2 = and run the following command:
|
swift.install2 = and run the following command:
|
||||||
vagrant.install = To add a Vagrant box, run the following command:
|
vagrant.install = To add a Vagrant box, run the following command:
|
||||||
|
@ -130,6 +130,7 @@ unpin=取消置顶
|
|||||||
|
|
||||||
artifacts=产物
|
artifacts=产物
|
||||||
expired=已过期
|
expired=已过期
|
||||||
|
confirm_delete_artifact=您确定要删除产物「%s」吗?
|
||||||
|
|
||||||
archived=已归档
|
archived=已归档
|
||||||
|
|
||||||
@ -459,6 +460,7 @@ oauth_signin_submit=绑定账号
|
|||||||
oauth.signin.error.general=处理授权请求时出错:%s。如果此错误仍然存在,请与站点管理员联系。
|
oauth.signin.error.general=处理授权请求时出错:%s。如果此错误仍然存在,请与站点管理员联系。
|
||||||
oauth.signin.error.access_denied=授权请求被拒绝。
|
oauth.signin.error.access_denied=授权请求被拒绝。
|
||||||
oauth.signin.error.temporarily_unavailable=授权失败,因为认证服务器暂时不可用。请稍后再试。
|
oauth.signin.error.temporarily_unavailable=授权失败,因为认证服务器暂时不可用。请稍后再试。
|
||||||
|
oauth_callback_unable_auto_reg=自动注册已启用,但 OAuth2 提供商 %[1]s 返回缺失的字段:%[2]s,无法自动创建帐户,请创建或链接到一个帐户,或联系站点管理员。
|
||||||
openid_connect_submit=连接
|
openid_connect_submit=连接
|
||||||
openid_connect_title=连接到现有的帐户
|
openid_connect_title=连接到现有的帐户
|
||||||
openid_connect_desc=所选的 OpenID URI 未知。在这里关联一个新帐户。
|
openid_connect_desc=所选的 OpenID URI 未知。在这里关联一个新帐户。
|
||||||
@ -499,6 +501,7 @@ activate_email.text=请在 <b>%s</b> 时间内,点击以下链接,以验证
|
|||||||
|
|
||||||
register_notify=欢迎来到 %s
|
register_notify=欢迎来到 %s
|
||||||
register_notify.title=%[1]s,欢迎来到 %[2]s
|
register_notify.title=%[1]s,欢迎来到 %[2]s
|
||||||
|
register_notify.text_1=这是您的 %s 注册确认邮件 !
|
||||||
register_notify.text_3=如果此账户已为您创建,请先 <a href="%s">设置您的密码</a>。
|
register_notify.text_3=如果此账户已为您创建,请先 <a href="%s">设置您的密码</a>。
|
||||||
|
|
||||||
reset_password=恢复您的账户
|
reset_password=恢复您的账户
|
||||||
@ -723,6 +726,7 @@ webauthn=两步验证(安全密钥)
|
|||||||
public_profile=公开信息
|
public_profile=公开信息
|
||||||
biography_placeholder=告诉我们一点您自己! (您可以使用 Markdown)
|
biography_placeholder=告诉我们一点您自己! (您可以使用 Markdown)
|
||||||
location_placeholder=与他人分享您的大概位置
|
location_placeholder=与他人分享您的大概位置
|
||||||
|
profile_desc=控制您的个人资料对其他用户的显示方式。您的主邮箱地址将用于通知、密码恢复和基于网页的 Git 操作。
|
||||||
password_username_disabled=您不被允许更改您的用户名。更多详情请联系您的系统管理员。
|
password_username_disabled=您不被允许更改您的用户名。更多详情请联系您的系统管理员。
|
||||||
password_full_name_disabled=您不被允许更改您的全名。请联系您的站点管理员了解更多详情。
|
password_full_name_disabled=您不被允许更改您的全名。请联系您的站点管理员了解更多详情。
|
||||||
full_name=自定义名称
|
full_name=自定义名称
|
||||||
@ -801,6 +805,7 @@ activations_pending=等待激活
|
|||||||
can_not_add_email_activations_pending=有一个待处理的激活请求,请稍等几分钟后再尝试添加新的邮箱地址。
|
can_not_add_email_activations_pending=有一个待处理的激活请求,请稍等几分钟后再尝试添加新的邮箱地址。
|
||||||
delete_email=移除
|
delete_email=移除
|
||||||
email_deletion=移除邮箱地址
|
email_deletion=移除邮箱地址
|
||||||
|
email_deletion_desc=邮箱地址和相关信息将会被删除。使用此邮箱地址发送的Git提交将会保留,继续?
|
||||||
email_deletion_success=您的邮箱地址已移除。
|
email_deletion_success=您的邮箱地址已移除。
|
||||||
theme_update_success=您的主题已更新。
|
theme_update_success=您的主题已更新。
|
||||||
theme_update_error=所选主题不存在。
|
theme_update_error=所选主题不存在。
|
||||||
@ -986,6 +991,7 @@ webauthn_alternative_tip=您可能想要配置额外的身份验证方法。
|
|||||||
|
|
||||||
manage_account_links=管理绑定过的账号
|
manage_account_links=管理绑定过的账号
|
||||||
manage_account_links_desc=这些外部帐户已经绑定到您的 Gitea 帐户。
|
manage_account_links_desc=这些外部帐户已经绑定到您的 Gitea 帐户。
|
||||||
|
account_links_not_available=当前没有外部帐户链接到您的 Gitea 帐户。
|
||||||
link_account=链接账户
|
link_account=链接账户
|
||||||
remove_account_link=删除已绑定的账号
|
remove_account_link=删除已绑定的账号
|
||||||
remove_account_link_desc=删除已绑定帐户将吊销其对您的 Gitea 帐户的访问权限。继续?
|
remove_account_link_desc=删除已绑定帐户将吊销其对您的 Gitea 帐户的访问权限。继续?
|
||||||
@ -1008,6 +1014,8 @@ email_notifications.onmention=仅被提及时通知
|
|||||||
email_notifications.disable=停用邮件通知
|
email_notifications.disable=停用邮件通知
|
||||||
email_notifications.submit=设置邮件通知
|
email_notifications.submit=设置邮件通知
|
||||||
email_notifications.andyourown=仅与您相关的通知
|
email_notifications.andyourown=仅与您相关的通知
|
||||||
|
email_notifications.actions.desc=设置了 <a target="_blank" href="%s">Gitea 工作流</a> 的仓库中工作流运行的通知。
|
||||||
|
email_notifications.actions.failure_only=仅在工作流运行失败时通知
|
||||||
|
|
||||||
visibility=用户可见性
|
visibility=用户可见性
|
||||||
visibility.public=公开
|
visibility.public=公开
|
||||||
@ -1022,6 +1030,8 @@ new_repo_helper=代码仓库包含了所有的项目文件,包括版本历史
|
|||||||
owner=拥有者
|
owner=拥有者
|
||||||
owner_helper=由于最大仓库数量限制,一些组织可能不会显示在下拉列表中。
|
owner_helper=由于最大仓库数量限制,一些组织可能不会显示在下拉列表中。
|
||||||
repo_name=仓库名称
|
repo_name=仓库名称
|
||||||
|
repo_name_profile_public_hint=.profile 是一个特殊的仓库,您可以使用它将 README.md 添加到您的公共组织资料中,任何人都可以看到。请确保它是公开的,并使用个人资料目录中的 README 对其进行初始化以开始使用。
|
||||||
|
repo_name_profile_private_hint=.profile-private 是一个特殊的仓库,您可以使用它向您的组织成员个人资料添加 README.md,仅对组织成员可见。请确保它是私有的,并使用个人资料目录中的 README 对其进行初始化以开始使用。
|
||||||
repo_name_helper=理想的仓库名称应由简短、有意义和独特的关键词组成。「.profile」和「.profile-private」可用于为用户/组织添加 README.md。
|
repo_name_helper=理想的仓库名称应由简短、有意义和独特的关键词组成。「.profile」和「.profile-private」可用于为用户/组织添加 README.md。
|
||||||
repo_size=仓库大小
|
repo_size=仓库大小
|
||||||
template=模板
|
template=模板
|
||||||
@ -1380,6 +1390,8 @@ editor.failed_to_commit=提交更改失败。
|
|||||||
editor.failed_to_commit_summary=错误信息:
|
editor.failed_to_commit_summary=错误信息:
|
||||||
|
|
||||||
editor.fork_create=派生仓库以请求变更
|
editor.fork_create=派生仓库以请求变更
|
||||||
|
editor.fork_create_description=您不能直接编辑此仓库。您可以派生此仓库,进行编辑并创建一个合并请求。
|
||||||
|
editor.fork_edit_description=您不能直接编辑此仓库。 更改将写入您的派生仓库 <b>%s</b>,以便您可以创建一个合并请求。
|
||||||
editor.fork_not_editable=您已经派生了此仓库,但您的派生是不可编辑的。
|
editor.fork_not_editable=您已经派生了此仓库,但您的派生是不可编辑的。
|
||||||
editor.fork_failed_to_push_branch=推送分支 %s 到仓库失败。
|
editor.fork_failed_to_push_branch=推送分支 %s 到仓库失败。
|
||||||
editor.fork_branch_exists=分支「%s」已存在于您的派生仓库中,请选择一个新的分支名称。
|
editor.fork_branch_exists=分支「%s」已存在于您的派生仓库中,请选择一个新的分支名称。
|
||||||
@ -2795,6 +2807,7 @@ team_permission_desc=权限
|
|||||||
team_unit_desc=允许访问仓库单元
|
team_unit_desc=允许访问仓库单元
|
||||||
team_unit_disabled=(已禁用)
|
team_unit_disabled=(已禁用)
|
||||||
|
|
||||||
|
form.name_been_taken=组织名称「%s」已经被占用。
|
||||||
form.name_reserved=组织名称「%s」是保留的。
|
form.name_reserved=组织名称「%s」是保留的。
|
||||||
form.name_pattern_not_allowed=组织名中不允许使用「%s」格式。
|
form.name_pattern_not_allowed=组织名中不允许使用「%s」格式。
|
||||||
form.create_org_not_allowed=此账号禁止创建组织
|
form.create_org_not_allowed=此账号禁止创建组织
|
||||||
@ -2836,6 +2849,7 @@ settings.delete_notices_2=此操作将永久删除 <strong>%s</strong> 的所有
|
|||||||
settings.delete_notices_3=此操作将永久删除 <strong>%s</strong> 的所有 <strong>软件包</strong>。
|
settings.delete_notices_3=此操作将永久删除 <strong>%s</strong> 的所有 <strong>软件包</strong>。
|
||||||
settings.delete_notices_4=此操作将永久删除 <strong>%s</strong> 的所有 <strong>项目</strong>。
|
settings.delete_notices_4=此操作将永久删除 <strong>%s</strong> 的所有 <strong>项目</strong>。
|
||||||
settings.confirm_delete_account=确认删除组织
|
settings.confirm_delete_account=确认删除组织
|
||||||
|
settings.delete_failed=由于内部错误,删除组织失败
|
||||||
settings.delete_successful=组织 <b>%s</b> 已成功删除。
|
settings.delete_successful=组织 <b>%s</b> 已成功删除。
|
||||||
settings.hooks_desc=在此处添加的 Web 钩子将会应用到该组织下的 <strong>所有仓库</strong>。
|
settings.hooks_desc=在此处添加的 Web 钩子将会应用到该组织下的 <strong>所有仓库</strong>。
|
||||||
|
|
||||||
@ -2985,6 +2999,8 @@ dashboard.resync_all_sshprincipals=使用 Gitea 的 SSH 规则更新「.ssh/auth
|
|||||||
dashboard.resync_all_hooks=重新同步所有仓库的 pre-receive、update 和 post-receive 钩子
|
dashboard.resync_all_hooks=重新同步所有仓库的 pre-receive、update 和 post-receive 钩子
|
||||||
dashboard.reinit_missing_repos=重新初始化所有丢失的 Git 仓库存在的记录
|
dashboard.reinit_missing_repos=重新初始化所有丢失的 Git 仓库存在的记录
|
||||||
dashboard.sync_external_users=同步外部用户数据
|
dashboard.sync_external_users=同步外部用户数据
|
||||||
|
dashboard.cleanup_hook_task_table=清理 hook_task 表
|
||||||
|
dashboard.cleanup_actions=清理过期的工作流资源
|
||||||
dashboard.server_uptime=服务运行时间
|
dashboard.server_uptime=服务运行时间
|
||||||
dashboard.current_goroutine=当前 Goroutines 数量
|
dashboard.current_goroutine=当前 Goroutines 数量
|
||||||
dashboard.current_memory_usage=当前内存使用量
|
dashboard.current_memory_usage=当前内存使用量
|
||||||
@ -3215,6 +3231,8 @@ auths.oauth2_required_claim_name_helper=设置此名称,只有具有此名称
|
|||||||
auths.oauth2_required_claim_value=必须填写 Claim 声明的值
|
auths.oauth2_required_claim_value=必须填写 Claim 声明的值
|
||||||
auths.oauth2_required_claim_value_helper=设置此值,只有拥有对应的声明(Claim)的名称和值的用户才被允许从此源登录
|
auths.oauth2_required_claim_value_helper=设置此值,只有拥有对应的声明(Claim)的名称和值的用户才被允许从此源登录
|
||||||
auths.oauth2_group_claim_name=用于提供用户组名称的 Claim 声明名称。(可选)
|
auths.oauth2_group_claim_name=用于提供用户组名称的 Claim 声明名称。(可选)
|
||||||
|
auths.oauth2_full_name_claim_name=全名声明名称。(可选,如果设置,用户的全名将始终与此声明同步)
|
||||||
|
auths.oauth2_ssh_public_key_claim_name=SSH 公钥声明名称
|
||||||
auths.oauth2_admin_group=管理员用户组的 Claim 声明值。(可选 - 需要上面的声明名称)
|
auths.oauth2_admin_group=管理员用户组的 Claim 声明值。(可选 - 需要上面的声明名称)
|
||||||
auths.oauth2_restricted_group=受限用户组的 Claim 声明值。(可选 - 需要上面的声明名称)
|
auths.oauth2_restricted_group=受限用户组的 Claim 声明值。(可选 - 需要上面的声明名称)
|
||||||
auths.oauth2_map_group_to_team=映射声明的组到组织团队。(可选 - 要求在上面填写声明的名字)
|
auths.oauth2_map_group_to_team=映射声明的组到组织团队。(可选 - 要求在上面填写声明的名字)
|
||||||
@ -3690,6 +3708,7 @@ owner.settings.cargo.initialize.success=Cargo 索引已经成功创建。
|
|||||||
owner.settings.cargo.rebuild=重建索引
|
owner.settings.cargo.rebuild=重建索引
|
||||||
owner.settings.cargo.rebuild.description=如果索引与存储的 Cargo 包不同步,重建可能会有用。
|
owner.settings.cargo.rebuild.description=如果索引与存储的 Cargo 包不同步,重建可能会有用。
|
||||||
owner.settings.cargo.rebuild.error=无法重建 Cargo 索引: %v
|
owner.settings.cargo.rebuild.error=无法重建 Cargo 索引: %v
|
||||||
|
owner.settings.cargo.rebuild.success=Cargo 索引已成功重建。
|
||||||
owner.settings.cleanuprules.title=管理清理规则
|
owner.settings.cleanuprules.title=管理清理规则
|
||||||
owner.settings.cleanuprules.add=添加清理规则
|
owner.settings.cleanuprules.add=添加清理规则
|
||||||
owner.settings.cleanuprules.edit=编辑清理规则
|
owner.settings.cleanuprules.edit=编辑清理规则
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
[virtualenvs]
|
|
||||||
in-project = true
|
|
||||||
options.no-pip = true
|
|
@ -46,7 +46,7 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: owner of the repository
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -216,7 +216,7 @@ func (Action) GetVariable(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -270,7 +270,7 @@ func (Action) DeleteVariable(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -319,7 +319,7 @@ func (Action) CreateVariable(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -386,7 +386,7 @@ func (Action) UpdateVariable(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -458,7 +458,7 @@ func (Action) ListVariables(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -660,7 +660,7 @@ func (Action) ListWorkflowJobs(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -704,7 +704,7 @@ func (Action) ListWorkflowRuns(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -1110,7 +1110,7 @@ func GetWorkflowRun(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -1156,7 +1156,7 @@ func ListWorkflowRunJobs(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -1215,7 +1215,7 @@ func GetWorkflowJob(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -1261,7 +1261,7 @@ func GetArtifactsOfRun(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -1330,7 +1330,7 @@ func DeleteActionRun(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -1382,7 +1382,7 @@ func GetArtifacts(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -1443,7 +1443,7 @@ func GetArtifact(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -1492,7 +1492,7 @@ func DeleteArtifact(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
@ -1559,7 +1559,7 @@ func DownloadArtifact(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
|
@ -21,7 +21,7 @@ func DownloadActionsRunJobLogs(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the owner
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
|
@ -52,7 +52,7 @@ func ListPullRequests(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: Owner of the repo
|
// description: owner of the repo
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: repo
|
// - name: repo
|
||||||
|
@ -329,7 +329,7 @@ func Generate(ctx *context.APIContext) {
|
|||||||
// parameters:
|
// parameters:
|
||||||
// - name: template_owner
|
// - name: template_owner
|
||||||
// in: path
|
// in: path
|
||||||
// description: name of the template repository owner
|
// description: owner of the template repository
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// - name: template_repo
|
// - name: template_repo
|
||||||
|
@ -49,28 +49,21 @@ func deleteDeployKeyFromDB(ctx context.Context, key *asymkey_model.DeployKey) er
|
|||||||
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
|
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
|
||||||
// Permissions check should be done outside.
|
// Permissions check should be done outside.
|
||||||
func DeleteDeployKey(ctx context.Context, repo *repo_model.Repository, id int64) error {
|
func DeleteDeployKey(ctx context.Context, repo *repo_model.Repository, id int64) error {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
key, err := asymkey_model.GetDeployKeyByID(ctx, id)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
if asymkey_model.IsErrDeployKeyNotExist(err) {
|
||||||
defer committer.Close()
|
return nil
|
||||||
|
}
|
||||||
key, err := asymkey_model.GetDeployKeyByID(ctx, id)
|
return fmt.Errorf("GetDeployKeyByID: %w", err)
|
||||||
if err != nil {
|
|
||||||
if asymkey_model.IsErrDeployKeyNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf("GetDeployKeyByID: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.RepoID != repo.ID {
|
if key.RepoID != repo.ID {
|
||||||
return fmt.Errorf("deploy key %d does not belong to repository %d", id, repo.ID)
|
return fmt.Errorf("deploy key %d does not belong to repository %d", id, repo.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := deleteDeployKeyFromDB(dbCtx, key); err != nil {
|
return deleteDeployKeyFromDB(ctx, key)
|
||||||
return err
|
}); err != nil {
|
||||||
}
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,20 +27,9 @@ func DeletePublicKey(ctx context.Context, doer *user_model.User, id int64) (err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
if _, err = db.DeleteByID[asymkey_model.PublicKey](ctx, id); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if _, err = db.DeleteByID[asymkey_model.PublicKey](dbCtx, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = committer.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
committer.Close()
|
|
||||||
|
|
||||||
if key.Type == asymkey_model.KeyTypePrincipal {
|
if key.Type == asymkey_model.KeyTypePrincipal {
|
||||||
return RewriteAllPrincipalKeys(ctx)
|
return RewriteAllPrincipalKeys(ctx)
|
||||||
|
@ -14,24 +14,6 @@ import (
|
|||||||
|
|
||||||
// AddPrincipalKey adds new principal to database and authorized_principals file.
|
// AddPrincipalKey adds new principal to database and authorized_principals file.
|
||||||
func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSourceID int64) (*asymkey_model.PublicKey, error) {
|
func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSourceID int64) (*asymkey_model.PublicKey, error) {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
// Principals cannot be duplicated.
|
|
||||||
has, err := db.GetEngine(dbCtx).
|
|
||||||
Where("content = ? AND type = ?", content, asymkey_model.KeyTypePrincipal).
|
|
||||||
Get(new(asymkey_model.PublicKey))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if has {
|
|
||||||
return nil, asymkey_model.ErrKeyAlreadyExist{
|
|
||||||
Content: content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
key := &asymkey_model.PublicKey{
|
key := &asymkey_model.PublicKey{
|
||||||
OwnerID: ownerID,
|
OwnerID: ownerID,
|
||||||
Name: content,
|
Name: content,
|
||||||
@ -40,15 +22,27 @@ func AddPrincipalKey(ctx context.Context, ownerID int64, content string, authSou
|
|||||||
Type: asymkey_model.KeyTypePrincipal,
|
Type: asymkey_model.KeyTypePrincipal,
|
||||||
LoginSourceID: authSourceID,
|
LoginSourceID: authSourceID,
|
||||||
}
|
}
|
||||||
if err = db.Insert(dbCtx, key); err != nil {
|
|
||||||
return nil, fmt.Errorf("addKey: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = committer.Commit(); err != nil {
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
// Principals cannot be duplicated.
|
||||||
|
has, err := db.GetEngine(ctx).
|
||||||
|
Where("content = ? AND type = ?", content, asymkey_model.KeyTypePrincipal).
|
||||||
|
Get(new(asymkey_model.PublicKey))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if has {
|
||||||
|
return asymkey_model.ErrKeyAlreadyExist{
|
||||||
|
Content: content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = db.Insert(ctx, key); err != nil {
|
||||||
|
return fmt.Errorf("addKey: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
committer.Close()
|
|
||||||
|
|
||||||
return key, RewriteAllPrincipalKeys(ctx)
|
return key, RewriteAllPrincipalKeys(ctx)
|
||||||
}
|
}
|
||||||
|
@ -46,32 +46,24 @@ func AddLabels(ctx context.Context, issue *issues_model.Issue, doer *user_model.
|
|||||||
|
|
||||||
// RemoveLabel removes a label from issue by given ID.
|
// RemoveLabel removes a label from issue by given ID.
|
||||||
func RemoveLabel(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, label *issues_model.Label) error {
|
func RemoveLabel(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, label *issues_model.Label) error {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err := issue.LoadRepo(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := issue.LoadRepo(dbCtx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
perm, err := access_model.GetUserRepoPermission(dbCtx, issue.Repo, doer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
|
|
||||||
if label.OrgID > 0 {
|
|
||||||
return issues_model.ErrOrgLabelNotExist{}
|
|
||||||
}
|
}
|
||||||
return issues_model.ErrRepoLabelNotExist{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := issues_model.DeleteIssueLabel(dbCtx, issue, label, doer); err != nil {
|
perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||||
|
if label.OrgID > 0 {
|
||||||
|
return issues_model.ErrOrgLabelNotExist{}
|
||||||
|
}
|
||||||
|
return issues_model.ErrRepoLabelNotExist{}
|
||||||
|
}
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
return issues_model.DeleteIssueLabel(ctx, issue, label, doer)
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,21 +69,12 @@ func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *is
|
|||||||
|
|
||||||
// ChangeMilestoneAssign changes assignment of milestone for issue.
|
// ChangeMilestoneAssign changes assignment of milestone for issue.
|
||||||
func ChangeMilestoneAssign(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, oldMilestoneID int64) (err error) {
|
func ChangeMilestoneAssign(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, oldMilestoneID int64) (err error) {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
if err := db.WithTx(ctx, func(dbCtx context.Context) error {
|
||||||
if err != nil {
|
return changeMilestoneAssign(dbCtx, doer, issue, oldMilestoneID)
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = changeMilestoneAssign(dbCtx, doer, issue, oldMilestoneID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = committer.Commit(); err != nil {
|
|
||||||
return fmt.Errorf("Commit: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
notify_service.IssueChangeMilestone(ctx, doer, issue, oldMilestoneID)
|
notify_service.IssueChangeMilestone(ctx, doer, issue, oldMilestoneID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -15,31 +15,25 @@ import (
|
|||||||
|
|
||||||
// CloseIssue close an issue.
|
// CloseIssue close an issue.
|
||||||
func CloseIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string) error {
|
func CloseIssue(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, commitID string) error {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
var comment *issues_model.Comment
|
||||||
if err != nil {
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
return err
|
var err error
|
||||||
}
|
comment, err = issues_model.CloseIssue(ctx, issue, doer)
|
||||||
defer committer.Close()
|
if err != nil {
|
||||||
|
if issues_model.IsErrDependenciesLeft(err) {
|
||||||
comment, err := issues_model.CloseIssue(dbCtx, issue, doer)
|
if _, err := issues_model.FinishIssueStopwatch(ctx, doer, issue); err != nil {
|
||||||
if err != nil {
|
log.Error("Unable to stop stopwatch for issue[%d]#%d: %v", issue.ID, issue.Index, err)
|
||||||
if issues_model.IsErrDependenciesLeft(err) {
|
}
|
||||||
if _, err := issues_model.FinishIssueStopwatch(dbCtx, doer, issue); err != nil {
|
|
||||||
log.Error("Unable to stop stopwatch for issue[%d]#%d: %v", issue.ID, issue.Index, err)
|
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := issues_model.FinishIssueStopwatch(dbCtx, doer, issue); err != nil {
|
_, err = issues_model.FinishIssueStopwatch(ctx, doer, issue)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
committer.Close()
|
|
||||||
|
|
||||||
notify_service.IssueChangeStatus(ctx, doer, commitID, issue, comment, true)
|
notify_service.IssueChangeStatus(ctx, doer, commitID, issue, comment, true)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -54,39 +54,33 @@ func NewTeam(ctx context.Context, t *organization.Team) (err error) {
|
|||||||
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
|
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err = db.Insert(ctx, t); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = db.Insert(ctx, t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert units for team
|
|
||||||
if len(t.Units) > 0 {
|
|
||||||
for _, unit := range t.Units {
|
|
||||||
unit.TeamID = t.ID
|
|
||||||
}
|
|
||||||
if err = db.Insert(ctx, &t.Units); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add all repositories to the team if it has access to all of them.
|
// insert units for team
|
||||||
if t.IncludesAllRepositories {
|
if len(t.Units) > 0 {
|
||||||
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
for _, unit := range t.Units {
|
||||||
if err != nil {
|
unit.TeamID = t.ID
|
||||||
return fmt.Errorf("addAllRepositories: %w", err)
|
}
|
||||||
|
if err = db.Insert(ctx, &t.Units); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Update organization number of teams.
|
// Add all repositories to the team if it has access to all of them.
|
||||||
if _, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
|
if t.IncludesAllRepositories {
|
||||||
|
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("addAllRepositories: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update organization number of teams.
|
||||||
|
_, err = db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTeam updates information of team.
|
// UpdateTeam updates information of team.
|
||||||
@ -99,128 +93,117 @@ func UpdateTeam(ctx context.Context, t *organization.Team, authChanged, includeA
|
|||||||
t.Description = t.Description[:255]
|
t.Description = t.Description[:255]
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
t.LowerName = strings.ToLower(t.Name)
|
||||||
return err
|
has, err := db.Exist[organization.Team](ctx, builder.Eq{
|
||||||
}
|
"org_id": t.OrgID,
|
||||||
defer committer.Close()
|
"lower_name": t.LowerName,
|
||||||
|
}.And(builder.Neq{"id": t.ID}),
|
||||||
t.LowerName = strings.ToLower(t.Name)
|
)
|
||||||
has, err := db.Exist[organization.Team](ctx, builder.Eq{
|
|
||||||
"org_id": t.OrgID,
|
|
||||||
"lower_name": t.LowerName,
|
|
||||||
}.And(builder.Neq{"id": t.ID}),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if has {
|
|
||||||
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := db.GetEngine(ctx)
|
|
||||||
if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
|
|
||||||
"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
|
|
||||||
return fmt.Errorf("update: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// update units for team
|
|
||||||
if len(t.Units) > 0 {
|
|
||||||
for _, unit := range t.Units {
|
|
||||||
unit.TeamID = t.ID
|
|
||||||
}
|
|
||||||
// Delete team-unit.
|
|
||||||
if _, err := sess.
|
|
||||||
Where("team_id=?", t.ID).
|
|
||||||
Delete(new(organization.TeamUnit)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update access for team members if needed.
|
|
||||||
if authChanged {
|
|
||||||
repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
|
|
||||||
TeamID: t.ID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetTeamRepositories: %w", err)
|
return err
|
||||||
|
} else if has {
|
||||||
|
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, repo := range repos {
|
sess := db.GetEngine(ctx)
|
||||||
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
|
if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
|
||||||
return fmt.Errorf("recalculateTeamAccesses: %w", err)
|
"can_create_org_repo", "authorize", "includes_all_repositories").Update(t); err != nil {
|
||||||
|
return fmt.Errorf("update: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update units for team
|
||||||
|
if len(t.Units) > 0 {
|
||||||
|
for _, unit := range t.Units {
|
||||||
|
unit.TeamID = t.ID
|
||||||
|
}
|
||||||
|
// Delete team-unit.
|
||||||
|
if _, err := sess.
|
||||||
|
Where("team_id=?", t.ID).
|
||||||
|
Delete(new(organization.TeamUnit)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add all repositories to the team if it has access to all of them.
|
// Update access for team members if needed.
|
||||||
if includeAllChanged && t.IncludesAllRepositories {
|
if authChanged {
|
||||||
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
|
||||||
if err != nil {
|
TeamID: t.ID,
|
||||||
return fmt.Errorf("addAllRepositories: %w", err)
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("GetTeamRepositories: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, repo := range repos {
|
||||||
|
if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
|
||||||
|
return fmt.Errorf("recalculateTeamAccesses: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
// Add all repositories to the team if it has access to all of them.
|
||||||
|
if includeAllChanged && t.IncludesAllRepositories {
|
||||||
|
err = repo_service.AddAllRepositoriesToTeam(ctx, t)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("addAllRepositories: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTeam deletes given team.
|
// DeleteTeam deletes given team.
|
||||||
// It's caller's responsibility to assign organization ID.
|
// It's caller's responsibility to assign organization ID.
|
||||||
func DeleteTeam(ctx context.Context, t *organization.Team) error {
|
func DeleteTeam(ctx context.Context, t *organization.Team) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if err := t.LoadMembers(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := t.LoadMembers(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// update branch protections
|
|
||||||
{
|
|
||||||
protections := make([]*git_model.ProtectedBranch, 0, 10)
|
|
||||||
err := db.GetEngine(ctx).In("repo_id",
|
|
||||||
builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
|
|
||||||
Find(&protections)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("findProtectedBranches: %w", err)
|
|
||||||
}
|
}
|
||||||
for _, p := range protections {
|
|
||||||
if err := git_model.RemoveTeamIDFromProtectedBranch(ctx, p, t.ID); err != nil {
|
// update branch protections
|
||||||
|
{
|
||||||
|
protections := make([]*git_model.ProtectedBranch, 0, 10)
|
||||||
|
err := db.GetEngine(ctx).In("repo_id",
|
||||||
|
builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
|
||||||
|
Find(&protections)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("findProtectedBranches: %w", err)
|
||||||
|
}
|
||||||
|
for _, p := range protections {
|
||||||
|
if err := git_model.RemoveTeamIDFromProtectedBranch(ctx, p, t.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := repo_service.RemoveAllRepositoriesFromTeam(ctx, t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.DeleteBeans(ctx,
|
||||||
|
&organization.Team{ID: t.ID},
|
||||||
|
&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
|
||||||
|
&organization.TeamUnit{TeamID: t.ID},
|
||||||
|
&organization.TeamInvite{TeamID: t.ID},
|
||||||
|
&issues_model.Review{Type: issues_model.ReviewTypeRequest, ReviewerTeamID: t.ID}, // batch delete the binding relationship between team and PR (request review from team)
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tm := range t.Members {
|
||||||
|
if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := repo_service.RemoveAllRepositoriesFromTeam(ctx, t); err != nil {
|
// Update organization number of teams.
|
||||||
|
_, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID)
|
||||||
return err
|
return err
|
||||||
}
|
})
|
||||||
|
|
||||||
if err := db.DeleteBeans(ctx,
|
|
||||||
&organization.Team{ID: t.ID},
|
|
||||||
&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
|
|
||||||
&organization.TeamUnit{TeamID: t.ID},
|
|
||||||
&organization.TeamInvite{TeamID: t.ID},
|
|
||||||
&issues_model.Review{Type: issues_model.ReviewTypeRequest, ReviewerTeamID: t.ID}, // batch delete the binding relationship between team and PR (request review from team)
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tm := range t.Members {
|
|
||||||
if err := removeInvalidOrgUser(ctx, t.OrgID, tm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update organization number of teams.
|
|
||||||
if _, err := db.Exec(ctx, "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTeamMember adds new membership of given team to given organization,
|
// AddTeamMember adds new membership of given team to given organization,
|
||||||
@ -363,13 +346,7 @@ func removeInvalidOrgUser(ctx context.Context, orgID int64, user *user_model.Use
|
|||||||
|
|
||||||
// RemoveTeamMember removes member from given team of given organization.
|
// RemoveTeamMember removes member from given team of given organization.
|
||||||
func RemoveTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
|
func RemoveTeamMember(ctx context.Context, team *organization.Team, user *user_model.User) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return removeTeamMember(ctx, team, user)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
if err := removeTeamMember(ctx, team, user); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
@ -164,42 +164,38 @@ func ExecuteCleanupRules(ctx context.Context) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error {
|
func CleanupExpiredData(ctx context.Context, olderThan time.Duration) error {
|
||||||
ctx, committer, err := db.TxContext(outerCtx)
|
pbs := make([]*packages_model.PackageBlob, 0, 100)
|
||||||
if err != nil {
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
return err
|
if err := container_service.Cleanup(ctx, olderThan); err != nil {
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := container_service.Cleanup(ctx, olderThan); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ps, err := packages_model.FindUnreferencedPackages(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, p := range ps {
|
|
||||||
if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil {
|
|
||||||
|
ps, err := packages_model.FindUnreferencedPackages(ctx)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
for _, p := range ps {
|
||||||
|
if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pbs, err := packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan)
|
pbs, err = packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pb := range pbs {
|
|
||||||
if err := packages_model.DeleteBlobByID(ctx, pb.ID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
for _, pb := range pbs {
|
||||||
|
if err := packages_model.DeleteBlobByID(ctx, pb.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,24 +469,15 @@ func RemovePackageVersionByNameAndVersion(ctx context.Context, doer *user_model.
|
|||||||
|
|
||||||
// RemovePackageVersion deletes the package version and all associated files
|
// RemovePackageVersion deletes the package version and all associated files
|
||||||
func RemovePackageVersion(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error {
|
func RemovePackageVersion(ctx context.Context, doer *user_model.User, pv *packages_model.PackageVersion) error {
|
||||||
dbCtx, committer, err := db.TxContext(ctx)
|
pd, err := packages_model.GetPackageDescriptor(ctx, pv)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
pd, err := packages_model.GetPackageDescriptor(dbCtx, pv)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("Deleting package: %v", pv.ID)
|
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
|
log.Trace("Deleting package: %v", pv.ID)
|
||||||
if err := DeletePackageVersionAndReferences(dbCtx, pv); err != nil {
|
return DeletePackageVersionAndReferences(ctx, pv)
|
||||||
return err
|
}); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,48 +687,40 @@ func SetMerged(ctx context.Context, pr *issues_model.PullRequest, mergedCommitID
|
|||||||
return false, fmt.Errorf("unable to merge PullRequest[%d], some required fields are empty", pr.Index)
|
return false, fmt.Errorf("unable to merge PullRequest[%d], some required fields are empty", pr.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx2(ctx, func(ctx context.Context) (bool, error) {
|
||||||
if err != nil {
|
pr.Issue = nil
|
||||||
return false, err
|
if err := pr.LoadIssue(ctx); err != nil {
|
||||||
}
|
return false, err
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
pr.Issue = nil
|
if err := pr.Issue.LoadRepo(ctx); err != nil {
|
||||||
if err := pr.LoadIssue(ctx); err != nil {
|
return false, err
|
||||||
return false, err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if err := pr.Issue.LoadRepo(ctx); err != nil {
|
if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
|
// Removing an auto merge pull and ignore if not exist
|
||||||
return false, err
|
if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
|
||||||
}
|
return false, fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Removing an auto merge pull and ignore if not exist
|
// Set issue as closed
|
||||||
if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
|
if _, err := issues_model.SetIssueAsClosed(ctx, pr.Issue, pr.Merger, true); err != nil {
|
||||||
return false, fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", pr.ID, err)
|
return false, fmt.Errorf("ChangeIssueStatus: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set issue as closed
|
// We need to save all of the data used to compute this merge as it may have already been changed by testPullRequestBranchMergeable. FIXME: need to set some state to prevent testPullRequestBranchMergeable from running whilst we are merging.
|
||||||
if _, err := issues_model.SetIssueAsClosed(ctx, pr.Issue, pr.Merger, true); err != nil {
|
if cnt, err := db.GetEngine(ctx).Where("id = ?", pr.ID).
|
||||||
return false, fmt.Errorf("ChangeIssueStatus: %w", err)
|
And("has_merged = ?", false).
|
||||||
}
|
Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").
|
||||||
|
Update(pr); err != nil {
|
||||||
|
return false, fmt.Errorf("failed to update pr[%d]: %w", pr.ID, err)
|
||||||
|
} else if cnt != 1 {
|
||||||
|
return false, issues_model.ErrIssueAlreadyChanged
|
||||||
|
}
|
||||||
|
|
||||||
// We need to save all of the data used to compute this merge as it may have already been changed by testPullRequestBranchMergeable. FIXME: need to set some state to prevent testPullRequestBranchMergeable from running whilst we are merging.
|
return true, nil
|
||||||
if cnt, err := db.GetEngine(ctx).Where("id = ?", pr.ID).
|
})
|
||||||
And("has_merged = ?", false).
|
|
||||||
Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").
|
|
||||||
Update(pr); err != nil {
|
|
||||||
return false, fmt.Errorf("failed to update pr[%d]: %w", pr.ID, err)
|
|
||||||
} else if cnt != 1 {
|
|
||||||
return false, issues_model.ErrIssueAlreadyChanged
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := committer.Commit(); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
}
|
||||||
|
@ -29,35 +29,30 @@ func UploadAvatar(ctx context.Context, repo *repo_model.Repository, data []byte)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
oldAvatarPath := repo.CustomAvatarRelativePath()
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
oldAvatarPath := repo.CustomAvatarRelativePath()
|
// Users can upload the same image to other repo - prefix it with ID
|
||||||
|
// Then repo will be removed - only it avatar file will be removed
|
||||||
// Users can upload the same image to other repo - prefix it with ID
|
repo.Avatar = newAvatar
|
||||||
// Then repo will be removed - only it avatar file will be removed
|
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
|
||||||
repo.Avatar = newAvatar
|
return fmt.Errorf("UploadAvatar: Update repository avatar: %w", err)
|
||||||
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
|
|
||||||
return fmt.Errorf("UploadAvatar: Update repository avatar: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error {
|
|
||||||
_, err := w.Write(avatarData)
|
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %w", repo.RepoPath(), newAvatar, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(oldAvatarPath) > 0 {
|
|
||||||
if err := storage.RepoAvatars.Delete(oldAvatarPath); err != nil {
|
|
||||||
return fmt.Errorf("UploadAvatar: Failed to remove old repo avatar %s: %w", oldAvatarPath, err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error {
|
||||||
|
_, err := w.Write(avatarData)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %w", repo.RepoPath(), newAvatar, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(oldAvatarPath) > 0 {
|
||||||
|
if err := storage.RepoAvatars.Delete(oldAvatarPath); err != nil {
|
||||||
|
return fmt.Errorf("UploadAvatar: Failed to remove old repo avatar %s: %w", oldAvatarPath, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAvatar deletes the repos's custom avatar.
|
// DeleteAvatar deletes the repos's custom avatar.
|
||||||
@ -70,22 +65,17 @@ func DeleteAvatar(ctx context.Context, repo *repo_model.Repository) error {
|
|||||||
avatarPath := repo.CustomAvatarRelativePath()
|
avatarPath := repo.CustomAvatarRelativePath()
|
||||||
log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath)
|
log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath)
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
repo.Avatar = ""
|
||||||
return err
|
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
|
||||||
}
|
return fmt.Errorf("DeleteAvatar: Update repository avatar: %w", err)
|
||||||
defer committer.Close()
|
}
|
||||||
|
|
||||||
repo.Avatar = ""
|
if err := storage.RepoAvatars.Delete(avatarPath); err != nil {
|
||||||
if err := repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "avatar"); err != nil {
|
return fmt.Errorf("DeleteAvatar: Failed to remove %s: %w", avatarPath, err)
|
||||||
return fmt.Errorf("DeleteAvatar: Update repository avatar: %w", err)
|
}
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
if err := storage.RepoAvatars.Delete(avatarPath); err != nil {
|
|
||||||
return fmt.Errorf("DeleteAvatar: Failed to remove %s: %w", avatarPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories
|
// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories
|
||||||
|
@ -71,40 +71,32 @@ func DeleteCollaboration(ctx context.Context, repo *repo_model.Repository, colla
|
|||||||
UserID: collaborator.ID,
|
UserID: collaborator.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
} else if has == 0 {
|
||||||
defer committer.Close()
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil {
|
if err := repo.LoadOwner(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if has == 0 {
|
}
|
||||||
return committer.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := repo.LoadOwner(ctx); err != nil {
|
if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
|
if err = repo_model.WatchRepo(ctx, collaborator, repo, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = repo_model.WatchRepo(ctx, collaborator, repo, false); err != nil {
|
if err = ReconsiderWatches(ctx, repo, collaborator); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = ReconsiderWatches(ctx, repo, collaborator); err != nil {
|
// Unassign a user from any issue (s)he has been assigned to in the repository
|
||||||
return err
|
return ReconsiderRepoIssuesAssignee(ctx, repo, collaborator)
|
||||||
}
|
})
|
||||||
|
|
||||||
// Unassign a user from any issue (s)he has been assigned to in the repository
|
|
||||||
if err := ReconsiderRepoIssuesAssignee(ctx, repo, collaborator); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
||||||
|
@ -86,17 +86,9 @@ func RemoveAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return removeAllRepositoriesFromTeam(ctx, t)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = removeAllRepositoriesFromTeam(ctx, t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeAllRepositoriesFromTeam removes all repositories from team and recalculates access
|
// removeAllRepositoriesFromTeam removes all repositories from team and recalculates access
|
||||||
@ -167,17 +159,9 @@ func RemoveRepositoryFromTeam(ctx context.Context, t *organization.Team, repoID
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return removeRepositoryFromTeam(ctx, t, repo, true)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err = removeRepositoryFromTeam(ctx, t, repo, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeRepositoryFromTeam removes a repository from a team and recalculates access
|
// removeRepositoryFromTeam removes a repository from a team and recalculates access
|
||||||
|
@ -16,41 +16,37 @@ import (
|
|||||||
|
|
||||||
// UpdateRepositoryUnits updates a repository's units
|
// UpdateRepositoryUnits updates a repository's units
|
||||||
func UpdateRepositoryUnits(ctx context.Context, repo *repo_model.Repository, units []repo_model.RepoUnit, deleteUnitTypes []unit.Type) (err error) {
|
func UpdateRepositoryUnits(ctx context.Context, repo *repo_model.Repository, units []repo_model.RepoUnit, deleteUnitTypes []unit.Type) (err error) {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
// Delete existing settings of units before adding again
|
||||||
return err
|
for _, u := range units {
|
||||||
}
|
deleteUnitTypes = append(deleteUnitTypes, u.Type)
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
// Delete existing settings of units before adding again
|
|
||||||
for _, u := range units {
|
|
||||||
deleteUnitTypes = append(deleteUnitTypes, u.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
if slices.Contains(deleteUnitTypes, unit.TypeActions) {
|
|
||||||
if err := actions_service.CleanRepoScheduleTasks(ctx, repo); err != nil {
|
|
||||||
log.Error("CleanRepoScheduleTasks: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for _, u := range units {
|
if slices.Contains(deleteUnitTypes, unit.TypeActions) {
|
||||||
if u.Type == unit.TypeActions {
|
if err := actions_service.CleanRepoScheduleTasks(ctx, repo); err != nil {
|
||||||
if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil {
|
log.Error("CleanRepoScheduleTasks: %v", err)
|
||||||
log.Error("DetectAndHandleSchedules: %v", err)
|
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(repo_model.RepoUnit)); err != nil {
|
for _, u := range units {
|
||||||
return err
|
if u.Type == unit.TypeActions {
|
||||||
}
|
if err := actions_service.DetectAndHandleSchedules(ctx, repo); err != nil {
|
||||||
|
log.Error("DetectAndHandleSchedules: %v", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(units) > 0 {
|
if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(repo_model.RepoUnit)); err != nil {
|
||||||
if err = db.Insert(ctx, units); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
if len(units) > 0 {
|
||||||
|
if err = db.Insert(ctx, units); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -24,26 +24,22 @@ func UploadAvatar(ctx context.Context, u *user_model.User, data []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
u.UseCustomAvatar = true
|
||||||
return err
|
u.Avatar = avatar.HashAvatar(u.ID, data)
|
||||||
}
|
if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
|
||||||
defer committer.Close()
|
return fmt.Errorf("updateUser: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
u.UseCustomAvatar = true
|
if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
|
||||||
u.Avatar = avatar.HashAvatar(u.ID, data)
|
_, err := w.Write(avatarData)
|
||||||
if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
|
return err
|
||||||
return fmt.Errorf("updateUser: %w", err)
|
}); err != nil {
|
||||||
}
|
return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
|
return nil
|
||||||
_, err := w.Write(avatarData)
|
})
|
||||||
return err
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("Failed to create dir %s: %w", u.CustomAvatarRelativePath(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAvatar deletes the user's custom avatar.
|
// DeleteAvatar deletes the user's custom avatar.
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<link href="{{AssetUrlPrefix}}/css/swagger.css?v={{AssetVersion}}" rel="stylesheet">
|
<link href="{{AssetUrlPrefix}}/css/swagger.css?v={{AssetVersion}}" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
{{/* TODO: add Help & Glossary to help users understand the API, and explain some concepts like "Owner" */}}
|
||||||
<a class="swagger-back-link" href="{{AppSubUrl}}/">{{svg "octicon-reply"}}{{ctx.Locale.Tr "return_to_gitea"}}</a>
|
<a class="swagger-back-link" href="{{AppSubUrl}}/">{{svg "octicon-reply"}}{{ctx.Locale.Tr "return_to_gitea"}}</a>
|
||||||
<div id="swagger-ui" data-source="{{AppSubUrl}}/swagger.{{.APIJSONVersion}}.json"></div>
|
<div id="swagger-ui" data-source="{{AppSubUrl}}/swagger.{{.APIJSONVersion}}.json"></div>
|
||||||
<footer class="page-footer"></footer>
|
<footer class="page-footer"></footer>
|
||||||
|
50
templates/swagger/v1_json.tmpl
generated
50
templates/swagger/v1_json.tmpl
generated
@ -4585,7 +4585,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -4630,7 +4630,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -4674,7 +4674,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -4720,7 +4720,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -4766,7 +4766,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -4823,7 +4823,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -4869,7 +4869,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5108,7 +5108,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5189,7 +5189,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5233,7 +5233,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5279,7 +5279,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5331,7 +5331,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5395,7 +5395,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "owner of the repository",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5609,7 +5609,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5660,7 +5660,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5704,7 +5704,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5758,7 +5758,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -5812,7 +5812,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the owner",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -13424,7 +13424,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Owner of the repo",
|
"description": "owner of the repo",
|
||||||
"name": "owner",
|
"name": "owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -17463,7 +17463,7 @@
|
|||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "name of the template repository owner",
|
"description": "owner of the template repository",
|
||||||
"name": "template_owner",
|
"name": "template_owner",
|
||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true
|
"required": true
|
||||||
@ -24968,7 +24968,7 @@
|
|||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
},
|
},
|
||||||
"GenerateRepoOption": {
|
"GenerateRepoOption": {
|
||||||
"description": "GenerateRepoOption options when creating repository using a template",
|
"description": "GenerateRepoOption options when creating a repository using a template",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"owner",
|
"owner",
|
||||||
@ -25006,13 +25006,12 @@
|
|||||||
"x-go-name": "Labels"
|
"x-go-name": "Labels"
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"description": "Name of the repository to create",
|
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"uniqueItems": true,
|
"uniqueItems": true,
|
||||||
"x-go-name": "Name"
|
"x-go-name": "Name"
|
||||||
},
|
},
|
||||||
"owner": {
|
"owner": {
|
||||||
"description": "The organization or person who will own the new repository",
|
"description": "the organization's name or individual user's name who will own the new repository",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-go-name": "Owner"
|
"x-go-name": "Owner"
|
||||||
},
|
},
|
||||||
@ -25545,6 +25544,7 @@
|
|||||||
"x-go-name": "Index"
|
"x-go-name": "Index"
|
||||||
},
|
},
|
||||||
"owner": {
|
"owner": {
|
||||||
|
"description": "owner of the issue's repo",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-go-name": "Owner"
|
"x-go-name": "Owner"
|
||||||
},
|
},
|
||||||
@ -25929,7 +25929,7 @@
|
|||||||
"x-go-name": "RepoName"
|
"x-go-name": "RepoName"
|
||||||
},
|
},
|
||||||
"repo_owner": {
|
"repo_owner": {
|
||||||
"description": "Name of User or Organisation who will own Repo after migration",
|
"description": "the organization's name or individual user's name who will own the migrated repository",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-go-name": "RepoOwner"
|
"x-go-name": "RepoOwner"
|
||||||
},
|
},
|
||||||
@ -25949,7 +25949,7 @@
|
|||||||
"x-go-name": "Service"
|
"x-go-name": "Service"
|
||||||
},
|
},
|
||||||
"uid": {
|
"uid": {
|
||||||
"description": "deprecated (only for backwards compatibility)",
|
"description": "deprecated (only for backwards compatibility, use repo_owner instead)",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64",
|
"format": "int64",
|
||||||
"x-go-name": "RepoOwnerID"
|
"x-go-name": "RepoOwnerID"
|
||||||
|
@ -415,7 +415,7 @@ func testForkToEditFile(t *testing.T, session *TestSession, user, owner, repo, b
|
|||||||
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s-1/_edit/%s/%s?from_base_branch=%s", user, repo, branch, filePath, branch), editRequestForm)
|
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s-1/_edit/%s/%s?from_base_branch=%s", user, repo, branch, filePath, branch), editRequestForm)
|
||||||
resp = session.MakeRequest(t, req, http.StatusBadRequest)
|
resp = session.MakeRequest(t, req, http.StatusBadRequest)
|
||||||
respJSON := test.ParseJSONError(resp.Body.Bytes())
|
respJSON := test.ParseJSONError(resp.Body.Bytes())
|
||||||
assert.Equal(t, `Branch "master" already exists in your fork, please choose a new branch name.`, respJSON.ErrorMessage)
|
assert.Equal(t, `Branch "master" already exists in your fork. Please choose a new branch name.`, respJSON.ErrorMessage)
|
||||||
|
|
||||||
// change a file in the forked repo (should succeed)
|
// change a file in the forked repo (should succeed)
|
||||||
newBranchName := htmlDoc.GetInputValueByName("new_branch_name")
|
newBranchName := htmlDoc.GetInputValueByName("new_branch_name")
|
||||||
|
Loading…
Reference in New Issue
Block a user