mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Remove dependent on session auth for api/v1 routers (#19321)
* Remove dependent on session auth for api/v1 routers * Remove unnecessary session on API context * remove missed header * fix test * fix missed api/v1
This commit is contained in:
		| @@ -168,12 +168,11 @@ func TestAPIEditIssue(t *testing.T) { | |||||||
| func TestAPISearchIssues(t *testing.T) { | func TestAPISearchIssues(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	session := loginUser(t, "user2") | 	token := getUserToken(t, "user2") | ||||||
| 	token := getTokenForLoggedInUser(t, session) |  | ||||||
|  |  | ||||||
| 	link, _ := url.Parse("/api/v1/repos/issues/search") | 	link, _ := url.Parse("/api/v1/repos/issues/search") | ||||||
| 	req := NewRequest(t, "GET", link.String()) | 	req := NewRequest(t, "GET", link.String()+"?token="+token) | ||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 	var apiIssues []*api.Issue | 	var apiIssues []*api.Issue | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 10) | 	assert.Len(t, apiIssues, 10) | ||||||
| @@ -181,7 +180,7 @@ func TestAPISearchIssues(t *testing.T) { | |||||||
| 	query := url.Values{"token": {token}} | 	query := url.Values{"token": {token}} | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 10) | 	assert.Len(t, apiIssues, 10) | ||||||
|  |  | ||||||
| @@ -189,9 +188,10 @@ func TestAPISearchIssues(t *testing.T) { | |||||||
| 	before := time.Unix(999307200, 0).Format(time.RFC3339) | 	before := time.Unix(999307200, 0).Format(time.RFC3339) | ||||||
| 	query.Add("since", since) | 	query.Add("since", since) | ||||||
| 	query.Add("before", before) | 	query.Add("before", before) | ||||||
|  | 	query.Add("token", token) | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 8) | 	assert.Len(t, apiIssues, 8) | ||||||
| 	query.Del("since") | 	query.Del("since") | ||||||
| @@ -200,14 +200,14 @@ func TestAPISearchIssues(t *testing.T) { | |||||||
| 	query.Add("state", "closed") | 	query.Add("state", "closed") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
|  |  | ||||||
| 	query.Set("state", "all") | 	query.Set("state", "all") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.EqualValues(t, "15", resp.Header().Get("X-Total-Count")) | 	assert.EqualValues(t, "15", resp.Header().Get("X-Total-Count")) | ||||||
| 	assert.Len(t, apiIssues, 10) // there are more but 10 is page item limit | 	assert.Len(t, apiIssues, 10) // there are more but 10 is page item limit | ||||||
| @@ -215,49 +215,49 @@ func TestAPISearchIssues(t *testing.T) { | |||||||
| 	query.Add("limit", "20") | 	query.Add("limit", "20") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 15) | 	assert.Len(t, apiIssues, 15) | ||||||
|  |  | ||||||
| 	query = url.Values{"assigned": {"true"}, "state": {"all"}} | 	query = url.Values{"assigned": {"true"}, "state": {"all"}, "token": {token}} | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 1) | 	assert.Len(t, apiIssues, 1) | ||||||
|  |  | ||||||
| 	query = url.Values{"milestones": {"milestone1"}, "state": {"all"}} | 	query = url.Values{"milestones": {"milestone1"}, "state": {"all"}, "token": {token}} | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 1) | 	assert.Len(t, apiIssues, 1) | ||||||
|  |  | ||||||
| 	query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}} | 	query = url.Values{"milestones": {"milestone1,milestone3"}, "state": {"all"}, "token": {token}} | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
|  |  | ||||||
| 	query = url.Values{"owner": {"user2"}} // user | 	query = url.Values{"owner": {"user2"}, "token": {token}} // user | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 6) | 	assert.Len(t, apiIssues, 6) | ||||||
|  |  | ||||||
| 	query = url.Values{"owner": {"user3"}} // organization | 	query = url.Values{"owner": {"user3"}, "token": {token}} // organization | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 3) | 	assert.Len(t, apiIssues, 3) | ||||||
|  |  | ||||||
| 	query = url.Values{"owner": {"user3"}, "team": {"team1"}} // organization + team | 	query = url.Values{"owner": {"user3"}, "team": {"team1"}, "token": {token}} // organization + team | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
| } | } | ||||||
| @@ -265,12 +265,11 @@ func TestAPISearchIssues(t *testing.T) { | |||||||
| func TestAPISearchIssuesWithLabels(t *testing.T) { | func TestAPISearchIssuesWithLabels(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	session := loginUser(t, "user1") | 	token := getUserToken(t, "user1") | ||||||
| 	token := getTokenForLoggedInUser(t, session) |  | ||||||
|  |  | ||||||
| 	link, _ := url.Parse("/api/v1/repos/issues/search") | 	link, _ := url.Parse("/api/v1/repos/issues/search") | ||||||
| 	req := NewRequest(t, "GET", link.String()) | 	req := NewRequest(t, "GET", link.String()+"?token="+token) | ||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 	var apiIssues []*api.Issue | 	var apiIssues []*api.Issue | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
|  |  | ||||||
| @@ -280,14 +279,14 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { | |||||||
| 	query.Add("token", token) | 	query.Add("token", token) | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 10) | 	assert.Len(t, apiIssues, 10) | ||||||
|  |  | ||||||
| 	query.Add("labels", "label1") | 	query.Add("labels", "label1") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
|  |  | ||||||
| @@ -295,7 +294,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { | |||||||
| 	query.Set("labels", "label1,label2") | 	query.Set("labels", "label1,label2") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
|  |  | ||||||
| @@ -303,7 +302,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { | |||||||
| 	query.Set("labels", "orglabel4") | 	query.Set("labels", "orglabel4") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 1) | 	assert.Len(t, apiIssues, 1) | ||||||
|  |  | ||||||
| @@ -312,7 +311,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { | |||||||
| 	query.Add("state", "all") | 	query.Add("state", "all") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
|  |  | ||||||
| @@ -320,7 +319,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { | |||||||
| 	query.Set("labels", "label1,orglabel4") | 	query.Set("labels", "label1,orglabel4") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,9 +20,8 @@ import ( | |||||||
|  |  | ||||||
| func TestAPIOrgCreate(t *testing.T) { | func TestAPIOrgCreate(t *testing.T) { | ||||||
| 	onGiteaRun(t, func(*testing.T, *url.URL) { | 	onGiteaRun(t, func(*testing.T, *url.URL) { | ||||||
| 		session := loginUser(t, "user1") | 		token := getUserToken(t, "user1") | ||||||
|  |  | ||||||
| 		token := getTokenForLoggedInUser(t, session) |  | ||||||
| 		org := api.CreateOrgOption{ | 		org := api.CreateOrgOption{ | ||||||
| 			UserName:    "user1_org", | 			UserName:    "user1_org", | ||||||
| 			FullName:    "User1's organization", | 			FullName:    "User1's organization", | ||||||
| @@ -32,7 +31,7 @@ func TestAPIOrgCreate(t *testing.T) { | |||||||
| 			Visibility:  "limited", | 			Visibility:  "limited", | ||||||
| 		} | 		} | ||||||
| 		req := NewRequestWithJSON(t, "POST", "/api/v1/orgs?token="+token, &org) | 		req := NewRequestWithJSON(t, "POST", "/api/v1/orgs?token="+token, &org) | ||||||
| 		resp := session.MakeRequest(t, req, http.StatusCreated) | 		resp := MakeRequest(t, req, http.StatusCreated) | ||||||
|  |  | ||||||
| 		var apiOrg api.Organization | 		var apiOrg api.Organization | ||||||
| 		DecodeJSON(t, resp, &apiOrg) | 		DecodeJSON(t, resp, &apiOrg) | ||||||
| @@ -50,13 +49,13 @@ func TestAPIOrgCreate(t *testing.T) { | |||||||
| 			FullName:  org.FullName, | 			FullName:  org.FullName, | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
| 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s", org.UserName) | 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s?token=%s", org.UserName, token) | ||||||
| 		resp = session.MakeRequest(t, req, http.StatusOK) | 		resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 		DecodeJSON(t, resp, &apiOrg) | 		DecodeJSON(t, resp, &apiOrg) | ||||||
| 		assert.EqualValues(t, org.UserName, apiOrg.UserName) | 		assert.EqualValues(t, org.UserName, apiOrg.UserName) | ||||||
|  |  | ||||||
| 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org.UserName) | 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s/repos?token=%s", org.UserName, token) | ||||||
| 		resp = session.MakeRequest(t, req, http.StatusOK) | 		resp = MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 		var repos []*api.Repository | 		var repos []*api.Repository | ||||||
| 		DecodeJSON(t, resp, &repos) | 		DecodeJSON(t, resp, &repos) | ||||||
| @@ -64,8 +63,8 @@ func TestAPIOrgCreate(t *testing.T) { | |||||||
| 			assert.False(t, repo.Private) | 			assert.False(t, repo.Private) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members", org.UserName) | 		req = NewRequestf(t, "GET", "/api/v1/orgs/%s/members?token=%s", org.UserName, token) | ||||||
| 		resp = session.MakeRequest(t, req, http.StatusOK) | 		resp = MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 		// user1 on this org is public | 		// user1 on this org is public | ||||||
| 		var users []*api.User | 		var users []*api.User | ||||||
|   | |||||||
| @@ -25,12 +25,11 @@ func TestAPIListReleases(t *testing.T) { | |||||||
|  |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 	session := loginUser(t, user2.LowerName) | 	token := getUserToken(t, user2.LowerName) | ||||||
| 	token := getTokenForLoggedInUser(t, session) |  | ||||||
|  |  | ||||||
| 	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) | 	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/releases", user2.Name, repo.Name)) | ||||||
| 	link.RawQuery = url.Values{"token": {token}}.Encode() | 	link.RawQuery = url.Values{"token": {token}}.Encode() | ||||||
| 	resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) | 	resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) | ||||||
| 	var apiReleases []*api.Release | 	var apiReleases []*api.Release | ||||||
| 	DecodeJSON(t, resp, &apiReleases) | 	DecodeJSON(t, resp, &apiReleases) | ||||||
| 	if assert.Len(t, apiReleases, 3) { | 	if assert.Len(t, apiReleases, 3) { | ||||||
| @@ -53,13 +52,11 @@ func TestAPIListReleases(t *testing.T) { | |||||||
|  |  | ||||||
| 	// test filter | 	// test filter | ||||||
| 	testFilterByLen := func(auth bool, query url.Values, expectedLength int, msgAndArgs ...string) { | 	testFilterByLen := func(auth bool, query url.Values, expectedLength int, msgAndArgs ...string) { | ||||||
| 		link.RawQuery = query.Encode() |  | ||||||
| 		if auth { | 		if auth { | ||||||
| 			query.Set("token", token) | 			query.Set("token", token) | ||||||
| 			resp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) |  | ||||||
| 		} else { |  | ||||||
| 			resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) |  | ||||||
| 		} | 		} | ||||||
|  | 		link.RawQuery = query.Encode() | ||||||
|  | 		resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) | ||||||
| 		DecodeJSON(t, resp, &apiReleases) | 		DecodeJSON(t, resp, &apiReleases) | ||||||
| 		assert.Len(t, apiReleases, expectedLength, msgAndArgs) | 		assert.Len(t, apiReleases, expectedLength, msgAndArgs) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -59,36 +59,34 @@ func TestAPIRepoTopic(t *testing.T) { | |||||||
| 	repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository) | 	repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository) | ||||||
|  |  | ||||||
| 	// Get user2's token | 	// Get user2's token | ||||||
| 	session := loginUser(t, user2.Name) | 	token2 := getUserToken(t, user2.Name) | ||||||
| 	token2 := getTokenForLoggedInUser(t, session) |  | ||||||
|  |  | ||||||
| 	// Test read topics using login | 	// Test read topics using login | ||||||
| 	url := fmt.Sprintf("/api/v1/repos/%s/%s/topics", user2.Name, repo2.Name) | 	url := fmt.Sprintf("/api/v1/repos/%s/%s/topics", user2.Name, repo2.Name) | ||||||
| 	req := NewRequest(t, "GET", url) | 	req := NewRequest(t, "GET", url+"?token="+token2) | ||||||
| 	res := session.MakeRequest(t, req, http.StatusOK) | 	res := MakeRequest(t, req, http.StatusOK) | ||||||
| 	var topics *api.TopicName | 	var topics *api.TopicName | ||||||
| 	DecodeJSON(t, res, &topics) | 	DecodeJSON(t, res, &topics) | ||||||
| 	assert.ElementsMatch(t, []string{"topicname1", "topicname2"}, topics.TopicNames) | 	assert.ElementsMatch(t, []string{"topicname1", "topicname2"}, topics.TopicNames) | ||||||
|  |  | ||||||
| 	// Log out user2 | 	// Log out user2 | ||||||
| 	session = emptyTestSession(t) |  | ||||||
| 	url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user2.Name, repo2.Name, token2) | 	url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user2.Name, repo2.Name, token2) | ||||||
|  |  | ||||||
| 	// Test delete a topic | 	// Test delete a topic | ||||||
| 	req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Topicname1", token2) | 	req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Topicname1", token2) | ||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
| 	// Test add an existing topic | 	// Test add an existing topic | ||||||
| 	req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Golang", token2) | 	req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Golang", token2) | ||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
| 	// Test add a topic | 	// Test add a topic | ||||||
| 	req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "topicName3", token2) | 	req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "topicName3", token2) | ||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
| 	// Test read topics using token | 	// Test read topics using token | ||||||
| 	req = NewRequest(t, "GET", url) | 	req = NewRequest(t, "GET", url) | ||||||
| 	res = session.MakeRequest(t, req, http.StatusOK) | 	res = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, res, &topics) | 	DecodeJSON(t, res, &topics) | ||||||
| 	assert.ElementsMatch(t, []string{"topicname2", "golang", "topicname3"}, topics.TopicNames) | 	assert.ElementsMatch(t, []string{"topicname2", "golang", "topicname3"}, topics.TopicNames) | ||||||
|  |  | ||||||
| @@ -97,9 +95,9 @@ func TestAPIRepoTopic(t *testing.T) { | |||||||
| 	req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ | 	req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ | ||||||
| 		Topics: newTopics, | 		Topics: newTopics, | ||||||
| 	}) | 	}) | ||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	MakeRequest(t, req, http.StatusNoContent) | ||||||
| 	req = NewRequest(t, "GET", url) | 	req = NewRequest(t, "GET", url) | ||||||
| 	res = session.MakeRequest(t, req, http.StatusOK) | 	res = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, res, &topics) | 	DecodeJSON(t, res, &topics) | ||||||
| 	assert.ElementsMatch(t, []string{"windows", "mac"}, topics.TopicNames) | 	assert.ElementsMatch(t, []string{"windows", "mac"}, topics.TopicNames) | ||||||
|  |  | ||||||
| @@ -108,9 +106,9 @@ func TestAPIRepoTopic(t *testing.T) { | |||||||
| 	req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ | 	req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ | ||||||
| 		Topics: newTopics, | 		Topics: newTopics, | ||||||
| 	}) | 	}) | ||||||
| 	session.MakeRequest(t, req, http.StatusUnprocessableEntity) | 	MakeRequest(t, req, http.StatusUnprocessableEntity) | ||||||
| 	req = NewRequest(t, "GET", url) | 	req = NewRequest(t, "GET", url) | ||||||
| 	res = session.MakeRequest(t, req, http.StatusOK) | 	res = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, res, &topics) | 	DecodeJSON(t, res, &topics) | ||||||
| 	assert.ElementsMatch(t, []string{"windows", "mac"}, topics.TopicNames) | 	assert.ElementsMatch(t, []string{"windows", "mac"}, topics.TopicNames) | ||||||
|  |  | ||||||
| @@ -119,9 +117,9 @@ func TestAPIRepoTopic(t *testing.T) { | |||||||
| 	req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ | 	req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ | ||||||
| 		Topics: newTopics, | 		Topics: newTopics, | ||||||
| 	}) | 	}) | ||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	MakeRequest(t, req, http.StatusNoContent) | ||||||
| 	req = NewRequest(t, "GET", url) | 	req = NewRequest(t, "GET", url) | ||||||
| 	res = session.MakeRequest(t, req, http.StatusOK) | 	res = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, res, &topics) | 	DecodeJSON(t, res, &topics) | ||||||
| 	assert.Len(t, topics.TopicNames, 25) | 	assert.Len(t, topics.TopicNames, 25) | ||||||
|  |  | ||||||
| @@ -130,29 +128,27 @@ func TestAPIRepoTopic(t *testing.T) { | |||||||
| 	req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ | 	req = NewRequestWithJSON(t, "PUT", url, &api.RepoTopicOptions{ | ||||||
| 		Topics: newTopics, | 		Topics: newTopics, | ||||||
| 	}) | 	}) | ||||||
| 	session.MakeRequest(t, req, http.StatusUnprocessableEntity) | 	MakeRequest(t, req, http.StatusUnprocessableEntity) | ||||||
|  |  | ||||||
| 	// Test add a topic when there is already maximum | 	// Test add a topic when there is already maximum | ||||||
| 	req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "t26", token2) | 	req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "t26", token2) | ||||||
| 	session.MakeRequest(t, req, http.StatusUnprocessableEntity) | 	MakeRequest(t, req, http.StatusUnprocessableEntity) | ||||||
|  |  | ||||||
| 	// Test delete a topic that repo doesn't have | 	// Test delete a topic that repo doesn't have | ||||||
| 	req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Topicname1", token2) | 	req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/topics/%s?token=%s", user2.Name, repo2.Name, "Topicname1", token2) | ||||||
| 	session.MakeRequest(t, req, http.StatusNotFound) | 	MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
| 	// Get user4's token | 	// Get user4's token | ||||||
| 	session = loginUser(t, user4.Name) | 	token4 := getUserToken(t, user4.Name) | ||||||
| 	token4 := getTokenForLoggedInUser(t, session) |  | ||||||
| 	session = emptyTestSession(t) |  | ||||||
|  |  | ||||||
| 	// Test read topics with write access | 	// Test read topics with write access | ||||||
| 	url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user3.Name, repo3.Name, token4) | 	url = fmt.Sprintf("/api/v1/repos/%s/%s/topics?token=%s", user3.Name, repo3.Name, token4) | ||||||
| 	req = NewRequest(t, "GET", url) | 	req = NewRequest(t, "GET", url) | ||||||
| 	res = session.MakeRequest(t, req, http.StatusOK) | 	res = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, res, &topics) | 	DecodeJSON(t, res, &topics) | ||||||
| 	assert.Empty(t, topics.TopicNames) | 	assert.Empty(t, topics.TopicNames) | ||||||
|  |  | ||||||
| 	// Test add a topic to repo with write access (requires repo admin access) | 	// Test add a topic to repo with write access (requires repo admin access) | ||||||
| 	req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user3.Name, repo3.Name, "topicName", token4) | 	req = NewRequestf(t, "PUT", "/api/v1/repos/%s/%s/topics/%s?token=%s", user3.Name, repo3.Name, "topicName", token4) | ||||||
| 	session.MakeRequest(t, req, http.StatusForbidden) | 	MakeRequest(t, req, http.StatusForbidden) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -224,11 +224,9 @@ func TestAPITeamSearch(t *testing.T) { | |||||||
|  |  | ||||||
| 	var results TeamSearchResults | 	var results TeamSearchResults | ||||||
|  |  | ||||||
| 	session := loginUser(t, user.Name) | 	token := getUserToken(t, user.Name) | ||||||
| 	csrf := GetCSRF(t, session, "/"+org.Name) | 	req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "_team", token) | ||||||
| 	req := NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s", org.Name, "_team") | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 	req.Header.Add("X-Csrf-Token", csrf) |  | ||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) |  | ||||||
| 	DecodeJSON(t, resp, &results) | 	DecodeJSON(t, resp, &results) | ||||||
| 	assert.NotEmpty(t, results.Data) | 	assert.NotEmpty(t, results.Data) | ||||||
| 	assert.Len(t, results.Data, 1) | 	assert.Len(t, results.Data, 1) | ||||||
| @@ -236,9 +234,8 @@ func TestAPITeamSearch(t *testing.T) { | |||||||
|  |  | ||||||
| 	// no access if not organization member | 	// no access if not organization member | ||||||
| 	user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) | 	user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) | ||||||
| 	session = loginUser(t, user5.Name) | 	token5 := getUserToken(t, user5.Name) | ||||||
| 	csrf = GetCSRF(t, session, "/"+org.Name) |  | ||||||
| 	req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s", org.Name, "team") | 	req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s&token=%s", org.Name, "team", token5) | ||||||
| 	req.Header.Add("X-Csrf-Token", csrf) | 	MakeRequest(t, req, http.StatusForbidden) | ||||||
| 	session.MakeRequest(t, req, http.StatusForbidden) |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,15 +20,15 @@ func TestUserHeatmap(t *testing.T) { | |||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
| 	adminUsername := "user1" | 	adminUsername := "user1" | ||||||
| 	normalUsername := "user2" | 	normalUsername := "user2" | ||||||
| 	session := loginUser(t, adminUsername) | 	token := getUserToken(t, adminUsername) | ||||||
|  |  | ||||||
| 	fakeNow := time.Date(2011, 10, 20, 0, 0, 0, 0, time.Local) | 	fakeNow := time.Date(2011, 10, 20, 0, 0, 0, 0, time.Local) | ||||||
| 	timeutil.Set(fakeNow) | 	timeutil.Set(fakeNow) | ||||||
| 	defer timeutil.Unset() | 	defer timeutil.Unset() | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/users/%s/heatmap", normalUsername) | 	urlStr := fmt.Sprintf("/api/v1/users/%s/heatmap?token=%s", normalUsername, token) | ||||||
| 	req := NewRequest(t, "GET", urlStr) | 	req := NewRequest(t, "GET", urlStr) | ||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 	var heatmap []*models.UserHeatmapData | 	var heatmap []*models.UserHeatmapData | ||||||
| 	DecodeJSON(t, resp, &heatmap) | 	DecodeJSON(t, resp, &heatmap) | ||||||
| 	var dummyheatmap []*models.UserHeatmapData | 	var dummyheatmap []*models.UserHeatmapData | ||||||
|   | |||||||
| @@ -359,6 +359,10 @@ func emptyTestSession(t testing.TB) *TestSession { | |||||||
| 	return &TestSession{jar: jar} | 	return &TestSession{jar: jar} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func getUserToken(t testing.TB, userName string) string { | ||||||
|  | 	return getTokenForLoggedInUser(t, loginUser(t, userName)) | ||||||
|  | } | ||||||
|  |  | ||||||
| func loginUser(t testing.TB, userName string) *TestSession { | func loginUser(t testing.TB, userName string) *TestSession { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 	if session, ok := loginSessionCache[userName]; ok { | 	if session, ok := loginSessionCache[userName]; ok { | ||||||
|   | |||||||
| @@ -448,27 +448,29 @@ func TestSearchIssues(t *testing.T) { | |||||||
| func TestSearchIssuesWithLabels(t *testing.T) { | func TestSearchIssuesWithLabels(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	session := loginUser(t, "user1") | 	token := getUserToken(t, "user1") | ||||||
|  |  | ||||||
| 	link, _ := url.Parse("/api/v1/repos/issues/search") | 	link, _ := url.Parse("/api/v1/repos/issues/search?token=" + token) | ||||||
| 	req := NewRequest(t, "GET", link.String()) | 	req := NewRequest(t, "GET", link.String()) | ||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
| 	var apiIssues []*api.Issue | 	var apiIssues []*api.Issue | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
|  |  | ||||||
| 	assert.Len(t, apiIssues, 10) | 	assert.Len(t, apiIssues, 10) | ||||||
|  |  | ||||||
| 	query := url.Values{} | 	query := url.Values{ | ||||||
|  | 		"token": []string{token}, | ||||||
|  | 	} | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 10) | 	assert.Len(t, apiIssues, 10) | ||||||
|  |  | ||||||
| 	query.Add("labels", "label1") | 	query.Add("labels", "label1") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
|  |  | ||||||
| @@ -476,7 +478,7 @@ func TestSearchIssuesWithLabels(t *testing.T) { | |||||||
| 	query.Set("labels", "label1,label2") | 	query.Set("labels", "label1,label2") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
|  |  | ||||||
| @@ -484,7 +486,7 @@ func TestSearchIssuesWithLabels(t *testing.T) { | |||||||
| 	query.Set("labels", "orglabel4") | 	query.Set("labels", "orglabel4") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 1) | 	assert.Len(t, apiIssues, 1) | ||||||
|  |  | ||||||
| @@ -493,7 +495,7 @@ func TestSearchIssuesWithLabels(t *testing.T) { | |||||||
| 	query.Add("state", "all") | 	query.Add("state", "all") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
|  |  | ||||||
| @@ -501,7 +503,7 @@ func TestSearchIssuesWithLabels(t *testing.T) { | |||||||
| 	query.Set("labels", "label1,orglabel4") | 	query.Set("labels", "label1,orglabel4") | ||||||
| 	link.RawQuery = query.Encode() | 	link.RawQuery = query.Encode() | ||||||
| 	req = NewRequest(t, "GET", link.String()) | 	req = NewRequest(t, "GET", link.String()) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = MakeRequest(t, req, http.StatusOK) | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, 2) | 	assert.Len(t, apiIssues, 2) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ package context | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"html" |  | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -20,8 +19,6 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/web/middleware" | 	"code.gitea.io/gitea/modules/web/middleware" | ||||||
| 	auth_service "code.gitea.io/gitea/services/auth" | 	auth_service "code.gitea.io/gitea/services/auth" | ||||||
|  |  | ||||||
| 	"gitea.com/go-chi/session" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // APIContext is a specific context for API service | // APIContext is a specific context for API service | ||||||
| @@ -191,17 +188,6 @@ func (ctx *APIContext) SetLinkHeader(total, pageSize int) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // RequireCSRF requires a validated a CSRF token |  | ||||||
| func (ctx *APIContext) RequireCSRF() { |  | ||||||
| 	headerToken := ctx.Req.Header.Get(ctx.csrf.GetHeaderName()) |  | ||||||
| 	formValueToken := ctx.Req.FormValue(ctx.csrf.GetFormName()) |  | ||||||
| 	if len(headerToken) > 0 || len(formValueToken) > 0 { |  | ||||||
| 		Validate(ctx.Context, ctx.csrf) |  | ||||||
| 	} else { |  | ||||||
| 		ctx.Context.Error(http.StatusUnauthorized, "Missing CSRF token.") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CheckForOTP validates OTP | // CheckForOTP validates OTP | ||||||
| func (ctx *APIContext) CheckForOTP() { | func (ctx *APIContext) CheckForOTP() { | ||||||
| 	if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { | 	if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) { | ||||||
| @@ -253,17 +239,14 @@ func APIAuth(authMethod auth_service.Method) func(*APIContext) { | |||||||
|  |  | ||||||
| // APIContexter returns apicontext as middleware | // APIContexter returns apicontext as middleware | ||||||
| func APIContexter() func(http.Handler) http.Handler { | func APIContexter() func(http.Handler) http.Handler { | ||||||
| 	csrfOpts := getCsrfOpts() |  | ||||||
|  |  | ||||||
| 	return func(next http.Handler) http.Handler { | 	return func(next http.Handler) http.Handler { | ||||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||||
| 			locale := middleware.Locale(w, req) | 			locale := middleware.Locale(w, req) | ||||||
| 			ctx := APIContext{ | 			ctx := APIContext{ | ||||||
| 				Context: &Context{ | 				Context: &Context{ | ||||||
| 					Resp:    NewResponse(w), | 					Resp:   NewResponse(w), | ||||||
| 					Data:    map[string]interface{}{}, | 					Data:   map[string]interface{}{}, | ||||||
| 					Locale:  locale, | 					Locale: locale, | ||||||
| 					Session: session.GetSession(req), |  | ||||||
| 					Repo: &Repository{ | 					Repo: &Repository{ | ||||||
| 						PullRequest: &PullRequest{}, | 						PullRequest: &PullRequest{}, | ||||||
| 					}, | 					}, | ||||||
| @@ -273,7 +256,6 @@ func APIContexter() func(http.Handler) http.Handler { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx) | 			ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx) | ||||||
| 			ctx.csrf = Csrfer(csrfOpts, ctx.Context) |  | ||||||
|  |  | ||||||
| 			// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. | 			// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. | ||||||
| 			if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { | 			if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { | ||||||
| @@ -285,7 +267,6 @@ func APIContexter() func(http.Handler) http.Handler { | |||||||
|  |  | ||||||
| 			ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) | 			ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) | ||||||
|  |  | ||||||
| 			ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken()) |  | ||||||
| 			ctx.Data["Context"] = &ctx | 			ctx.Data["Context"] = &ctx | ||||||
|  |  | ||||||
| 			next.ServeHTTP(ctx.Resp, ctx.Req) | 			next.ServeHTTP(ctx.Resp, ctx.Req) | ||||||
|   | |||||||
| @@ -216,7 +216,6 @@ func reqToken() func(ctx *context.APIContext) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		if ctx.IsSigned { | 		if ctx.IsSigned { | ||||||
| 			ctx.RequireCSRF() |  | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		ctx.Error(http.StatusUnauthorized, "reqToken", "token is required") | 		ctx.Error(http.StatusUnauthorized, "reqToken", "token is required") | ||||||
| @@ -584,8 +583,7 @@ func bind(obj interface{}) http.HandlerFunc { | |||||||
| func buildAuthGroup() *auth.Group { | func buildAuthGroup() *auth.Group { | ||||||
| 	group := auth.NewGroup( | 	group := auth.NewGroup( | ||||||
| 		&auth.OAuth2{}, | 		&auth.OAuth2{}, | ||||||
| 		&auth.Basic{},      // FIXME: this should be removed once we don't allow basic auth in API | 		&auth.Basic{}, // FIXME: this should be removed once we don't allow basic auth in API | ||||||
| 		auth.SharedSession, // FIXME: this should be removed once all UI don't reference API/v1, see https://github.com/go-gitea/gitea/pull/16052 |  | ||||||
| 	) | 	) | ||||||
| 	if setting.Service.EnableReverseProxyAuth { | 	if setting.Service.EnableReverseProxyAuth { | ||||||
| 		group.Add(&auth.ReverseProxy{}) | 		group.Add(&auth.ReverseProxy{}) | ||||||
| @@ -596,11 +594,9 @@ func buildAuthGroup() *auth.Group { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Routes registers all v1 APIs routes to web application. | // Routes registers all v1 APIs routes to web application. | ||||||
| func Routes(sessioner func(http.Handler) http.Handler) *web.Route { | func Routes() *web.Route { | ||||||
| 	m := web.NewRoute() | 	m := web.NewRoute() | ||||||
|  |  | ||||||
| 	m.Use(sessioner) |  | ||||||
|  |  | ||||||
| 	m.Use(securityHeaders()) | 	m.Use(securityHeaders()) | ||||||
| 	if setting.CORSConfig.Enabled { | 	if setting.CORSConfig.Enabled { | ||||||
| 		m.Use(cors.Handler(cors.Options{ | 		m.Use(cors.Handler(cors.Options{ | ||||||
| @@ -609,7 +605,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { | |||||||
| 			// setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option | 			// setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option | ||||||
| 			AllowedMethods:   setting.CORSConfig.Methods, | 			AllowedMethods:   setting.CORSConfig.Methods, | ||||||
| 			AllowCredentials: setting.CORSConfig.AllowCredentials, | 			AllowCredentials: setting.CORSConfig.AllowCredentials, | ||||||
| 			AllowedHeaders:   []string{"Authorization", "X-CSRFToken", "X-Gitea-OTP"}, | 			AllowedHeaders:   []string{"Authorization", "X-Gitea-OTP"}, | ||||||
| 			MaxAge:           int(setting.CORSConfig.MaxAge.Seconds()), | 			MaxAge:           int(setting.CORSConfig.MaxAge.Seconds()), | ||||||
| 		})) | 		})) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -48,8 +48,6 @@ import ( | |||||||
| 	"code.gitea.io/gitea/services/repository/archiver" | 	"code.gitea.io/gitea/services/repository/archiver" | ||||||
| 	"code.gitea.io/gitea/services/task" | 	"code.gitea.io/gitea/services/task" | ||||||
| 	"code.gitea.io/gitea/services/webhook" | 	"code.gitea.io/gitea/services/webhook" | ||||||
|  |  | ||||||
| 	"gitea.com/go-chi/session" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func mustInit(fn func() error) { | func mustInit(fn func() error) { | ||||||
| @@ -174,20 +172,8 @@ func NormalRoutes() *web.Route { | |||||||
| 		r.Use(middle) | 		r.Use(middle) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sessioner := session.Sessioner(session.Options{ | 	r.Mount("/", web_routers.Routes()) | ||||||
| 		Provider:       setting.SessionConfig.Provider, | 	r.Mount("/api/v1", apiv1.Routes()) | ||||||
| 		ProviderConfig: setting.SessionConfig.ProviderConfig, |  | ||||||
| 		CookieName:     setting.SessionConfig.CookieName, |  | ||||||
| 		CookiePath:     setting.SessionConfig.CookiePath, |  | ||||||
| 		Gclifetime:     setting.SessionConfig.Gclifetime, |  | ||||||
| 		Maxlifetime:    setting.SessionConfig.Maxlifetime, |  | ||||||
| 		Secure:         setting.SessionConfig.Secure, |  | ||||||
| 		SameSite:       setting.SessionConfig.SameSite, |  | ||||||
| 		Domain:         setting.SessionConfig.Domain, |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	r.Mount("/", web_routers.Routes(sessioner)) |  | ||||||
| 	r.Mount("/api/v1", apiv1.Routes(sessioner)) |  | ||||||
| 	r.Mount("/api/internal", private.Routes()) | 	r.Mount("/api/internal", private.Routes()) | ||||||
| 	if setting.Packages.Enabled { | 	if setting.Packages.Enabled { | ||||||
| 		r.Mount("/api/packages", packages_router.Routes()) | 		r.Mount("/api/packages", packages_router.Routes()) | ||||||
|   | |||||||
							
								
								
									
										98
									
								
								routers/web/misc/markdown.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								routers/web/misc/markdown.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | |||||||
|  | // Copyright 2014 The Gogs Authors. All rights reserved. | ||||||
|  | // 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 misc | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/modules/context" | ||||||
|  | 	"code.gitea.io/gitea/modules/markup" | ||||||
|  | 	"code.gitea.io/gitea/modules/markup/markdown" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	api "code.gitea.io/gitea/modules/structs" | ||||||
|  | 	"code.gitea.io/gitea/modules/util" | ||||||
|  | 	"code.gitea.io/gitea/modules/web" | ||||||
|  | 	"mvdan.cc/xurls/v2" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Markdown render markdown document to HTML | ||||||
|  | func Markdown(ctx *context.Context) { | ||||||
|  | 	// swagger:operation POST /markdown miscellaneous renderMarkdown | ||||||
|  | 	// --- | ||||||
|  | 	// summary: Render a markdown document as HTML | ||||||
|  | 	// parameters: | ||||||
|  | 	// - name: body | ||||||
|  | 	//   in: body | ||||||
|  | 	//   schema: | ||||||
|  | 	//     "$ref": "#/definitions/MarkdownOption" | ||||||
|  | 	// consumes: | ||||||
|  | 	// - application/json | ||||||
|  | 	// produces: | ||||||
|  | 	//     - text/html | ||||||
|  | 	// responses: | ||||||
|  | 	//   "200": | ||||||
|  | 	//     "$ref": "#/responses/MarkdownRender" | ||||||
|  | 	//   "422": | ||||||
|  | 	//     "$ref": "#/responses/validationError" | ||||||
|  |  | ||||||
|  | 	form := web.GetForm(ctx).(*api.MarkdownOption) | ||||||
|  |  | ||||||
|  | 	if ctx.HasAPIError() { | ||||||
|  | 		ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(form.Text) == 0 { | ||||||
|  | 		_, _ = ctx.Write([]byte("")) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch form.Mode { | ||||||
|  | 	case "comment": | ||||||
|  | 		fallthrough | ||||||
|  | 	case "gfm": | ||||||
|  | 		urlPrefix := form.Context | ||||||
|  | 		meta := map[string]string{} | ||||||
|  | 		if !strings.HasPrefix(setting.AppSubURL+"/", urlPrefix) { | ||||||
|  | 			// check if urlPrefix is already set to a URL | ||||||
|  | 			linkRegex, _ := xurls.StrictMatchingScheme("https?://") | ||||||
|  | 			m := linkRegex.FindStringIndex(urlPrefix) | ||||||
|  | 			if m == nil { | ||||||
|  | 				urlPrefix = util.URLJoin(setting.AppURL, form.Context) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if ctx.Repo != nil && ctx.Repo.Repository != nil { | ||||||
|  | 			// "gfm" = Github Flavored Markdown - set this to render as a document | ||||||
|  | 			if form.Mode == "gfm" { | ||||||
|  | 				meta = ctx.Repo.Repository.ComposeDocumentMetas() | ||||||
|  | 			} else { | ||||||
|  | 				meta = ctx.Repo.Repository.ComposeMetas() | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if form.Mode == "gfm" { | ||||||
|  | 			meta["mode"] = "document" | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if err := markdown.Render(&markup.RenderContext{ | ||||||
|  | 			Ctx:       ctx, | ||||||
|  | 			URLPrefix: urlPrefix, | ||||||
|  | 			Metas:     meta, | ||||||
|  | 			IsWiki:    form.Wiki, | ||||||
|  | 		}, strings.NewReader(form.Text), ctx.Resp); err != nil { | ||||||
|  | 			ctx.Error(http.StatusInternalServerError, err.Error()) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		if err := markdown.RenderRaw(&markup.RenderContext{ | ||||||
|  | 			Ctx:       ctx, | ||||||
|  | 			URLPrefix: form.Context, | ||||||
|  | 		}, strings.NewReader(form.Text), ctx.Resp); err != nil { | ||||||
|  | 			ctx.Error(http.StatusInternalServerError, err.Error()) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -25,13 +25,13 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/validation" | 	"code.gitea.io/gitea/modules/validation" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"code.gitea.io/gitea/modules/web" | ||||||
| 	"code.gitea.io/gitea/modules/web/routing" | 	"code.gitea.io/gitea/modules/web/routing" | ||||||
| 	"code.gitea.io/gitea/routers/api/v1/misc" |  | ||||||
| 	"code.gitea.io/gitea/routers/web/admin" | 	"code.gitea.io/gitea/routers/web/admin" | ||||||
| 	"code.gitea.io/gitea/routers/web/auth" | 	"code.gitea.io/gitea/routers/web/auth" | ||||||
| 	"code.gitea.io/gitea/routers/web/dev" | 	"code.gitea.io/gitea/routers/web/dev" | ||||||
| 	"code.gitea.io/gitea/routers/web/events" | 	"code.gitea.io/gitea/routers/web/events" | ||||||
| 	"code.gitea.io/gitea/routers/web/explore" | 	"code.gitea.io/gitea/routers/web/explore" | ||||||
| 	"code.gitea.io/gitea/routers/web/feed" | 	"code.gitea.io/gitea/routers/web/feed" | ||||||
|  | 	"code.gitea.io/gitea/routers/web/misc" | ||||||
| 	"code.gitea.io/gitea/routers/web/org" | 	"code.gitea.io/gitea/routers/web/org" | ||||||
| 	"code.gitea.io/gitea/routers/web/repo" | 	"code.gitea.io/gitea/routers/web/repo" | ||||||
| 	"code.gitea.io/gitea/routers/web/user" | 	"code.gitea.io/gitea/routers/web/user" | ||||||
| @@ -46,6 +46,7 @@ import ( | |||||||
| 	_ "code.gitea.io/gitea/modules/session" // to registers all internal adapters | 	_ "code.gitea.io/gitea/modules/session" // to registers all internal adapters | ||||||
|  |  | ||||||
| 	"gitea.com/go-chi/captcha" | 	"gitea.com/go-chi/captcha" | ||||||
|  | 	"gitea.com/go-chi/session" | ||||||
| 	"github.com/NYTimes/gziphandler" | 	"github.com/NYTimes/gziphandler" | ||||||
| 	"github.com/go-chi/chi/v5/middleware" | 	"github.com/go-chi/chi/v5/middleware" | ||||||
| 	"github.com/go-chi/cors" | 	"github.com/go-chi/cors" | ||||||
| @@ -85,7 +86,7 @@ func buildAuthGroup() *auth_service.Group { | |||||||
| 	group := auth_service.NewGroup( | 	group := auth_service.NewGroup( | ||||||
| 		&auth_service.OAuth2{}, // FIXME: this should be removed and only applied in download and oauth realted routers | 		&auth_service.OAuth2{}, // FIXME: this should be removed and only applied in download and oauth realted routers | ||||||
| 		&auth_service.Basic{},  // FIXME: this should be removed and only applied in download and git/lfs routers | 		&auth_service.Basic{},  // FIXME: this should be removed and only applied in download and git/lfs routers | ||||||
| 		auth_service.SharedSession, | 		&auth_service.Session{}, | ||||||
| 	) | 	) | ||||||
| 	if setting.Service.EnableReverseProxyAuth { | 	if setting.Service.EnableReverseProxyAuth { | ||||||
| 		group.Add(&auth_service.ReverseProxy{}) | 		group.Add(&auth_service.ReverseProxy{}) | ||||||
| @@ -96,7 +97,7 @@ func buildAuthGroup() *auth_service.Group { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Routes returns all web routes | // Routes returns all web routes | ||||||
| func Routes(sessioner func(http.Handler) http.Handler) *web.Route { | func Routes() *web.Route { | ||||||
| 	routes := web.NewRoute() | 	routes := web.NewRoute() | ||||||
|  |  | ||||||
| 	routes.Use(web.WrapWithPrefix(public.AssetsURLPathPrefix, public.AssetsHandlerFunc(&public.Options{ | 	routes.Use(web.WrapWithPrefix(public.AssetsURLPathPrefix, public.AssetsHandlerFunc(&public.Options{ | ||||||
| @@ -105,6 +106,17 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route { | |||||||
| 		CorsHandler: CorsHandler(), | 		CorsHandler: CorsHandler(), | ||||||
| 	}), "AssetsHandler")) | 	}), "AssetsHandler")) | ||||||
|  |  | ||||||
|  | 	sessioner := session.Sessioner(session.Options{ | ||||||
|  | 		Provider:       setting.SessionConfig.Provider, | ||||||
|  | 		ProviderConfig: setting.SessionConfig.ProviderConfig, | ||||||
|  | 		CookieName:     setting.SessionConfig.CookieName, | ||||||
|  | 		CookiePath:     setting.SessionConfig.CookiePath, | ||||||
|  | 		Gclifetime:     setting.SessionConfig.Gclifetime, | ||||||
|  | 		Maxlifetime:    setting.SessionConfig.Maxlifetime, | ||||||
|  | 		Secure:         setting.SessionConfig.Secure, | ||||||
|  | 		SameSite:       setting.SessionConfig.SameSite, | ||||||
|  | 		Domain:         setting.SessionConfig.Domain, | ||||||
|  | 	}) | ||||||
| 	routes.Use(sessioner) | 	routes.Use(sessioner) | ||||||
|  |  | ||||||
| 	routes.Use(Recovery()) | 	routes.Use(Recovery()) | ||||||
| @@ -878,6 +890,7 @@ func RegisterRoutes(m *web.Route) { | |||||||
| 		m.Group("/comments/{id}", func() { | 		m.Group("/comments/{id}", func() { | ||||||
| 			m.Get("/attachments", repo.GetCommentAttachments) | 			m.Get("/attachments", repo.GetCommentAttachments) | ||||||
| 		}) | 		}) | ||||||
|  | 		m.Post("/markdown", bindIgnErr(structs.MarkdownOption{}), misc.Markdown) | ||||||
| 		m.Group("/labels", func() { | 		m.Group("/labels", func() { | ||||||
| 			m.Post("/new", bindIgnErr(forms.CreateLabelForm{}), repo.NewLabel) | 			m.Post("/new", bindIgnErr(forms.CreateLabelForm{}), repo.NewLabel) | ||||||
| 			m.Post("/edit", bindIgnErr(forms.CreateLabelForm{}), repo.UpdateLabel) | 			m.Post("/edit", bindIgnErr(forms.CreateLabelForm{}), repo.UpdateLabel) | ||||||
|   | |||||||
| @@ -20,16 +20,6 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/web/middleware" | 	"code.gitea.io/gitea/modules/web/middleware" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // The purpose of the following three function variables is to let the linter know that |  | ||||||
| // those functions are not dead code and are actually being used |  | ||||||
| var ( |  | ||||||
| 	_ = handleSignIn |  | ||||||
|  |  | ||||||
| 	// SharedSession the session auth should only be used by web, but now both web and API/v1 |  | ||||||
| 	// will use it. We can remove this after Web removed dependent API/v1 |  | ||||||
| 	SharedSession = &Session{} |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Init should be called exactly once when the application starts to allow plugins | // Init should be called exactly once when the application starts to allow plugins | ||||||
| // to allocate necessary resources | // to allocate necessary resources | ||||||
| func Init() { | func Init() { | ||||||
|   | |||||||
| @@ -162,7 +162,7 @@ | |||||||
| 				<div class="ui comment form"> | 				<div class="ui comment form"> | ||||||
| 					<div class="ui top attached tabular menu"> | 					<div class="ui top attached tabular menu"> | ||||||
| 						<a class="active write item">{{$.i18n.Tr "write"}}</a> | 						<a class="active write item">{{$.i18n.Tr "write"}}</a> | ||||||
| 						<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | 						<a class="preview item" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="ui bottom attached active write tab segment"> | 					<div class="ui bottom attached active write tab segment"> | ||||||
| 						<textarea class="review-textarea" tabindex="1" name="content"></textarea> | 						<textarea class="review-textarea" tabindex="1" name="content"></textarea> | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
| 		<input type="hidden" name="diff_base_cid"> | 		<input type="hidden" name="diff_base_cid"> | ||||||
| 		<div class="ui top tabular menu" data-write="write" data-preview="preview"> | 		<div class="ui top tabular menu" data-write="write" data-preview="preview"> | ||||||
| 			<a class="active item" data-tab="write">{{$.root.i18n.Tr "write"}}</a> | 			<a class="active item" data-tab="write">{{$.root.i18n.Tr "write"}}</a> | ||||||
| 			<a class="item" data-tab="preview" data-url="{{$.root.Repository.APIURL}}/markdown" data-context="{{$.root.RepoLink}}">{{$.root.i18n.Tr "preview"}}</a> | 			<a class="item" data-tab="preview" data-url="{{$.root.Repository.HTMLURL}}/markdown" data-context="{{$.root.RepoLink}}">{{$.root.i18n.Tr "preview"}}</a> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="field"> | 		<div class="field"> | ||||||
| 			<div class="ui active tab" data-tab="write"> | 			<div class="ui active tab" data-tab="write"> | ||||||
|   | |||||||
| @@ -31,13 +31,13 @@ | |||||||
| 				<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">{{svg "octicon-code"}} {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a> | 					<a class="active item" data-tab="write">{{svg "octicon-code"}} {{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="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-preview-file-modes="{{.PreviewableFileModes}}" data-markdown-mode="gfm">{{svg "octicon-eye"}} {{.i18n.Tr "preview"}}</a> | 					<a class="item" data-tab="preview" data-url="{{.Repository.HTMLURL}}/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-preview-file-modes="{{.PreviewableFileModes}}" data-markdown-mode="gfm">{{svg "octicon-eye"}} {{.i18n.Tr "preview"}}</a> | ||||||
| 					<a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}" data-context="{{.BranchLink}}">{{svg "octicon-diff"}} {{.i18n.Tr "repo.editor.preview_changes"}}</a> | 					<a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}" data-context="{{.BranchLink}}">{{svg "octicon-diff"}} {{.i18n.Tr "repo.editor.preview_changes"}}</a> | ||||||
| 					{{end}} | 					{{end}} | ||||||
| 				</div> | 				</div> | ||||||
| 				<div class="ui bottom attached active tab segment" data-tab="write"> | 				<div class="ui bottom attached active tab segment" data-tab="write"> | ||||||
| 					<textarea id="edit_area" name="content" class="hide" data-id="repo-{{.Repository.Name}}-{{.TreePath}}" | 					<textarea id="edit_area" name="content" class="hide" data-id="repo-{{.Repository.Name}}-{{.TreePath}}" | ||||||
| 						data-url="{{.Repository.APIURL}}/markdown" | 						data-url="{{.Repository.HTMLURL}}/markdown" | ||||||
| 						data-context="{{.RepoLink}}" | 						data-context="{{.RepoLink}}" | ||||||
| 						data-markdown-file-exts="{{.MarkdownFileExts}}" | 						data-markdown-file-exts="{{.MarkdownFileExts}}" | ||||||
| 						data-line-wrap-extensions="{{.LineWrapExtensions}}"> | 						data-line-wrap-extensions="{{.LineWrapExtensions}}"> | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| <div class="ui top tabular menu" data-write="write" data-preview="preview"> | <div class="ui top tabular menu" data-write="write" data-preview="preview"> | ||||||
| 	<a class="active item" data-tab="write">{{.i18n.Tr "write"}}</a> | 	<a class="active item" data-tab="write">{{.i18n.Tr "write"}}</a> | ||||||
| 	<a class="item" data-tab="preview" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "preview"}}</a> | 	<a class="item" data-tab="preview" data-url="{{.Repository.HTMLURL}}/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "preview"}}</a> | ||||||
| </div> | </div> | ||||||
| <div class="field"> | <div class="field"> | ||||||
| 	<div class="ui bottom active tab" data-tab="write"> | 	<div class="ui bottom active tab" data-tab="write"> | ||||||
| 		<textarea id="content" class="edit_area js-quick-submit" name="content" tabindex="4" data-id="issue-{{.RepoName}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.Repo.RepoLink}}"> | 		<textarea id="content" class="edit_area js-quick-submit" name="content" tabindex="4" data-id="issue-{{.RepoName}}" data-url="{{.Repository.HTMLURL}}/markdown" data-context="{{.Repo.RepoLink}}"> | ||||||
| 			{{- if .BodyQuery}}{{.BodyQuery}}{{else if .IssueTemplate}}{{.IssueTemplate}}{{else if .PullRequestTemplate}}{{.PullRequestTemplate}}{{else}}{{.content}}{{end -}} | 			{{- if .BodyQuery}}{{.BodyQuery}}{{else if .IssueTemplate}}{{.IssueTemplate}}{{else if .PullRequestTemplate}}{{.PullRequestTemplate}}{{else}}{{.content}}{{end -}} | ||||||
| 		</textarea> | 		</textarea> | ||||||
| 	</div> | 	</div> | ||||||
|   | |||||||
| @@ -195,7 +195,7 @@ | |||||||
| 	<div class="ui comment form"> | 	<div class="ui comment form"> | ||||||
| 		<div class="ui top tabular menu"> | 		<div class="ui top tabular menu"> | ||||||
| 			<a class="active write item">{{$.i18n.Tr "write"}}</a> | 			<a class="active write item">{{$.i18n.Tr "write"}}</a> | ||||||
| 			<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | 			<a class="preview item" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="field"> | 		<div class="field"> | ||||||
| 			<div class="ui bottom active tab write"> | 			<div class="ui bottom active tab write"> | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ | |||||||
| 					<label>{{.i18n.Tr "repo.release.content"}}</label> | 					<label>{{.i18n.Tr "repo.release.content"}}</label> | ||||||
| 					<div class="ui top tabular menu" data-write="write" data-preview="preview"> | 					<div class="ui top tabular menu" data-write="write" data-preview="preview"> | ||||||
| 						<a class="active write item" data-tab="write">{{$.i18n.Tr "write"}}</a> | 						<a class="active write item" data-tab="write">{{$.i18n.Tr "write"}}</a> | ||||||
| 						<a class="preview item" data-tab="preview" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | 						<a class="preview item" data-tab="preview" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="ui bottom active tab" data-tab="write"> | 					<div class="ui bottom active tab" data-tab="write"> | ||||||
| 						<textarea name="content">{{.content}}</textarea> | 						<textarea name="content">{{.content}}</textarea> | ||||||
|   | |||||||
| @@ -21,11 +21,11 @@ | |||||||
| 			</div> | 			</div> | ||||||
| 			<div class="ui top attached tabular menu previewtabs" data-write="write" data-preview="preview"> | 			<div class="ui top attached tabular menu previewtabs" data-write="write" data-preview="preview"> | ||||||
| 				<a class="active item" data-tab="write">{{.i18n.Tr "write"}}</a> | 				<a class="active item" data-tab="write">{{.i18n.Tr "write"}}</a> | ||||||
| 				<a class="item" data-tab="preview" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | 				<a class="item" data-tab="preview" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="field content" data-loading="{{.i18n.Tr "loading"}}"> | 			<div class="field content" data-loading="{{.i18n.Tr "loading"}}"> | ||||||
| 				<div class="ui bottom active tab" data-tab="write"> | 				<div class="ui bottom active tab" data-tab="write"> | ||||||
| 					<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}">{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea> | 					<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{.Repository.HTMLURL}}/markdown" data-context="{{.RepoLink}}">{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea> | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="field"> | 			<div class="field"> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user