Changes
32 changed files (+1082/-74)
-
-
@@ -7,6 +7,7 @@ package integrationsimport ( "fmt" "net/http" "net/url" "path" "strconv" "strings"
-
@@ -20,6 +21,7 @@ import ("code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" "github.com/PuerkitoBio/goquery"
-
@@ -347,3 +349,209 @@ func TestIssueRedirect(t *testing.T) {resp = session.MakeRequest(t, req, http.StatusSeeOther) assert.Equal(t, "/"+path.Join("org26", "repo_external_tracker_alpha", "pulls", "1"), test.RedirectURL(resp)) } func TestSearchIssues(t *testing.T) { defer prepareTestEnv(t)() session := loginUser(t, "user2") link, _ := url.Parse("/issues/search") req := NewRequest(t, "GET", link.String()) resp := session.MakeRequest(t, req, http.StatusOK) var apiIssues []*api.Issue DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) since := "2000-01-01T00%3A50%3A01%2B00%3A00" // 946687801 before := time.Unix(999307200, 0).Format(time.RFC3339) query := url.Values{} query.Add("since", since) query.Add("before", before) link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 8) query.Del("since") query.Del("before") query.Add("state", "closed") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) query.Set("state", "all") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.EqualValues(t, "15", resp.Header().Get("X-Total-Count")) assert.Len(t, apiIssues, 10) // there are more but 10 is page item limit query.Add("limit", "20") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 15) query = url.Values{"assigned": {"true"}, "state": {"all"}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) query = url.Values{"milestones": {"milestone1"}, "state": {"all"}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) query = url.Values{"owner": {"user2"}} // user link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 6) query = url.Values{"owner": {"user3"}} // organization link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 3) query = url.Values{"owner": {"user3"}, "team": {"team1"}} // organization + team link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) } func TestSearchIssuesWithLabels(t *testing.T) { defer prepareTestEnv(t)() session := loginUser(t, "user1") link, _ := url.Parse("/api/v1/repos/issues/search") req := NewRequest(t, "GET", link.String()) resp := session.MakeRequest(t, req, http.StatusOK) var apiIssues []*api.Issue DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) query := url.Values{} link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 10) query.Add("labels", "label1") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) // multiple labels query.Set("labels", "label1,label2") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) // an org label query.Set("labels", "orglabel4") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 1) // org and repo label query.Set("labels", "label2,orglabel4") query.Add("state", "all") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) // org and repo label which share the same issue query.Set("labels", "label1,orglabel4") link.RawQuery = query.Encode() req = NewRequest(t, "GET", link.String()) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) } func TestGetIssueInfo(t *testing.T) { defer prepareTestEnv(t)() issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) assert.NoError(t, issue.LoadAttributes()) assert.Equal(t, int64(1019307200), int64(issue.DeadlineUnix)) assert.Equal(t, api.StateOpen, issue.State()) session := loginUser(t, owner.Name) urlStr := fmt.Sprintf("/%s/%s/issues/%d/info", owner.Name, repo.Name, issue.Index) req := NewRequest(t, "GET", urlStr) resp := session.MakeRequest(t, req, http.StatusOK) var apiIssue api.Issue DecodeJSON(t, resp, &apiIssue) assert.EqualValues(t, issue.ID, apiIssue.ID) } func TestUpdateIssueDeadline(t *testing.T) { defer prepareTestEnv(t)() issueBefore := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue) repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User) assert.NoError(t, issueBefore.LoadAttributes()) assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix)) assert.Equal(t, api.StateOpen, issueBefore.State()) session := loginUser(t, owner.Name) issueURL := fmt.Sprintf("%s/%s/issues/%d", owner.Name, repoBefore.Name, issueBefore.Index) req := NewRequest(t, "GET", issueURL) resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) urlStr := issueURL + "/deadline?_csrf=" + htmlDoc.GetCSRF() req = NewRequestWithJSON(t, "POST", urlStr, map[string]string{ "due_date": "2022-04-06T00:00:00.000Z", }) resp = session.MakeRequest(t, req, http.StatusCreated) var apiIssue api.IssueDeadline DecodeJSON(t, resp, &apiIssue) assert.EqualValues(t, "2022-04-06", apiIssue.Deadline.Format("2006-01-02")) }
-
-
-
@@ -10,6 +10,8 @@ import ("strings" "testing" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert"
-
@@ -173,3 +175,30 @@ func TestOrgRestrictedUser(t *testing.T) {req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName)) restrictedSession.MakeRequest(t, req, http.StatusOK) } func TestTeamSearch(t *testing.T) { defer prepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) var results TeamSearchResults session := loginUser(t, user.Name) csrf := GetCSRF(t, session, "/"+org.Name) req := NewRequestf(t, "GET", "/org/%s/teams/-/search?q=%s", org.Name, "_team") req.Header.Add("X-Csrf-Token", csrf) resp := session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &results) assert.NotEmpty(t, results.Data) assert.Len(t, results.Data, 1) assert.Equal(t, "test_team", results.Data[0].Name) // no access if not organization member user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) session = loginUser(t, user5.Name) csrf = GetCSRF(t, session, "/"+org.Name) req = NewRequestf(t, "GET", "/org/%s/teams/-/search?q=%s", org.Name, "team") req.Header.Add("X-Csrf-Token", csrf) session.MakeRequest(t, req, http.StatusNotFound) }
-
-
-
@@ -0,0 +1,46 @@// Copyright 2022 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package integrations import ( "net/http" "net/url" "testing" api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" ) func TestTopicSearch(t *testing.T) { defer prepareTestEnv(t)() searchURL, _ := url.Parse("/explore/topics/search") var topics struct { TopicNames []*api.TopicResponse `json:"topics"` } query := url.Values{"page": []string{"1"}, "limit": []string{"4"}} searchURL.RawQuery = query.Encode() res := MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) DecodeJSON(t, res, &topics) assert.Len(t, topics.TopicNames, 4) assert.EqualValues(t, "6", res.Header().Get("x-total-count")) query.Add("q", "topic") searchURL.RawQuery = query.Encode() res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) DecodeJSON(t, res, &topics) assert.Len(t, topics.TopicNames, 2) query.Set("q", "database") searchURL.RawQuery = query.Encode() res = MakeRequest(t, NewRequest(t, "GET", searchURL.String()), http.StatusOK) DecodeJSON(t, res, &topics) if assert.Len(t, topics.TopicNames, 1) { assert.EqualValues(t, 2, topics.TopicNames[0].ID) assert.EqualValues(t, "database", topics.TopicNames[0].Name) assert.EqualValues(t, 1, topics.TopicNames[0].RepoCount) } }
-
-
-
@@ -8,8 +8,11 @@ import ("net/http" "testing" "code.gitea.io/gitea/models" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation/i18n"
-
@@ -222,3 +225,26 @@ func testExportUserGPGKeys(t *testing.T, user, expected string) {// t.Log(resp.Body.String()) assert.Equal(t, expected, resp.Body.String()) } func TestListStopWatches(t *testing.T) { defer prepareTestEnv(t)() repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) session := loginUser(t, owner.Name) req := NewRequestf(t, "GET", "/user/stopwatches") resp := session.MakeRequest(t, req, http.StatusOK) var apiWatches []*api.StopWatch DecodeJSON(t, resp, &apiWatches) stopwatch := unittest.AssertExistsAndLoadBean(t, &models.Stopwatch{UserID: owner.ID}).(*models.Stopwatch) issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: stopwatch.IssueID}).(*models.Issue) if assert.Len(t, apiWatches, 1) { assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex) assert.EqualValues(t, issue.Title, apiWatches[0].IssueTitle) assert.EqualValues(t, repo.Name, apiWatches[0].RepoName) assert.EqualValues(t, repo.OwnerName, apiWatches[0].RepoOwnerName) assert.Greater(t, int64(apiWatches[0].Seconds), int64(0)) } }
-
-
-
@@ -191,22 +191,6 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) {} } // SetTotalCountHeader set "X-Total-Count" header func (ctx *APIContext) SetTotalCountHeader(total int64) { ctx.RespHeader().Set("X-Total-Count", fmt.Sprint(total)) ctx.AppendAccessControlExposeHeaders("X-Total-Count") } // AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header func (ctx *APIContext) AppendAccessControlExposeHeaders(names ...string) { val := ctx.RespHeader().Get("Access-Control-Expose-Headers") if len(val) != 0 { ctx.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", "))) } else { ctx.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", ")) } } // RequireCSRF requires a validated a CSRF token func (ctx *APIContext) RequireCSRF() { headerToken := ctx.Req.Header.Get(ctx.csrf.GetHeaderName())
-
-
-
@@ -10,6 +10,7 @@ import ("crypto/sha256" "encoding/hex" "errors" "fmt" "html" "html/template" "io"
-
@@ -21,6 +22,7 @@ import ("strings" "time" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base"
-
@@ -577,6 +579,22 @@ func (ctx *Context) Value(key interface{}) interface{} {return ctx.Req.Context().Value(key) } // SetTotalCountHeader set "X-Total-Count" header func (ctx *Context) SetTotalCountHeader(total int64) { ctx.RespHeader().Set("X-Total-Count", fmt.Sprint(total)) ctx.AppendAccessControlExposeHeaders("X-Total-Count") } // AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header func (ctx *Context) AppendAccessControlExposeHeaders(names ...string) { val := ctx.RespHeader().Get("Access-Control-Expose-Headers") if len(val) != 0 { ctx.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", "))) } else { ctx.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", ")) } } // Handler represents a custom handler type Handler func(*Context)
-
@@ -780,3 +798,21 @@ func Contexter() func(next http.Handler) http.Handler {}) } } // SearchOrderByMap represents all possible search order var SearchOrderByMap = map[string]map[string]db.SearchOrderBy{ "asc": { "alpha": db.SearchOrderByAlphabetically, "created": db.SearchOrderByOldest, "updated": db.SearchOrderByLeastUpdated, "size": db.SearchOrderBySize, "id": db.SearchOrderByID, }, "desc": { "alpha": db.SearchOrderByAlphabeticallyReverse, "created": db.SearchOrderByNewest, "updated": db.SearchOrderByRecentUpdated, "size": db.SearchOrderBySizeReverse, "id": db.SearchOrderByIDReverse, }, }
-
-
-
@@ -2,20 +2,16 @@// Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package utils package context import ( "net/url" "strings" "time" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" ) // GetQueryBeforeSince return parsed time (unix format) from URL query's before and since func GetQueryBeforeSince(ctx *context.APIContext) (before, since int64, err error) { func GetQueryBeforeSince(ctx *Context) (before, since int64, err error) { qCreatedBefore, err := prepareQueryArg(ctx, "before") if err != nil { return 0, 0, err
-
@@ -53,16 +49,8 @@ func parseTime(value string) (int64, error) {} // prepareQueryArg unescape and trim a query arg func prepareQueryArg(ctx *context.APIContext, name string) (value string, err error) { func prepareQueryArg(ctx *Context, name string) (value string, err error) { value, err = url.PathUnescape(ctx.FormString(name)) value = strings.TrimSpace(value) return } // GetListOptions returns list options using the page and limit parameters func GetListOptions(ctx *context.APIContext) db.ListOptions { return db.ListOptions{ Page: ctx.FormInt("page"), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), } }
-
-
-
@@ -26,7 +26,7 @@ func NewAvailable(ctx *context.APIContext) {} func getFindNotificationOptions(ctx *context.APIContext) *models.FindNotificationOptions { before, since, err := utils.GetQueryBeforeSince(ctx) before, since, err := context.GetQueryBeforeSince(ctx.Context) if err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return nil
-
-
-
@@ -111,7 +111,7 @@ func SearchIssues(ctx *context.APIContext) {// "200": // "$ref": "#/responses/IssueList" before, since, err := utils.GetQueryBeforeSince(ctx) before, since, err := context.GetQueryBeforeSince(ctx.Context) if err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return
-
@@ -359,7 +359,7 @@ func ListIssues(ctx *context.APIContext) {// responses: // "200": // "$ref": "#/responses/IssueList" before, since, err := utils.GetQueryBeforeSince(ctx) before, since, err := context.GetQueryBeforeSince(ctx.Context) if err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return
-
-
-
@@ -58,7 +58,7 @@ func ListIssueComments(ctx *context.APIContext) {// "200": // "$ref": "#/responses/CommentList" before, since, err := utils.GetQueryBeforeSince(ctx) before, since, err := context.GetQueryBeforeSince(ctx.Context) if err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return
-
@@ -150,7 +150,7 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {// "200": // "$ref": "#/responses/TimelineList" before, since, err := utils.GetQueryBeforeSince(ctx) before, since, err := context.GetQueryBeforeSince(ctx.Context) if err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return
-
@@ -253,7 +253,7 @@ func ListRepoIssueComments(ctx *context.APIContext) {// "200": // "$ref": "#/responses/CommentList" before, since, err := utils.GetQueryBeforeSince(ctx) before, since, err := context.GetQueryBeforeSince(ctx.Context) if err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return
-
-
-
@@ -103,7 +103,7 @@ func ListTrackedTimes(ctx *context.APIContext) {opts.UserID = user.ID } if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil { if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return }
-
@@ -522,7 +522,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {} var err error if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil { if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return }
-
@@ -597,7 +597,7 @@ func ListMyTrackedTimes(ctx *context.APIContext) {} var err error if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil { if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil { ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) return }
-
-
-
@@ -31,23 +31,6 @@ import (repo_service "code.gitea.io/gitea/services/repository" ) var searchOrderByMap = map[string]map[string]db.SearchOrderBy{ "asc": { "alpha": db.SearchOrderByAlphabetically, "created": db.SearchOrderByOldest, "updated": db.SearchOrderByLeastUpdated, "size": db.SearchOrderBySize, "id": db.SearchOrderByID, }, "desc": { "alpha": db.SearchOrderByAlphabeticallyReverse, "created": db.SearchOrderByNewest, "updated": db.SearchOrderByRecentUpdated, "size": db.SearchOrderBySizeReverse, "id": db.SearchOrderByIDReverse, }, } // Search repositories via options func Search(ctx *context.APIContext) { // swagger:operation GET /repos/search repository repoSearch
-
@@ -193,7 +176,7 @@ func Search(ctx *context.APIContext) {if len(sortOrder) == 0 { sortOrder = "asc" } if searchModeMap, ok := searchOrderByMap[sortOrder]; ok { if searchModeMap, ok := context.SearchOrderByMap[sortOrder]; ok { if orderBy, ok := searchModeMap[sortMode]; ok { opts.OrderBy = orderBy } else {
-
-
-
@@ -0,0 +1,19 @@// Copyright 2017 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package utils import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" ) // GetListOptions returns list options using the page and limit parameters func GetListOptions(ctx *context.APIContext) db.ListOptions { return db.ListOptions{ Page: ctx.FormInt("page"), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), } }
-
-
-
@@ -0,0 +1,42 @@// Copyright 2022 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package explore import ( "net/http" "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" ) // TopicSearch search for creating topic func TopicSearch(ctx *context.Context) { opts := &repo_model.FindTopicOptions{ Keyword: ctx.FormString("q"), ListOptions: db.ListOptions{ Page: ctx.FormInt("page"), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), }, } topics, total, err := repo_model.FindTopics(opts) if err != nil { ctx.Error(http.StatusInternalServerError) return } topicResponses := make([]*api.TopicResponse, len(topics)) for i, topic := range topics { topicResponses[i] = convert.ToTopicResponse(topic) } ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, map[string]interface{}{ "topics": topicResponses, }) }
-
-
-
@@ -13,6 +13,7 @@ import ("strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo"
-
@@ -20,7 +21,9 @@ import (user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/forms"
-
@@ -329,6 +332,51 @@ func TeamRepositories(ctx *context.Context) {ctx.HTML(http.StatusOK, tplTeamRepositories) } // SearchTeam api for searching teams func SearchTeam(ctx *context.Context) { listOptions := db.ListOptions{ Page: ctx.FormInt("page"), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), } opts := &organization.SearchTeamOptions{ UserID: ctx.Doer.ID, Keyword: ctx.FormTrim("q"), OrgID: ctx.Org.Organization.ID, IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"), ListOptions: listOptions, } teams, maxResults, err := organization.SearchTeam(opts) if err != nil { log.Error("SearchTeam failed: %v", err) ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ "ok": false, "error": "SearchTeam internal failure", }) return } apiTeams := make([]*api.Team, len(teams)) for i := range teams { if err := teams[i].GetUnits(); err != nil { log.Error("Team GetUnits failed: %v", err) ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ "ok": false, "error": "SearchTeam failed to get units", }) return } apiTeams[i] = convert.ToTeam(teams[i]) } ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, "data": apiTeams, }) } // EditTeam render team edit page func EditTeam(ctx *context.Context) { ctx.Data["Title"] = ctx.Org.Organization.FullName
-
-
-
@@ -16,6 +16,7 @@ import ("path" "strconv" "strings" "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db"
-
@@ -36,6 +37,7 @@ import ("code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/templates/vars" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web"
-
@@ -1762,6 +1764,20 @@ func getActionIssues(ctx *context.Context) []*models.Issue {return issues } // GetIssueInfo get an issue of a repository func GetIssueInfo(ctx *context.Context) { issue, err := models.GetIssueWithAttrsByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { ctx.Error(http.StatusNotFound) } else { ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err.Error()) } return } ctx.JSON(http.StatusOK, convert.ToAPIIssue(issue)) } // UpdateIssueTitle change issue's title func UpdateIssueTitle(ctx *context.Context) { issue := GetActionIssue(ctx)
-
@@ -1856,6 +1872,40 @@ func UpdateIssueContent(ctx *context.Context) {}) } // UpdateIssueDeadline updates an issue deadline func UpdateIssueDeadline(ctx *context.Context) { form := web.GetForm(ctx).(*api.EditDeadlineOption) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { ctx.NotFound("GetIssueByIndex", err) } else { ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err.Error()) } return } if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { ctx.Error(http.StatusForbidden, "", "Not repo writer") return } var deadlineUnix timeutil.TimeStamp var deadline time.Time if form.Deadline != nil && !form.Deadline.IsZero() { deadline = time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(), 23, 59, 59, 0, time.Local) deadlineUnix = timeutil.TimeStamp(deadline.Unix()) } if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.Doer); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err.Error()) return } ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: &deadline}) } // UpdateIssueMilestone change issue's milestone func UpdateIssueMilestone(ctx *context.Context) { issues := getActionIssues(ctx)
-
@@ -2052,6 +2102,338 @@ func UpdatePullReviewRequest(ctx *context.Context) {}) } // SearchIssues searches for issues across the repositories that the user has access to func SearchIssues(ctx *context.Context) { before, since, err := context.GetQueryBeforeSince(ctx) if err != nil { ctx.Error(http.StatusUnprocessableEntity, err.Error()) return } var isClosed util.OptionalBool switch ctx.FormString("state") { case "closed": isClosed = util.OptionalBoolTrue case "all": isClosed = util.OptionalBoolNone default: isClosed = util.OptionalBoolFalse } // find repos user can access (for issue search) opts := &models.SearchRepoOptions{ Private: false, AllPublic: true, TopicOnly: false, Collaborate: util.OptionalBoolNone, // This needs to be a column that is not nil in fixtures or // MySQL will return different results when sorting by null in some cases OrderBy: db.SearchOrderByAlphabetically, Actor: ctx.Doer, } if ctx.IsSigned { opts.Private = true opts.AllLimited = true } if ctx.FormString("owner") != "" { owner, err := user_model.GetUserByName(ctx.FormString("owner")) if err != nil { if user_model.IsErrUserNotExist(err) { ctx.Error(http.StatusBadRequest, "Owner not found", err.Error()) } else { ctx.Error(http.StatusInternalServerError, "GetUserByName", err.Error()) } return } opts.OwnerID = owner.ID opts.AllLimited = false opts.AllPublic = false opts.Collaborate = util.OptionalBoolFalse } if ctx.FormString("team") != "" { if ctx.FormString("owner") == "" { ctx.Error(http.StatusBadRequest, "", "Owner organisation is required for filtering on team") return } team, err := organization.GetTeam(opts.OwnerID, ctx.FormString("team")) if err != nil { if organization.IsErrTeamNotExist(err) { ctx.Error(http.StatusBadRequest, "Team not found", err.Error()) } else { ctx.Error(http.StatusInternalServerError, "GetUserByName", err.Error()) } return } opts.TeamID = team.ID } repoIDs, _, err := models.SearchRepositoryIDs(opts) if err != nil { ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err.Error()) return } var issues []*models.Issue var filteredCount int64 keyword := ctx.FormTrim("q") if strings.IndexByte(keyword, 0) >= 0 { keyword = "" } var issueIDs []int64 if len(keyword) > 0 && len(repoIDs) > 0 { if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword); err != nil { ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err.Error()) return } } var isPull util.OptionalBool switch ctx.FormString("type") { case "pulls": isPull = util.OptionalBoolTrue case "issues": isPull = util.OptionalBoolFalse default: isPull = util.OptionalBoolNone } labels := ctx.FormTrim("labels") var includedLabelNames []string if len(labels) > 0 { includedLabelNames = strings.Split(labels, ",") } milestones := ctx.FormTrim("milestones") var includedMilestones []string if len(milestones) > 0 { includedMilestones = strings.Split(milestones, ",") } // this api is also used in UI, // so the default limit is set to fit UI needs limit := ctx.FormInt("limit") if limit == 0 { limit = setting.UI.IssuePagingNum } else if limit > setting.API.MaxResponseItems { limit = setting.API.MaxResponseItems } // Only fetch the issues if we either don't have a keyword or the search returned issues // This would otherwise return all issues if no issues were found by the search. if len(keyword) == 0 || len(issueIDs) > 0 || len(includedLabelNames) > 0 || len(includedMilestones) > 0 { issuesOpt := &models.IssuesOptions{ ListOptions: db.ListOptions{ Page: ctx.FormInt("page"), PageSize: limit, }, RepoIDs: repoIDs, IsClosed: isClosed, IssueIDs: issueIDs, IncludedLabelNames: includedLabelNames, IncludeMilestones: includedMilestones, SortType: "priorityrepo", PriorityRepoID: ctx.FormInt64("priority_repo_id"), IsPull: isPull, UpdatedBeforeUnix: before, UpdatedAfterUnix: since, } ctxUserID := int64(0) if ctx.IsSigned { ctxUserID = ctx.Doer.ID } // Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested if ctx.FormBool("created") { issuesOpt.PosterID = ctxUserID } if ctx.FormBool("assigned") { issuesOpt.AssigneeID = ctxUserID } if ctx.FormBool("mentioned") { issuesOpt.MentionedID = ctxUserID } if ctx.FormBool("review_requested") { issuesOpt.ReviewRequestedID = ctxUserID } if issues, err = models.Issues(issuesOpt); err != nil { ctx.Error(http.StatusInternalServerError, "Issues", err.Error()) return } issuesOpt.ListOptions = db.ListOptions{ Page: -1, } if filteredCount, err = models.CountIssues(issuesOpt); err != nil { ctx.Error(http.StatusInternalServerError, "CountIssues", err.Error()) return } } ctx.SetTotalCountHeader(filteredCount) ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) } func getUserIDForFilter(ctx *context.Context, queryName string) int64 { userName := ctx.FormString(queryName) if len(userName) == 0 { return 0 } user, err := user_model.GetUserByName(userName) if user_model.IsErrUserNotExist(err) { ctx.NotFound("", err) return 0 } if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return 0 } return user.ID } // ListIssues list the issues of a repository func ListIssues(ctx *context.Context) { before, since, err := context.GetQueryBeforeSince(ctx) if err != nil { ctx.Error(http.StatusUnprocessableEntity, err.Error()) return } var isClosed util.OptionalBool switch ctx.FormString("state") { case "closed": isClosed = util.OptionalBoolTrue case "all": isClosed = util.OptionalBoolNone default: isClosed = util.OptionalBoolFalse } var issues []*models.Issue var filteredCount int64 keyword := ctx.FormTrim("q") if strings.IndexByte(keyword, 0) >= 0 { keyword = "" } var issueIDs []int64 var labelIDs []int64 if len(keyword) > 0 { issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } } if splitted := strings.Split(ctx.FormString("labels"), ","); len(splitted) > 0 { labelIDs, err = models.GetLabelIDsInRepoByNames(ctx.Repo.Repository.ID, splitted) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } } var mileIDs []int64 if part := strings.Split(ctx.FormString("milestones"), ","); len(part) > 0 { for i := range part { // uses names and fall back to ids // non existent milestones are discarded mile, err := models.GetMilestoneByRepoIDANDName(ctx.Repo.Repository.ID, part[i]) if err == nil { mileIDs = append(mileIDs, mile.ID) continue } if !models.IsErrMilestoneNotExist(err) { ctx.Error(http.StatusInternalServerError, err.Error()) return } id, err := strconv.ParseInt(part[i], 10, 64) if err != nil { continue } mile, err = models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, id) if err == nil { mileIDs = append(mileIDs, mile.ID) continue } if models.IsErrMilestoneNotExist(err) { continue } ctx.Error(http.StatusInternalServerError, err.Error()) } } listOptions := db.ListOptions{ Page: ctx.FormInt("page"), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), } var isPull util.OptionalBool switch ctx.FormString("type") { case "pulls": isPull = util.OptionalBoolTrue case "issues": isPull = util.OptionalBoolFalse default: isPull = util.OptionalBoolNone } // FIXME: we should be more efficient here createdByID := getUserIDForFilter(ctx, "created_by") if ctx.Written() { return } assignedByID := getUserIDForFilter(ctx, "assigned_by") if ctx.Written() { return } mentionedByID := getUserIDForFilter(ctx, "mentioned_by") if ctx.Written() { return } // Only fetch the issues if we either don't have a keyword or the search returned issues // This would otherwise return all issues if no issues were found by the search. if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { issuesOpt := &models.IssuesOptions{ ListOptions: listOptions, RepoIDs: []int64{ctx.Repo.Repository.ID}, IsClosed: isClosed, IssueIDs: issueIDs, LabelIDs: labelIDs, MilestoneIDs: mileIDs, IsPull: isPull, UpdatedBeforeUnix: before, UpdatedAfterUnix: since, PosterID: createdByID, AssigneeID: assignedByID, MentionedID: mentionedByID, } if issues, err = models.Issues(issuesOpt); err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } issuesOpt.ListOptions = db.ListOptions{ Page: -1, } if filteredCount, err = models.CountIssues(issuesOpt); err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } } ctx.SetTotalCountHeader(filteredCount) ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues)) } // UpdateIssueStatus change issue's status func UpdateIssueStatus(ctx *context.Context) { issues := getActionIssues(ctx)
-
-
-
@@ -20,11 +20,14 @@ import (user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/forms" repo_service "code.gitea.io/gitea/services/repository"
-
@@ -503,3 +506,112 @@ func InitiateDownload(ctx *context.Context) {"complete": completed, }) } // SearchRepo repositories via options func SearchRepo(ctx *context.Context) { opts := &models.SearchRepoOptions{ ListOptions: db.ListOptions{ Page: ctx.FormInt("page"), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), }, Actor: ctx.Doer, Keyword: ctx.FormTrim("q"), OwnerID: ctx.FormInt64("uid"), PriorityOwnerID: ctx.FormInt64("priority_owner_id"), TeamID: ctx.FormInt64("team_id"), TopicOnly: ctx.FormBool("topic"), Collaborate: util.OptionalBoolNone, Private: ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")), Template: util.OptionalBoolNone, StarredByID: ctx.FormInt64("starredBy"), IncludeDescription: ctx.FormBool("includeDesc"), } if ctx.FormString("template") != "" { opts.Template = util.OptionalBoolOf(ctx.FormBool("template")) } if ctx.FormBool("exclusive") { opts.Collaborate = util.OptionalBoolFalse } mode := ctx.FormString("mode") switch mode { case "source": opts.Fork = util.OptionalBoolFalse opts.Mirror = util.OptionalBoolFalse case "fork": opts.Fork = util.OptionalBoolTrue case "mirror": opts.Mirror = util.OptionalBoolTrue case "collaborative": opts.Mirror = util.OptionalBoolFalse opts.Collaborate = util.OptionalBoolTrue case "": default: ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid search mode: \"%s\"", mode)) return } if ctx.FormString("archived") != "" { opts.Archived = util.OptionalBoolOf(ctx.FormBool("archived")) } if ctx.FormString("is_private") != "" { opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private")) } sortMode := ctx.FormString("sort") if len(sortMode) > 0 { sortOrder := ctx.FormString("order") if len(sortOrder) == 0 { sortOrder = "asc" } if searchModeMap, ok := context.SearchOrderByMap[sortOrder]; ok { if orderBy, ok := searchModeMap[sortMode]; ok { opts.OrderBy = orderBy } else { ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid sort mode: \"%s\"", sortMode)) return } } else { ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid sort order: \"%s\"", sortOrder)) return } } var err error repos, count, err := models.SearchRepository(opts) if err != nil { ctx.JSON(http.StatusInternalServerError, api.SearchError{ OK: false, Error: err.Error(), }) return } results := make([]*api.Repository, len(repos)) for i, repo := range repos { if err = repo.GetOwner(ctx); err != nil { ctx.JSON(http.StatusInternalServerError, api.SearchError{ OK: false, Error: err.Error(), }) return } accessMode, err := models.AccessLevel(ctx.Doer, repo) if err != nil { ctx.JSON(http.StatusInternalServerError, api.SearchError{ OK: false, Error: err.Error(), }) } results[i] = convert.ToRepo(repo, accessMode) } ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, api.SearchResults{ OK: true, Data: results, }) }
-
-
-
@@ -15,6 +15,7 @@ import ("code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" ) const (
-
@@ -191,3 +192,8 @@ func NotificationPurgePost(c *context.Context) {c.Redirect(setting.AppSubURL+"/notifications", http.StatusSeeOther) } // NewAvailable returns the notification counts func NewAvailable(ctx *context.APIContext) { ctx.JSON(http.StatusOK, api.NotificationCount{New: models.CountUnread(ctx.Doer)}) }
-
-
-
@@ -0,0 +1,44 @@// Copyright 2022 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package user import ( "net/http" "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" ) // Search search users func Search(ctx *context.Context) { listOptions := db.ListOptions{ Page: ctx.FormInt("page"), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), } users, maxResults, err := user_model.SearchUsers(&user_model.SearchUserOptions{ Actor: ctx.Doer, Keyword: ctx.FormTrim("q"), UID: ctx.FormInt64("uid"), Type: user_model.UserTypeIndividual, ListOptions: listOptions, }) if err != nil { ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ "ok": false, "error": err.Error(), }) return } ctx.SetTotalCountHeader(maxResults) ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, "data": convert.ToUsers(ctx.Doer, users), }) }
-
-
-
@@ -0,0 +1,41 @@// Copyright 2022 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package user import ( "net/http" "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" ) // GetStopwatches get all stopwatches func GetStopwatches(ctx *context.Context) { sws, err := models.GetUserStopwatches(ctx.Doer.ID, db.ListOptions{ Page: ctx.FormInt("page"), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), }) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } count, err := models.CountUserStopwatches(ctx.Doer.ID) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } apiSWs, err := convert.ToStopWatches(sws) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiSWs) }
-
-
-
@@ -20,6 +20,7 @@ import ("code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web"
-
@@ -289,8 +290,13 @@ func RegisterRoutes(m *web.Route) {m.Get("/users", explore.Users) m.Get("/organizations", explore.Organizations) m.Get("/code", explore.Code) m.Get("/topics/search", explore.TopicSearch) }, ignExploreSignIn) m.Get("/issues", reqSignIn, user.Issues) m.Group("/issues", func() { m.Get("", user.Issues) m.Get("/search", repo.SearchIssues) }, reqSignIn) m.Get("/pulls", reqSignIn, user.Pulls) m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones)
-
@@ -421,6 +427,8 @@ func RegisterRoutes(m *web.Route) {m.Post("/forgot_password", auth.ForgotPasswdPost) m.Post("/logout", auth.SignOut) m.Get("/task/{task}", user.TaskStatus) m.Get("/stopwatches", user.GetStopwatches, reqSignIn) m.Get("/search", user.Search, ignExploreSignIn) }) // ***** END: User *****
-
@@ -605,6 +613,7 @@ func RegisterRoutes(m *web.Route) {m.Group("/{org}", func() { m.Get("/teams/new", org.NewTeam) m.Post("/teams/new", bindIgnErr(forms.CreateTeamForm{}), org.NewTeamPost) m.Get("/teams/-/search", org.SearchTeam) m.Get("/teams/{team}/edit", org.EditTeam) m.Post("/teams/{team}/edit", bindIgnErr(forms.CreateTeamForm{}), org.EditTeamPost) m.Post("/teams/{team}/delete", org.DeleteTeam)
-
@@ -669,6 +678,7 @@ func RegisterRoutes(m *web.Route) {m.Combo("/{repoid}").Get(repo.Fork). Post(bindIgnErr(forms.CreateRepoForm{}), repo.ForkPost) }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) m.Get("/search", repo.SearchRepo) }, reqSignIn) m.Group("/{username}/-", func() {
-
@@ -811,13 +821,16 @@ func RegisterRoutes(m *web.Route) {Post(bindIgnErr(forms.CreateIssueForm{}), repo.NewIssuePost) m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate) }) m.Get("/search", repo.ListIssues) }, context.RepoMustNotBeArchived(), reqRepoIssueReader) // FIXME: should use different URLs but mostly same logic for comments of issue and pull request. // So they can apply their own enable/disable logic on routers. m.Group("/{type:issues|pulls}", func() { m.Group("/{index}", func() { m.Get("/info", repo.GetIssueInfo) m.Post("/title", repo.UpdateIssueTitle) m.Post("/content", repo.UpdateIssueContent) m.Post("/deadline", bindIgnErr(structs.EditDeadlineOption{}), repo.UpdateIssueDeadline) m.Post("/watch", repo.IssueWatch) m.Post("/ref", repo.UpdateIssueRef) m.Group("/dependency", func() {
-
@@ -1195,6 +1208,7 @@ func RegisterRoutes(m *web.Route) {m.Get("", user.Notifications) m.Post("/status", user.NotificationStatusPost) m.Post("/purge", user.NotificationPurgePost) m.Get("/new", user.NewAvailable) }, reqSignIn) if setting.API.EnableSwagger {
-
-
-
@@ -429,7 +429,7 @@{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}} <div {{if ne .Issue.DeadlineUnix 0}} style="display: none;"{{end}} id="deadlineForm"> <form class="ui fluid action input issue-due-form" action="{{AppSubUrl}}/api/v1/repos/{{PathEscape .Repository.Owner.Name}}/{{PathEscape .Repository.Name}}/issues/{{.Issue.Index}}" method="post" id="update-issue-deadline-form"> <form class="ui fluid action input issue-due-form" action="{{AppSubUrl}}/{{PathEscape .Repository.Owner.Name}}/{{PathEscape .Repository.Name}}/issues/{{.Issue.Index}}/deadline" method="post" id="update-issue-deadline-form"> {{$.CsrfTokenHtml}} <input required placeholder="{{.i18n.Tr "repo.issues.due_date_form"}}" {{if gt .Issue.DeadlineUnix 0}}value="{{.Issue.DeadlineUnix.Format "2006-01-02"}}"{{end}} type="date" name="deadlineDate" id="deadlineDate"> <button class="ui green icon button">
-
-
-
@@ -120,7 +120,7 @@ export default {load(data, callback) { this.loading = true; this.i18nErrorMessage = null; $.get(`${appSubUrl}/api/v1/repos/${data.owner}/${data.repo}/issues/${data.index}`).done((issue) => { $.get(`${appSubUrl}/${data.owner}/${data.repo}/issues/${data.index}/info`).done((issue) => { this.issue = issue; }).fail((jqXHR) => { if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
-
-
-
@@ -124,7 +124,7 @@ function initVueComponents() {return this.repos.length > 0 && this.repos.length < this.counts[`${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`]; }, searchURL() { return `${this.subUrl}/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=${this.searchQuery return `${this.subUrl}/repo/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=${this.searchQuery }&page=${this.page}&limit=${this.searchLimit}&mode=${this.repoTypes[this.reposFilter].searchMode }${this.reposFilter !== 'all' ? '&exclusive=1' : '' }${this.archivedFilter === 'archived' ? '&archived=true' : ''}${this.archivedFilter === 'unarchived' ? '&archived=false' : ''
-
@@ -302,7 +302,7 @@ function initVueComponents() {this.isLoading = true; if (!this.reposTotalCount) { const totalCountSearchURL = `${this.subUrl}/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`; const totalCountSearchURL = `${this.subUrl}/repo/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`; $.getJSON(totalCountSearchURL, (_result, _textStatus, request) => { this.reposTotalCount = request.getResponseHeader('X-Total-Count'); });
-
-
-
@@ -8,7 +8,7 @@ export function initCompSearchUserBox() {$searchUserBox.search({ minCharacters: 2, apiSettings: { url: `${appSubUrl}/api/v1/users/search?q={query}`, url: `${appSubUrl}/user/search?q={query}`, onResponse(response) { const items = []; const searchQueryUppercase = $searchUserBox.find('input').val().toUpperCase();
-
-
-
@@ -158,7 +158,7 @@ async function updateNotificationTable() {async function updateNotificationCount() { const data = await $.ajax({ type: 'GET', url: `${appSubUrl}/api/v1/notifications/new`, url: `${appSubUrl}/notifications/new`, headers: { 'X-Csrf-Token': csrfToken, },
-
-
-
@@ -20,7 +20,7 @@ export function initOrgTeamSearchRepoBox() {$searchRepoBox.search({ minCharacters: 2, apiSettings: { url: `${appSubUrl}/api/v1/repos/search?q={query}&uid=${$searchRepoBox.data('uid')}`, url: `${appSubUrl}/repo/search?q={query}&uid=${$searchRepoBox.data('uid')}`, onResponse(response) { const items = []; $.each(response.data, (_i, item) => {
-
-
-
@@ -91,7 +91,7 @@ export function initRepoTopicBar() {label: 'ui small label' }, apiSettings: { url: `${appSubUrl}/api/v1/topics/search?q={query}`, url: `${appSubUrl}/explore/topics/search?q={query}`, throttle: 500, cache: false, onResponse(res) {
-
-
-
@@ -54,7 +54,7 @@ function updateDeadline(deadlineString) {realDeadline = new Date(newDate); } $.ajax(`${$('#update-issue-deadline-form').attr('action')}/deadline`, { $.ajax(`${$('#update-issue-deadline-form').attr('action')}`, { data: JSON.stringify({ due_date: realDeadline, }),
-
@@ -91,9 +91,9 @@ export function initRepoIssueList() {const repoId = $('#repoId').val(); const crossRepoSearch = $('#crossRepoSearch').val(); const tp = $('#type').val(); let issueSearchUrl = `${appSubUrl}/api/v1/repos/${repolink}/issues?q={query}&type=${tp}`; let issueSearchUrl = `${appSubUrl}/${repolink}/issues/search?q={query}&type=${tp}`; if (crossRepoSearch === 'true') { issueSearchUrl = `${appSubUrl}/api/v1/repos/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`; issueSearchUrl = `${appSubUrl}/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`; } $('#new-dependency-drop-list') .dropdown({
-
@@ -292,7 +292,7 @@ export function initRepoIssueReferenceRepositorySearch() {$('.issue_reference_repository_search') .dropdown({ apiSettings: { url: `${appSubUrl}/api/v1/repos/search?q={query}&limit=20`, url: `${appSubUrl}/repo/search?q={query}&limit=20`, onResponse(response) { const filteredResponse = {success: true, results: []}; $.each(response.data, (_r, repo) => {
-
-
-
@@ -21,7 +21,7 @@ export function initRepoSettingSearchTeamBox() {$searchTeamBox.search({ minCharacters: 2, apiSettings: { url: `${appSubUrl}/api/v1/orgs/${$searchTeamBox.data('org')}/teams/search?q={query}`, url: `${appSubUrl}/org/${$searchTeamBox.data('org')}/teams/-/search?q={query}`, headers: {'X-Csrf-Token': csrfToken}, onResponse(response) { const items = [];
-
-
-
@@ -23,7 +23,7 @@ export function initRepoTemplateSearch() {$('#repo_template_search') .dropdown({ apiSettings: { url: `${appSubUrl}/api/v1/repos/search?q={query}&template=true&priority_owner_id=${$('#uid').val()}`, url: `${appSubUrl}/repo/search?q={query}&template=true&priority_owner_id=${$('#uid').val()}`, onResponse(response) { const filteredResponse = {success: true, results: []}; filteredResponse.results.push({
-
-
-
@@ -111,7 +111,7 @@ async function updateStopwatchWithCallback(callback, timeout) {async function updateStopwatch() { const data = await $.ajax({ type: 'GET', url: `${appSubUrl}/api/v1/user/stopwatches`, url: `${appSubUrl}/user/stopwatches`, headers: {'X-Csrf-Token': csrfToken}, });
-