mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge remote-tracking branch 'upstream/master' into team-grant-all-repos
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/password"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/services/mailer"
|
||||
@@ -94,7 +95,10 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
|
||||
u.LoginName = form.LoginName
|
||||
}
|
||||
}
|
||||
|
||||
if !password.IsComplexEnough(form.Password) {
|
||||
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserNew, &form)
|
||||
return
|
||||
}
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
switch {
|
||||
case models.IsErrUserAlreadyExist(err):
|
||||
@@ -201,6 +205,10 @@ func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {
|
||||
ctx.ServerError("UpdateUser", err)
|
||||
return
|
||||
}
|
||||
if !password.IsComplexEnough(form.Password) {
|
||||
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserEdit, &form)
|
||||
return
|
||||
}
|
||||
u.HashPassword(form.Password)
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestNewUserPost_MustChangePassword(t *testing.T) {
|
||||
LoginName: "local",
|
||||
UserName: username,
|
||||
Email: email,
|
||||
Password: "xxxxxxxx",
|
||||
Password: "abc123ABC!=$",
|
||||
SendNotify: false,
|
||||
MustChangePassword: true,
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) {
|
||||
LoginName: "local",
|
||||
UserName: username,
|
||||
Email: email,
|
||||
Password: "xxxxxxxx",
|
||||
Password: "abc123ABC!=$",
|
||||
SendNotify: false,
|
||||
MustChangePassword: false,
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/password"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||
"code.gitea.io/gitea/routers/api/v1/user"
|
||||
@@ -73,7 +76,11 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if !password.IsComplexEnough(form.Password) {
|
||||
err := errors.New("PasswordComplexity")
|
||||
ctx.Error(400, "PasswordComplexity", err)
|
||||
return
|
||||
}
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
if models.IsErrUserAlreadyExist(err) ||
|
||||
models.IsErrEmailAlreadyUsed(err) ||
|
||||
@@ -131,6 +138,11 @@ func EditUser(ctx *context.APIContext, form api.EditUserOption) {
|
||||
}
|
||||
|
||||
if len(form.Password) > 0 {
|
||||
if !password.IsComplexEnough(form.Password) {
|
||||
err := errors.New("PasswordComplexity")
|
||||
ctx.Error(400, "PasswordComplexity", err)
|
||||
return
|
||||
}
|
||||
var err error
|
||||
if u.Salt, err = models.GetUserSalt(); err != nil {
|
||||
ctx.Error(500, "UpdateUser", err)
|
||||
|
||||
@@ -507,6 +507,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Get("/swagger", misc.Swagger)
|
||||
}
|
||||
m.Get("/version", misc.Version)
|
||||
m.Get("/signing-key.gpg", misc.SigningKey)
|
||||
m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
|
||||
m.Post("/markdown/raw", misc.MarkdownRaw)
|
||||
|
||||
@@ -771,6 +772,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Delete("", bind(api.DeleteFileOptions{}), repo.DeleteFile)
|
||||
}, reqRepoWriter(models.UnitTypeCode), reqToken())
|
||||
}, reqRepoReader(models.UnitTypeCode))
|
||||
m.Get("/signing-key.gpg", misc.SigningKey)
|
||||
m.Group("/topics", func() {
|
||||
m.Combo("").Get(repo.ListTopics).
|
||||
Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
@@ -84,17 +85,21 @@ func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
|
||||
// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
|
||||
func ToVerification(c *git.Commit) *api.PayloadCommitVerification {
|
||||
verif := models.ParseCommitWithSignature(c)
|
||||
var signature, payload string
|
||||
commitVerification := &api.PayloadCommitVerification{
|
||||
Verified: verif.Verified,
|
||||
Reason: verif.Reason,
|
||||
}
|
||||
if c.Signature != nil {
|
||||
signature = c.Signature.Signature
|
||||
payload = c.Signature.Payload
|
||||
commitVerification.Signature = c.Signature.Signature
|
||||
commitVerification.Payload = c.Signature.Payload
|
||||
}
|
||||
return &api.PayloadCommitVerification{
|
||||
Verified: verif.Verified,
|
||||
Reason: verif.Reason,
|
||||
Signature: signature,
|
||||
Payload: payload,
|
||||
if verif.SigningUser != nil {
|
||||
commitVerification.Signer = &structs.PayloadUser{
|
||||
Name: verif.SigningUser.Name,
|
||||
Email: verif.SigningUser.Email,
|
||||
}
|
||||
}
|
||||
return commitVerification
|
||||
}
|
||||
|
||||
// ToPublicKey convert models.PublicKey to api.PublicKey
|
||||
@@ -233,12 +238,9 @@ func ToTeam(team *models.Team) *api.Team {
|
||||
// ToUser convert models.User to api.User
|
||||
func ToUser(user *models.User, signed, authed bool) *api.User {
|
||||
result := &api.User{
|
||||
ID: user.ID,
|
||||
UserName: user.Name,
|
||||
AvatarURL: user.AvatarLink(),
|
||||
FullName: markup.Sanitize(user.FullName),
|
||||
IsAdmin: user.IsAdmin,
|
||||
LastLogin: user.LastLoginUnix.AsTime(),
|
||||
Created: user.CreatedUnix.AsTime(),
|
||||
}
|
||||
// hide primary email if API caller isn't user itself or an admin
|
||||
@@ -246,8 +248,11 @@ func ToUser(user *models.User, signed, authed bool) *api.User {
|
||||
result.Email = ""
|
||||
} else if user.KeepEmailPrivate && !authed {
|
||||
result.Email = user.GetEmail()
|
||||
} else {
|
||||
} else { // only user himself and admin could visit these information
|
||||
result.ID = user.ID
|
||||
result.Email = user.Email
|
||||
result.IsAdmin = user.IsAdmin
|
||||
result.LastLogin = user.LastLoginUnix.AsTime()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// SigningKey returns the public key of the default signing key if it exists
|
||||
func SigningKey(ctx *context.Context) {
|
||||
// swagger:operation GET /signing-key.gpg miscellaneous getSigningKey
|
||||
// ---
|
||||
// summary: Get default signing-key.gpg
|
||||
// produces:
|
||||
// - text/plain
|
||||
// responses:
|
||||
// "200":
|
||||
// description: "GPG armored public key"
|
||||
// schema:
|
||||
// type: string
|
||||
|
||||
// swagger:operation GET /repos/{owner}/{repo}/signing-key.gpg repository repoSigningKey
|
||||
// ---
|
||||
// summary: Get signing-key.gpg for given repository
|
||||
// produces:
|
||||
// - text/plain
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// description: "GPG armored public key"
|
||||
// schema:
|
||||
// type: string
|
||||
|
||||
path := ""
|
||||
if ctx.Repo != nil && ctx.Repo.Repository != nil {
|
||||
path = ctx.Repo.Repository.RepoPath()
|
||||
}
|
||||
|
||||
content, err := models.PublicSigningKey(path)
|
||||
if err != nil {
|
||||
ctx.ServerError("gpg export", err)
|
||||
return
|
||||
}
|
||||
_, err = ctx.Write([]byte(content))
|
||||
if err != nil {
|
||||
log.Error("Error writing key content %v", err)
|
||||
ctx.Error(http.StatusInternalServerError, fmt.Sprintf("%v", err))
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ func GetEditorconfig(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
fileName := ctx.Params("filename")
|
||||
def := ec.GetDefinitionForFilename(fileName)
|
||||
def, err := ec.GetDefinitionForFilename(fileName)
|
||||
if def == nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
|
||||
@@ -213,12 +213,31 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the passed assignees is assignable
|
||||
for _, aID := range assigneeIDs {
|
||||
assignee, err := models.GetUserByID(aID)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetUserByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
valid, err := models.CanBeAssigned(assignee, ctx.Repo.Repository, false)
|
||||
if err != nil {
|
||||
ctx.Error(500, "canBeAssigned", err)
|
||||
return
|
||||
}
|
||||
if !valid {
|
||||
ctx.Error(422, "canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: ctx.Repo.Repository.Name})
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// setting labels is not allowed if user is not a writer
|
||||
form.Labels = make([]int64, 0)
|
||||
}
|
||||
|
||||
if err := issue_service.NewIssue(ctx.Repo.Repository, issue, form.Labels, assigneeIDs, nil); err != nil {
|
||||
if err := issue_service.NewIssue(ctx.Repo.Repository, issue, form.Labels, nil); err != nil {
|
||||
if models.IsErrUserDoesNotHaveAccessToRepo(err) {
|
||||
ctx.Error(400, "UserDoesNotHaveAccessToRepo", err)
|
||||
return
|
||||
@@ -227,6 +246,11 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := issue_service.AddAssignees(issue, ctx.User, assigneeIDs); err != nil {
|
||||
ctx.ServerError("AddAssignees", err)
|
||||
return
|
||||
}
|
||||
|
||||
notification.NotifyNewIssue(issue)
|
||||
|
||||
if form.Closed {
|
||||
@@ -336,9 +360,9 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
||||
oneAssignee = *form.Assignee
|
||||
}
|
||||
|
||||
err = models.UpdateAPIAssignee(issue, oneAssignee, form.Assignees, ctx.User)
|
||||
err = issue_service.UpdateAssignees(issue, oneAssignee, form.Assignees, ctx.User)
|
||||
if err != nil {
|
||||
ctx.Error(500, "UpdateAPIAssignee", err)
|
||||
ctx.Error(500, "UpdateAssignees", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
)
|
||||
|
||||
// ListIssueLabels list all the labels of an issue
|
||||
@@ -116,7 +117,7 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = issue.AddLabels(ctx.User, labels); err != nil {
|
||||
if err = issue_service.AddLabels(issue, ctx.User, labels); err != nil {
|
||||
ctx.Error(500, "AddLabels", err)
|
||||
return
|
||||
}
|
||||
@@ -314,7 +315,7 @@ func ClearIssueLabels(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := issue.ClearLabels(ctx.User); err != nil {
|
||||
if err := issue_service.ClearLabels(issue, ctx.User); err != nil {
|
||||
ctx.Error(500, "ClearLabels", err)
|
||||
return
|
||||
}
|
||||
|
||||
+36
-13
@@ -17,6 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
milestone_service "code.gitea.io/gitea/services/milestone"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
)
|
||||
@@ -190,7 +191,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
)
|
||||
|
||||
// Get repo/branch information
|
||||
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
|
||||
_, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -265,15 +266,14 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
DeadlineUnix: deadlineUnix,
|
||||
}
|
||||
pr := &models.PullRequest{
|
||||
HeadRepoID: headRepo.ID,
|
||||
BaseRepoID: repo.ID,
|
||||
HeadUserName: headUser.Name,
|
||||
HeadBranch: headBranch,
|
||||
BaseBranch: baseBranch,
|
||||
HeadRepo: headRepo,
|
||||
BaseRepo: repo,
|
||||
MergeBase: compareInfo.MergeBase,
|
||||
Type: models.PullRequestGitea,
|
||||
HeadRepoID: headRepo.ID,
|
||||
BaseRepoID: repo.ID,
|
||||
HeadBranch: headBranch,
|
||||
BaseBranch: baseBranch,
|
||||
HeadRepo: headRepo,
|
||||
BaseRepo: repo,
|
||||
MergeBase: compareInfo.MergeBase,
|
||||
Type: models.PullRequestGitea,
|
||||
}
|
||||
|
||||
// Get all assignee IDs
|
||||
@@ -286,8 +286,26 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
}
|
||||
return
|
||||
}
|
||||
// Check if the passed assignees is assignable
|
||||
for _, aID := range assigneeIDs {
|
||||
assignee, err := models.GetUserByID(aID)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetUserByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := pull_service.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, patch, assigneeIDs); err != nil {
|
||||
valid, err := models.CanBeAssigned(assignee, repo, true)
|
||||
if err != nil {
|
||||
ctx.Error(500, "canBeAssigned", err)
|
||||
return
|
||||
}
|
||||
if !valid {
|
||||
ctx.Error(422, "canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: repo.Name})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := pull_service.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, patch); err != nil {
|
||||
if models.IsErrUserDoesNotHaveAccessToRepo(err) {
|
||||
ctx.Error(400, "UserDoesNotHaveAccessToRepo", err)
|
||||
return
|
||||
@@ -299,6 +317,11 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
return
|
||||
}
|
||||
|
||||
if err := issue_service.AddAssignees(prIssue, ctx.User, assigneeIDs); err != nil {
|
||||
ctx.ServerError("AddAssignees", err)
|
||||
return
|
||||
}
|
||||
|
||||
notification.NotifyNewPullRequest(pr)
|
||||
|
||||
log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
|
||||
@@ -388,12 +411,12 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
||||
// Send an empty array ([]) to clear all assignees from the Issue.
|
||||
|
||||
if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) {
|
||||
err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User)
|
||||
err = issue_service.UpdateAssignees(issue, form.Assignee, form.Assignees, ctx.User)
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
|
||||
} else {
|
||||
ctx.Error(500, "UpdateAPIAssignee", err)
|
||||
ctx.Error(500, "UpdateAssignees", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+23
-14
@@ -8,6 +8,7 @@ package repo
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/migrations"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/validation"
|
||||
@@ -397,21 +399,28 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
|
||||
return
|
||||
}
|
||||
|
||||
var gitServiceType = structs.PlainGitService
|
||||
u, err := url.Parse(remoteAddr)
|
||||
if err == nil && strings.EqualFold(u.Host, "github.com") {
|
||||
gitServiceType = structs.GithubService
|
||||
}
|
||||
|
||||
var opts = migrations.MigrateOptions{
|
||||
RemoteURL: remoteAddr,
|
||||
Name: form.RepoName,
|
||||
Description: form.Description,
|
||||
Private: form.Private || setting.Repository.ForcePrivate,
|
||||
Mirror: form.Mirror,
|
||||
AuthUsername: form.AuthUsername,
|
||||
AuthPassword: form.AuthPassword,
|
||||
Wiki: form.Wiki,
|
||||
Issues: form.Issues,
|
||||
Milestones: form.Milestones,
|
||||
Labels: form.Labels,
|
||||
Comments: true,
|
||||
PullRequests: form.PullRequests,
|
||||
Releases: form.Releases,
|
||||
CloneAddr: remoteAddr,
|
||||
RepoName: form.RepoName,
|
||||
Description: form.Description,
|
||||
Private: form.Private || setting.Repository.ForcePrivate,
|
||||
Mirror: form.Mirror,
|
||||
AuthUsername: form.AuthUsername,
|
||||
AuthPassword: form.AuthPassword,
|
||||
Wiki: form.Wiki,
|
||||
Issues: form.Issues,
|
||||
Milestones: form.Milestones,
|
||||
Labels: form.Labels,
|
||||
Comments: true,
|
||||
PullRequests: form.PullRequests,
|
||||
Releases: form.Releases,
|
||||
GitServiceType: gitServiceType,
|
||||
}
|
||||
if opts.Mirror {
|
||||
opts.Issues = false
|
||||
|
||||
@@ -52,7 +52,7 @@ func ListUserRepos(ctx *context.APIContext) {
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
private := ctx.IsSigned && (ctx.User.ID == user.ID || ctx.User.IsAdmin)
|
||||
private := ctx.IsSigned
|
||||
listUserRepos(ctx, user, private)
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ func GetInfo(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(200, convert.ToUser(u, ctx.IsSigned, ctx.User.ID == u.ID || ctx.User.IsAdmin))
|
||||
ctx.JSON(200, convert.ToUser(u, ctx.IsSigned, ctx.User != nil && (ctx.User.ID == u.ID || ctx.User.IsAdmin)))
|
||||
}
|
||||
|
||||
// GetAuthenticatedUser get current user's information
|
||||
|
||||
+1
-1
@@ -273,7 +273,7 @@ func ExploreOrganizations(ctx *context.Context) {
|
||||
// ExploreCode render explore code page
|
||||
func ExploreCode(ctx *context.Context) {
|
||||
if !setting.Indexer.RepoIndexerEnabled {
|
||||
ctx.Redirect("/explore", 302)
|
||||
ctx.Redirect(setting.AppSubURL+"/explore", 302)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+17
-6
@@ -18,8 +18,10 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/external"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/ssh"
|
||||
"code.gitea.io/gitea/modules/task"
|
||||
"code.gitea.io/gitea/services/mailer"
|
||||
mirror_service "code.gitea.io/gitea/services/mirror"
|
||||
|
||||
@@ -43,6 +45,7 @@ func NewServices() {
|
||||
setting.NewServices()
|
||||
mailer.NewContext()
|
||||
_ = cache.NewContext()
|
||||
notification.NewContext()
|
||||
}
|
||||
|
||||
// In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology
|
||||
@@ -95,21 +98,29 @@ func GlobalInit() {
|
||||
|
||||
// Booting long running goroutines.
|
||||
cron.NewContext()
|
||||
if err := issue_indexer.InitIssueIndexer(false); err != nil {
|
||||
log.Fatal("Failed to initialize issue indexer: %v", err)
|
||||
}
|
||||
issue_indexer.InitIssueIndexer(false)
|
||||
models.InitRepoIndexer()
|
||||
mirror_service.InitSyncMirrors()
|
||||
models.InitDeliverHooks()
|
||||
models.InitTestPullRequests()
|
||||
if err := task.Init(); err != nil {
|
||||
log.Fatal("Failed to initialize task scheduler: %v", err)
|
||||
}
|
||||
}
|
||||
if setting.EnableSQLite3 {
|
||||
log.Info("SQLite3 Supported")
|
||||
}
|
||||
checkRunMode()
|
||||
|
||||
if setting.InstallLock && setting.SSH.StartBuiltinServer {
|
||||
ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
|
||||
log.Info("SSH server started on %s:%d. Cipher list (%v), key exchange algorithms (%v), MACs (%v)", setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
|
||||
// Now because Install will re-run GlobalInit once it has set InstallLock
|
||||
// we can't tell if the ssh port will remain unused until that's done.
|
||||
// However, see FIXME comment in install.go
|
||||
if setting.InstallLock {
|
||||
if setting.SSH.StartBuiltinServer {
|
||||
ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
|
||||
log.Info("SSH server started on %s:%d. Cipher list (%v), key exchange algorithms (%v), MACs (%v)", setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
|
||||
} else {
|
||||
ssh.Unused()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -20,9 +20,9 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/user"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/unknwon/com"
|
||||
"gopkg.in/ini.v1"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -386,6 +386,12 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
|
||||
}
|
||||
|
||||
log.Info("First-time run install finished!")
|
||||
// FIXME: This isn't really enough to completely take account of new configuration
|
||||
// We should really be restarting:
|
||||
// - On windows this is probably just a simple restart
|
||||
// - On linux we can't just use graceful.RestartProcess() everything that was passed in on LISTEN_FDS
|
||||
// (active or not) needs to be passed out and everything new passed out too.
|
||||
// This means we need to prevent the cleanup goroutine from running prior to the second GlobalInit
|
||||
ctx.Flash.Success(ctx.Tr("install.install_success"))
|
||||
ctx.Redirect(form.AppURL + "user/login")
|
||||
}
|
||||
|
||||
+5
-4
@@ -47,10 +47,11 @@ func CreatePost(ctx *context.Context, form auth.CreateOrgForm) {
|
||||
}
|
||||
|
||||
org := &models.User{
|
||||
Name: form.OrgName,
|
||||
IsActive: true,
|
||||
Type: models.UserTypeOrganization,
|
||||
Visibility: form.Visibility,
|
||||
Name: form.OrgName,
|
||||
IsActive: true,
|
||||
Type: models.UserTypeOrganization,
|
||||
Visibility: form.Visibility,
|
||||
RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
|
||||
}
|
||||
|
||||
if err := models.CreateOrganization(org, ctx.User); err != nil {
|
||||
|
||||
@@ -33,6 +33,7 @@ func HookPreReceive(ctx *macaron.Context) {
|
||||
gitAlternativeObjectDirectories := ctx.QueryTrim("gitAlternativeObjectDirectories")
|
||||
gitQuarantinePath := ctx.QueryTrim("gitQuarantinePath")
|
||||
prID := ctx.QueryInt64("prID")
|
||||
isDeployKey := ctx.QueryBool("isDeployKey")
|
||||
|
||||
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||
@@ -95,7 +96,12 @@ func HookPreReceive(ctx *macaron.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
canPush := protectBranch.CanUserPush(userID)
|
||||
canPush := false
|
||||
if isDeployKey {
|
||||
canPush = protectBranch.WhitelistDeployKeys
|
||||
} else {
|
||||
canPush = protectBranch.CanUserPush(userID)
|
||||
}
|
||||
if !canPush && prID > 0 {
|
||||
pr, err := models.GetPullRequestByID(prID)
|
||||
if err != nil {
|
||||
|
||||
@@ -119,6 +119,15 @@ func ServCommand(ctx *macaron.Context) {
|
||||
repo.OwnerName = ownerName
|
||||
results.RepoID = repo.ID
|
||||
|
||||
if repo.IsBeingCreated() {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "InternalServerError",
|
||||
"err": "Repository is being created, you could retry after it finished",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// We can shortcut at this point if the repo is a mirror
|
||||
if mode > models.AccessModeRead && repo.IsMirror {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
|
||||
@@ -63,3 +63,25 @@ func UploadAttachment(ctx *context.Context) {
|
||||
"uuid": attach.UUID,
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteAttachment response for deleting issue's attachment
|
||||
func DeleteAttachment(ctx *context.Context) {
|
||||
file := ctx.Query("file")
|
||||
attach, err := models.GetAttachmentByUUID(file)
|
||||
if !ctx.IsSigned || (ctx.User.ID != attach.UploaderID) {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ctx.Error(400, err.Error())
|
||||
return
|
||||
}
|
||||
err = models.DeleteAttachment(attach, true)
|
||||
if err != nil {
|
||||
ctx.Error(500, fmt.Sprintf("DeleteAttachment: %v", err))
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, map[string]string{
|
||||
"uuid": attach.UUID,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ type Branch struct {
|
||||
Commit *git.Commit
|
||||
IsProtected bool
|
||||
IsDeleted bool
|
||||
IsIncluded bool
|
||||
DeletedBranch *models.DeletedBranch
|
||||
CommitsAhead int
|
||||
CommitsBehind int
|
||||
@@ -203,10 +204,13 @@ func loadBranches(ctx *context.Context) []*Branch {
|
||||
}
|
||||
}
|
||||
|
||||
isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName
|
||||
|
||||
branches[i] = &Branch{
|
||||
Name: branchName,
|
||||
Commit: commit,
|
||||
IsProtected: isProtected,
|
||||
IsIncluded: isIncluded,
|
||||
CommitsAhead: divergence.Ahead,
|
||||
CommitsBehind: divergence.Behind,
|
||||
LatestPullRequest: pr,
|
||||
|
||||
@@ -91,7 +91,9 @@ func Graph(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
graph, err := models.GetCommitGraph(ctx.Repo.GitRepo)
|
||||
page := ctx.QueryInt("page")
|
||||
|
||||
graph, err := models.GetCommitGraph(ctx.Repo.GitRepo, page)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommitGraph", err)
|
||||
return
|
||||
@@ -103,8 +105,8 @@ func Graph(ctx *context.Context) {
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
ctx.Data["Branch"] = ctx.Repo.BranchName
|
||||
ctx.Data["RequireGitGraph"] = true
|
||||
ctx.Data["Page"] = context.NewPagination(int(commitsCount), setting.UI.GraphMaxCommitNum, page, 5)
|
||||
ctx.HTML(200, tplGraph)
|
||||
|
||||
}
|
||||
|
||||
// SearchCommits render commits filtered by keyword
|
||||
|
||||
@@ -84,6 +84,11 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err = lfsDataRc.Close(); err != nil {
|
||||
log.Error("ServeBlobOrLFS: Close: %v", err)
|
||||
}
|
||||
}()
|
||||
return ServeData(ctx, ctx.Repo.TreePath, lfsDataRc)
|
||||
}
|
||||
|
||||
|
||||
@@ -263,6 +263,7 @@ func HTTP(ctx *context.Context) {
|
||||
models.EnvPusherName + "=" + authUser.Name,
|
||||
models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID),
|
||||
models.ProtectedBranchRepoID + fmt.Sprintf("=%d", repo.ID),
|
||||
models.EnvIsDeployKey + "=false",
|
||||
}
|
||||
|
||||
if !authUser.KeepEmailPrivate {
|
||||
|
||||
+143
-13
@@ -34,6 +34,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tplAttachment base.TplName = "repo/issue/view_content/attachments"
|
||||
|
||||
tplIssues base.TplName = "repo/issue/list"
|
||||
tplIssueNew base.TplName = "repo/issue/new"
|
||||
tplIssueView base.TplName = "repo/issue/view"
|
||||
@@ -501,21 +503,21 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull b
|
||||
return nil, nil, 0
|
||||
}
|
||||
|
||||
// Check if the passed assignees actually exists and has write access to the repo
|
||||
// Check if the passed assignees actually exists and is assignable
|
||||
for _, aID := range assigneeIDs {
|
||||
user, err := models.GetUserByID(aID)
|
||||
assignee, err := models.GetUserByID(aID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserByID", err)
|
||||
return nil, nil, 0
|
||||
}
|
||||
|
||||
perm, err := models.GetUserRepoPermission(repo, user)
|
||||
valid, err := models.CanBeAssigned(assignee, repo, isPull)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
ctx.ServerError("canBeAssigned", err)
|
||||
return nil, nil, 0
|
||||
}
|
||||
if !perm.CanWriteIssuesOrPulls(isPull) {
|
||||
ctx.ServerError("CanWriteIssuesOrPulls", fmt.Errorf("No permission for %s", user.Name))
|
||||
if !valid {
|
||||
ctx.ServerError("canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: repo.Name})
|
||||
return nil, nil, 0
|
||||
}
|
||||
}
|
||||
@@ -572,7 +574,7 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
|
||||
Content: form.Content,
|
||||
Ref: form.Ref,
|
||||
}
|
||||
if err := issue_service.NewIssue(repo, issue, labelIDs, assigneeIDs, attachments); err != nil {
|
||||
if err := issue_service.NewIssue(repo, issue, labelIDs, attachments); err != nil {
|
||||
if models.IsErrUserDoesNotHaveAccessToRepo(err) {
|
||||
ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error())
|
||||
return
|
||||
@@ -581,6 +583,11 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := issue_service.AddAssignees(issue, ctx.User, assigneeIDs); err != nil {
|
||||
log.Error("AddAssignees: %v", err)
|
||||
ctx.Flash.Error(ctx.Tr("issues.assignee.error"))
|
||||
}
|
||||
|
||||
notification.NotifyNewIssue(issue)
|
||||
|
||||
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
|
||||
@@ -1044,7 +1051,7 @@ func UpdateIssueTitle(ctx *context.Context) {
|
||||
}
|
||||
|
||||
oldTitle := issue.Title
|
||||
if err := issue.ChangeTitle(ctx.User, title); err != nil {
|
||||
if err := issue_service.ChangeTitle(issue, ctx.User, title); err != nil {
|
||||
ctx.ServerError("ChangeTitle", err)
|
||||
return
|
||||
}
|
||||
@@ -1074,8 +1081,14 @@ func UpdateIssueContent(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
files := ctx.QueryStrings("files[]")
|
||||
if err := updateAttachments(issue, files); err != nil {
|
||||
ctx.ServerError("UpdateAttachments", err)
|
||||
}
|
||||
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"content": string(markdown.Render([]byte(issue.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
|
||||
"content": string(markdown.Render([]byte(issue.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
|
||||
"attachments": attachmentsHTML(ctx, issue.Attachments),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1104,7 +1117,7 @@ func UpdateIssueMilestone(ctx *context.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateIssueAssignee change issue's assignee
|
||||
// UpdateIssueAssignee change issue's or pull's assignee
|
||||
func UpdateIssueAssignee(ctx *context.Context) {
|
||||
issues := getActionIssues(ctx)
|
||||
if ctx.Written() {
|
||||
@@ -1122,10 +1135,29 @@ func UpdateIssueAssignee(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
default:
|
||||
if err := issue.ChangeAssignee(ctx.User, assigneeID); err != nil {
|
||||
ctx.ServerError("ChangeAssignee", err)
|
||||
assignee, err := models.GetUserByID(assigneeID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
valid, err := models.CanBeAssigned(assignee, issue.Repo, issue.IsPull)
|
||||
if err != nil {
|
||||
ctx.ServerError("canBeAssigned", err)
|
||||
return
|
||||
}
|
||||
if !valid {
|
||||
ctx.ServerError("canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: issue.Repo.Name})
|
||||
return
|
||||
}
|
||||
|
||||
removed, comment, err := issue.ToggleAssignee(ctx.User, assigneeID)
|
||||
if err != nil {
|
||||
ctx.ServerError("ToggleAssignee", err)
|
||||
return
|
||||
}
|
||||
|
||||
notification.NotifyIssueChangeAssignee(ctx.User, issue, assignee, removed, comment)
|
||||
}
|
||||
}
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
@@ -1325,6 +1357,13 @@ func UpdateCommentContent(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Type == models.CommentTypeComment {
|
||||
if err := comment.LoadAttachments(); err != nil {
|
||||
ctx.ServerError("LoadAttachments", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Error(403)
|
||||
return
|
||||
@@ -1346,10 +1385,16 @@ func UpdateCommentContent(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
files := ctx.QueryStrings("files[]")
|
||||
if err := updateAttachments(comment, files); err != nil {
|
||||
ctx.ServerError("UpdateAttachments", err)
|
||||
}
|
||||
|
||||
notification.NotifyUpdateComment(ctx.User, comment, oldContent)
|
||||
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"content": string(markdown.Render([]byte(comment.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
|
||||
"content": string(markdown.Render([]byte(comment.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
|
||||
"attachments": attachmentsHTML(ctx, comment.Attachments),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1603,3 +1648,88 @@ func filterXRefComments(ctx *context.Context, issue *models.Issue) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIssueAttachments returns attachments for the issue
|
||||
func GetIssueAttachments(ctx *context.Context) {
|
||||
issue := GetActionIssue(ctx)
|
||||
var attachments = make([]*api.Attachment, len(issue.Attachments))
|
||||
for i := 0; i < len(issue.Attachments); i++ {
|
||||
attachments[i] = issue.Attachments[i].APIFormat()
|
||||
}
|
||||
ctx.JSON(200, attachments)
|
||||
}
|
||||
|
||||
// GetCommentAttachments returns attachments for the comment
|
||||
func GetCommentAttachments(ctx *context.Context) {
|
||||
comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err)
|
||||
return
|
||||
}
|
||||
var attachments = make([]*api.Attachment, 0)
|
||||
if comment.Type == models.CommentTypeComment {
|
||||
if err := comment.LoadAttachments(); err != nil {
|
||||
ctx.ServerError("LoadAttachments", err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(comment.Attachments); i++ {
|
||||
attachments = append(attachments, comment.Attachments[i].APIFormat())
|
||||
}
|
||||
}
|
||||
ctx.JSON(200, attachments)
|
||||
}
|
||||
|
||||
func updateAttachments(item interface{}, files []string) error {
|
||||
var attachments []*models.Attachment
|
||||
switch content := item.(type) {
|
||||
case *models.Issue:
|
||||
attachments = content.Attachments
|
||||
case *models.Comment:
|
||||
attachments = content.Attachments
|
||||
default:
|
||||
return fmt.Errorf("Unknow Type")
|
||||
}
|
||||
for i := 0; i < len(attachments); i++ {
|
||||
if util.IsStringInSlice(attachments[i].UUID, files) {
|
||||
continue
|
||||
}
|
||||
if err := models.DeleteAttachment(attachments[i], true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var err error
|
||||
if len(files) > 0 {
|
||||
switch content := item.(type) {
|
||||
case *models.Issue:
|
||||
err = content.UpdateAttachments(files)
|
||||
case *models.Comment:
|
||||
err = content.UpdateAttachments(files)
|
||||
default:
|
||||
return fmt.Errorf("Unknow Type")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
switch content := item.(type) {
|
||||
case *models.Issue:
|
||||
content.Attachments, err = models.GetAttachmentsByIssueID(content.ID)
|
||||
case *models.Comment:
|
||||
content.Attachments, err = models.GetAttachmentsByCommentID(content.ID)
|
||||
default:
|
||||
return fmt.Errorf("Unknow Type")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func attachmentsHTML(ctx *context.Context, attachments []*models.Attachment) string {
|
||||
attachHTML, err := ctx.HTMLString(string(tplAttachment), map[string]interface{}{
|
||||
"ctx": ctx.Data,
|
||||
"Attachments": attachments,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("attachmentsHTML.HTMLString", err)
|
||||
return ""
|
||||
}
|
||||
return attachHTML
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -132,7 +133,7 @@ func UpdateIssueLabel(ctx *context.Context) {
|
||||
switch action := ctx.Query("action"); action {
|
||||
case "clear":
|
||||
for _, issue := range issues {
|
||||
if err := issue.ClearLabels(ctx.User); err != nil {
|
||||
if err := issue_service.ClearLabels(issue, ctx.User); err != nil {
|
||||
ctx.ServerError("ClearLabels", err)
|
||||
return
|
||||
}
|
||||
@@ -161,14 +162,14 @@ func UpdateIssueLabel(ctx *context.Context) {
|
||||
|
||||
if action == "attach" {
|
||||
for _, issue := range issues {
|
||||
if err = issue.AddLabel(ctx.User, label); err != nil {
|
||||
if err = issue_service.AddLabel(issue, ctx.User, label); err != nil {
|
||||
ctx.ServerError("AddLabel", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, issue := range issues {
|
||||
if err = issue.RemoveLabel(ctx.User, label); err != nil {
|
||||
if err = issue_service.RemoveLabel(issue, ctx.User, label); err != nil {
|
||||
ctx.ServerError("RemoveLabel", err)
|
||||
return
|
||||
}
|
||||
|
||||
+27
-18
@@ -24,6 +24,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
@@ -242,6 +243,10 @@ func checkPullInfo(ctx *context.Context) *models.Issue {
|
||||
ctx.ServerError("LoadPoster", err)
|
||||
return nil
|
||||
}
|
||||
if err := issue.LoadRepo(); err != nil {
|
||||
ctx.ServerError("LoadRepo", err)
|
||||
return nil
|
||||
}
|
||||
ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
|
||||
ctx.Data["Issue"] = issue
|
||||
|
||||
@@ -272,12 +277,12 @@ func checkPullInfo(ctx *context.Context) *models.Issue {
|
||||
}
|
||||
|
||||
func setMergeTarget(ctx *context.Context, pull *models.PullRequest) {
|
||||
if ctx.Repo.Owner.Name == pull.HeadUserName {
|
||||
if ctx.Repo.Owner.Name == pull.MustHeadUserName() {
|
||||
ctx.Data["HeadTarget"] = pull.HeadBranch
|
||||
} else if pull.HeadRepo == nil {
|
||||
ctx.Data["HeadTarget"] = pull.HeadUserName + ":" + pull.HeadBranch
|
||||
ctx.Data["HeadTarget"] = pull.MustHeadUserName() + ":" + pull.HeadBranch
|
||||
} else {
|
||||
ctx.Data["HeadTarget"] = pull.HeadUserName + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch
|
||||
ctx.Data["HeadTarget"] = pull.MustHeadUserName() + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch
|
||||
}
|
||||
ctx.Data["BaseTarget"] = pull.BaseBranch
|
||||
}
|
||||
@@ -440,7 +445,7 @@ func ViewPullCommits(ctx *context.Context) {
|
||||
ctx.NotFound("ViewPullCommits", nil)
|
||||
return
|
||||
}
|
||||
ctx.Data["Username"] = pull.HeadUserName
|
||||
ctx.Data["Username"] = pull.MustHeadUserName()
|
||||
ctx.Data["Reponame"] = pull.HeadRepo.Name
|
||||
commits = prInfo.Commits
|
||||
}
|
||||
@@ -512,7 +517,7 @@ func ViewPullFiles(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
headRepoPath := models.RepoPath(pull.HeadUserName, pull.HeadRepo.Name)
|
||||
headRepoPath := pull.HeadRepo.RepoPath()
|
||||
|
||||
headGitRepo, err := git.OpenRepository(headRepoPath)
|
||||
if err != nil {
|
||||
@@ -531,8 +536,8 @@ func ViewPullFiles(ctx *context.Context) {
|
||||
endCommitID = headCommitID
|
||||
gitRepo = headGitRepo
|
||||
|
||||
headTarget = path.Join(pull.HeadUserName, pull.HeadRepo.Name)
|
||||
ctx.Data["Username"] = pull.HeadUserName
|
||||
headTarget = path.Join(pull.MustHeadUserName(), pull.HeadRepo.Name)
|
||||
ctx.Data["Username"] = pull.MustHeadUserName()
|
||||
ctx.Data["Reponame"] = pull.HeadRepo.Name
|
||||
}
|
||||
|
||||
@@ -754,20 +759,19 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
|
||||
Content: form.Content,
|
||||
}
|
||||
pullRequest := &models.PullRequest{
|
||||
HeadRepoID: headRepo.ID,
|
||||
BaseRepoID: repo.ID,
|
||||
HeadUserName: headUser.Name,
|
||||
HeadBranch: headBranch,
|
||||
BaseBranch: baseBranch,
|
||||
HeadRepo: headRepo,
|
||||
BaseRepo: repo,
|
||||
MergeBase: prInfo.MergeBase,
|
||||
Type: models.PullRequestGitea,
|
||||
HeadRepoID: headRepo.ID,
|
||||
BaseRepoID: repo.ID,
|
||||
HeadBranch: headBranch,
|
||||
BaseBranch: baseBranch,
|
||||
HeadRepo: headRepo,
|
||||
BaseRepo: repo,
|
||||
MergeBase: prInfo.MergeBase,
|
||||
Type: models.PullRequestGitea,
|
||||
}
|
||||
// FIXME: check error in the case two people send pull request at almost same time, give nice error prompt
|
||||
// instead of 500.
|
||||
|
||||
if err := pull_service.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch, assigneeIDs); err != nil {
|
||||
if err := pull_service.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch); err != nil {
|
||||
if models.IsErrUserDoesNotHaveAccessToRepo(err) {
|
||||
ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error())
|
||||
return
|
||||
@@ -779,6 +783,11 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
|
||||
return
|
||||
}
|
||||
|
||||
if err := issue_service.AddAssignees(pullIssue, ctx.User, assigneeIDs); err != nil {
|
||||
log.Error("AddAssignees: %v", err)
|
||||
ctx.Flash.Error(ctx.Tr("issues.assignee.error"))
|
||||
}
|
||||
|
||||
notification.NotifyNewPullRequest(pullRequest)
|
||||
|
||||
log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
|
||||
@@ -820,7 +829,7 @@ func TriggerTask(ctx *context.Context) {
|
||||
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
|
||||
|
||||
go models.HookQueue.Add(repo.ID)
|
||||
go models.AddTestPullRequestTask(pusher, repo.ID, branch, true)
|
||||
go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true)
|
||||
ctx.Status(202)
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) {
|
||||
return
|
||||
}
|
||||
// No current review. Create a new one!
|
||||
if review, err = models.CreateReview(models.CreateReviewOptions{
|
||||
if review, err = pull_service.CreateReview(models.CreateReviewOptions{
|
||||
Type: reviewType,
|
||||
Issue: issue,
|
||||
Reviewer: ctx.User,
|
||||
@@ -169,7 +169,7 @@ func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) {
|
||||
} else {
|
||||
review.Content = form.Content
|
||||
review.Type = reviewType
|
||||
if err = models.UpdateReview(review); err != nil {
|
||||
if err = pull_service.UpdateReview(review); err != nil {
|
||||
ctx.ServerError("UpdateReview", err)
|
||||
return
|
||||
}
|
||||
|
||||
+62
-41
@@ -19,6 +19,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/migrations"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/task"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
@@ -133,8 +134,6 @@ func Create(ctx *context.Context) {
|
||||
|
||||
func handleCreateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) {
|
||||
switch {
|
||||
case migrations.IsRateLimitError(err):
|
||||
ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tpl, form)
|
||||
case models.IsErrReachLimitOfRepo(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
|
||||
case models.IsErrRepoAlreadyExist(err):
|
||||
@@ -221,6 +220,40 @@ func Migrate(ctx *context.Context) {
|
||||
ctx.HTML(200, tplMigrate)
|
||||
}
|
||||
|
||||
func handleMigrateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form *auth.MigrateRepoForm) {
|
||||
switch {
|
||||
case migrations.IsRateLimitError(err):
|
||||
ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tpl, form)
|
||||
case migrations.IsTwoFactorAuthError(err):
|
||||
ctx.RenderWithErr(ctx.Tr("form.2fa_auth_required"), tpl, form)
|
||||
case models.IsErrReachLimitOfRepo(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
|
||||
case models.IsErrRepoAlreadyExist(err):
|
||||
ctx.Data["Err_RepoName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
|
||||
case models.IsErrNameReserved(err):
|
||||
ctx.Data["Err_RepoName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
|
||||
case models.IsErrNamePatternNotAllowed(err):
|
||||
ctx.Data["Err_RepoName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
|
||||
default:
|
||||
remoteAddr, _ := form.ParseRemoteAddr(owner)
|
||||
err = util.URLSanitizedError(err, remoteAddr)
|
||||
if strings.Contains(err.Error(), "Authentication failed") ||
|
||||
strings.Contains(err.Error(), "Bad credentials") ||
|
||||
strings.Contains(err.Error(), "could not read Username") {
|
||||
ctx.Data["Err_Auth"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.auth_failed", err.Error()), tpl, form)
|
||||
} else if strings.Contains(err.Error(), "fatal:") {
|
||||
ctx.Data["Err_CloneAddr"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", err.Error()), tpl, form)
|
||||
} else {
|
||||
ctx.ServerError(name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MigratePost response for migrating from external git repository
|
||||
func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
|
||||
ctx.Data["Title"] = ctx.Tr("new_migrate")
|
||||
@@ -258,8 +291,8 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
|
||||
}
|
||||
|
||||
var opts = migrations.MigrateOptions{
|
||||
RemoteURL: remoteAddr,
|
||||
Name: form.RepoName,
|
||||
CloneAddr: remoteAddr,
|
||||
RepoName: form.RepoName,
|
||||
Description: form.Description,
|
||||
Private: form.Private || setting.Repository.ForcePrivate,
|
||||
Mirror: form.Mirror,
|
||||
@@ -282,47 +315,19 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
|
||||
opts.Releases = false
|
||||
}
|
||||
|
||||
repo, err := migrations.MigrateRepository(ctx.User, ctxUser.Name, opts)
|
||||
if err == nil {
|
||||
notification.NotifyCreateRepository(ctx.User, ctxUser, repo)
|
||||
|
||||
log.Trace("Repository migrated [%d]: %s/%s successfully", repo.ID, ctxUser.Name, form.RepoName)
|
||||
ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + form.RepoName)
|
||||
err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName)
|
||||
if err != nil {
|
||||
handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form)
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case models.IsErrReachLimitOfRepo(err):
|
||||
ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", ctxUser.MaxCreationLimit()), tplMigrate, &form)
|
||||
case models.IsErrNameReserved(err):
|
||||
ctx.Data["Err_RepoName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tplMigrate, &form)
|
||||
case models.IsErrRepoAlreadyExist(err):
|
||||
ctx.Data["Err_RepoName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tplMigrate, &form)
|
||||
case models.IsErrNamePatternNotAllowed(err):
|
||||
ctx.Data["Err_RepoName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplMigrate, &form)
|
||||
case migrations.IsRateLimitError(err):
|
||||
ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tplMigrate, &form)
|
||||
case migrations.IsTwoFactorAuthError(err):
|
||||
ctx.Data["Err_Auth"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.2fa_auth_required"), tplMigrate, &form)
|
||||
default:
|
||||
// remoteAddr may contain credentials, so we sanitize it
|
||||
err = util.URLSanitizedError(err, remoteAddr)
|
||||
if strings.Contains(err.Error(), "Authentication failed") ||
|
||||
strings.Contains(err.Error(), "Bad credentials") ||
|
||||
strings.Contains(err.Error(), "could not read Username") {
|
||||
ctx.Data["Err_Auth"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.auth_failed", err.Error()), tplMigrate, &form)
|
||||
} else if strings.Contains(err.Error(), "fatal:") {
|
||||
ctx.Data["Err_CloneAddr"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", err.Error()), tplMigrate, &form)
|
||||
} else {
|
||||
ctx.ServerError("MigratePost", err)
|
||||
}
|
||||
err = task.MigrateRepository(ctx.User, ctxUser, opts)
|
||||
if err == nil {
|
||||
ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + opts.RepoName)
|
||||
return
|
||||
}
|
||||
|
||||
handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form)
|
||||
}
|
||||
|
||||
// Action response for actions to a repository
|
||||
@@ -460,3 +465,19 @@ func Download(ctx *context.Context) {
|
||||
|
||||
ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+refName+ext)
|
||||
}
|
||||
|
||||
// Status returns repository's status
|
||||
func Status(ctx *context.Context) {
|
||||
task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"status": ctx.Repo.Repository.Status,
|
||||
"err": task.Errors,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -117,9 +117,9 @@ func SettingsProtectedBranch(c *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
users, err := c.Repo.Repository.GetWriters()
|
||||
users, err := c.Repo.Repository.GetReaders()
|
||||
if err != nil {
|
||||
c.ServerError("Repo.Repository.GetWriters", err)
|
||||
c.ServerError("Repo.Repository.GetReaders", err)
|
||||
return
|
||||
}
|
||||
c.Data["Users"] = users
|
||||
@@ -213,6 +213,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm)
|
||||
|
||||
protectBranch.EnableStatusCheck = f.EnableStatusCheck
|
||||
protectBranch.StatusCheckContexts = f.StatusCheckContexts
|
||||
protectBranch.WhitelistDeployKeys = f.WhitelistDeployKeys
|
||||
|
||||
protectBranch.RequiredApprovals = f.RequiredApprovals
|
||||
if strings.TrimSpace(f.ApprovalsWhitelistUsers) != "" {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
gotemplate "html/template"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
@@ -31,6 +32,7 @@ const (
|
||||
tplRepoHome base.TplName = "repo/home"
|
||||
tplWatchers base.TplName = "repo/watchers"
|
||||
tplForks base.TplName = "repo/forks"
|
||||
tplMigrating base.TplName = "repo/migrating"
|
||||
)
|
||||
|
||||
func renderDirectory(ctx *context.Context, treeLink string) {
|
||||
@@ -304,6 +306,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
||||
|
||||
var output bytes.Buffer
|
||||
lines := strings.Split(fileContent, "\n")
|
||||
ctx.Data["NumLines"] = len(lines)
|
||||
|
||||
//Remove blank line at the end of file
|
||||
if len(lines) > 0 && lines[len(lines)-1] == "" {
|
||||
lines = lines[:len(lines)-1]
|
||||
@@ -342,6 +346,20 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
||||
ctx.Data["IsAudioFile"] = true
|
||||
case base.IsImageFile(buf):
|
||||
ctx.Data["IsImageFile"] = true
|
||||
default:
|
||||
if fileSize >= setting.UI.MaxDisplayFileSize {
|
||||
ctx.Data["IsFileTooLarge"] = true
|
||||
break
|
||||
}
|
||||
|
||||
if markupType := markup.Type(blob.Name()); markupType != "" {
|
||||
d, _ := ioutil.ReadAll(dataRc)
|
||||
buf = append(buf, d...)
|
||||
ctx.Data["IsMarkup"] = true
|
||||
ctx.Data["MarkupType"] = markupType
|
||||
ctx.Data["FileContent"] = string(markup.Render(blob.Name(), buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ctx.Repo.CanEnableEditor() {
|
||||
@@ -354,9 +372,37 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
||||
}
|
||||
}
|
||||
|
||||
func safeURL(address string) string {
|
||||
u, err := url.Parse(address)
|
||||
if err != nil {
|
||||
return address
|
||||
}
|
||||
u.User = nil
|
||||
return u.String()
|
||||
}
|
||||
|
||||
// Home render repository home page
|
||||
func Home(ctx *context.Context) {
|
||||
if len(ctx.Repo.Units) > 0 {
|
||||
if ctx.Repo.Repository.IsBeingCreated() {
|
||||
task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("models.GetMigratingTask", err)
|
||||
return
|
||||
}
|
||||
cfg, err := task.MigrateConfig()
|
||||
if err != nil {
|
||||
ctx.ServerError("task.MigrateConfig", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Repo"] = ctx.Repo
|
||||
ctx.Data["MigrateTask"] = task
|
||||
ctx.Data["CloneAddr"] = safeURL(cfg.CloneAddr)
|
||||
ctx.HTML(200, tplMigrating)
|
||||
return
|
||||
}
|
||||
|
||||
var firstUnit *models.Unit
|
||||
for _, repoUnit := range ctx.Repo.Units {
|
||||
if repoUnit.Type == models.UnitTypeCode {
|
||||
|
||||
@@ -8,6 +8,7 @@ package repo
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -68,11 +69,22 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The longest name should be checked first
|
||||
for _, entry := range entries {
|
||||
if entry.IsRegular() && entry.Name() == target {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
// Then the unescaped, shortest alternative
|
||||
var unescapedTarget string
|
||||
if unescapedTarget, err = url.QueryUnescape(target); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsRegular() && entry.Name() == unescapedTarget {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -139,14 +139,14 @@ func NewMacaron() *macaron.Macaron {
|
||||
m.Use(public.Custom(
|
||||
&public.Options{
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
ExpiresAfter: time.Hour * 6,
|
||||
ExpiresAfter: setting.StaticCacheTime,
|
||||
},
|
||||
))
|
||||
m.Use(public.Static(
|
||||
&public.Options{
|
||||
Directory: path.Join(setting.StaticRootPath, "public"),
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
ExpiresAfter: time.Hour * 6,
|
||||
ExpiresAfter: setting.StaticCacheTime,
|
||||
},
|
||||
))
|
||||
m.Use(public.StaticHandler(
|
||||
@@ -154,7 +154,7 @@ func NewMacaron() *macaron.Macaron {
|
||||
&public.Options{
|
||||
Prefix: "avatars",
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
ExpiresAfter: time.Hour * 6,
|
||||
ExpiresAfter: setting.StaticCacheTime,
|
||||
},
|
||||
))
|
||||
m.Use(public.StaticHandler(
|
||||
@@ -162,7 +162,7 @@ func NewMacaron() *macaron.Macaron {
|
||||
&public.Options{
|
||||
Prefix: "repo-avatars",
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
ExpiresAfter: time.Hour * 6,
|
||||
ExpiresAfter: setting.StaticCacheTime,
|
||||
},
|
||||
))
|
||||
|
||||
@@ -513,8 +513,9 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
})
|
||||
}, ignSignIn)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Post("/attachments", repo.UploadAttachment)
|
||||
m.Group("/attachments", func() {
|
||||
m.Post("", repo.UploadAttachment)
|
||||
m.Post("/delete", repo.DeleteAttachment)
|
||||
}, reqSignIn)
|
||||
|
||||
m.Group("/:username", func() {
|
||||
@@ -710,6 +711,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction)
|
||||
m.Post("/lock", reqRepoIssueWriter, bindIgnErr(auth.IssueLockForm{}), repo.LockIssue)
|
||||
m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue)
|
||||
m.Get("/attachments", repo.GetIssueAttachments)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
|
||||
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
|
||||
@@ -721,6 +723,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Post("", repo.UpdateCommentContent)
|
||||
m.Post("/delete", repo.DeleteComment)
|
||||
m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction)
|
||||
m.Get("/attachments", repo.GetCommentAttachments)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
m.Group("/labels", func() {
|
||||
m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
|
||||
@@ -845,6 +848,8 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
|
||||
m.Get("/archive/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.Download)
|
||||
|
||||
m.Get("/status", reqRepoCodeReader, repo.Status)
|
||||
|
||||
m.Group("/branches", func() {
|
||||
m.Get("", repo.Branches)
|
||||
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
|
||||
|
||||
+60
-45
@@ -17,10 +17,12 @@ import (
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/password"
|
||||
"code.gitea.io/gitea/modules/recaptcha"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/externalaccount"
|
||||
"code.gitea.io/gitea/services/mailer"
|
||||
|
||||
"gitea.com/macaron/captcha"
|
||||
@@ -277,7 +279,7 @@ func TwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) {
|
||||
return
|
||||
}
|
||||
|
||||
err = models.LinkAccountToUser(u, gothUser.(goth.User))
|
||||
err = externalaccount.LinkAccountToUser(u, gothUser.(goth.User))
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
@@ -452,7 +454,7 @@ func U2FSign(ctx *context.Context, signResp u2f.SignResponse) {
|
||||
return
|
||||
}
|
||||
|
||||
err = models.LinkAccountToUser(user, gothUser.(goth.User))
|
||||
err = externalaccount.LinkAccountToUser(user, gothUser.(goth.User))
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
@@ -601,36 +603,42 @@ func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context
|
||||
// Instead, redirect them to the 2FA authentication page.
|
||||
_, err = models.GetTwoFactorByUID(u.ID)
|
||||
if err != nil {
|
||||
if models.IsErrTwoFactorNotEnrolled(err) {
|
||||
err = ctx.Session.Set("uid", u.ID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
err = ctx.Session.Set("uname", u.Name)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
|
||||
// Clear whatever CSRF has right now, force to generate a new one
|
||||
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
|
||||
|
||||
// Register last login
|
||||
u.SetLastLogin()
|
||||
if err := models.UpdateUserCols(u, "last_login_unix"); err != nil {
|
||||
ctx.ServerError("UpdateUserCols", err)
|
||||
return
|
||||
}
|
||||
|
||||
if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 {
|
||||
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
|
||||
ctx.RedirectToFirst(redirectTo)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
} else {
|
||||
if !models.IsErrTwoFactorNotEnrolled(err) {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = ctx.Session.Set("uid", u.ID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
err = ctx.Session.Set("uname", u.Name)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
|
||||
// Clear whatever CSRF has right now, force to generate a new one
|
||||
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
|
||||
|
||||
// Register last login
|
||||
u.SetLastLogin()
|
||||
if err := models.UpdateUserCols(u, "last_login_unix"); err != nil {
|
||||
ctx.ServerError("UpdateUserCols", err)
|
||||
return
|
||||
}
|
||||
|
||||
// update external user information
|
||||
if err := models.UpdateExternalUser(u, gothUser); err != nil {
|
||||
log.Error("UpdateExternalUser failed: %v", err)
|
||||
}
|
||||
|
||||
if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 {
|
||||
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
|
||||
ctx.RedirectToFirst(redirectTo)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -675,7 +683,7 @@ func oAuth2UserLoginCallback(loginSource *models.LoginSource, request *http.Requ
|
||||
}
|
||||
|
||||
if hasUser {
|
||||
return user, goth.User{}, nil
|
||||
return user, gothUser, nil
|
||||
}
|
||||
|
||||
// search in external linked users
|
||||
@@ -689,7 +697,7 @@ func oAuth2UserLoginCallback(loginSource *models.LoginSource, request *http.Requ
|
||||
}
|
||||
if hasUser {
|
||||
user, err = models.GetUserByID(externalLoginUser.UserID)
|
||||
return user, goth.User{}, err
|
||||
return user, gothUser, err
|
||||
}
|
||||
|
||||
// no user found to login
|
||||
@@ -789,16 +797,18 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
|
||||
// Instead, redirect them to the 2FA authentication page.
|
||||
_, err = models.GetTwoFactorByUID(u.ID)
|
||||
if err != nil {
|
||||
if models.IsErrTwoFactorNotEnrolled(err) {
|
||||
err = models.LinkAccountToUser(u, gothUser.(goth.User))
|
||||
if err != nil {
|
||||
ctx.ServerError("UserLinkAccount", err)
|
||||
} else {
|
||||
handleSignIn(ctx, u, signInForm.Remember)
|
||||
}
|
||||
} else {
|
||||
if !models.IsErrTwoFactorNotEnrolled(err) {
|
||||
ctx.ServerError("UserLinkAccount", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = externalaccount.LinkAccountToUser(u, gothUser.(goth.User))
|
||||
if err != nil {
|
||||
ctx.ServerError("UserLinkAccount", err)
|
||||
return
|
||||
}
|
||||
|
||||
handleSignIn(ctx, u, signInForm.Remember)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -947,6 +957,11 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
|
||||
}
|
||||
}
|
||||
|
||||
// update external user information
|
||||
if err := models.UpdateExternalUser(u, gothUser.(goth.User)); err != nil {
|
||||
log.Error("UpdateExternalUser failed: %v", err)
|
||||
}
|
||||
|
||||
// Send confirmation email
|
||||
if setting.Service.RegisterEmailConfirm && u.ID > 1 {
|
||||
mailer.SendActivateAccountMail(ctx.Locale, u)
|
||||
@@ -1320,6 +1335,11 @@ func ResetPasswdPost(ctx *context.Context) {
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplResetPassword, nil)
|
||||
return
|
||||
} else if !password.IsComplexEnough(passwd) {
|
||||
ctx.Data["IsResetForm"] = true
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplResetPassword, nil)
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
@@ -1350,7 +1370,6 @@ func ResetPasswdPost(ctx *context.Context) {
|
||||
func MustChangePassword(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
|
||||
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password"
|
||||
|
||||
ctx.HTML(200, tplMustChangePassword)
|
||||
}
|
||||
|
||||
@@ -1358,16 +1377,12 @@ func MustChangePassword(ctx *context.Context) {
|
||||
// account was created by an admin
|
||||
func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form auth.MustChangePasswordForm) {
|
||||
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
|
||||
|
||||
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password"
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(200, tplMustChangePassword)
|
||||
return
|
||||
}
|
||||
|
||||
u := ctx.User
|
||||
|
||||
// Make sure only requests for users who are eligible to change their password via
|
||||
// this method passes through
|
||||
if !u.MustChangePassword {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/password"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/services/mailer"
|
||||
@@ -27,7 +28,6 @@ func Account(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings")
|
||||
ctx.Data["PageIsSettingsAccount"] = true
|
||||
ctx.Data["Email"] = ctx.User.Email
|
||||
ctx.Data["EmailNotificationsPreference"] = ctx.User.EmailNotifications()
|
||||
|
||||
loadAccountData(ctx)
|
||||
|
||||
@@ -52,6 +52,8 @@ func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) {
|
||||
ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
|
||||
} else if form.Password != form.Retype {
|
||||
ctx.Flash.Error(ctx.Tr("form.password_not_match"))
|
||||
} else if !password.IsComplexEnough(form.Password) {
|
||||
ctx.Flash.Error(ctx.Tr("form.password_complexity"))
|
||||
} else {
|
||||
var err error
|
||||
if ctx.User.Salt, err = models.GetUserSalt(); err != nil {
|
||||
@@ -227,4 +229,5 @@ func loadAccountData(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
ctx.Data["Emails"] = emails
|
||||
ctx.Data["EmailNotificationsPreference"] = ctx.User.EmailNotifications()
|
||||
}
|
||||
|
||||
@@ -19,36 +19,65 @@ import (
|
||||
func TestChangePassword(t *testing.T) {
|
||||
oldPassword := "password"
|
||||
setting.MinPasswordLength = 6
|
||||
var pcALL = []string{"lower", "upper", "digit", "spec"}
|
||||
var pcLUN = []string{"lower", "upper", "digit"}
|
||||
var pcLU = []string{"lower", "upper"}
|
||||
|
||||
for _, req := range []struct {
|
||||
OldPassword string
|
||||
NewPassword string
|
||||
Retype string
|
||||
Message string
|
||||
OldPassword string
|
||||
NewPassword string
|
||||
Retype string
|
||||
Message string
|
||||
PasswordComplexity []string
|
||||
}{
|
||||
{
|
||||
OldPassword: oldPassword,
|
||||
NewPassword: "123456",
|
||||
Retype: "123456",
|
||||
Message: "",
|
||||
OldPassword: oldPassword,
|
||||
NewPassword: "Qwerty123456-",
|
||||
Retype: "Qwerty123456-",
|
||||
Message: "",
|
||||
PasswordComplexity: pcALL,
|
||||
},
|
||||
{
|
||||
OldPassword: oldPassword,
|
||||
NewPassword: "12345",
|
||||
Retype: "12345",
|
||||
Message: "auth.password_too_short",
|
||||
OldPassword: oldPassword,
|
||||
NewPassword: "12345",
|
||||
Retype: "12345",
|
||||
Message: "auth.password_too_short",
|
||||
PasswordComplexity: pcALL,
|
||||
},
|
||||
{
|
||||
OldPassword: "12334",
|
||||
NewPassword: "123456",
|
||||
Retype: "123456",
|
||||
Message: "settings.password_incorrect",
|
||||
OldPassword: "12334",
|
||||
NewPassword: "123456",
|
||||
Retype: "123456",
|
||||
Message: "settings.password_incorrect",
|
||||
PasswordComplexity: pcALL,
|
||||
},
|
||||
{
|
||||
OldPassword: oldPassword,
|
||||
NewPassword: "123456",
|
||||
Retype: "12345",
|
||||
Message: "form.password_not_match",
|
||||
OldPassword: oldPassword,
|
||||
NewPassword: "123456",
|
||||
Retype: "12345",
|
||||
Message: "form.password_not_match",
|
||||
PasswordComplexity: pcALL,
|
||||
},
|
||||
{
|
||||
OldPassword: oldPassword,
|
||||
NewPassword: "Qwerty",
|
||||
Retype: "Qwerty",
|
||||
Message: "form.password_complexity",
|
||||
PasswordComplexity: pcALL,
|
||||
},
|
||||
{
|
||||
OldPassword: oldPassword,
|
||||
NewPassword: "Qwerty",
|
||||
Retype: "Qwerty",
|
||||
Message: "form.password_complexity",
|
||||
PasswordComplexity: pcLUN,
|
||||
},
|
||||
{
|
||||
OldPassword: oldPassword,
|
||||
NewPassword: "QWERTY",
|
||||
Retype: "QWERTY",
|
||||
Message: "form.password_complexity",
|
||||
PasswordComplexity: pcLU,
|
||||
},
|
||||
} {
|
||||
models.PrepareTestEnv(t)
|
||||
|
||||
Reference in New Issue
Block a user