* Make URL scheme unambiguous Redirect old routes to new routes * Fix redirects to new URL scheme, and update template * Fix branches/_new endpoints, and update integration testtags/v1.21.12.1
| @@ -111,7 +111,7 @@ func testEditFile(t *testing.T, session *TestSession, user, repo, branch, filePa | |||||
| resp = session.MakeRequest(t, req, http.StatusFound) | resp = session.MakeRequest(t, req, http.StatusFound) | ||||
| // Verify the change | // Verify the change | ||||
| req = NewRequest(t, "GET", path.Join(user, repo, "raw", branch, filePath)) | |||||
| req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", branch, filePath)) | |||||
| resp = session.MakeRequest(t, req, http.StatusOK) | resp = session.MakeRequest(t, req, http.StatusOK) | ||||
| assert.EqualValues(t, newContent, string(resp.Body)) | assert.EqualValues(t, newContent, string(resp.Body)) | ||||
| @@ -142,7 +142,7 @@ func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, bra | |||||
| resp = session.MakeRequest(t, req, http.StatusFound) | resp = session.MakeRequest(t, req, http.StatusFound) | ||||
| // Verify the change | // Verify the change | ||||
| req = NewRequest(t, "GET", path.Join(user, repo, "raw", targetBranch, filePath)) | |||||
| req = NewRequest(t, "GET", path.Join(user, repo, "raw/branch", targetBranch, filePath)) | |||||
| resp = session.MakeRequest(t, req, http.StatusOK) | resp = session.MakeRequest(t, req, http.StatusOK) | ||||
| assert.EqualValues(t, newContent, string(resp.Body)) | assert.EqualValues(t, newContent, string(resp.Body)) | ||||
| @@ -10,6 +10,8 @@ import ( | |||||
| "testing" | "testing" | ||||
| api "code.gitea.io/sdk/gitea" | api "code.gitea.io/sdk/gitea" | ||||
| "github.com/stretchr/testify/assert" | |||||
| ) | ) | ||||
| func TestLinksNoLogin(t *testing.T) { | func TestLinksNoLogin(t *testing.T) { | ||||
| @@ -38,6 +40,20 @@ func TestLinksNoLogin(t *testing.T) { | |||||
| } | } | ||||
| } | } | ||||
| func TestRedirectsNoLogin(t *testing.T) { | |||||
| prepareTestEnv(t) | |||||
| var redirects = map[string]string{ | |||||
| "/user2/repo1/commits/master": "/user2/repo1/commits/branch/master", | |||||
| "/user2/repo1/src/master": "/user2/repo1/src/branch/master", | |||||
| } | |||||
| for link, redirectLink := range redirects { | |||||
| req := NewRequest(t, "GET", link) | |||||
| resp := MakeRequest(t, req, http.StatusFound) | |||||
| assert.EqualValues(t, redirectLink, RedirectURL(t, resp)) | |||||
| } | |||||
| } | |||||
| func testLinksAsUser(userName string, t *testing.T) { | func testLinksAsUser(userName string, t *testing.T) { | ||||
| var links = []string{ | var links = []string{ | ||||
| "/explore/repos", | "/explore/repos", | ||||
| @@ -99,7 +115,7 @@ func testLinksAsUser(userName string, t *testing.T) { | |||||
| "", | "", | ||||
| "/issues", | "/issues", | ||||
| "/pulls", | "/pulls", | ||||
| "/commits/master", | |||||
| "/commits/branch/master", | |||||
| "/graph", | "/graph", | ||||
| "/settings", | "/settings", | ||||
| "/settings/collaboration", | "/settings/collaboration", | ||||
| @@ -14,14 +14,14 @@ import ( | |||||
| "github.com/stretchr/testify/assert" | "github.com/stretchr/testify/assert" | ||||
| ) | ) | ||||
| func testCreateBranch(t *testing.T, session *TestSession, user, repo, oldRefName, newBranchName string, expectedStatus int) string { | |||||
| func testCreateBranch(t *testing.T, session *TestSession, user, repo, oldRefSubURL, newBranchName string, expectedStatus int) string { | |||||
| var csrf string | var csrf string | ||||
| if expectedStatus == http.StatusNotFound { | if expectedStatus == http.StatusNotFound { | ||||
| csrf = GetCSRF(t, session, path.Join(user, repo, "src/master")) | |||||
| csrf = GetCSRF(t, session, path.Join(user, repo, "src/branch/master")) | |||||
| } else { | } else { | ||||
| csrf = GetCSRF(t, session, path.Join(user, repo, "src", oldRefName)) | |||||
| csrf = GetCSRF(t, session, path.Join(user, repo, "src", oldRefSubURL)) | |||||
| } | } | ||||
| req := NewRequestWithValues(t, "POST", path.Join(user, repo, "branches/_new", oldRefName), map[string]string{ | |||||
| req := NewRequestWithValues(t, "POST", path.Join(user, repo, "branches/_new", oldRefSubURL), map[string]string{ | |||||
| "_csrf": csrf, | "_csrf": csrf, | ||||
| "new_branch_name": newBranchName, | "new_branch_name": newBranchName, | ||||
| }) | }) | ||||
| @@ -34,72 +34,72 @@ func testCreateBranch(t *testing.T, session *TestSession, user, repo, oldRefName | |||||
| func TestCreateBranch(t *testing.T) { | func TestCreateBranch(t *testing.T) { | ||||
| tests := []struct { | tests := []struct { | ||||
| OldBranchOrCommit string | |||||
| NewBranch string | |||||
| CreateRelease string | |||||
| FlashMessage string | |||||
| ExpectedStatus int | |||||
| OldRefSubURL string | |||||
| NewBranch string | |||||
| CreateRelease string | |||||
| FlashMessage string | |||||
| ExpectedStatus int | |||||
| }{ | }{ | ||||
| { | { | ||||
| OldBranchOrCommit: "master", | |||||
| NewBranch: "feature/test1", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.create_success", "feature/test1"), | |||||
| OldRefSubURL: "branch/master", | |||||
| NewBranch: "feature/test1", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.create_success", "feature/test1"), | |||||
| }, | }, | ||||
| { | { | ||||
| OldBranchOrCommit: "master", | |||||
| NewBranch: "", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.require_error"), | |||||
| OldRefSubURL: "branch/master", | |||||
| NewBranch: "", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.require_error"), | |||||
| }, | }, | ||||
| { | { | ||||
| OldBranchOrCommit: "master", | |||||
| NewBranch: "feature=test1", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.git_ref_name_error"), | |||||
| OldRefSubURL: "branch/master", | |||||
| NewBranch: "feature=test1", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.git_ref_name_error"), | |||||
| }, | }, | ||||
| { | { | ||||
| OldBranchOrCommit: "master", | |||||
| NewBranch: strings.Repeat("b", 101), | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.max_size_error", "100"), | |||||
| OldRefSubURL: "branch/master", | |||||
| NewBranch: strings.Repeat("b", 101), | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "form.NewBranchName") + i18n.Tr("en", "form.max_size_error", "100"), | |||||
| }, | }, | ||||
| { | { | ||||
| OldBranchOrCommit: "master", | |||||
| NewBranch: "master", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.branch_already_exists", "master"), | |||||
| OldRefSubURL: "branch/master", | |||||
| NewBranch: "master", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.branch_already_exists", "master"), | |||||
| }, | }, | ||||
| { | { | ||||
| OldBranchOrCommit: "master", | |||||
| NewBranch: "master/test", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.branch_name_conflict", "master/test", "master"), | |||||
| OldRefSubURL: "branch/master", | |||||
| NewBranch: "master/test", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.branch_name_conflict", "master/test", "master"), | |||||
| }, | }, | ||||
| { | { | ||||
| OldBranchOrCommit: "acd1d892867872cb47f3993468605b8aa59aa2e0", | |||||
| NewBranch: "feature/test2", | |||||
| ExpectedStatus: http.StatusNotFound, | |||||
| OldRefSubURL: "commit/acd1d892867872cb47f3993468605b8aa59aa2e0", | |||||
| NewBranch: "feature/test2", | |||||
| ExpectedStatus: http.StatusNotFound, | |||||
| }, | }, | ||||
| { | { | ||||
| OldBranchOrCommit: "65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
| NewBranch: "feature/test3", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.create_success", "feature/test3"), | |||||
| OldRefSubURL: "commit/65f1bf27bc3bf70f64657658635e66094edbcb4d", | |||||
| NewBranch: "feature/test3", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.create_success", "feature/test3"), | |||||
| }, | }, | ||||
| { | { | ||||
| OldBranchOrCommit: "master", | |||||
| NewBranch: "v1.0.0", | |||||
| CreateRelease: "v1.0.0", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.tag_collision", "v1.0.0"), | |||||
| OldRefSubURL: "branch/master", | |||||
| NewBranch: "v1.0.0", | |||||
| CreateRelease: "v1.0.0", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.tag_collision", "v1.0.0"), | |||||
| }, | }, | ||||
| { | { | ||||
| OldBranchOrCommit: "v1.0.0", | |||||
| NewBranch: "feature/test4", | |||||
| CreateRelease: "v1.0.0", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.create_success", "feature/test4"), | |||||
| OldRefSubURL: "tag/v1.0.0", | |||||
| NewBranch: "feature/test4", | |||||
| CreateRelease: "v1.0.0", | |||||
| ExpectedStatus: http.StatusFound, | |||||
| FlashMessage: i18n.Tr("en", "repo.branch.create_success", "feature/test4"), | |||||
| }, | }, | ||||
| } | } | ||||
| for _, test := range tests { | for _, test := range tests { | ||||
| @@ -108,7 +108,7 @@ func TestCreateBranch(t *testing.T) { | |||||
| if test.CreateRelease != "" { | if test.CreateRelease != "" { | ||||
| createNewRelease(t, session, "/user2/repo1", test.CreateRelease, test.CreateRelease, false, false) | createNewRelease(t, session, "/user2/repo1", test.CreateRelease, test.CreateRelease, false, false) | ||||
| } | } | ||||
| redirectURL := testCreateBranch(t, session, "user2", "repo1", test.OldBranchOrCommit, test.NewBranch, test.ExpectedStatus) | |||||
| redirectURL := testCreateBranch(t, session, "user2", "repo1", test.OldRefSubURL, test.NewBranch, test.ExpectedStatus) | |||||
| if test.ExpectedStatus == http.StatusFound { | if test.ExpectedStatus == http.StatusFound { | ||||
| req := NewRequest(t, "GET", redirectURL) | req := NewRequest(t, "GET", redirectURL) | ||||
| resp := session.MakeRequest(t, req, http.StatusOK) | resp := session.MakeRequest(t, req, http.StatusOK) | ||||
| @@ -124,7 +124,7 @@ func TestCreateBranch(t *testing.T) { | |||||
| func TestCreateBranchInvalidCSRF(t *testing.T) { | func TestCreateBranchInvalidCSRF(t *testing.T) { | ||||
| prepareTestEnv(t) | prepareTestEnv(t) | ||||
| session := loginUser(t, "user2") | session := loginUser(t, "user2") | ||||
| req := NewRequestWithValues(t, "POST", "user2/repo1/branches/_new/master", map[string]string{ | |||||
| req := NewRequestWithValues(t, "POST", "user2/repo1/branches/_new/branch/master", map[string]string{ | |||||
| "_csrf": "fake_csrf", | "_csrf": "fake_csrf", | ||||
| "new_branch_name": "test", | "new_branch_name": "test", | ||||
| }) | }) | ||||
| @@ -20,7 +20,7 @@ func TestRepoCommits(t *testing.T) { | |||||
| session := loginUser(t, "user2") | session := loginUser(t, "user2") | ||||
| // Request repository commits page | // Request repository commits page | ||||
| req := NewRequest(t, "GET", "/user2/repo1/commits/master") | |||||
| req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master") | |||||
| resp := session.MakeRequest(t, req, http.StatusOK) | resp := session.MakeRequest(t, req, http.StatusOK) | ||||
| doc := NewHTMLParser(t, resp.Body) | doc := NewHTMLParser(t, resp.Body) | ||||
| @@ -35,7 +35,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) { | |||||
| session := loginUser(t, "user2") | session := loginUser(t, "user2") | ||||
| // Request repository commits page | // Request repository commits page | ||||
| req := NewRequest(t, "GET", "/user2/repo1/commits/master") | |||||
| req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master") | |||||
| resp := session.MakeRequest(t, req, http.StatusOK) | resp := session.MakeRequest(t, req, http.StatusOK) | ||||
| doc := NewHTMLParser(t, resp.Body) | doc := NewHTMLParser(t, resp.Body) | ||||
| @@ -56,7 +56,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) { | |||||
| resp = session.MakeRequest(t, req, http.StatusCreated) | resp = session.MakeRequest(t, req, http.StatusCreated) | ||||
| req = NewRequest(t, "GET", "/user2/repo1/commits/master") | |||||
| req = NewRequest(t, "GET", "/user2/repo1/commits/branch/master") | |||||
| resp = session.MakeRequest(t, req, http.StatusOK) | resp = session.MakeRequest(t, req, http.StatusOK) | ||||
| doc = NewHTMLParser(t, resp.Body) | doc = NewHTMLParser(t, resp.Body) | ||||
| @@ -80,12 +80,22 @@ func SlackLinkFormatter(url string, text string) string { | |||||
| return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text)) | return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text)) | ||||
| } | } | ||||
| func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) { | |||||
| // created tag/branch | |||||
| refName := git.RefEndName(p.Ref) | |||||
| // SlackLinkToRef slack-formatter link to a repo ref | |||||
| func SlackLinkToRef(repoURL, ref string) string { | |||||
| refName := git.RefEndName(ref) | |||||
| switch { | |||||
| case strings.HasPrefix(ref, git.BranchPrefix): | |||||
| return SlackLinkFormatter(repoURL+"/src/branch/"+refName, refName) | |||||
| case strings.HasPrefix(ref, git.TagPrefix): | |||||
| return SlackLinkFormatter(repoURL+"/src/tag/"+refName, refName) | |||||
| default: | |||||
| return SlackLinkFormatter(repoURL+"/src/commit/"+refName, refName) | |||||
| } | |||||
| } | |||||
| func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) { | |||||
| repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) | repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) | ||||
| refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName) | |||||
| refLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref) | |||||
| text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName) | text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName) | ||||
| return &SlackPayload{ | return &SlackPayload{ | ||||
| @@ -99,7 +109,6 @@ func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayloa | |||||
| func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) { | func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) { | ||||
| // n new commits | // n new commits | ||||
| var ( | var ( | ||||
| branchName = git.RefEndName(p.Ref) | |||||
| commitDesc string | commitDesc string | ||||
| commitString string | commitString string | ||||
| ) | ) | ||||
| @@ -116,7 +125,7 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e | |||||
| } | } | ||||
| repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) | repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) | ||||
| branchLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+branchName, branchName) | |||||
| branchLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref) | |||||
| text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.UserName) | text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.UserName) | ||||
| var attachmentText string | var attachmentText string | ||||
| @@ -14,6 +14,7 @@ import ( | |||||
| "code.gitea.io/git" | "code.gitea.io/git" | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/cache" | "code.gitea.io/gitea/modules/cache" | ||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
| @@ -117,6 +118,20 @@ func (r *Repository) GetCommitsCount() (int64, error) { | |||||
| }) | }) | ||||
| } | } | ||||
| // BranchNameSubURL sub-URL for the BranchName field | |||||
| func (r *Repository) BranchNameSubURL() string { | |||||
| switch { | |||||
| case r.IsViewBranch: | |||||
| return "branch/" + r.BranchName | |||||
| case r.IsViewTag: | |||||
| return "tag/" + r.BranchName | |||||
| case r.IsViewCommit: | |||||
| return "commit/" + r.BranchName | |||||
| } | |||||
| log.Error(4, "Unknown view type for repo: %v", r) | |||||
| return "" | |||||
| } | |||||
| // GetEditorconfig returns the .editorconfig definition if found in the | // GetEditorconfig returns the .editorconfig definition if found in the | ||||
| // HEAD of the default repo branch. | // HEAD of the default repo branch. | ||||
| func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) { | func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) { | ||||
| @@ -444,8 +459,81 @@ func RepoAssignment() macaron.Handler { | |||||
| } | } | ||||
| } | } | ||||
| // RepoRef handles repository reference name including those contain `/`. | |||||
| // RepoRefType type of repo reference | |||||
| type RepoRefType int | |||||
| const ( | |||||
| // RepoRefLegacy unknown type, make educated guess and redirect. | |||||
| // for backward compatibility with previous URL scheme | |||||
| RepoRefLegacy RepoRefType = iota | |||||
| // RepoRefBranch branch | |||||
| RepoRefBranch | |||||
| // RepoRefTag tag | |||||
| RepoRefTag | |||||
| // RepoRefCommit commit | |||||
| RepoRefCommit | |||||
| ) | |||||
| // RepoRef handles repository reference names when the ref name is not | |||||
| // explicitly given | |||||
| func RepoRef() macaron.Handler { | func RepoRef() macaron.Handler { | ||||
| // since no ref name is explicitly specified, ok to just use branch | |||||
| return RepoRefByType(RepoRefBranch) | |||||
| } | |||||
| func getRefNameFromPath(ctx *Context, path string, isExist func(string) bool) string { | |||||
| refName := "" | |||||
| parts := strings.Split(path, "/") | |||||
| for i, part := range parts { | |||||
| refName = strings.TrimPrefix(refName+"/"+part, "/") | |||||
| if isExist(refName) { | |||||
| ctx.Repo.TreePath = strings.Join(parts[i+1:], "/") | |||||
| return refName | |||||
| } | |||||
| } | |||||
| return "" | |||||
| } | |||||
| func getRefName(ctx *Context, pathType RepoRefType) string { | |||||
| path := ctx.Params("*") | |||||
| switch pathType { | |||||
| case RepoRefLegacy: | |||||
| if refName := getRefName(ctx, RepoRefBranch); len(refName) > 0 { | |||||
| return refName | |||||
| } | |||||
| if refName := getRefName(ctx, RepoRefTag); len(refName) > 0 { | |||||
| return refName | |||||
| } | |||||
| return getRefName(ctx, RepoRefCommit) | |||||
| case RepoRefBranch: | |||||
| return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsBranchExist) | |||||
| case RepoRefTag: | |||||
| return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsTagExist) | |||||
| case RepoRefCommit: | |||||
| parts := strings.Split(path, "/") | |||||
| if len(parts) > 0 && len(parts[0]) == 40 { | |||||
| ctx.Repo.TreePath = strings.Join(parts[1:], "/") | |||||
| return parts[0] | |||||
| } | |||||
| default: | |||||
| log.Error(4, "Unrecognized path type: %v", path) | |||||
| } | |||||
| return "" | |||||
| } | |||||
| // URL to redirect to for deprecated URL scheme | |||||
| func repoRefRedirect(ctx *Context) string { | |||||
| urlPath := ctx.Req.URL.String() | |||||
| idx := strings.LastIndex(urlPath, ctx.Params("*")) | |||||
| if idx < 0 { | |||||
| idx = len(urlPath) | |||||
| } | |||||
| return path.Join(urlPath[:idx], ctx.Repo.BranchNameSubURL()) | |||||
| } | |||||
| // RepoRefByType handles repository reference name for a specific type | |||||
| // of repository reference | |||||
| func RepoRefByType(refType RepoRefType) macaron.Handler { | |||||
| return func(ctx *Context) { | return func(ctx *Context) { | ||||
| // Empty repository does not have reference information. | // Empty repository does not have reference information. | ||||
| if ctx.Repo.Repository.IsBare { | if ctx.Repo.Repository.IsBare { | ||||
| @@ -470,6 +558,7 @@ func RepoRef() macaron.Handler { | |||||
| // Get default branch. | // Get default branch. | ||||
| if len(ctx.Params("*")) == 0 { | if len(ctx.Params("*")) == 0 { | ||||
| refName = ctx.Repo.Repository.DefaultBranch | refName = ctx.Repo.Repository.DefaultBranch | ||||
| ctx.Repo.BranchName = refName | |||||
| if !ctx.Repo.GitRepo.IsBranchExist(refName) { | if !ctx.Repo.GitRepo.IsBranchExist(refName) { | ||||
| brs, err := ctx.Repo.GitRepo.GetBranches() | brs, err := ctx.Repo.GitRepo.GetBranches() | ||||
| if err != nil { | if err != nil { | ||||
| @@ -492,25 +581,8 @@ func RepoRef() macaron.Handler { | |||||
| ctx.Repo.IsViewBranch = true | ctx.Repo.IsViewBranch = true | ||||
| } else { | } else { | ||||
| hasMatched := false | |||||
| parts := strings.Split(ctx.Params("*"), "/") | |||||
| for i, part := range parts { | |||||
| refName = strings.TrimPrefix(refName+"/"+part, "/") | |||||
| if ctx.Repo.GitRepo.IsBranchExist(refName) || | |||||
| ctx.Repo.GitRepo.IsTagExist(refName) { | |||||
| if i < len(parts)-1 { | |||||
| ctx.Repo.TreePath = strings.Join(parts[i+1:], "/") | |||||
| } | |||||
| hasMatched = true | |||||
| break | |||||
| } | |||||
| } | |||||
| if !hasMatched && len(parts[0]) == 40 { | |||||
| refName = parts[0] | |||||
| ctx.Repo.TreePath = strings.Join(parts[1:], "/") | |||||
| } | |||||
| refName = getRefName(ctx, refType) | |||||
| ctx.Repo.BranchName = refName | |||||
| if ctx.Repo.GitRepo.IsBranchExist(refName) { | if ctx.Repo.GitRepo.IsBranchExist(refName) { | ||||
| ctx.Repo.IsViewBranch = true | ctx.Repo.IsViewBranch = true | ||||
| @@ -542,10 +614,16 @@ func RepoRef() macaron.Handler { | |||||
| ctx.Handle(404, "RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName)) | ctx.Handle(404, "RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName)) | ||||
| return | return | ||||
| } | } | ||||
| if refType == RepoRefLegacy { | |||||
| // redirect from old URL scheme to new URL scheme | |||||
| ctx.Redirect(repoRefRedirect(ctx)) | |||||
| return | |||||
| } | |||||
| } | } | ||||
| ctx.Repo.BranchName = refName | |||||
| ctx.Data["BranchName"] = ctx.Repo.BranchName | ctx.Data["BranchName"] = ctx.Repo.BranchName | ||||
| ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL() | |||||
| ctx.Data["CommitID"] = ctx.Repo.CommitID | ctx.Data["CommitID"] = ctx.Repo.CommitID | ||||
| ctx.Data["TreePath"] = ctx.Repo.TreePath | ctx.Data["TreePath"] = ctx.Repo.TreePath | ||||
| ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch | ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch | ||||
| @@ -391,7 +391,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| Post(reqToken(), bind(api.CreateForkOption{}), repo.CreateFork) | Post(reqToken(), bind(api.CreateForkOption{}), repo.CreateFork) | ||||
| m.Group("/branches", func() { | m.Group("/branches", func() { | ||||
| m.Get("", repo.ListBranches) | m.Get("", repo.ListBranches) | ||||
| m.Get("/*", context.RepoRef(), repo.GetBranch) | |||||
| m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch) | |||||
| }) | }) | ||||
| m.Group("/keys", func() { | m.Group("/keys", func() { | ||||
| m.Combo("").Get(repo.ListDeployKeys). | m.Combo("").Get(repo.ListDeployKeys). | ||||
| @@ -202,7 +202,7 @@ func CreateBranch(ctx *context.Context, form auth.NewBranchForm) { | |||||
| if ctx.HasError() { | if ctx.HasError() { | ||||
| ctx.Flash.Error(ctx.GetErrMsg()) | ctx.Flash.Error(ctx.GetErrMsg()) | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) | |||||
| return | return | ||||
| } | } | ||||
| @@ -216,19 +216,19 @@ func CreateBranch(ctx *context.Context, form auth.NewBranchForm) { | |||||
| if models.IsErrTagAlreadyExists(err) { | if models.IsErrTagAlreadyExists(err) { | ||||
| e := err.(models.ErrTagAlreadyExists) | e := err.(models.ErrTagAlreadyExists) | ||||
| ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName)) | ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName)) | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) | |||||
| return | return | ||||
| } | } | ||||
| if models.IsErrBranchAlreadyExists(err) { | if models.IsErrBranchAlreadyExists(err) { | ||||
| e := err.(models.ErrBranchAlreadyExists) | e := err.(models.ErrBranchAlreadyExists) | ||||
| ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", e.BranchName)) | ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", e.BranchName)) | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) | |||||
| return | return | ||||
| } | } | ||||
| if models.IsErrBranchNameConflict(err) { | if models.IsErrBranchNameConflict(err) { | ||||
| e := err.(models.ErrBranchNameConflict) | e := err.(models.ErrBranchNameConflict) | ||||
| ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName)) | ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName)) | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) | |||||
| return | return | ||||
| } | } | ||||
| @@ -237,5 +237,5 @@ func CreateBranch(ctx *context.Context, form auth.NewBranchForm) { | |||||
| } | } | ||||
| ctx.Flash.Success(ctx.Tr("repo.branch.create_success", form.NewBranchName)) | ctx.Flash.Success(ctx.Tr("repo.branch.create_success", form.NewBranchName)) | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + form.NewBranchName) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + form.NewBranchName) | |||||
| } | } | ||||
| @@ -120,7 +120,7 @@ func SearchCommits(ctx *context.Context) { | |||||
| keyword := strings.Trim(ctx.Query("q"), " ") | keyword := strings.Trim(ctx.Query("q"), " ") | ||||
| if len(keyword) == 0 { | if len(keyword) == 0 { | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchName) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchNameSubURL()) | |||||
| return | return | ||||
| } | } | ||||
| all := ctx.QueryBool("all") | all := ctx.QueryBool("all") | ||||
| @@ -113,7 +113,7 @@ func editFile(ctx *context.Context, isNewFile bool) { | |||||
| ctx.Data["TreeNames"] = treeNames | ctx.Data["TreeNames"] = treeNames | ||||
| ctx.Data["TreePaths"] = treePaths | ctx.Data["TreePaths"] = treePaths | ||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName | |||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() | |||||
| ctx.Data["commit_summary"] = "" | ctx.Data["commit_summary"] = "" | ||||
| ctx.Data["commit_message"] = "" | ctx.Data["commit_message"] = "" | ||||
| if canCommit { | if canCommit { | ||||
| @@ -164,7 +164,7 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo | |||||
| ctx.Data["TreePath"] = form.TreePath | ctx.Data["TreePath"] = form.TreePath | ||||
| ctx.Data["TreeNames"] = treeNames | ctx.Data["TreeNames"] = treeNames | ||||
| ctx.Data["TreePaths"] = treePaths | ctx.Data["TreePaths"] = treePaths | ||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + branchName | |||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + branchName | |||||
| ctx.Data["FileContent"] = form.Content | ctx.Data["FileContent"] = form.Content | ||||
| ctx.Data["commit_summary"] = form.CommitSummary | ctx.Data["commit_summary"] = form.CommitSummary | ||||
| ctx.Data["commit_message"] = form.CommitMessage | ctx.Data["commit_message"] = form.CommitMessage | ||||
| @@ -304,7 +304,7 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo | |||||
| return | return | ||||
| } | } | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName + "/" + strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(form.TreePath)) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(form.TreePath)) | |||||
| } | } | ||||
| // EditFilePost response for editing file | // EditFilePost response for editing file | ||||
| @@ -348,7 +348,7 @@ func DiffPreviewPost(ctx *context.Context, form auth.EditPreviewDiffForm) { | |||||
| // DeleteFile render delete file page | // DeleteFile render delete file page | ||||
| func DeleteFile(ctx *context.Context) { | func DeleteFile(ctx *context.Context) { | ||||
| ctx.Data["PageIsDelete"] = true | ctx.Data["PageIsDelete"] = true | ||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName | |||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() | |||||
| ctx.Data["TreePath"] = ctx.Repo.TreePath | ctx.Data["TreePath"] = ctx.Repo.TreePath | ||||
| canCommit := renderCommitRights(ctx) | canCommit := renderCommitRights(ctx) | ||||
| @@ -367,7 +367,7 @@ func DeleteFile(ctx *context.Context) { | |||||
| // DeleteFilePost response for deleting file | // DeleteFilePost response for deleting file | ||||
| func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) { | func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) { | ||||
| ctx.Data["PageIsDelete"] = true | ctx.Data["PageIsDelete"] = true | ||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName | |||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() | |||||
| ctx.Data["TreePath"] = ctx.Repo.TreePath | ctx.Data["TreePath"] = ctx.Repo.TreePath | ||||
| canCommit := renderCommitRights(ctx) | canCommit := renderCommitRights(ctx) | ||||
| @@ -422,7 +422,7 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) { | |||||
| } | } | ||||
| ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath)) | ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath)) | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName) | |||||
| } | } | ||||
| func renderUploadSettings(ctx *context.Context) { | func renderUploadSettings(ctx *context.Context) { | ||||
| @@ -446,7 +446,7 @@ func UploadFile(ctx *context.Context) { | |||||
| ctx.Data["TreeNames"] = treeNames | ctx.Data["TreeNames"] = treeNames | ||||
| ctx.Data["TreePaths"] = treePaths | ctx.Data["TreePaths"] = treePaths | ||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName | |||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() | |||||
| ctx.Data["commit_summary"] = "" | ctx.Data["commit_summary"] = "" | ||||
| ctx.Data["commit_message"] = "" | ctx.Data["commit_message"] = "" | ||||
| if canCommit { | if canCommit { | ||||
| @@ -482,7 +482,7 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) { | |||||
| ctx.Data["TreePath"] = form.TreePath | ctx.Data["TreePath"] = form.TreePath | ||||
| ctx.Data["TreeNames"] = treeNames | ctx.Data["TreeNames"] = treeNames | ||||
| ctx.Data["TreePaths"] = treePaths | ctx.Data["TreePaths"] = treePaths | ||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + branchName | |||||
| ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + branchName | |||||
| ctx.Data["commit_summary"] = form.CommitSummary | ctx.Data["commit_summary"] = form.CommitSummary | ||||
| ctx.Data["commit_message"] = form.CommitMessage | ctx.Data["commit_message"] = form.CommitMessage | ||||
| ctx.Data["commit_choice"] = form.CommitChoice | ctx.Data["commit_choice"] = form.CommitChoice | ||||
| @@ -551,7 +551,7 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) { | |||||
| return | return | ||||
| } | } | ||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/" + branchName + "/" + form.TreePath) | |||||
| ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + branchName + "/" + form.TreePath) | |||||
| } | } | ||||
| // UploadFileToServer upload file to server file dir not git | // UploadFileToServer upload file to server file dir not git | ||||
| @@ -34,6 +34,21 @@ func MustBeNotBare(ctx *context.Context) { | |||||
| } | } | ||||
| } | } | ||||
| // MustBeEditable check that repo can be edited | |||||
| func MustBeEditable(ctx *context.Context) { | |||||
| if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit { | |||||
| ctx.Handle(404, "", nil) | |||||
| return | |||||
| } | |||||
| } | |||||
| // MustBeAbleToUpload check that repo can be uploaded to | |||||
| func MustBeAbleToUpload(ctx *context.Context) { | |||||
| if !setting.Repository.Upload.Enabled { | |||||
| ctx.Handle(404, "", nil) | |||||
| } | |||||
| } | |||||
| func checkContextUser(ctx *context.Context, uid int64) *models.User { | func checkContextUser(ctx *context.Context, uid int64) *models.User { | ||||
| orgs, err := models.GetOwnedOrgsByUserIDDesc(ctx.User.ID, "updated_unix") | orgs, err := models.GetOwnedOrgsByUserIDDesc(ctx.User.ID, "updated_unix") | ||||
| if err != nil { | if err != nil { | ||||
| @@ -297,9 +297,9 @@ func renderCode(ctx *context.Context) { | |||||
| ctx.Data["Title"] = title | ctx.Data["Title"] = title | ||||
| ctx.Data["RequireHighlightJS"] = true | ctx.Data["RequireHighlightJS"] = true | ||||
| branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchName | |||||
| branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() | |||||
| treeLink := branchLink | treeLink := branchLink | ||||
| rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchName | |||||
| rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() | |||||
| if len(ctx.Repo.TreePath) > 0 { | if len(ctx.Repo.TreePath) > 0 { | ||||
| treeLink += "/" + ctx.Repo.TreePath | treeLink += "/" + ctx.Repo.TreePath | ||||
| @@ -522,34 +522,30 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) | Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) | ||||
| m.Group("", func() { | m.Group("", func() { | ||||
| m.Combo("/_edit/*").Get(repo.EditFile). | |||||
| Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost) | |||||
| m.Combo("/_new/*").Get(repo.NewFile). | |||||
| Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost) | |||||
| m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost) | |||||
| m.Combo("/_delete/*").Get(repo.DeleteFile). | |||||
| Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost) | |||||
| m.Group("", func() { | m.Group("", func() { | ||||
| m.Combo("/_upload/*").Get(repo.UploadFile). | |||||
| m.Combo("/_edit/*").Get(repo.EditFile). | |||||
| Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost) | |||||
| m.Combo("/_new/*").Get(repo.NewFile). | |||||
| Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost) | |||||
| m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost) | |||||
| m.Combo("/_delete/*").Get(repo.DeleteFile). | |||||
| Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost) | |||||
| m.Combo("/_upload/*", repo.MustBeAbleToUpload). | |||||
| Get(repo.UploadFile). | |||||
| Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost) | Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost) | ||||
| }, context.RepoRefByType(context.RepoRefBranch), repo.MustBeEditable) | |||||
| m.Group("", func() { | |||||
| m.Post("/upload-file", repo.UploadFileToServer) | m.Post("/upload-file", repo.UploadFileToServer) | ||||
| m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) | m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) | ||||
| }, func(ctx *context.Context) { | |||||
| if !setting.Repository.Upload.Enabled { | |||||
| ctx.Handle(404, "", nil) | |||||
| return | |||||
| } | |||||
| }) | |||||
| }, repo.MustBeNotBare, reqRepoWriter, context.RepoRef(), func(ctx *context.Context) { | |||||
| if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit { | |||||
| ctx.Handle(404, "", nil) | |||||
| return | |||||
| } | |||||
| }) | |||||
| }, context.RepoRef(), repo.MustBeEditable, repo.MustBeAbleToUpload) | |||||
| }, repo.MustBeNotBare, reqRepoWriter) | |||||
| m.Group("/branches", func() { | m.Group("/branches", func() { | ||||
| m.Post("/_new/*", context.RepoRef(), bindIgnErr(auth.NewBranchForm{}), repo.CreateBranch) | |||||
| m.Group("/_new/", func() { | |||||
| m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch) | |||||
| m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch) | |||||
| m.Post("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.CreateBranch) | |||||
| }, bindIgnErr(auth.NewBranchForm{})) | |||||
| m.Post("/delete", repo.DeleteBranchPost) | m.Post("/delete", repo.DeleteBranchPost) | ||||
| m.Post("/restore", repo.RestoreBranchPost) | m.Post("/restore", repo.RestoreBranchPost) | ||||
| }, reqRepoWriter, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode)) | }, reqRepoWriter, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode)) | ||||
| @@ -629,15 +625,36 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Post("/cleanup", context.RepoRef(), repo.CleanUpPullRequest) | m.Post("/cleanup", context.RepoRef(), repo.CleanUpPullRequest) | ||||
| }, repo.MustAllowPulls) | }, repo.MustAllowPulls) | ||||
| m.Group("/raw", func() { | |||||
| m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload) | |||||
| m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownload) | |||||
| m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownload) | |||||
| // "/*" route is deprecated, and kept for backward compatibility | |||||
| m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload) | |||||
| }, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode)) | |||||
| m.Group("/commits", func() { | |||||
| m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits) | |||||
| m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefCommits) | |||||
| m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits) | |||||
| // "/*" route is deprecated, and kept for backward compatibility | |||||
| m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits) | |||||
| }, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode)) | |||||
| m.Group("", func() { | m.Group("", func() { | ||||
| m.Get("/raw/*", repo.SingleDownload) | |||||
| m.Get("/commits/*", repo.RefCommits) | |||||
| m.Get("/graph", repo.Graph) | m.Get("/graph", repo.Graph) | ||||
| m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) | m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) | ||||
| }, repo.MustBeNotBare, context.RepoRef(), context.CheckUnit(models.UnitTypeCode)) | }, repo.MustBeNotBare, context.RepoRef(), context.CheckUnit(models.UnitTypeCode)) | ||||
| m.Group("/src", func() { | |||||
| m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) | |||||
| m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home) | |||||
| m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home) | |||||
| // "/*" route is deprecated, and kept for backward compatibility | |||||
| m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) | |||||
| }, repo.SetEditorconfigIfExists) | |||||
| m.Group("", func() { | m.Group("", func() { | ||||
| m.Get("/src/*", repo.SetEditorconfigIfExists, repo.Home) | |||||
| m.Get("/forks", repo.Forks) | m.Get("/forks", repo.Forks) | ||||
| }, context.RepoRef(), context.CheckUnit(models.UnitTypeCode)) | }, context.RepoRef(), context.CheckUnit(models.UnitTypeCode)) | ||||
| m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", | m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", | ||||
| @@ -10,10 +10,10 @@ | |||||
| </div> | </div> | ||||
| <div class="data" style="display: none" data-mode="{{if .IsViewTag}}tags{{else}}branches{{end}}"> | <div class="data" style="display: none" data-mode="{{if .IsViewTag}}tags{{else}}branches{{end}}"> | ||||
| {{range .Branches}} | {{range .Branches}} | ||||
| <div class="item branch {{if eq $.BranchName .}}selected{{end}}" data-url="{{$.RepoLink}}/{{if $.PageIsCommits}}commits{{else}}src{{end}}/{{EscapePound .}}{{if $.TreePath}}/{{EscapePound $.TreePath}}{{end}}">{{.}}</div> | |||||
| <div class="item branch {{if eq $.BranchName .}}selected{{end}}" data-url="{{$.RepoLink}}/{{if $.PageIsCommits}}commits{{else}}src{{end}}/branch/{{EscapePound .}}{{if $.TreePath}}/{{EscapePound $.TreePath}}{{end}}">{{.}}</div> | |||||
| {{end}} | {{end}} | ||||
| {{range .Tags}} | {{range .Tags}} | ||||
| <div class="item tag {{if eq $.BranchName .}}selected{{end}}" data-url="{{$.RepoLink}}/{{if $.PageIsCommits}}commits{{else}}src{{end}}/{{EscapePound .}}{{if $.TreePath}}/{{EscapePound $.TreePath}}{{end}}">{{.}}</div> | |||||
| <div class="item tag {{if eq $.BranchName .}}selected{{end}}" data-url="{{$.RepoLink}}/{{if $.PageIsCommits}}commits{{else}}src{{end}}/tag/{{EscapePound .}}{{if $.TreePath}}/{{EscapePound $.TreePath}}{{end}}">{{.}}</div> | |||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| <div class="menu transition" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak> | <div class="menu transition" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak> | ||||
| @@ -2,7 +2,7 @@ | |||||
| {{.CommitCount}} {{.i18n.Tr "repo.commits.commits"}} {{if .Branch}}({{.Branch}}){{end}} | {{.CommitCount}} {{.i18n.Tr "repo.commits.commits"}} {{if .Branch}}({{.Branch}}){{end}} | ||||
| {{if .PageIsCommits}} | {{if .PageIsCommits}} | ||||
| <div class="ui right"> | <div class="ui right"> | ||||
| <form action="{{.RepoLink}}/commits/{{.BranchName}}/search"> | |||||
| <form action="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/search"> | |||||
| <div class="ui tiny search input"> | <div class="ui tiny search input"> | ||||
| <input name="q" placeholder="{{.i18n.Tr "repo.commits.search"}}" value="{{.Keyword}}" autofocus> | <input name="q" placeholder="{{.i18n.Tr "repo.commits.search"}}" value="{{.Keyword}}" autofocus> | ||||
| </div> | </div> | ||||
| @@ -75,17 +75,17 @@ | |||||
| {{if gt .TotalPages 1}} | {{if gt .TotalPages 1}} | ||||
| <div class="center page buttons"> | <div class="center page buttons"> | ||||
| <div class="ui borderless pagination menu"> | <div class="ui borderless pagination menu"> | ||||
| <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.RepoLink}}/commits/{{$.BranchName}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Previous}}"{{end}}> | |||||
| <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.RepoLink}}/commits/{{$.BranchNameSubURL}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Previous}}"{{end}}> | |||||
| <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}} | <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}} | ||||
| </a> | </a> | ||||
| {{range .Pages}} | {{range .Pages}} | ||||
| {{if eq .Num -1}} | {{if eq .Num -1}} | ||||
| <a class="disabled item">...</a> | <a class="disabled item">...</a> | ||||
| {{else}} | {{else}} | ||||
| <a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.RepoLink}}/commits/{{$.BranchName}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Num}}"{{end}}>{{.Num}}</a> | |||||
| <a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.RepoLink}}/commits/{{$.BranchNameSubURL}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Num}}"{{end}}>{{.Num}}</a> | |||||
| {{end}} | {{end}} | ||||
| {{end}} | {{end}} | ||||
| <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.RepoLink}}/commits/{{$.BranchName}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Next}}"{{end}}> | |||||
| <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.RepoLink}}/commits/{{$.BranchNameSubURL}}{{if $.FileName}}/{{$.FileName}}{{end}}?page={{.Next}}"{{end}}> | |||||
| {{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i> | {{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i> | ||||
| </a> | </a> | ||||
| </div> | </div> | ||||
| @@ -30,7 +30,7 @@ | |||||
| <div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff"> | <div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff"> | ||||
| <a class="active item" data-tab="write"><i class="octicon octicon-code"></i> {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a> | <a class="active item" data-tab="write"><i class="octicon octicon-code"></i> {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a> | ||||
| {{if not .IsNewFile}} | {{if not .IsNewFile}} | ||||
| <a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/src/{{.BranchName}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "repo.release.preview"}}</a> | |||||
| <a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "repo.release.preview"}}</a> | |||||
| <a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName}}/{{.TreePath}}" data-context="{{.BranchLink}}"><i class="octicon octicon-diff"></i> {{.i18n.Tr "repo.editor.preview_changes"}}</a> | <a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName}}/{{.TreePath}}" data-context="{{.BranchLink}}"><i class="octicon octicon-diff"></i> {{.i18n.Tr "repo.editor.preview_changes"}}</a> | ||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| @@ -35,7 +35,7 @@ | |||||
| {{template "repo/branch_dropdown" .}} | {{template "repo/branch_dropdown" .}} | ||||
| {{ $n := len .TreeNames}} | {{ $n := len .TreeNames}} | ||||
| {{ $l := Subtract $n 1}} | {{ $l := Subtract $n 1}} | ||||
| <div class="fitted item"><span class="ui breadcrumb repo-path"><a class="section" href="{{.RepoLink}}/src/{{EscapePound .BranchName}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section">{{EllipsisString $v 30}}</span>{{else}}{{ $p := index $.Paths $i}}<span class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div> | |||||
| <div class="fitted item"><span class="ui breadcrumb repo-path"><a class="section" href="{{.RepoLink}}/src/{{EscapePound .BranchNameSubURL}}">{{EllipsisString .Repository.Name 30}}</a>{{range $i, $v := .TreeNames}}<span class="divider">/</span>{{if eq $i $l}}<span class="active section">{{EllipsisString $v 30}}</span>{{else}}{{ $p := index $.Paths $i}}<span class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div> | |||||
| <div class="right fitted item"> | <div class="right fitted item"> | ||||
| {{if .Repository.CanEnableEditor}} | {{if .Repository.CanEnableEditor}} | ||||
| <div id="file-buttons" class="ui tiny blue buttons"> | <div id="file-buttons" class="ui tiny blue buttons"> | ||||
| @@ -172,7 +172,7 @@ | |||||
| <a class="title has-emoji" href="{{$.Link}}/{{.Index}}">{{.Title}}</a> | <a class="title has-emoji" href="{{$.Link}}/{{.Index}}">{{.Title}}</a> | ||||
| {{if .Ref}} | {{if .Ref}} | ||||
| <a class="ui label" href="{{$.RepoLink}}/src/{{.Ref}}">{{.Ref}}</a> | |||||
| <a class="ui label" href="{{$.RepoLink}}/src/commit/{{.Ref}}">{{.Ref}}</a> | |||||
| {{end}} | {{end}} | ||||
| {{range .Labels}} | {{range .Labels}} | ||||
| <a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{.Name | Sanitize}}</a> | <a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{.Name | Sanitize}}</a> | ||||
| @@ -28,26 +28,26 @@ | |||||
| <span class="ui green label">{{$.i18n.Tr "repo.release.stable"}}</span> | <span class="ui green label">{{$.i18n.Tr "repo.release.stable"}}</span> | ||||
| {{end}} | {{end}} | ||||
| <span class="tag text blue"> | <span class="tag text blue"> | ||||
| <a href="{{$.RepoLink}}/src/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a> | |||||
| <a href="{{$.RepoLink}}/src/tag/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a> | |||||
| </span> | </span> | ||||
| <span class="commit"> | <span class="commit"> | ||||
| <a href="{{$.RepoLink}}/src/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a> | |||||
| <a href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a> | |||||
| </span> | </span> | ||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| <div class="ui twelve wide column detail"> | <div class="ui twelve wide column detail"> | ||||
| {{if .IsTag}} | {{if .IsTag}} | ||||
| <h4> | <h4> | ||||
| <a href="{{$.RepoLink}}/src/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a> | |||||
| <a href="{{$.RepoLink}}/src/tag/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a> | |||||
| </h4> | </h4> | ||||
| <div class="download"> | <div class="download"> | ||||
| <a href="{{$.RepoLink}}/src/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a> | |||||
| <a href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a> | |||||
| <a href="{{$.RepoLink}}/archive/{{.TagName}}.zip" rel="nofollow"><i class="octicon octicon-file-zip"></i> ZIP</a> | <a href="{{$.RepoLink}}/archive/{{.TagName}}.zip" rel="nofollow"><i class="octicon octicon-file-zip"></i> ZIP</a> | ||||
| <a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> TAR.GZ</a> | <a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> TAR.GZ</a> | ||||
| </div> | </div> | ||||
| {{else}} | {{else}} | ||||
| <h3> | <h3> | ||||
| <a href="{{$.RepoLink}}/src/{{.TagName}}">{{.Title}}</a> | |||||
| <a href="{{$.RepoLink}}/src/tag/{{.TagName}}">{{.Title}}</a> | |||||
| {{if $.IsRepositoryWriter}}<small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName}}" rel="nofollow">{{$.i18n.Tr "repo.release.edit"}}</a>)</small>{{end}} | {{if $.IsRepositoryWriter}}<small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName}}" rel="nofollow">{{$.i18n.Tr "repo.release.edit"}}</a>)</small>{{end}} | ||||
| </h3> | </h3> | ||||
| <p class="text grey"> | <p class="text grey"> | ||||
| @@ -15,9 +15,9 @@ | |||||
| <div class="ui right file-actions"> | <div class="ui right file-actions"> | ||||
| <div class="ui buttons"> | <div class="ui buttons"> | ||||
| {{if not .IsViewCommit}} | {{if not .IsViewCommit}} | ||||
| <a class="ui button" href="{{.RepoLink}}/src/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_permalink"}}</a> | |||||
| <a class="ui button" href="{{.RepoLink}}/src/commit/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_permalink"}}</a> | |||||
| {{end}} | {{end}} | ||||
| <a class="ui button" href="{{.RepoLink}}/commits/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_history"}}</a> | |||||
| <a class="ui button" href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_history"}}</a> | |||||
| <a class="ui button" href="{{EscapePound $.RawFileLink}}">{{.i18n.Tr "repo.file_raw"}}</a> | <a class="ui button" href="{{EscapePound $.RawFileLink}}">{{.i18n.Tr "repo.file_raw"}}</a> | ||||
| </div> | </div> | ||||
| {{if .Repository.CanEnableEditor}} | {{if .Repository.CanEnableEditor}} | ||||