mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	Compare commits
	
		
			42 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3c8842d58c | ||
| 
						 | 
					e786f098d5 | ||
| 
						 | 
					22dec1cea6 | ||
| 
						 | 
					0c5e2e2e4c | ||
| 
						 | 
					158b716387 | ||
| 
						 | 
					e611dbbe86 | ||
| 
						 | 
					68bca621cd | ||
| 
						 | 
					c4e0f717e7 | ||
| 
						 | 
					e3e024876e | ||
| 
						 | 
					cebc125f7f | ||
| 
						 | 
					c975287149 | ||
| 
						 | 
					b31068652a | ||
| 
						 | 
					918e640590 | ||
| 
						 | 
					9809fe27c4 | ||
| 
						 | 
					ed6a2f2e2e | ||
| 
						 | 
					a0435fcd63 | ||
| 
						 | 
					a2e2045a96 | ||
| 
						 | 
					b67a023bec | ||
| 
						 | 
					b1a90f7286 | ||
| 
						 | 
					45c8a3aeeb | ||
| 
						 | 
					0e5126da22 | ||
| 
						 | 
					cd3e52d91b | ||
| 
						 | 
					319c92163f | ||
| 
						 | 
					a15a644c05 | ||
| 
						 | 
					2ccd92407a | ||
| 
						 | 
					17c691f8aa | ||
| 
						 | 
					cb3fe4cbf1 | ||
| 
						 | 
					c63a80138a | ||
| 
						 | 
					b4b8c9679f | ||
| 
						 | 
					c0bb5ebc15 | ||
| 
						 | 
					9409ac9030 | ||
| 
						 | 
					a3928fd820 | ||
| 
						 | 
					f0bda12c49 | ||
| 
						 | 
					58c38ab4b6 | ||
| 
						 | 
					a276aaf61e | ||
| 
						 | 
					e03934f035 | ||
| 
						 | 
					3a14a69e8a | ||
| 
						 | 
					f0f48e0fff | ||
| 
						 | 
					1aeeaa8e89 | ||
| 
						 | 
					eb8d5f6aff | ||
| 
						 | 
					9ef148abeb | ||
| 
						 | 
					f11df80058 | 
@@ -1,44 +1,44 @@
 | 
			
		||||
repo: go-gitea/gitea
 | 
			
		||||
groups:
 | 
			
		||||
  - 
 | 
			
		||||
  -
 | 
			
		||||
    name: BREAKING
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/breaking
 | 
			
		||||
  - 
 | 
			
		||||
  -
 | 
			
		||||
    name: FEATURE
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/feature
 | 
			
		||||
  -
 | 
			
		||||
    name: SECURITY
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/security
 | 
			
		||||
  -
 | 
			
		||||
    name: BUGFIXES
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/bug
 | 
			
		||||
  - 
 | 
			
		||||
  -
 | 
			
		||||
    name: ENHANCEMENT
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/enhancement
 | 
			
		||||
      - kind/refactor
 | 
			
		||||
      - kind/ui
 | 
			
		||||
  -
 | 
			
		||||
    name: SECURITY
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/security
 | 
			
		||||
  - 
 | 
			
		||||
    name: TESTING
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/testing
 | 
			
		||||
  - 
 | 
			
		||||
  -
 | 
			
		||||
    name: TRANSLATION
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/translation
 | 
			
		||||
  - 
 | 
			
		||||
  -
 | 
			
		||||
    name: BUILD
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/build
 | 
			
		||||
      - kind/lint
 | 
			
		||||
  - 
 | 
			
		||||
  -
 | 
			
		||||
    name: DOCS
 | 
			
		||||
    labels:
 | 
			
		||||
      - kind/docs
 | 
			
		||||
  - 
 | 
			
		||||
  -
 | 
			
		||||
    name: MISC
 | 
			
		||||
    default: true
 | 
			
		||||
    default: true
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ services:
 | 
			
		||||
    image: postgres:9.5
 | 
			
		||||
    environment:
 | 
			
		||||
      POSTGRES_DB: test
 | 
			
		||||
      POSTGRES_PASSWORD: postgres
 | 
			
		||||
 | 
			
		||||
  - name: mssql
 | 
			
		||||
    pull: default
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -4,6 +4,57 @@ This changelog goes through all the changes that have been made in each release
 | 
			
		||||
without substantial changes to our git log; to see the highlights of what has
 | 
			
		||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
			
		||||
 | 
			
		||||
## [1.10.4](https://github.com/go-gitea/gitea/releases/tag/v1.10.4) - 2020-02-16
 | 
			
		||||
 | 
			
		||||
* FEATURE
 | 
			
		||||
  * Prevent empty LDAP search from deactivating all users (#9879) (#9890)
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Fix reply on code review (#10261) (#10227)
 | 
			
		||||
  * Fix branch page pull request title and link error (#10092) (#10098)
 | 
			
		||||
  * Fix milestone API state parameter unhandled (#10049) (#10053)
 | 
			
		||||
  * Fix wiki raw view on sub path (#10002) (#10041)
 | 
			
		||||
  * Fix RocketChat Webhook (#9908) (#9921) (#9925)
 | 
			
		||||
  * Fix bug about wrong dependencies permissions check and other wrong permissions check (#9884) (Partial backport #9842)
 | 
			
		||||
  * Ensure that 2fa is checked on reset-password (#9857) (#9877)
 | 
			
		||||
 | 
			
		||||
## [1.10.3](https://github.com/go-gitea/gitea/releases/tag/v1.10.3) - 2020-01-17
 | 
			
		||||
* SECURITY
 | 
			
		||||
  * Hide credentials when submitting migration (#9102) (#9704)
 | 
			
		||||
  * Never allow an empty password to validate (#9682) (#9684)
 | 
			
		||||
  * Prevent redirect to Host (#9678) (#9680)
 | 
			
		||||
  * Hide public repos owned by private orgs (#9609) (#9616)
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9838)
 | 
			
		||||
  * Fix download file wrong content-type (#9825) (#9835)
 | 
			
		||||
  * Fix wrong identify poster on a migrated pull request when submit review (#9827) (#9831)
 | 
			
		||||
  * Fix dump non-exist log directory (#9818) (#9820)
 | 
			
		||||
  * Fix compare (#9808) (#9815)
 | 
			
		||||
  * Fix missing msteam webhook on organization (#9781) (#9795)
 | 
			
		||||
  * Fix add team on collaborator page when same name as organization (#9783)
 | 
			
		||||
  * Fix cache problem on dashboard (#9358) (#9703)
 | 
			
		||||
  * Send tag create and push webhook when release created on UI (#8671) (#9702)
 | 
			
		||||
  * Branches not at ref commit ID should not be listed as Merged (#9614) (#9639)
 | 
			
		||||
 | 
			
		||||
## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
 | 
			
		||||
  * Add ErrReactionAlreadyExist error (#9550) (#9564)
 | 
			
		||||
  * Fix bug when migrate from API (#8631) (#9563)
 | 
			
		||||
  * Use default avatar for ghost user (#9536) (#9537)
 | 
			
		||||
  * Fix repository issues pagination bug when there are more than one label filter (#9512) (#9528)
 | 
			
		||||
  * Fix deleted branch not removed when push the branch again (#9516) (#9524)
 | 
			
		||||
  * Fix missing repository status when migrating repository via API (#9511)
 | 
			
		||||
  * Trigger webhook when deleting a branch after merging a PR (#9510)
 | 
			
		||||
  * Fix paging on /repos/{owner}/{repo}/git/trees/{sha} API endpoint (#9482)
 | 
			
		||||
  * Fix NewCommitStatus (#9434) (#9435)
 | 
			
		||||
  * Use OriginalURL instead of CloneAddr in migration logging (#9418) (#9420)
 | 
			
		||||
  * Fix Slack webhook payload title generation to work with Mattermost (#9404)
 | 
			
		||||
  * DefaultBranch needs to be prefixed by BranchPrefix (#9356) (#9359)
 | 
			
		||||
  * Fix issue indexer not triggered when migrating a repository (#9333)
 | 
			
		||||
  * Fix bug that release attachment files not deleted when deleting repository (#9322) (#9329)
 | 
			
		||||
  * Fix migration releases (#9319) (#9326) (#9328)
 | 
			
		||||
  * Fix File Edit: Author/Committer interchanged (#9297) (#9300)
 | 
			
		||||
 | 
			
		||||
## [1.10.1](https://github.com/go-gitea/gitea/releases/tag/v1.10.1) - 2019-12-05
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Fix max length check and limit in multiple repo forms (#9148) (#9204)
 | 
			
		||||
@@ -48,7 +99,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
			
		||||
  * Add option to initialize repository with labels (#6061)
 | 
			
		||||
  * Add additional password hash algorithms (#6023)
 | 
			
		||||
* BUGFIXES
 | 
			
		||||
  * Allow to merge if file path contains " or \ (#8629) (#8771) 
 | 
			
		||||
  * Allow to merge if file path contains " or \ (#8629) (#8771)
 | 
			
		||||
  * On windows set core.longpaths true (#8776) (#8786)
 | 
			
		||||
  * Fix 500 when edit hook (#8782) (#8789)
 | 
			
		||||
  * Fix Checkbox at RepoSettings Protected Branch (#8799) (#8801)
 | 
			
		||||
@@ -60,7 +111,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
			
		||||
  * Fix require external registration password (#8885) (#8890)
 | 
			
		||||
  * Fix password complexity check on registration (#8887) (#8888)
 | 
			
		||||
  * Update Github Migration Tests (#8896) (#8938) (#8945)
 | 
			
		||||
  * Enable punctuations ending mentions (#8889) (#8894) 
 | 
			
		||||
  * Enable punctuations ending mentions (#8889) (#8894)
 | 
			
		||||
  * Add Close() method to gogitRepository (#8901) (#8956)
 | 
			
		||||
  * Hotfix for review actions and notifications (#8965)
 | 
			
		||||
  * Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) (#8618)
 | 
			
		||||
 
 | 
			
		||||
@@ -61,6 +61,10 @@ var (
 | 
			
		||||
			Name:  "admin-filter",
 | 
			
		||||
			Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
 | 
			
		||||
		},
 | 
			
		||||
		cli.BoolFlag{
 | 
			
		||||
			Name:  "allow-deactivate-all",
 | 
			
		||||
			Usage: "Allow empty search results to deactivate all users.",
 | 
			
		||||
		},
 | 
			
		||||
		cli.StringFlag{
 | 
			
		||||
			Name:  "username-attribute",
 | 
			
		||||
			Usage: "The attribute of the user’s LDAP record containing the user name.",
 | 
			
		||||
@@ -231,6 +235,9 @@ func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
 | 
			
		||||
	if c.IsSet("admin-filter") {
 | 
			
		||||
		config.Source.AdminFilter = c.String("admin-filter")
 | 
			
		||||
	}
 | 
			
		||||
	if c.IsSet("allow-deactivate-all") {
 | 
			
		||||
		config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -145,8 +145,10 @@ func runDump(ctx *cli.Context) error {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := z.AddDir("log", setting.LogRootPath); err != nil {
 | 
			
		||||
		log.Fatalf("Failed to include log: %v", err)
 | 
			
		||||
	if com.IsExist(setting.LogRootPath) {
 | 
			
		||||
		if err := z.AddDir("log", setting.LogRootPath); err != nil {
 | 
			
		||||
			log.Fatalf("Failed to include log: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = z.Close(); err != nil {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								integrations/api_issue_milestone_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								integrations/api_issue_milestone_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
// Copyright 2020 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 (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/structs"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestAPIIssuesMilestone(t *testing.T) {
 | 
			
		||||
	prepareTestEnv(t)
 | 
			
		||||
 | 
			
		||||
	milestone := models.AssertExistsAndLoadBean(t, &models.Milestone{ID: 1}).(*models.Milestone)
 | 
			
		||||
	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: milestone.RepoID}).(*models.Repository)
 | 
			
		||||
	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
 | 
			
		||||
	assert.Equal(t, int64(1), int64(milestone.NumIssues))
 | 
			
		||||
	assert.Equal(t, structs.StateOpen, milestone.State())
 | 
			
		||||
 | 
			
		||||
	session := loginUser(t, owner.Name)
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session)
 | 
			
		||||
 | 
			
		||||
	// update values of issue
 | 
			
		||||
	milestoneState := "closed"
 | 
			
		||||
 | 
			
		||||
	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/milestones/%d?token=%s", owner.Name, repo.Name, milestone.ID, token)
 | 
			
		||||
	req := NewRequestWithJSON(t, "PATCH", urlStr, structs.EditMilestoneOption{
 | 
			
		||||
		State: &milestoneState,
 | 
			
		||||
	})
 | 
			
		||||
	resp := session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
	var apiMilestone structs.Milestone
 | 
			
		||||
	DecodeJSON(t, resp, &apiMilestone)
 | 
			
		||||
	assert.EqualValues(t, "closed", apiMilestone.State)
 | 
			
		||||
 | 
			
		||||
	req = NewRequest(t, "GET", urlStr)
 | 
			
		||||
	resp = session.MakeRequest(t, req, http.StatusOK)
 | 
			
		||||
	var apiMilestone2 structs.Milestone
 | 
			
		||||
	DecodeJSON(t, resp, &apiMilestone2)
 | 
			
		||||
	assert.EqualValues(t, "closed", apiMilestone2.State)
 | 
			
		||||
}
 | 
			
		||||
@@ -8,6 +8,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	api "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
@@ -62,3 +63,61 @@ func TestAPICreateIssue(t *testing.T) {
 | 
			
		||||
		Title:      title,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestAPIEditIssue(t *testing.T) {
 | 
			
		||||
	prepareTestEnv(t)
 | 
			
		||||
 | 
			
		||||
	issueBefore := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 9}).(*models.Issue)
 | 
			
		||||
	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
 | 
			
		||||
	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.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)
 | 
			
		||||
	token := getTokenForLoggedInUser(t, session)
 | 
			
		||||
 | 
			
		||||
	// update values of issue
 | 
			
		||||
	issueState := "closed"
 | 
			
		||||
	removeDeadline := time.Unix(0, 0)
 | 
			
		||||
	milestone := int64(4)
 | 
			
		||||
	body := "new content!"
 | 
			
		||||
	title := "new title from api set"
 | 
			
		||||
 | 
			
		||||
	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repo.Name, issueBefore.Index, token)
 | 
			
		||||
	req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
 | 
			
		||||
		State:     &issueState,
 | 
			
		||||
		Deadline:  &removeDeadline,
 | 
			
		||||
		Milestone: &milestone,
 | 
			
		||||
		Body:      &body,
 | 
			
		||||
		Title:     title,
 | 
			
		||||
 | 
			
		||||
		// ToDo change more
 | 
			
		||||
	})
 | 
			
		||||
	resp := session.MakeRequest(t, req, http.StatusCreated)
 | 
			
		||||
	var apiIssue api.Issue
 | 
			
		||||
	DecodeJSON(t, resp, &apiIssue)
 | 
			
		||||
 | 
			
		||||
	issueAfter := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 9}).(*models.Issue)
 | 
			
		||||
 | 
			
		||||
	// check deleted user
 | 
			
		||||
	assert.Equal(t, int64(500), issueAfter.PosterID)
 | 
			
		||||
	assert.NoError(t, issueAfter.LoadAttributes())
 | 
			
		||||
	assert.Equal(t, int64(-1), issueAfter.PosterID)
 | 
			
		||||
	assert.Equal(t, int64(-1), issueBefore.PosterID)
 | 
			
		||||
	assert.Equal(t, int64(-1), apiIssue.Poster.ID)
 | 
			
		||||
 | 
			
		||||
	// API response
 | 
			
		||||
	assert.Equal(t, api.StateClosed, apiIssue.State)
 | 
			
		||||
	assert.Equal(t, milestone, apiIssue.Milestone.ID)
 | 
			
		||||
	assert.Equal(t, body, apiIssue.Body)
 | 
			
		||||
	assert.True(t, apiIssue.Deadline == nil)
 | 
			
		||||
	assert.Equal(t, title, apiIssue.Title)
 | 
			
		||||
 | 
			
		||||
	// in database
 | 
			
		||||
	assert.Equal(t, api.StateClosed, issueAfter.State())
 | 
			
		||||
	assert.Equal(t, milestone, issueAfter.MilestoneID)
 | 
			
		||||
	assert.Equal(t, int64(0), int64(issueAfter.DeadlineUnix))
 | 
			
		||||
	assert.Equal(t, body, issueAfter.Content)
 | 
			
		||||
	assert.Equal(t, title, issueAfter.Title)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,12 +30,12 @@ func getCreateFileOptions() api.CreateFileOptions {
 | 
			
		||||
			NewBranchName: "master",
 | 
			
		||||
			Message:       "Making this new file new/file.txt",
 | 
			
		||||
			Author: api.Identity{
 | 
			
		||||
				Name:  "John Doe",
 | 
			
		||||
				Email: "johndoe@example.com",
 | 
			
		||||
				Name:  "Anne Doe",
 | 
			
		||||
				Email: "annedoe@example.com",
 | 
			
		||||
			},
 | 
			
		||||
			Committer: api.Identity{
 | 
			
		||||
				Name:  "Jane Doe",
 | 
			
		||||
				Email: "janedoe@example.com",
 | 
			
		||||
				Name:  "John Doe",
 | 
			
		||||
				Email: "johndoe@example.com",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		Content: contentEncoded,
 | 
			
		||||
@@ -77,8 +77,8 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon
 | 
			
		||||
			HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
 | 
			
		||||
			Author: &api.CommitUser{
 | 
			
		||||
				Identity: api.Identity{
 | 
			
		||||
					Name:  "Jane Doe",
 | 
			
		||||
					Email: "janedoe@example.com",
 | 
			
		||||
					Name:  "Anne Doe",
 | 
			
		||||
					Email: "annedoe@example.com",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			Committer: &api.CommitUser{
 | 
			
		||||
 
 | 
			
		||||
@@ -35,8 +35,8 @@ func getUpdateFileOptions() *api.UpdateFileOptions {
 | 
			
		||||
					Email: "johndoe@example.com",
 | 
			
		||||
				},
 | 
			
		||||
				Committer: api.Identity{
 | 
			
		||||
					Name:  "Jane Doe",
 | 
			
		||||
					Email: "janedoe@example.com",
 | 
			
		||||
					Name:  "Anne Doe",
 | 
			
		||||
					Email: "annedoe@example.com",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885",
 | 
			
		||||
@@ -80,14 +80,14 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon
 | 
			
		||||
			HTMLURL: setting.AppURL + "user2/repo1/commit/" + commitID,
 | 
			
		||||
			Author: &api.CommitUser{
 | 
			
		||||
				Identity: api.Identity{
 | 
			
		||||
					Name:  "Jane Doe",
 | 
			
		||||
					Email: "janedoe@example.com",
 | 
			
		||||
					Name:  "John Doe",
 | 
			
		||||
					Email: "johndoe@example.com",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			Committer: &api.CommitUser{
 | 
			
		||||
				Identity: api.Identity{
 | 
			
		||||
					Name:  "John Doe",
 | 
			
		||||
					Email: "johndoe@example.com",
 | 
			
		||||
					Name:  "Anne Doe",
 | 
			
		||||
					Email: "annedoe@example.com",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			Message: "My update of README.md\n",
 | 
			
		||||
 
 | 
			
		||||
@@ -334,7 +334,7 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) {
 | 
			
		||||
		resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
 | 
			
		||||
		respJSON := map[string]string{}
 | 
			
		||||
		DecodeJSON(t, resp, &respJSON)
 | 
			
		||||
		assert.Equal(t, respJSON["message"], "The repository with the same name already exists.")
 | 
			
		||||
		assert.Equal(t, "The repository with the same name already exists.", respJSON["message"])
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
4a357436d925b5c974181ff12a994538ddc5a269
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1 +1 @@
 | 
			
		||||
0cf15c3f66ec8384480ed9c3cf87c9e97fbb0ec3
 | 
			
		||||
423313fbd38093bb10d0c8387db9105409c6f196
 | 
			
		||||
 
 | 
			
		||||
@@ -480,6 +480,12 @@ func (deletedBranch *DeletedBranch) LoadUser() {
 | 
			
		||||
	deletedBranch.DeletedBy = user
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveDeletedBranch removes all deleted branches
 | 
			
		||||
func RemoveDeletedBranch(repoID int64, branch string) error {
 | 
			
		||||
	_, err := x.Where("repo_id=? AND name=?", repoID, branch).Delete(new(DeletedBranch))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveOldDeletedBranches removes old deleted branches
 | 
			
		||||
func RemoveOldDeletedBranches() {
 | 
			
		||||
	log.Trace("Doing: DeletedBranchesCleanup")
 | 
			
		||||
 
 | 
			
		||||
@@ -1104,6 +1104,21 @@ func (err ErrNewIssueInsert) Error() string {
 | 
			
		||||
	return err.OriginalError.Error()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrReactionAlreadyExist is used when a existing reaction was try to created
 | 
			
		||||
type ErrReactionAlreadyExist struct {
 | 
			
		||||
	Reaction string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsErrReactionAlreadyExist checks if an error is a ErrReactionAlreadyExist.
 | 
			
		||||
func IsErrReactionAlreadyExist(err error) bool {
 | 
			
		||||
	_, ok := err.(ErrReactionAlreadyExist)
 | 
			
		||||
	return ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err ErrReactionAlreadyExist) Error() string {
 | 
			
		||||
	return fmt.Sprintf("reaction '%s' already exists", err.Reaction)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// __________      .__  .__ __________                                     __
 | 
			
		||||
// \______   \__ __|  | |  |\______   \ ____  ________ __   ____   _______/  |_
 | 
			
		||||
//  |     ___/  |  \  | |  | |       _// __ \/ ____/  |  \_/ __ \ /  ___/\   __\
 | 
			
		||||
 
 | 
			
		||||
@@ -96,4 +96,17 @@
 | 
			
		||||
  is_closed: false
 | 
			
		||||
  is_pull: true
 | 
			
		||||
  created_unix: 946684820
 | 
			
		||||
  updated_unix: 978307180
 | 
			
		||||
  updated_unix: 978307180
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
  id: 9
 | 
			
		||||
  repo_id: 42
 | 
			
		||||
  index: 1
 | 
			
		||||
  poster_id: 500
 | 
			
		||||
  name: issue from deleted account
 | 
			
		||||
  content: content from deleted account
 | 
			
		||||
  is_closed: false
 | 
			
		||||
  is_pull: false
 | 
			
		||||
  created_unix: 946684830
 | 
			
		||||
  updated_unix: 999307200
 | 
			
		||||
  deadline_unix: 1019307200
 | 
			
		||||
 
 | 
			
		||||
@@ -21,3 +21,11 @@
 | 
			
		||||
  content: content3
 | 
			
		||||
  is_closed: true
 | 
			
		||||
  num_issues: 0
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
  id: 4
 | 
			
		||||
  repo_id: 42
 | 
			
		||||
  name: milestone of repo42
 | 
			
		||||
  content: content random
 | 
			
		||||
  is_closed: false
 | 
			
		||||
  num_issues: 0
 | 
			
		||||
 
 | 
			
		||||
@@ -547,7 +547,8 @@
 | 
			
		||||
  is_private: false
 | 
			
		||||
  num_stars: 0
 | 
			
		||||
  num_forks: 0
 | 
			
		||||
  num_issues: 0
 | 
			
		||||
  num_issues: 1
 | 
			
		||||
  num_milestones: 1
 | 
			
		||||
  is_mirror: false
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
			
		||||
// Copyright 2020 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.
 | 
			
		||||
 | 
			
		||||
@@ -238,6 +239,16 @@ func (issue *Issue) loadReactions(e Engine) (err error) {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (issue *Issue) loadMilestone(e Engine) (err error) {
 | 
			
		||||
	if issue.Milestone == nil && issue.MilestoneID > 0 {
 | 
			
		||||
		issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
 | 
			
		||||
		if err != nil && !IsErrMilestoneNotExist(err) {
 | 
			
		||||
			return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (issue *Issue) loadAttributes(e Engine) (err error) {
 | 
			
		||||
	if err = issue.loadRepo(e); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
@@ -251,11 +262,8 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.Milestone == nil && issue.MilestoneID > 0 {
 | 
			
		||||
		issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
 | 
			
		||||
		if err != nil && !IsErrMilestoneNotExist(err) {
 | 
			
		||||
			return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
 | 
			
		||||
		}
 | 
			
		||||
	if err = issue.loadMilestone(e); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = issue.loadAssignees(e); err != nil {
 | 
			
		||||
@@ -295,6 +303,11 @@ func (issue *Issue) LoadAttributes() error {
 | 
			
		||||
	return issue.loadAttributes(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadMilestone load milestone of this issue.
 | 
			
		||||
func (issue *Issue) LoadMilestone() error {
 | 
			
		||||
	return issue.loadMilestone(x)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetIsRead load the `IsRead` field of the issue
 | 
			
		||||
func (issue *Issue) GetIsRead(userID int64) error {
 | 
			
		||||
	issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
 | 
			
		||||
@@ -416,7 +429,7 @@ func (issue *Issue) HashTag() string {
 | 
			
		||||
 | 
			
		||||
// IsPoster returns true if given user by ID is the poster.
 | 
			
		||||
func (issue *Issue) IsPoster(uid int64) bool {
 | 
			
		||||
	return issue.PosterID == uid
 | 
			
		||||
	return issue.OriginalAuthorID == 0 && issue.PosterID == uid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (issue *Issue) hasLabel(e Engine, labelID int64) bool {
 | 
			
		||||
@@ -1730,22 +1743,17 @@ func SearchIssueIDsByKeyword(kw string, repoID int64, limit, start int) (int64,
 | 
			
		||||
	return total, ids, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateIssue(e Engine, issue *Issue) error {
 | 
			
		||||
	_, err := e.ID(issue.ID).AllCols().Update(issue)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateIssue updates all fields of given issue.
 | 
			
		||||
func UpdateIssue(issue *Issue) error {
 | 
			
		||||
// UpdateIssueByAPI updates all allowed fields of given issue.
 | 
			
		||||
func UpdateIssueByAPI(issue *Issue) error {
 | 
			
		||||
	sess := x.NewSession()
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := updateIssue(sess, issue); err != nil {
 | 
			
		||||
	if _, err := sess.ID(issue.ID).Cols(
 | 
			
		||||
		"name", "is_closed", "content", "milestone_id", "priority",
 | 
			
		||||
		"deadline_unix", "updated_unix", "closed_unix", "is_locked").
 | 
			
		||||
		Update(issue); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := issue.neuterCrossReferences(sess); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -253,12 +253,33 @@ func updateMilestone(e Engine, m *Milestone) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateMilestone updates information of given milestone.
 | 
			
		||||
func UpdateMilestone(m *Milestone) error {
 | 
			
		||||
	if err := updateMilestone(x, m); err != nil {
 | 
			
		||||
func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
 | 
			
		||||
	sess := x.NewSession()
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return updateMilestoneCompleteness(x, m.ID)
 | 
			
		||||
	if m.IsClosed && !oldIsClosed {
 | 
			
		||||
		m.ClosedDateUnix = timeutil.TimeStampNow()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := updateMilestone(sess, m); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := updateMilestoneCompleteness(sess, m.ID); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if IsClosed changed, update milestone numbers of repository
 | 
			
		||||
	if oldIsClosed != m.IsClosed {
 | 
			
		||||
		if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sess.Commit()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
 | 
			
		||||
 
 | 
			
		||||
@@ -160,8 +160,9 @@ func TestUpdateMilestone(t *testing.T) {
 | 
			
		||||
	milestone := AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
 | 
			
		||||
	milestone.Name = "newMilestoneName"
 | 
			
		||||
	milestone.Content = "newMilestoneContent"
 | 
			
		||||
	assert.NoError(t, UpdateMilestone(milestone))
 | 
			
		||||
	AssertExistsAndLoadBean(t, milestone)
 | 
			
		||||
	assert.NoError(t, UpdateMilestone(milestone, milestone.IsClosed))
 | 
			
		||||
	milestone = AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
 | 
			
		||||
	assert.EqualValues(t, "newMilestoneName", milestone.Name)
 | 
			
		||||
	CheckConsistencyFor(t, &Milestone{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,8 @@ type Reaction struct {
 | 
			
		||||
type FindReactionsOptions struct {
 | 
			
		||||
	IssueID   int64
 | 
			
		||||
	CommentID int64
 | 
			
		||||
	UserID    int64
 | 
			
		||||
	Reaction  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (opts *FindReactionsOptions) toConds() builder.Cond {
 | 
			
		||||
@@ -40,6 +42,13 @@ func (opts *FindReactionsOptions) toConds() builder.Cond {
 | 
			
		||||
	if opts.CommentID > 0 {
 | 
			
		||||
		cond = cond.And(builder.Eq{"reaction.comment_id": opts.CommentID})
 | 
			
		||||
	}
 | 
			
		||||
	if opts.UserID > 0 {
 | 
			
		||||
		cond = cond.And(builder.Eq{"reaction.user_id": opts.UserID})
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Reaction != "" {
 | 
			
		||||
		cond = cond.And(builder.Eq{"reaction.type": opts.Reaction})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cond
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -57,9 +66,25 @@ func createReaction(e *xorm.Session, opts *ReactionOptions) (*Reaction, error) {
 | 
			
		||||
		UserID:  opts.Doer.ID,
 | 
			
		||||
		IssueID: opts.Issue.ID,
 | 
			
		||||
	}
 | 
			
		||||
	findOpts := FindReactionsOptions{
 | 
			
		||||
		IssueID:   opts.Issue.ID,
 | 
			
		||||
		CommentID: -1, // reaction to issue only
 | 
			
		||||
		Reaction:  opts.Type,
 | 
			
		||||
		UserID:    opts.Doer.ID,
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Comment != nil {
 | 
			
		||||
		reaction.CommentID = opts.Comment.ID
 | 
			
		||||
		findOpts.CommentID = opts.Comment.ID
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	existingR, err := findReactions(e, findOpts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(existingR) > 0 {
 | 
			
		||||
		return existingR[0], ErrReactionAlreadyExist{Reaction: opts.Type}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := e.Insert(reaction); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -76,19 +101,19 @@ type ReactionOptions struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateReaction creates reaction for issue or comment.
 | 
			
		||||
func CreateReaction(opts *ReactionOptions) (reaction *Reaction, err error) {
 | 
			
		||||
func CreateReaction(opts *ReactionOptions) (*Reaction, error) {
 | 
			
		||||
	sess := x.NewSession()
 | 
			
		||||
	defer sess.Close()
 | 
			
		||||
	if err = sess.Begin(); err != nil {
 | 
			
		||||
	if err := sess.Begin(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	reaction, err = createReaction(sess, opts)
 | 
			
		||||
	reaction, err := createReaction(sess, opts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return reaction, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = sess.Commit(); err != nil {
 | 
			
		||||
	if err := sess.Commit(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return reaction, nil
 | 
			
		||||
 
 | 
			
		||||
@@ -50,9 +50,10 @@ func TestIssueAddDuplicateReaction(t *testing.T) {
 | 
			
		||||
		Type:  "heart",
 | 
			
		||||
	})
 | 
			
		||||
	assert.Error(t, err)
 | 
			
		||||
	assert.Nil(t, reaction)
 | 
			
		||||
	assert.Equal(t, ErrReactionAlreadyExist{Reaction: "heart"}, err)
 | 
			
		||||
 | 
			
		||||
	AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID})
 | 
			
		||||
	existingR := AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID}).(*Reaction)
 | 
			
		||||
	assert.Equal(t, existingR.ID, reaction.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIssueDeleteReaction(t *testing.T) {
 | 
			
		||||
@@ -129,7 +130,6 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
 | 
			
		||||
	user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
 | 
			
		||||
	user3 := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
 | 
			
		||||
	user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
 | 
			
		||||
	ghost := NewGhostUser()
 | 
			
		||||
 | 
			
		||||
	issue1 := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
 | 
			
		||||
 | 
			
		||||
@@ -139,14 +139,13 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
 | 
			
		||||
	addReaction(t, user2, issue1, comment1, "heart")
 | 
			
		||||
	addReaction(t, user3, issue1, comment1, "heart")
 | 
			
		||||
	addReaction(t, user4, issue1, comment1, "+1")
 | 
			
		||||
	addReaction(t, ghost, issue1, comment1, "heart")
 | 
			
		||||
 | 
			
		||||
	err := comment1.LoadReactions()
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Len(t, comment1.Reactions, 5)
 | 
			
		||||
	assert.Len(t, comment1.Reactions, 4)
 | 
			
		||||
 | 
			
		||||
	reactions := comment1.Reactions.GroupByType()
 | 
			
		||||
	assert.Len(t, reactions["heart"], 4)
 | 
			
		||||
	assert.Len(t, reactions["heart"], 3)
 | 
			
		||||
	assert.Len(t, reactions["+1"], 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -160,7 +159,7 @@ func TestIssueCommentReactionCount(t *testing.T) {
 | 
			
		||||
	comment1 := AssertExistsAndLoadBean(t, &Comment{ID: 1}).(*Comment)
 | 
			
		||||
 | 
			
		||||
	addReaction(t, user1, issue1, comment1, "heart")
 | 
			
		||||
	DeleteCommentReaction(user1, issue1, comment1, "heart")
 | 
			
		||||
	assert.NoError(t, DeleteCommentReaction(user1, issue1, comment1, "heart"))
 | 
			
		||||
 | 
			
		||||
	AssertNotExistsBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1.ID, CommentID: comment1.ID})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -136,7 +136,7 @@ func (pr *PullRequest) LoadHeadRepo() error {
 | 
			
		||||
		if has, err := x.ID(pr.HeadRepoID).Get(&repo); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		} else if !has {
 | 
			
		||||
			return ErrRepoNotExist{ID: pr.BaseRepoID}
 | 
			
		||||
			return ErrRepoNotExist{ID: pr.HeadRepoID}
 | 
			
		||||
		}
 | 
			
		||||
		pr.HeadRepo = &repo
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1781,6 +1781,12 @@ func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) {
 | 
			
		||||
	return sess.Commit()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateRepositoryStatus updates a repository's status
 | 
			
		||||
func UpdateRepositoryStatus(repoID int64, status RepositoryStatus) error {
 | 
			
		||||
	_, err := x.Exec("UPDATE repository SET status = ? WHERE id = ?", status, repoID)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateRepositoryUpdatedTime updates a repository's updated time
 | 
			
		||||
func UpdateRepositoryUpdatedTime(repoID int64, updateTime time.Time) error {
 | 
			
		||||
	_, err := x.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", updateTime.Unix(), repoID)
 | 
			
		||||
@@ -1860,6 +1866,17 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	attachments := make([]*Attachment, 0, 20)
 | 
			
		||||
	if err = sess.Join("INNER", "`release`", "`release`.id = `attachment`.release_id").
 | 
			
		||||
		Where("`release`.repo_id = ?", repoID).
 | 
			
		||||
		Find(&attachments); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	releaseAttachments := make([]string, 0, len(attachments))
 | 
			
		||||
	for i := 0; i < len(attachments); i++ {
 | 
			
		||||
		releaseAttachments = append(releaseAttachments, attachments[i].LocalPath())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = deleteBeans(sess,
 | 
			
		||||
		&Access{RepoID: repo.ID},
 | 
			
		||||
		&Action{RepoID: repo.ID},
 | 
			
		||||
@@ -1910,13 +1927,13 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	attachmentPaths := make([]string, 0, 20)
 | 
			
		||||
	attachments := make([]*Attachment, 0, len(attachmentPaths))
 | 
			
		||||
	attachments = attachments[:0]
 | 
			
		||||
	if err = sess.Join("INNER", "issue", "issue.id = attachment.issue_id").
 | 
			
		||||
		Where("issue.repo_id = ?", repoID).
 | 
			
		||||
		Find(&attachments); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	attachmentPaths := make([]string, 0, len(attachments))
 | 
			
		||||
	for j := range attachments {
 | 
			
		||||
		attachmentPaths = append(attachmentPaths, attachments[j].LocalPath())
 | 
			
		||||
	}
 | 
			
		||||
@@ -1953,11 +1970,6 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove attachment files.
 | 
			
		||||
	for i := range attachmentPaths {
 | 
			
		||||
		removeAllWithNotice(sess, "Delete attachment", attachmentPaths[i])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove LFS objects
 | 
			
		||||
	var lfsObjects []*LFSMetaObject
 | 
			
		||||
	if err = sess.Where("repository_id=?", repoID).Find(&lfsObjects); err != nil {
 | 
			
		||||
@@ -1997,6 +2009,8 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 | 
			
		||||
		return fmt.Errorf("Commit: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sess.Close()
 | 
			
		||||
 | 
			
		||||
	if org.IsOrganization() {
 | 
			
		||||
		if err = PrepareWebhooks(repo, HookEventRepository, &api.RepositoryPayload{
 | 
			
		||||
			Action:       api.HookRepoDeleted,
 | 
			
		||||
@@ -2009,6 +2023,19 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 | 
			
		||||
		go HookQueue.Add(repo.ID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We should always delete the files after the database transaction succeed. If
 | 
			
		||||
	// we delete the file but the database rollback, the repository will be borken.
 | 
			
		||||
 | 
			
		||||
	// Remove issue attachment files.
 | 
			
		||||
	for i := range attachmentPaths {
 | 
			
		||||
		removeAllWithNotice(x, "Delete issue attachment", attachmentPaths[i])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Remove release attachment files.
 | 
			
		||||
	for i := range releaseAttachments {
 | 
			
		||||
		removeAllWithNotice(x, "Delete release attachment", releaseAttachments[i])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(repo.Avatar) > 0 {
 | 
			
		||||
		avatarPath := repo.CustomAvatarPath()
 | 
			
		||||
		if com.IsExist(avatarPath) {
 | 
			
		||||
@@ -2784,3 +2811,9 @@ func (repo *Repository) GetOriginalURLHostname() string {
 | 
			
		||||
 | 
			
		||||
	return u.Host
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateRepositoryCols updates repository's columns
 | 
			
		||||
func UpdateRepositoryCols(repo *Repository, cols ...string) error {
 | 
			
		||||
	_, err := x.ID(repo.ID).Cols(cols...).Update(repo)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -177,7 +177,7 @@ type fileUpdate struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDefaultBranchSha(repo *Repository) (string, error) {
 | 
			
		||||
	stdout, err := git.NewCommand("show-ref", "-s", repo.DefaultBranch).RunInDir(repo.RepoPath())
 | 
			
		||||
	stdout, err := git.NewCommand("show-ref", "-s", git.BranchPrefix+repo.DefaultBranch).RunInDir(repo.RepoPath())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -120,7 +120,8 @@ type SearchRepoOptions struct {
 | 
			
		||||
	StarredByID int64
 | 
			
		||||
	Page        int
 | 
			
		||||
	IsProfile   bool
 | 
			
		||||
	AllPublic   bool // Include also all public repositories
 | 
			
		||||
	AllPublic   bool // Include also all public repositories of users and public organisations
 | 
			
		||||
	AllLimited  bool // Include also all public repositories of limited organisations
 | 
			
		||||
	PageSize    int  // Can be smaller than or equal to setting.ExplorePagingNum
 | 
			
		||||
	// None -> include collaborative AND non-collaborative
 | 
			
		||||
	// True -> include just collaborative
 | 
			
		||||
@@ -240,7 +241,11 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if opts.AllPublic {
 | 
			
		||||
			accessCond = accessCond.Or(builder.Eq{"is_private": false})
 | 
			
		||||
			accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}))))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if opts.AllLimited {
 | 
			
		||||
			accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited}))))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cond = cond.And(accessCond)
 | 
			
		||||
 
 | 
			
		||||
@@ -177,8 +177,8 @@ func TestSearchRepository(t *testing.T) {
 | 
			
		||||
			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true},
 | 
			
		||||
			count: 22},
 | 
			
		||||
		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
 | 
			
		||||
			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true},
 | 
			
		||||
			count: 28},
 | 
			
		||||
			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true},
 | 
			
		||||
			count: 27},
 | 
			
		||||
		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
 | 
			
		||||
			opts:  &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true},
 | 
			
		||||
			count: 15},
 | 
			
		||||
 
 | 
			
		||||
@@ -502,7 +502,7 @@ func (u *User) ValidatePassword(passwd string) bool {
 | 
			
		||||
 | 
			
		||||
// IsPasswordSet checks if the password is set or left empty
 | 
			
		||||
func (u *User) IsPasswordSet() bool {
 | 
			
		||||
	return len(u.Passwd) > 0
 | 
			
		||||
	return !u.ValidatePassword("")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadAvatar saves custom avatar for user.
 | 
			
		||||
@@ -1715,6 +1715,15 @@ func SyncExternalUsers() {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if len(sr) == 0 {
 | 
			
		||||
				if !s.LDAP().AllowDeactivateAll {
 | 
			
		||||
					log.Error("LDAP search found no entries but did not report an error. Refusing to deactivate all users")
 | 
			
		||||
					continue
 | 
			
		||||
				} else {
 | 
			
		||||
					log.Warn("LDAP search found no entries but did not report an error. All users will be deactivated as per settings")
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, su := range sr {
 | 
			
		||||
				if len(su.Username) == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
 
 | 
			
		||||
@@ -36,10 +36,11 @@ type SlackPayload struct {
 | 
			
		||||
 | 
			
		||||
// SlackAttachment contains the slack message
 | 
			
		||||
type SlackAttachment struct {
 | 
			
		||||
	Fallback string `json:"fallback"`
 | 
			
		||||
	Color    string `json:"color"`
 | 
			
		||||
	Title    string `json:"title"`
 | 
			
		||||
	Text     string `json:"text"`
 | 
			
		||||
	Fallback  string `json:"fallback"`
 | 
			
		||||
	Color     string `json:"color"`
 | 
			
		||||
	Title     string `json:"title"`
 | 
			
		||||
	TitleLink string `json:"title_link"`
 | 
			
		||||
	Text      string `json:"text"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSecret sets the slack secret
 | 
			
		||||
@@ -133,70 +134,78 @@ func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, e
 | 
			
		||||
 | 
			
		||||
func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
 | 
			
		||||
	senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
 | 
			
		||||
	titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
 | 
			
		||||
		fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
 | 
			
		||||
	var text, title, attachmentText string
 | 
			
		||||
	title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
 | 
			
		||||
	titleLink := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index)
 | 
			
		||||
	repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
 | 
			
		||||
	var text, attachmentText string
 | 
			
		||||
 | 
			
		||||
	switch p.Action {
 | 
			
		||||
	case api.HookIssueOpened:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue submitted by %s", p.Repository.FullName, senderLink)
 | 
			
		||||
		title = titleLink
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue opened by %s", repoLink, senderLink)
 | 
			
		||||
		attachmentText = SlackTextFormatter(p.Issue.Body)
 | 
			
		||||
	case api.HookIssueClosed:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueReOpened:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueEdited:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
		attachmentText = SlackTextFormatter(p.Issue.Body)
 | 
			
		||||
	case api.HookIssueAssigned:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", p.Repository.FullName,
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue assigned to %s: [%s](%s) by %s", repoLink,
 | 
			
		||||
			SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
 | 
			
		||||
			titleLink, senderLink)
 | 
			
		||||
			title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueUnassigned:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueLabelUpdated:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueLabelCleared:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueSynchronized:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueMilestoned:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue milestoned to [%s](%s): [%s](%s) by %s", repoLink,
 | 
			
		||||
			p.Issue.Milestone.Title, mileStoneLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueDemilestoned:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Issue milestone cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &SlackPayload{
 | 
			
		||||
	pl := &SlackPayload{
 | 
			
		||||
		Channel:  slack.Channel,
 | 
			
		||||
		Text:     text,
 | 
			
		||||
		Username: slack.Username,
 | 
			
		||||
		IconURL:  slack.IconURL,
 | 
			
		||||
		Attachments: []SlackAttachment{{
 | 
			
		||||
			Color: slack.Color,
 | 
			
		||||
			Title: title,
 | 
			
		||||
			Text:  attachmentText,
 | 
			
		||||
		}},
 | 
			
		||||
	}, nil
 | 
			
		||||
	}
 | 
			
		||||
	if attachmentText != "" {
 | 
			
		||||
		pl.Attachments = []SlackAttachment{{
 | 
			
		||||
			Color:     slack.Color,
 | 
			
		||||
			Title:     title,
 | 
			
		||||
			TitleLink: titleLink,
 | 
			
		||||
			Text:      attachmentText,
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pl, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
 | 
			
		||||
	senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
 | 
			
		||||
	titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)),
 | 
			
		||||
		fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
 | 
			
		||||
	var text, title, attachmentText string
 | 
			
		||||
	title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
 | 
			
		||||
	repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
 | 
			
		||||
	var text, titleLink, attachmentText string
 | 
			
		||||
 | 
			
		||||
	switch p.Action {
 | 
			
		||||
	case api.HookIssueCommentCreated:
 | 
			
		||||
		text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink)
 | 
			
		||||
		title = titleLink
 | 
			
		||||
		text = fmt.Sprintf("[%s] New comment created by %s", repoLink, senderLink)
 | 
			
		||||
		titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
 | 
			
		||||
		attachmentText = SlackTextFormatter(p.Comment.Body)
 | 
			
		||||
	case api.HookIssueCommentEdited:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink)
 | 
			
		||||
		title = titleLink
 | 
			
		||||
		text = fmt.Sprintf("[%s] Comment edited by %s", repoLink, senderLink)
 | 
			
		||||
		titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
 | 
			
		||||
		attachmentText = SlackTextFormatter(p.Comment.Body)
 | 
			
		||||
	case api.HookIssueCommentDeleted:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink)
 | 
			
		||||
		title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index),
 | 
			
		||||
			fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
 | 
			
		||||
		text = fmt.Sprintf("[%s] Comment deleted by %s", repoLink, senderLink)
 | 
			
		||||
		titleLink = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
 | 
			
		||||
		attachmentText = SlackTextFormatter(p.Comment.Body)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -206,9 +215,10 @@ func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (
 | 
			
		||||
		Username: slack.Username,
 | 
			
		||||
		IconURL:  slack.IconURL,
 | 
			
		||||
		Attachments: []SlackAttachment{{
 | 
			
		||||
			Color: slack.Color,
 | 
			
		||||
			Title: title,
 | 
			
		||||
			Text:  attachmentText,
 | 
			
		||||
			Color:     slack.Color,
 | 
			
		||||
			Title:     title,
 | 
			
		||||
			TitleLink: titleLink,
 | 
			
		||||
			Text:      attachmentText,
 | 
			
		||||
		}},
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -273,73 +283,85 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
 | 
			
		||||
		Username: slack.Username,
 | 
			
		||||
		IconURL:  slack.IconURL,
 | 
			
		||||
		Attachments: []SlackAttachment{{
 | 
			
		||||
			Color: slack.Color,
 | 
			
		||||
			Text:  attachmentText,
 | 
			
		||||
			Color:     slack.Color,
 | 
			
		||||
			Title:     p.Repo.HTMLURL,
 | 
			
		||||
			TitleLink: p.Repo.HTMLURL,
 | 
			
		||||
			Text:      attachmentText,
 | 
			
		||||
		}},
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
 | 
			
		||||
	senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
 | 
			
		||||
	titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
 | 
			
		||||
		fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
 | 
			
		||||
	var text, title, attachmentText string
 | 
			
		||||
	title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
 | 
			
		||||
	titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
 | 
			
		||||
	repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
 | 
			
		||||
	var text, attachmentText string
 | 
			
		||||
 | 
			
		||||
	switch p.Action {
 | 
			
		||||
	case api.HookIssueOpened:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink)
 | 
			
		||||
		title = titleLink
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request opened by %s", repoLink, senderLink)
 | 
			
		||||
		attachmentText = SlackTextFormatter(p.PullRequest.Body)
 | 
			
		||||
	case api.HookIssueClosed:
 | 
			
		||||
		if p.PullRequest.HasMerged {
 | 
			
		||||
			text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
			text = fmt.Sprintf("[%s] Pull request merged: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
		} else {
 | 
			
		||||
			text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
			text = fmt.Sprintf("[%s] Pull request closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
		}
 | 
			
		||||
	case api.HookIssueReOpened:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueEdited:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
		attachmentText = SlackTextFormatter(p.PullRequest.Body)
 | 
			
		||||
	case api.HookIssueAssigned:
 | 
			
		||||
		list := make([]string, len(p.PullRequest.Assignees))
 | 
			
		||||
		for i, user := range p.PullRequest.Assignees {
 | 
			
		||||
			list[i] = SlackLinkFormatter(setting.AppURL+user.UserName, user.UserName)
 | 
			
		||||
		}
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName,
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request assigned to %s: [%s](%s) by %s", repoLink,
 | 
			
		||||
			strings.Join(list, ", "),
 | 
			
		||||
			titleLink, senderLink)
 | 
			
		||||
			title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueUnassigned:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueLabelUpdated:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueLabelCleared:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueSynchronized:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueMilestoned:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request milestoned to [%s](%s): [%s](%s) %s", repoLink,
 | 
			
		||||
			p.PullRequest.Milestone.Title, mileStoneLink, title, titleLink, senderLink)
 | 
			
		||||
	case api.HookIssueDemilestoned:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request milestone cleared: [%s](%s) %s", repoLink, title, titleLink, senderLink)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &SlackPayload{
 | 
			
		||||
	pl := &SlackPayload{
 | 
			
		||||
		Channel:  slack.Channel,
 | 
			
		||||
		Text:     text,
 | 
			
		||||
		Username: slack.Username,
 | 
			
		||||
		IconURL:  slack.IconURL,
 | 
			
		||||
		Attachments: []SlackAttachment{{
 | 
			
		||||
			Color: slack.Color,
 | 
			
		||||
			Title: title,
 | 
			
		||||
			Text:  attachmentText,
 | 
			
		||||
		}},
 | 
			
		||||
	}, nil
 | 
			
		||||
	}
 | 
			
		||||
	if attachmentText != "" {
 | 
			
		||||
		pl.Attachments = []SlackAttachment{{
 | 
			
		||||
			Color:     slack.Color,
 | 
			
		||||
			Title:     title,
 | 
			
		||||
			TitleLink: titleLink,
 | 
			
		||||
			Text:      attachmentText,
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pl, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event HookEventType) (*SlackPayload, error) {
 | 
			
		||||
	senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
 | 
			
		||||
	titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
 | 
			
		||||
		fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
 | 
			
		||||
	var text, title, attachmentText string
 | 
			
		||||
	title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
 | 
			
		||||
	titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
 | 
			
		||||
	repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
 | 
			
		||||
	var text string
 | 
			
		||||
 | 
			
		||||
	switch p.Action {
 | 
			
		||||
	case api.HookIssueSynchronized:
 | 
			
		||||
		action, err := parseHookPullRequestEventType(event)
 | 
			
		||||
@@ -347,7 +369,7 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request review %s : %s by %s", p.Repository.FullName, action, titleLink, senderLink)
 | 
			
		||||
		text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &SlackPayload{
 | 
			
		||||
@@ -355,21 +377,16 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM
 | 
			
		||||
		Text:     text,
 | 
			
		||||
		Username: slack.Username,
 | 
			
		||||
		IconURL:  slack.IconURL,
 | 
			
		||||
		Attachments: []SlackAttachment{{
 | 
			
		||||
			Color: slack.Color,
 | 
			
		||||
			Title: title,
 | 
			
		||||
			Text:  attachmentText,
 | 
			
		||||
		}},
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
 | 
			
		||||
	senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
 | 
			
		||||
	var text, title, attachmentText string
 | 
			
		||||
	var text string
 | 
			
		||||
 | 
			
		||||
	switch p.Action {
 | 
			
		||||
	case api.HookRepoCreated:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink)
 | 
			
		||||
		title = p.Repository.HTMLURL
 | 
			
		||||
	case api.HookRepoDeleted:
 | 
			
		||||
		text = fmt.Sprintf("[%s] Repository deleted by %s", p.Repository.FullName, senderLink)
 | 
			
		||||
	}
 | 
			
		||||
@@ -379,11 +396,6 @@ func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*Sla
 | 
			
		||||
		Text:     text,
 | 
			
		||||
		Username: slack.Username,
 | 
			
		||||
		IconURL:  slack.IconURL,
 | 
			
		||||
		Attachments: []SlackAttachment{{
 | 
			
		||||
			Color: slack.Color,
 | 
			
		||||
			Title: title,
 | 
			
		||||
			Text:  attachmentText,
 | 
			
		||||
		}},
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ type AuthenticationForm struct {
 | 
			
		||||
	SearchPageSize                int
 | 
			
		||||
	Filter                        string
 | 
			
		||||
	AdminFilter                   string
 | 
			
		||||
	AllowDeactivateAll            bool
 | 
			
		||||
	IsActive                      bool
 | 
			
		||||
	IsSyncEnabled                 bool
 | 
			
		||||
	SMTPAuth                      string
 | 
			
		||||
 
 | 
			
		||||
@@ -47,6 +47,7 @@ type Source struct {
 | 
			
		||||
	Filter                string // Query filter to validate entry
 | 
			
		||||
	AdminFilter           string // Query filter to check if user is admin
 | 
			
		||||
	Enabled               bool   // if this source is disabled
 | 
			
		||||
	AllowDeactivateAll    bool   // Allow an empty search response to deactivate all users from this source
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SearchResult : user data
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
			
		||||
// Copyright 2020 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.
 | 
			
		||||
 | 
			
		||||
@@ -122,7 +123,7 @@ func (ctx *Context) RedirectToFirst(location ...string) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		u, err := url.Parse(loc)
 | 
			
		||||
		if err != nil || (u.Scheme != "" && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
 | 
			
		||||
		if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -91,12 +91,12 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b
 | 
			
		||||
	// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
 | 
			
		||||
	isAssigned, _ := models.IsUserAssignedToIssue(issue, user)
 | 
			
		||||
	return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() ||
 | 
			
		||||
		r.Permission.CanWrite(models.UnitTypeIssues) || issue.IsPoster(user.ID) || isAssigned)
 | 
			
		||||
		r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CanCreateIssueDependencies returns whether or not a user can create dependencies.
 | 
			
		||||
func (r *Repository) CanCreateIssueDependencies(user *models.User) bool {
 | 
			
		||||
	return r.Permission.CanWrite(models.UnitTypeIssues) && r.Repository.IsDependenciesEnabled()
 | 
			
		||||
func (r *Repository) CanCreateIssueDependencies(user *models.User, isPull bool) bool {
 | 
			
		||||
	return r.Repository.IsDependenciesEnabled() && r.Permission.CanWriteIssuesOrPulls(isPull)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetCommitsCount returns cached commit count for current view
 | 
			
		||||
 
 | 
			
		||||
@@ -138,26 +138,31 @@ func populateIssueIndexer() {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, repo := range repos {
 | 
			
		||||
			is, err := models.Issues(&models.IssuesOptions{
 | 
			
		||||
				RepoIDs:  []int64{repo.ID},
 | 
			
		||||
				IsClosed: util.OptionalBoolNone,
 | 
			
		||||
				IsPull:   util.OptionalBoolNone,
 | 
			
		||||
			})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error("Issues: %v", err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if err = models.IssueList(is).LoadDiscussComments(); err != nil {
 | 
			
		||||
				log.Error("LoadComments: %v", err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			for _, issue := range is {
 | 
			
		||||
				UpdateIssueIndexer(issue)
 | 
			
		||||
			}
 | 
			
		||||
			UpdateRepoIndexer(repo)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateRepoIndexer add/update all issues of the repositories
 | 
			
		||||
func UpdateRepoIndexer(repo *models.Repository) {
 | 
			
		||||
	is, err := models.Issues(&models.IssuesOptions{
 | 
			
		||||
		RepoIDs:  []int64{repo.ID},
 | 
			
		||||
		IsClosed: util.OptionalBoolNone,
 | 
			
		||||
		IsPull:   util.OptionalBoolNone,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error("Issues: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err = models.IssueList(is).LoadDiscussComments(); err != nil {
 | 
			
		||||
		log.Error("LoadComments: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, issue := range is {
 | 
			
		||||
		UpdateIssueIndexer(issue)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateIssueIndexer add/update an issue to the issue indexer
 | 
			
		||||
func UpdateIssueIndexer(issue *models.Issue) {
 | 
			
		||||
	var comments []string
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ type Uploader interface {
 | 
			
		||||
	CreateTopics(topic ...string) error
 | 
			
		||||
	CreateMilestones(milestones ...*Milestone) error
 | 
			
		||||
	CreateReleases(releases ...*Release) error
 | 
			
		||||
	SyncTags() error
 | 
			
		||||
	CreateLabels(labels ...*Label) error
 | 
			
		||||
	CreateIssues(issues ...*Issue) error
 | 
			
		||||
	CreateComments(comments ...*Comment) error
 | 
			
		||||
 
 | 
			
		||||
@@ -288,11 +288,12 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
 | 
			
		||||
 | 
			
		||||
		rels = append(rels, &rel)
 | 
			
		||||
	}
 | 
			
		||||
	if err := models.InsertReleases(rels...); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// sync tags to releases in database
 | 
			
		||||
	return models.InsertReleases(rels...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SyncTags syncs releases with tags in the database
 | 
			
		||||
func (g *GiteaLocalUploader) SyncTags() error {
 | 
			
		||||
	return models.SyncReleasesWithTags(g.repo, g.gitRepo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOpt
 | 
			
		||||
		opts.PullRequests = false
 | 
			
		||||
		opts.GitServiceType = structs.PlainGitService
 | 
			
		||||
		downloader = NewPlainGitDownloader(ownerName, opts.RepoName, opts.CloneAddr)
 | 
			
		||||
		log.Trace("Will migrate from git: %s", opts.CloneAddr)
 | 
			
		||||
		log.Trace("Will migrate from git: %s", opts.OriginalURL)
 | 
			
		||||
	} else if opts.GitServiceType == structs.NotMigrated {
 | 
			
		||||
		opts.GitServiceType = theFactory.GitServiceType()
 | 
			
		||||
	}
 | 
			
		||||
@@ -68,7 +68,7 @@ func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOpt
 | 
			
		||||
			log.Error("rollback failed: %v", err1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err2 := models.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.CloneAddr, err)); err2 != nil {
 | 
			
		||||
		if err2 := models.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil {
 | 
			
		||||
			log.Error("create respotiry notice failed: ", err2)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -165,6 +165,11 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts
 | 
			
		||||
			}
 | 
			
		||||
			releases = releases[relBatchSize:]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Once all releases (if any) are inserted, sync any remaining non-release tags
 | 
			
		||||
		if err := uploader.SyncTags(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var commentBatchSize = uploader.MaxBatchInsertSize("comment")
 | 
			
		||||
 
 | 
			
		||||
@@ -107,3 +107,8 @@ func (r *indexerNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod
 | 
			
		||||
func (r *indexerNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
 | 
			
		||||
	issue_indexer.UpdateIssueIndexer(issue)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *indexerNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
 | 
			
		||||
	issue_indexer.UpdateRepoIndexer(repo)
 | 
			
		||||
	models.UpdateRepoIndexer(repo)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo
 | 
			
		||||
 | 
			
		||||
	message := strings.TrimSpace(opts.Message)
 | 
			
		||||
 | 
			
		||||
	author, committer := GetAuthorAndCommitterUsers(opts.Committer, opts.Author, doer)
 | 
			
		||||
	author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
 | 
			
		||||
 | 
			
		||||
	t, err := NewTemporaryUploadRepository(repo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ func GetFileCommitResponse(repo *models.Repository, commit *git.Commit) (*api.Fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAuthorAndCommitterUsers Gets the author and committer user objects from the IdentityOptions
 | 
			
		||||
func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models.User) (committerUser, authorUser *models.User) {
 | 
			
		||||
func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models.User) (authorUser, committerUser *models.User) {
 | 
			
		||||
	// Committer and author are optional. If they are not the doer (not same email address)
 | 
			
		||||
	// then we use bogus User objects for them to store their FullName and Email.
 | 
			
		||||
	// If only one of the two are provided, we set both of them to it.
 | 
			
		||||
 
 | 
			
		||||
@@ -79,11 +79,11 @@ func GetTreeBySHA(repo *models.Repository, sha string, page, perPage int, recurs
 | 
			
		||||
	for e := rangeStart; e < rangeEnd; e++ {
 | 
			
		||||
		i := e - rangeStart
 | 
			
		||||
 | 
			
		||||
		tree.Entries[e].Path = entries[e].Name()
 | 
			
		||||
		tree.Entries[e].Mode = fmt.Sprintf("%06o", entries[e].Mode())
 | 
			
		||||
		tree.Entries[e].Type = entries[e].Type()
 | 
			
		||||
		tree.Entries[e].Size = entries[e].Size()
 | 
			
		||||
		tree.Entries[e].SHA = entries[e].ID.String()
 | 
			
		||||
		tree.Entries[i].Path = entries[e].Name()
 | 
			
		||||
		tree.Entries[i].Mode = fmt.Sprintf("%06o", entries[e].Mode())
 | 
			
		||||
		tree.Entries[i].Type = entries[e].Type()
 | 
			
		||||
		tree.Entries[i].Size = entries[e].Size()
 | 
			
		||||
		tree.Entries[i].SHA = entries[e].ID.String()
 | 
			
		||||
 | 
			
		||||
		if entries[e].IsDir() {
 | 
			
		||||
			copy(treeURL[copyPos:], entries[e].ID.String())
 | 
			
		||||
 
 | 
			
		||||
@@ -167,7 +167,7 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
 | 
			
		||||
 | 
			
		||||
	message := strings.TrimSpace(opts.Message)
 | 
			
		||||
 | 
			
		||||
	author, committer := GetAuthorAndCommitterUsers(opts.Committer, opts.Author, doer)
 | 
			
		||||
	author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
 | 
			
		||||
 | 
			
		||||
	t, err := NewTemporaryUploadRepository(repo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -453,10 +453,15 @@ func PushUpdate(repo *models.Repository, branch string, opts models.PushUpdateOp
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else if !isDelRef {
 | 
			
		||||
		branchName := opts.RefFullName[len(git.BranchPrefix):]
 | 
			
		||||
		if err = models.RemoveDeletedBranch(repo.ID, branchName); err != nil {
 | 
			
		||||
			log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, branchName, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If is branch reference
 | 
			
		||||
 | 
			
		||||
		// Clear cache for branch commit count
 | 
			
		||||
		cache.Remove(repo.GetCommitsCountCacheKey(opts.RefFullName[len(git.BranchPrefix):], true))
 | 
			
		||||
		cache.Remove(repo.GetCommitsCountCacheKey(branchName, true))
 | 
			
		||||
 | 
			
		||||
		newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -97,8 +97,6 @@ func runMigrateTask(t *models.Task) (err error) {
 | 
			
		||||
	opts.MigrateToRepoID = t.RepoID
 | 
			
		||||
	repo, err := migrations.MigrateRepository(t.Doer, t.Owner.Name, *opts)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		notification.NotifyMigrateRepository(t.Doer, t.Owner, repo)
 | 
			
		||||
 | 
			
		||||
		log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -239,6 +239,14 @@ func NewFuncMap() []template.FuncMap {
 | 
			
		||||
		"MirrorFullAddress": mirror_service.AddressNoCredentials,
 | 
			
		||||
		"MirrorUserName":    mirror_service.Username,
 | 
			
		||||
		"MirrorPassword":    mirror_service.Password,
 | 
			
		||||
		"contain": func(s []int64, id int64) bool {
 | 
			
		||||
			for i := 0; i < len(s); i++ {
 | 
			
		||||
				if s[i] == id {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		},
 | 
			
		||||
	}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1700,6 +1700,7 @@ auths.attribute_surname = Surname Attribute
 | 
			
		||||
auths.attribute_mail = Email Attribute
 | 
			
		||||
auths.attribute_ssh_public_key = Public SSH Key Attribute
 | 
			
		||||
auths.attributes_in_bind = Fetch Attributes in Bind DN Context
 | 
			
		||||
auths.allow_deactivate_all = Allow an empty search result to deactivate all users
 | 
			
		||||
auths.use_paged_search = Use Paged Search
 | 
			
		||||
auths.search_page_size = Page Size
 | 
			
		||||
auths.filter = User Filter
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
/* globals wipPrefixes, issuesTribute, emojiTribute */
 | 
			
		||||
/* exported timeAddManual, toggleStopwatch, cancelStopwatch, initHeatmap */
 | 
			
		||||
/* exported toggleDeadlineForm, setDeadline, deleteDependencyModal, cancelCodeComment, onOAuthLoginClick */
 | 
			
		||||
/* exported toggleDeadlineForm, setDeadline, deleteDependencyModal, submitReply, cancelCodeComment, onOAuthLoginClick */
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
function htmlEncode(text) {
 | 
			
		||||
@@ -3134,10 +3134,11 @@ function deleteDependencyModal(id, type) {
 | 
			
		||||
 | 
			
		||||
function initIssueList() {
 | 
			
		||||
    const repolink = $('#repolink').val();
 | 
			
		||||
    const tp = $('#type').val();
 | 
			
		||||
    $('#new-dependency-drop-list')
 | 
			
		||||
        .dropdown({
 | 
			
		||||
            apiSettings: {
 | 
			
		||||
                url: suburl + '/api/v1/repos/' + repolink + '/issues?q={query}',
 | 
			
		||||
                url: suburl + '/api/v1/repos/' + repolink + '/issues?q={query}&type='+tp,
 | 
			
		||||
                onResponse: function(response) {
 | 
			
		||||
                    const filteredResponse = {'success': true, 'results': []};
 | 
			
		||||
                    const currIssueId = $('#new-dependency-drop-list').data('issue-id');
 | 
			
		||||
@@ -3170,6 +3171,14 @@ function cancelCodeComment(btn) {
 | 
			
		||||
        form.closest('.comment-code-cloud').remove()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function submitReply(btn) {
 | 
			
		||||
    const form = $(btn).closest('form');
 | 
			
		||||
    if (form.length > 0 && form.hasClass('comment-form')) {
 | 
			
		||||
        form.submit();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onOAuthLoginClick() {
 | 
			
		||||
    const oauthLoader = $('#oauth2-login-loader');
 | 
			
		||||
    const oauthNav = $('#oauth2-login-navigator');
 | 
			
		||||
 
 | 
			
		||||
@@ -115,6 +115,7 @@ func parseLDAPConfig(form auth.AuthenticationForm) *models.LDAPConfig {
 | 
			
		||||
			SearchPageSize:        pageSize,
 | 
			
		||||
			Filter:                form.Filter,
 | 
			
		||||
			AdminFilter:           form.AdminFilter,
 | 
			
		||||
			AllowDeactivateAll:    form.AllowDeactivateAll,
 | 
			
		||||
			Enabled:               true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,10 @@ func ListIssues(ctx *context.APIContext) {
 | 
			
		||||
	//   in: query
 | 
			
		||||
	//   description: search string
 | 
			
		||||
	//   type: string
 | 
			
		||||
	// - name: type
 | 
			
		||||
	//   in: query
 | 
			
		||||
	//   description: filter by type (issues / pulls) if set
 | 
			
		||||
	//   type: string
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/IssueList"
 | 
			
		||||
@@ -91,6 +95,16 @@ func ListIssues(ctx *context.APIContext) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var isPull util.OptionalBool
 | 
			
		||||
	switch ctx.Query("type") {
 | 
			
		||||
	case "pulls":
 | 
			
		||||
		isPull = util.OptionalBoolTrue
 | 
			
		||||
	case "issues":
 | 
			
		||||
		isPull = util.OptionalBoolFalse
 | 
			
		||||
	default:
 | 
			
		||||
		isPull = util.OptionalBoolNone
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 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 {
 | 
			
		||||
@@ -101,6 +115,7 @@ func ListIssues(ctx *context.APIContext) {
 | 
			
		||||
			IsClosed: isClosed,
 | 
			
		||||
			IssueIDs: issueIDs,
 | 
			
		||||
			LabelIDs: labelIDs,
 | 
			
		||||
			IsPull:   isPull,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -292,6 +307,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	issue.Repo = ctx.Repo.Repository
 | 
			
		||||
	canWrite := ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
 | 
			
		||||
 | 
			
		||||
	err = issue.LoadAttributes()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -299,7 +315,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypeIssues) {
 | 
			
		||||
	if !issue.IsPoster(ctx.User.ID) && !canWrite {
 | 
			
		||||
		ctx.Status(403)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -312,7 +328,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update the deadline
 | 
			
		||||
	if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) {
 | 
			
		||||
	if form.Deadline != nil && canWrite {
 | 
			
		||||
		deadlineUnix := timeutil.TimeStamp(form.Deadline.Unix())
 | 
			
		||||
		if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
 | 
			
		||||
			ctx.Error(500, "UpdateIssueDeadline", err)
 | 
			
		||||
@@ -329,7 +345,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 | 
			
		||||
	// Pass one or more user logins to replace the set of assignees on this Issue.
 | 
			
		||||
	// Send an empty array ([]) to clear all assignees from the Issue.
 | 
			
		||||
 | 
			
		||||
	if ctx.Repo.CanWrite(models.UnitTypeIssues) && (form.Assignees != nil || form.Assignee != nil) {
 | 
			
		||||
	if canWrite && (form.Assignees != nil || form.Assignee != nil) {
 | 
			
		||||
		oneAssignee := ""
 | 
			
		||||
		if form.Assignee != nil {
 | 
			
		||||
			oneAssignee = *form.Assignee
 | 
			
		||||
@@ -342,7 +358,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctx.Repo.CanWrite(models.UnitTypeIssues) && form.Milestone != nil &&
 | 
			
		||||
	if canWrite && form.Milestone != nil &&
 | 
			
		||||
		issue.MilestoneID != *form.Milestone {
 | 
			
		||||
		oldMilestoneID := issue.MilestoneID
 | 
			
		||||
		issue.MilestoneID = *form.Milestone
 | 
			
		||||
@@ -352,8 +368,8 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = models.UpdateIssue(issue); err != nil {
 | 
			
		||||
		ctx.Error(500, "UpdateIssue", err)
 | 
			
		||||
	if err = models.UpdateIssueByAPI(issue); err != nil {
 | 
			
		||||
		ctx.InternalServerError(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if form.State != nil {
 | 
			
		||||
@@ -372,7 +388,11 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 | 
			
		||||
	// Refetch from database to assign some automatic values
 | 
			
		||||
	issue, err = models.GetIssueByID(issue.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.Error(500, "GetIssueByID", err)
 | 
			
		||||
		ctx.InternalServerError(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if err = issue.LoadMilestone(); err != nil {
 | 
			
		||||
		ctx.InternalServerError(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx.JSON(201, issue.APIFormat())
 | 
			
		||||
@@ -426,7 +446,7 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
 | 
			
		||||
	if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
 | 
			
		||||
		ctx.Status(403)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -493,7 +513,7 @@ func StartIssueStopwatch(ctx *context.APIContext) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
 | 
			
		||||
	if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
 | 
			
		||||
		ctx.Status(403)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -562,7 +582,7 @@ func StopIssueStopwatch(ctx *context.APIContext) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
 | 
			
		||||
	if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
 | 
			
		||||
		ctx.Status(403)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -185,7 +185,7 @@ func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOpti
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
 | 
			
		||||
	if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.User.IsAdmin {
 | 
			
		||||
		ctx.Error(403, "CreateIssueComment", errors.New(ctx.Tr("repo.issues.comment_on_locked")))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -189,7 +189,12 @@ func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) {
 | 
			
		||||
		milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := models.UpdateMilestone(milestone); err != nil {
 | 
			
		||||
	var oldIsClosed = milestone.IsClosed
 | 
			
		||||
	if form.State != nil {
 | 
			
		||||
		milestone.IsClosed = *form.State == string(api.StateClosed)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := models.UpdateMilestone(milestone, oldIsClosed); err != nil {
 | 
			
		||||
		ctx.ServerError("UpdateMilestone", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -420,8 +420,8 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = models.UpdateIssue(issue); err != nil {
 | 
			
		||||
		ctx.Error(500, "UpdateIssue", err)
 | 
			
		||||
	if err = models.UpdateIssueByAPI(issue); err != nil {
 | 
			
		||||
		ctx.InternalServerError(err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if form.State != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@
 | 
			
		||||
package repo
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
@@ -431,15 +433,53 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
 | 
			
		||||
		opts.Releases = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	repo, err := migrations.MigrateRepository(ctx.User, ctxUser.Name, opts)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		notification.NotifyCreateRepository(ctx.User, ctxUser, repo)
 | 
			
		||||
 | 
			
		||||
		log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
 | 
			
		||||
		ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
 | 
			
		||||
	repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
 | 
			
		||||
		Name:        opts.RepoName,
 | 
			
		||||
		Description: opts.Description,
 | 
			
		||||
		OriginalURL: form.CloneAddr,
 | 
			
		||||
		IsPrivate:   opts.Private,
 | 
			
		||||
		IsMirror:    opts.Mirror,
 | 
			
		||||
		Status:      models.RepositoryBeingMigrated,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		handleMigrateError(ctx, ctxUser, remoteAddr, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	opts.MigrateToRepoID = repo.ID
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if e := recover(); e != nil {
 | 
			
		||||
			var buf bytes.Buffer
 | 
			
		||||
			fmt.Fprintf(&buf, "Handler crashed with error: %v", log.Stack(2))
 | 
			
		||||
			err = errors.New(buf.String())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			repo.Status = models.RepositoryReady
 | 
			
		||||
			if err := models.UpdateRepositoryCols(repo, "status"); err == nil {
 | 
			
		||||
				notification.NotifyMigrateRepository(ctx.User, ctxUser, repo)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if repo != nil {
 | 
			
		||||
			if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
 | 
			
		||||
				log.Error("DeleteRepository: %v", errDelete)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if _, err = migrations.MigrateRepository(ctx.User, ctxUser.Name, opts); err != nil {
 | 
			
		||||
		handleMigrateError(ctx, ctxUser, remoteAddr, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
 | 
			
		||||
	ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteAddr string, err error) {
 | 
			
		||||
	switch {
 | 
			
		||||
	case models.IsErrRepoAlreadyExist(err):
 | 
			
		||||
		ctx.Error(409, "", "The repository with the same name already exists.")
 | 
			
		||||
@@ -448,7 +488,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
 | 
			
		||||
	case migrations.IsTwoFactorAuthError(err):
 | 
			
		||||
		ctx.Error(422, "", "Remote visit required two factors authentication.")
 | 
			
		||||
	case models.IsErrReachLimitOfRepo(err):
 | 
			
		||||
		ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", ctxUser.MaxCreationLimit()))
 | 
			
		||||
		ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
 | 
			
		||||
	case models.IsErrNameReserved(err):
 | 
			
		||||
		ctx.Error(422, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
 | 
			
		||||
	case models.IsErrNamePatternNotAllowed(err):
 | 
			
		||||
 
 | 
			
		||||
@@ -41,8 +41,8 @@ func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) {
 | 
			
		||||
	//   schema:
 | 
			
		||||
	//     "$ref": "#/definitions/CreateStatusOption"
 | 
			
		||||
	// responses:
 | 
			
		||||
	//   "200":
 | 
			
		||||
	//     "$ref": "#/responses/StatusList"
 | 
			
		||||
	//   "201":
 | 
			
		||||
	//     "$ref": "#/responses/Status"
 | 
			
		||||
	sha := ctx.Params("sha")
 | 
			
		||||
	if len(sha) == 0 {
 | 
			
		||||
		ctx.Error(400, "sha not given", nil)
 | 
			
		||||
 
 | 
			
		||||
@@ -141,6 +141,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
 | 
			
		||||
		Keyword:            keyword,
 | 
			
		||||
		OwnerID:            opts.OwnerID,
 | 
			
		||||
		AllPublic:          true,
 | 
			
		||||
		AllLimited:         true,
 | 
			
		||||
		TopicOnly:          topicOnly,
 | 
			
		||||
		IncludeDescription: setting.UI.SearchRepoDescription,
 | 
			
		||||
	})
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ import (
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/repofiles"
 | 
			
		||||
	"code.gitea.io/gitea/modules/util"
 | 
			
		||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -32,6 +33,7 @@ type Branch struct {
 | 
			
		||||
	CommitsAhead      int
 | 
			
		||||
	CommitsBehind     int
 | 
			
		||||
	LatestPullRequest *models.PullRequest
 | 
			
		||||
	MergeMovedOn      bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Branches render repository branch page
 | 
			
		||||
@@ -168,6 +170,12 @@ func loadBranches(ctx *context.Context) []*Branch {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	repoIDToRepo := map[int64]*models.Repository{}
 | 
			
		||||
	repoIDToRepo[ctx.Repo.Repository.ID] = ctx.Repo.Repository
 | 
			
		||||
 | 
			
		||||
	repoIDToGitRepo := map[int64]*git.Repository{}
 | 
			
		||||
	repoIDToGitRepo[ctx.Repo.Repository.ID] = ctx.Repo.GitRepo
 | 
			
		||||
 | 
			
		||||
	branches := make([]*Branch, len(rawBranches))
 | 
			
		||||
	for i := range rawBranches {
 | 
			
		||||
		commit, err := rawBranches[i].GetCommit()
 | 
			
		||||
@@ -196,11 +204,46 @@ func loadBranches(ctx *context.Context) []*Branch {
 | 
			
		||||
			ctx.ServerError("GetLatestPullRequestByHeadInfo", err)
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		headCommit := commit.ID.String()
 | 
			
		||||
 | 
			
		||||
		mergeMovedOn := false
 | 
			
		||||
		if pr != nil {
 | 
			
		||||
			pr.HeadRepo = ctx.Repo.Repository
 | 
			
		||||
			if err := pr.LoadIssue(); err != nil {
 | 
			
		||||
				ctx.ServerError("pr.LoadIssue", err)
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			if repo, ok := repoIDToRepo[pr.BaseRepoID]; ok {
 | 
			
		||||
				pr.BaseRepo = repo
 | 
			
		||||
			} else if err := pr.LoadBaseRepo(); err != nil {
 | 
			
		||||
				ctx.ServerError("pr.LoadBaseRepo", err)
 | 
			
		||||
				return nil
 | 
			
		||||
			} else {
 | 
			
		||||
				repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
 | 
			
		||||
			}
 | 
			
		||||
			pr.Issue.Repo = pr.BaseRepo
 | 
			
		||||
 | 
			
		||||
			if pr.HasMerged {
 | 
			
		||||
				baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
 | 
			
		||||
				if !ok {
 | 
			
		||||
					baseGitRepo, err = git.OpenRepository(pr.BaseRepo.RepoPath())
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						ctx.ServerError("OpenRepository", err)
 | 
			
		||||
						return nil
 | 
			
		||||
					}
 | 
			
		||||
					defer baseGitRepo.Close()
 | 
			
		||||
					repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo
 | 
			
		||||
				}
 | 
			
		||||
				pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
 | 
			
		||||
				if err != nil && err != plumbing.ErrReferenceNotFound {
 | 
			
		||||
					ctx.ServerError("GetBranchCommitID", err)
 | 
			
		||||
					return nil
 | 
			
		||||
				}
 | 
			
		||||
				if err == nil && headCommit != pullCommit {
 | 
			
		||||
					// the head has moved on from the merge - we shouldn't delete
 | 
			
		||||
					mergeMovedOn = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		branches[i] = &Branch{
 | 
			
		||||
@@ -210,6 +253,7 @@ func loadBranches(ctx *context.Context) []*Branch {
 | 
			
		||||
			CommitsAhead:      divergence.Ahead,
 | 
			
		||||
			CommitsBehind:     divergence.Behind,
 | 
			
		||||
			LatestPullRequest: pr,
 | 
			
		||||
			MergeMovedOn:      mergeMovedOn,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -152,12 +152,12 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
 | 
			
		||||
			ctx.ServerError("OpenRepository", err)
 | 
			
		||||
			return nil, nil, nil, nil, "", ""
 | 
			
		||||
		}
 | 
			
		||||
		defer headGitRepo.Close()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// user should have permission to read baseRepo's codes and pulls, NOT headRepo's
 | 
			
		||||
	permBase, err := models.GetUserRepoPermission(baseRepo, ctx.User)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		headGitRepo.Close()
 | 
			
		||||
		ctx.ServerError("GetUserRepoPermission", err)
 | 
			
		||||
		return nil, nil, nil, nil, "", ""
 | 
			
		||||
	}
 | 
			
		||||
@@ -168,42 +168,40 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
 | 
			
		||||
				baseRepo,
 | 
			
		||||
				permBase)
 | 
			
		||||
		}
 | 
			
		||||
		headGitRepo.Close()
 | 
			
		||||
		ctx.NotFound("ParseCompareInfo", nil)
 | 
			
		||||
		return nil, nil, nil, nil, "", ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// user should have permission to read headrepo's codes
 | 
			
		||||
	permHead, err := models.GetUserRepoPermission(headRepo, ctx.User)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		headGitRepo.Close()
 | 
			
		||||
		ctx.ServerError("GetUserRepoPermission", err)
 | 
			
		||||
		return nil, nil, nil, nil, "", ""
 | 
			
		||||
	}
 | 
			
		||||
	if !permHead.CanRead(models.UnitTypeCode) {
 | 
			
		||||
		if log.IsTrace() {
 | 
			
		||||
			log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v",
 | 
			
		||||
				ctx.User,
 | 
			
		||||
				headRepo,
 | 
			
		||||
				permHead)
 | 
			
		||||
	if !isSameRepo {
 | 
			
		||||
		// user should have permission to read headrepo's codes
 | 
			
		||||
		permHead, err := models.GetUserRepoPermission(headRepo, ctx.User)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.ServerError("GetUserRepoPermission", err)
 | 
			
		||||
			return nil, nil, nil, nil, "", ""
 | 
			
		||||
		}
 | 
			
		||||
		if !permHead.CanRead(models.UnitTypeCode) {
 | 
			
		||||
			if log.IsTrace() {
 | 
			
		||||
				log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v",
 | 
			
		||||
					ctx.User,
 | 
			
		||||
					headRepo,
 | 
			
		||||
					permHead)
 | 
			
		||||
			}
 | 
			
		||||
			ctx.NotFound("ParseCompareInfo", nil)
 | 
			
		||||
			return nil, nil, nil, nil, "", ""
 | 
			
		||||
		}
 | 
			
		||||
		headGitRepo.Close()
 | 
			
		||||
		ctx.NotFound("ParseCompareInfo", nil)
 | 
			
		||||
		return nil, nil, nil, nil, "", ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if head branch is valid.
 | 
			
		||||
	headIsCommit := ctx.Repo.GitRepo.IsCommitExist(headBranch)
 | 
			
		||||
	headIsCommit := headGitRepo.IsCommitExist(headBranch)
 | 
			
		||||
	headIsBranch := headGitRepo.IsBranchExist(headBranch)
 | 
			
		||||
	headIsTag := headGitRepo.IsTagExist(headBranch)
 | 
			
		||||
	if !headIsCommit && !headIsBranch && !headIsTag {
 | 
			
		||||
		// Check if headBranch is short sha commit hash
 | 
			
		||||
		if headCommit, _ := ctx.Repo.GitRepo.GetCommit(headBranch); headCommit != nil {
 | 
			
		||||
		if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil {
 | 
			
		||||
			headBranch = headCommit.ID.String()
 | 
			
		||||
			ctx.Data["HeadBranch"] = headBranch
 | 
			
		||||
			headIsCommit = true
 | 
			
		||||
		} else {
 | 
			
		||||
			headGitRepo.Close()
 | 
			
		||||
			ctx.NotFound("IsRefExist", nil)
 | 
			
		||||
			return nil, nil, nil, nil, "", ""
 | 
			
		||||
		}
 | 
			
		||||
@@ -224,14 +222,12 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
 | 
			
		||||
				baseRepo,
 | 
			
		||||
				permBase)
 | 
			
		||||
		}
 | 
			
		||||
		headGitRepo.Close()
 | 
			
		||||
		ctx.NotFound("ParseCompareInfo", nil)
 | 
			
		||||
		return nil, nil, nil, nil, "", ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
 | 
			
		||||
	compareInfo, err := headGitRepo.GetCompareInfo(baseRepo.RepoPath(), baseBranch, headBranch)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		headGitRepo.Close()
 | 
			
		||||
		ctx.ServerError("GetCompareInfo", err)
 | 
			
		||||
		return nil, nil, nil, nil, "", ""
 | 
			
		||||
	}
 | 
			
		||||
@@ -377,7 +373,8 @@ func CompareDiff(ctx *context.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer headGitRepo.Close()
 | 
			
		||||
	if err := parseBaseRepoInfo(ctx, headRepo); err != nil {
 | 
			
		||||
	var err error
 | 
			
		||||
	if err = parseBaseRepoInfo(ctx, headRepo); err != nil {
 | 
			
		||||
		ctx.ServerError("parseBaseRepoInfo", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -410,7 +407,7 @@ func CompareDiff(ctx *context.Context) {
 | 
			
		||||
 | 
			
		||||
		if !nothingToCompare {
 | 
			
		||||
			// Setup information for new form.
 | 
			
		||||
			RetrieveRepoMetas(ctx, ctx.Repo.Repository)
 | 
			
		||||
			RetrieveRepoMetas(ctx, ctx.Repo.Repository, true)
 | 
			
		||||
			if ctx.Written() {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
@@ -419,6 +416,11 @@ func CompareDiff(ctx *context.Context) {
 | 
			
		||||
	beforeCommitID := ctx.Data["BeforeCommitID"].(string)
 | 
			
		||||
	afterCommitID := ctx.Data["AfterCommitID"].(string)
 | 
			
		||||
 | 
			
		||||
	if ctx.Data["Assignees"], err = ctx.Repo.Repository.GetAssignees(); err != nil {
 | 
			
		||||
		ctx.ServerError("GetAssignees", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + "..." + base.ShortSha(afterCommitID)
 | 
			
		||||
 | 
			
		||||
	ctx.Data["IsRepoToolbarCommits"] = true
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/modules/base"
 | 
			
		||||
	"code.gitea.io/gitea/modules/charset"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
	"code.gitea.io/gitea/modules/git"
 | 
			
		||||
	"code.gitea.io/gitea/modules/lfs"
 | 
			
		||||
@@ -33,7 +34,12 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error {
 | 
			
		||||
	name = strings.Replace(name, ",", " ", -1)
 | 
			
		||||
 | 
			
		||||
	if base.IsTextFile(buf) || ctx.QueryBool("render") {
 | 
			
		||||
		ctx.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8")
 | 
			
		||||
		cs, err := charset.DetectEncoding(buf)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err)
 | 
			
		||||
			cs = "utf-8"
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs))
 | 
			
		||||
	} else if base.IsImageFile(buf) || base.IsPDFFile(buf) {
 | 
			
		||||
		ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name))
 | 
			
		||||
	} else {
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,7 @@ func MustAllowUserComment(ctx *context.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
 | 
			
		||||
	if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.User.IsAdmin {
 | 
			
		||||
		ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked"))
 | 
			
		||||
		ctx.Redirect(issue.HTMLURL())
 | 
			
		||||
		return
 | 
			
		||||
@@ -261,7 +261,8 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.Data["IssueStats"] = issueStats
 | 
			
		||||
	ctx.Data["SelectLabels"] = com.StrTo(selectLabels).MustInt64()
 | 
			
		||||
	ctx.Data["SelLabelIDs"] = labelIDs
 | 
			
		||||
	ctx.Data["SelectLabels"] = selectLabels
 | 
			
		||||
	ctx.Data["ViewType"] = viewType
 | 
			
		||||
	ctx.Data["SortType"] = sortType
 | 
			
		||||
	ctx.Data["MilestoneID"] = milestoneID
 | 
			
		||||
@@ -345,8 +346,8 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RetrieveRepoMetas find all the meta information of a repository
 | 
			
		||||
func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository) []*models.Label {
 | 
			
		||||
	if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
 | 
			
		||||
func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository, isPull bool) []*models.Label {
 | 
			
		||||
	if !ctx.Repo.CanWriteIssuesOrPulls(isPull) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -370,7 +371,7 @@ func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository) []*models.
 | 
			
		||||
	ctx.Data["Branches"] = brs
 | 
			
		||||
 | 
			
		||||
	// Contains true if the user can create issue dependencies
 | 
			
		||||
	ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User)
 | 
			
		||||
	ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User, isPull)
 | 
			
		||||
 | 
			
		||||
	return labels
 | 
			
		||||
}
 | 
			
		||||
@@ -440,7 +441,7 @@ func NewIssue(ctx *context.Context) {
 | 
			
		||||
	setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates)
 | 
			
		||||
	renderAttachmentSettings(ctx)
 | 
			
		||||
 | 
			
		||||
	RetrieveRepoMetas(ctx, ctx.Repo.Repository)
 | 
			
		||||
	RetrieveRepoMetas(ctx, ctx.Repo.Repository, false)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -455,7 +456,7 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull b
 | 
			
		||||
		err  error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	labels := RetrieveRepoMetas(ctx, ctx.Repo.Repository)
 | 
			
		||||
	labels := RetrieveRepoMetas(ctx, ctx.Repo.Repository, isPull)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return nil, nil, 0
 | 
			
		||||
	}
 | 
			
		||||
@@ -775,8 +776,16 @@ func ViewIssue(ctx *context.Context) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.IsPull && !ctx.Repo.CanRead(models.UnitTypeIssues) {
 | 
			
		||||
		ctx.Data["IssueType"] = "pulls"
 | 
			
		||||
	} else if !issue.IsPull && !ctx.Repo.CanRead(models.UnitTypePullRequests) {
 | 
			
		||||
		ctx.Data["IssueType"] = "issues"
 | 
			
		||||
	} else {
 | 
			
		||||
		ctx.Data["IssueType"] = "all"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if the user can use the dependencies
 | 
			
		||||
	ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User)
 | 
			
		||||
	ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User, issue.IsPull)
 | 
			
		||||
 | 
			
		||||
	// Render comments and and fetch participants.
 | 
			
		||||
	participants[0] = issue.Poster
 | 
			
		||||
@@ -930,7 +939,10 @@ func ViewIssue(ctx *context.Context) {
 | 
			
		||||
			ctx.Data["IsBlockedByApprovals"] = pull.ProtectedBranch.RequiredApprovals > 0 && cnt < pull.ProtectedBranch.RequiredApprovals
 | 
			
		||||
			ctx.Data["GrantedApprovals"] = cnt
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Data["IsPullBranchDeletable"] = canDelete && pull.HeadRepo != nil && git.IsBranchExist(pull.HeadRepo.RepoPath(), pull.HeadBranch)
 | 
			
		||||
		ctx.Data["IsPullBranchDeletable"] = canDelete &&
 | 
			
		||||
			pull.HeadRepo != nil &&
 | 
			
		||||
			git.IsBranchExist(pull.HeadRepo.RepoPath(), pull.HeadBranch) &&
 | 
			
		||||
			(!pull.HasMerged || ctx.Data["HeadBranchCommitID"] == ctx.Data["PullHeadCommitID"])
 | 
			
		||||
 | 
			
		||||
		ctx.Data["PullReviewersWithType"], err = models.GetReviewersByPullID(issue.ID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -959,7 +971,6 @@ func ViewIssue(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID)
 | 
			
		||||
	ctx.Data["IsIssueWriter"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
 | 
			
		||||
	ctx.Data["IsRepoAdmin"] = ctx.IsSigned && (ctx.Repo.IsAdmin() || ctx.User.IsAdmin)
 | 
			
		||||
	ctx.Data["IsRepoIssuesWriter"] = ctx.IsSigned && (ctx.Repo.CanWrite(models.UnitTypeIssues) || ctx.User.IsAdmin)
 | 
			
		||||
	ctx.Data["LockReasons"] = setting.Repository.Issue.LockReasons
 | 
			
		||||
	ctx.HTML(200, tplIssueView)
 | 
			
		||||
}
 | 
			
		||||
@@ -1204,7 +1215,7 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) {
 | 
			
		||||
		ctx.Error(403)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
 | 
			
		||||
	if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.User.IsAdmin {
 | 
			
		||||
		ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked"))
 | 
			
		||||
		ctx.Redirect(issue.HTMLURL(), http.StatusSeeOther)
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -14,14 +14,6 @@ import (
 | 
			
		||||
 | 
			
		||||
// AddDependency adds new dependencies
 | 
			
		||||
func AddDependency(ctx *context.Context) {
 | 
			
		||||
	// Check if the Repo is allowed to have dependencies
 | 
			
		||||
	if !ctx.Repo.CanCreateIssueDependencies(ctx.User) {
 | 
			
		||||
		ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	depID := ctx.QueryInt64("newDependency")
 | 
			
		||||
 | 
			
		||||
	issueIndex := ctx.ParamsInt64("index")
 | 
			
		||||
	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -29,6 +21,14 @@ func AddDependency(ctx *context.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if the Repo is allowed to have dependencies
 | 
			
		||||
	if !ctx.Repo.CanCreateIssueDependencies(ctx.User, issue.IsPull) {
 | 
			
		||||
		ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	depID := ctx.QueryInt64("newDependency")
 | 
			
		||||
 | 
			
		||||
	// Redirect
 | 
			
		||||
	defer ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issueIndex), http.StatusSeeOther)
 | 
			
		||||
 | 
			
		||||
@@ -68,14 +68,6 @@ func AddDependency(ctx *context.Context) {
 | 
			
		||||
 | 
			
		||||
// RemoveDependency removes the dependency
 | 
			
		||||
func RemoveDependency(ctx *context.Context) {
 | 
			
		||||
	// Check if the Repo is allowed to have dependencies
 | 
			
		||||
	if !ctx.Repo.CanCreateIssueDependencies(ctx.User) {
 | 
			
		||||
		ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	depID := ctx.QueryInt64("removeDependencyID")
 | 
			
		||||
 | 
			
		||||
	issueIndex := ctx.ParamsInt64("index")
 | 
			
		||||
	issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -83,8 +75,13 @@ func RemoveDependency(ctx *context.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Redirect
 | 
			
		||||
	ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issueIndex), http.StatusSeeOther)
 | 
			
		||||
	// Check if the Repo is allowed to have dependencies
 | 
			
		||||
	if !ctx.Repo.CanCreateIssueDependencies(ctx.User, issue.IsPull) {
 | 
			
		||||
		ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	depID := ctx.QueryInt64("removeDependencyID")
 | 
			
		||||
 | 
			
		||||
	// Dependency Type
 | 
			
		||||
	depTypeStr := ctx.Req.PostForm.Get("dependencyType")
 | 
			
		||||
@@ -116,4 +113,7 @@ func RemoveDependency(ctx *context.Context) {
 | 
			
		||||
		ctx.ServerError("RemoveIssueDependency", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Redirect
 | 
			
		||||
	ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issueIndex), http.StatusSeeOther)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -192,7 +192,7 @@ func EditMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) {
 | 
			
		||||
	m.Name = form.Title
 | 
			
		||||
	m.Content = form.Content
 | 
			
		||||
	m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix())
 | 
			
		||||
	if err = models.UpdateMilestone(m); err != nil {
 | 
			
		||||
	if err = models.UpdateMilestone(m, m.IsClosed); err != nil {
 | 
			
		||||
		ctx.ServerError("UpdateMilestone", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ import (
 | 
			
		||||
	"code.gitea.io/gitea/modules/git"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/notification"
 | 
			
		||||
	"code.gitea.io/gitea/modules/repofiles"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	"code.gitea.io/gitea/modules/util"
 | 
			
		||||
	"code.gitea.io/gitea/services/gitdiff"
 | 
			
		||||
@@ -314,25 +315,37 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
 | 
			
		||||
	repo := ctx.Repo.Repository
 | 
			
		||||
	pull := issue.PullRequest
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	if err = pull.GetHeadRepo(); err != nil {
 | 
			
		||||
	if err := pull.GetHeadRepo(); err != nil {
 | 
			
		||||
		ctx.ServerError("GetHeadRepo", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := pull.GetBaseRepo(); err != nil {
 | 
			
		||||
		ctx.ServerError("GetBaseRepo", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	setMergeTarget(ctx, pull)
 | 
			
		||||
 | 
			
		||||
	if err = pull.LoadProtectedBranch(); err != nil {
 | 
			
		||||
	if err := pull.LoadProtectedBranch(); err != nil {
 | 
			
		||||
		ctx.ServerError("GetLatestCommitStatus", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Data["EnableStatusCheck"] = pull.ProtectedBranch != nil && pull.ProtectedBranch.EnableStatusCheck
 | 
			
		||||
 | 
			
		||||
	var headGitRepo *git.Repository
 | 
			
		||||
	baseGitRepo, err := git.OpenRepository(pull.BaseRepo.RepoPath())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("OpenRepository", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	defer baseGitRepo.Close()
 | 
			
		||||
	var headBranchExist bool
 | 
			
		||||
	var headBranchSha string
 | 
			
		||||
	// HeadRepo may be missing
 | 
			
		||||
	if pull.HeadRepo != nil {
 | 
			
		||||
		headGitRepo, err = git.OpenRepository(pull.HeadRepo.RepoPath())
 | 
			
		||||
		var err error
 | 
			
		||||
 | 
			
		||||
		headGitRepo, err := git.OpenRepository(pull.HeadRepo.RepoPath())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.ServerError("OpenRepository", err)
 | 
			
		||||
			return nil
 | 
			
		||||
@@ -342,46 +355,53 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
 | 
			
		||||
		headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch)
 | 
			
		||||
 | 
			
		||||
		if headBranchExist {
 | 
			
		||||
			sha, err := headGitRepo.GetBranchCommitID(pull.HeadBranch)
 | 
			
		||||
			headBranchSha, err = headGitRepo.GetBranchCommitID(pull.HeadBranch)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ctx.ServerError("GetBranchCommitID", err)
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			commitStatuses, err := models.GetLatestCommitStatus(repo, sha, 0)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ctx.ServerError("GetLatestCommitStatus", err)
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			if len(commitStatuses) > 0 {
 | 
			
		||||
				ctx.Data["LatestCommitStatuses"] = commitStatuses
 | 
			
		||||
				ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(commitStatuses)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if pull.ProtectedBranch != nil && pull.ProtectedBranch.EnableStatusCheck {
 | 
			
		||||
				ctx.Data["is_context_required"] = func(context string) bool {
 | 
			
		||||
					for _, c := range pull.ProtectedBranch.StatusCheckContexts {
 | 
			
		||||
						if c == context {
 | 
			
		||||
							return true
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
				ctx.Data["IsRequiredStatusCheckSuccess"] = pull_service.IsCommitStatusContextSuccess(commitStatuses, pull.ProtectedBranch.StatusCheckContexts)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pull.HeadRepo == nil || !headBranchExist {
 | 
			
		||||
		ctx.Data["IsPullRequestBroken"] = true
 | 
			
		||||
		ctx.Data["HeadTarget"] = "deleted"
 | 
			
		||||
		ctx.Data["NumCommits"] = 0
 | 
			
		||||
		ctx.Data["NumFiles"] = 0
 | 
			
		||||
	sha, err := baseGitRepo.GetRefCommitID(pull.GetGitRefName())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(repo.Owner.Name, repo.Name),
 | 
			
		||||
		pull.BaseBranch, pull.HeadBranch)
 | 
			
		||||
	commitStatuses, err := models.GetLatestCommitStatus(repo, sha, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ctx.ServerError("GetLatestCommitStatus", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if len(commitStatuses) > 0 {
 | 
			
		||||
		ctx.Data["LatestCommitStatuses"] = commitStatuses
 | 
			
		||||
		ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(commitStatuses)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pull.ProtectedBranch != nil && pull.ProtectedBranch.EnableStatusCheck {
 | 
			
		||||
		ctx.Data["is_context_required"] = func(context string) bool {
 | 
			
		||||
			for _, c := range pull.ProtectedBranch.StatusCheckContexts {
 | 
			
		||||
				if c == context {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		ctx.Data["IsRequiredStatusCheckSuccess"] = pull_service.IsCommitStatusContextSuccess(commitStatuses, pull.ProtectedBranch.StatusCheckContexts)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.Data["HeadBranchMovedOn"] = headBranchSha != sha
 | 
			
		||||
	ctx.Data["HeadBranchCommitID"] = headBranchSha
 | 
			
		||||
	ctx.Data["PullHeadCommitID"] = sha
 | 
			
		||||
 | 
			
		||||
	if pull.HeadRepo == nil || !headBranchExist || headBranchSha != sha {
 | 
			
		||||
		ctx.Data["IsPullRequestBroken"] = true
 | 
			
		||||
		ctx.Data["HeadTarget"] = "deleted"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(),
 | 
			
		||||
		pull.BaseBranch, pull.GetGitRefName())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if strings.Contains(err.Error(), "fatal: Not a valid object name") {
 | 
			
		||||
			ctx.Data["IsPullRequestBroken"] = true
 | 
			
		||||
@@ -928,6 +948,21 @@ func CleanUpPullRequest(ctx *context.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := repofiles.PushUpdate(
 | 
			
		||||
		pr.HeadRepo,
 | 
			
		||||
		pr.HeadBranch,
 | 
			
		||||
		models.PushUpdateOptions{
 | 
			
		||||
			RefFullName:  git.BranchPrefix + pr.HeadBranch,
 | 
			
		||||
			OldCommitID:  branchCommitID,
 | 
			
		||||
			NewCommitID:  git.EmptySHA,
 | 
			
		||||
			PusherID:     ctx.User.ID,
 | 
			
		||||
			PusherName:   ctx.User.Name,
 | 
			
		||||
			RepoUserName: pr.HeadRepo.Owner.Name,
 | 
			
		||||
			RepoName:     pr.HeadRepo.Name,
 | 
			
		||||
		}); err != nil {
 | 
			
		||||
		log.Error("Update: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := models.AddDeletePRBranchComment(ctx.User, pr.BaseRepo, issue.ID, pr.HeadBranch); err != nil {
 | 
			
		||||
		// Do not fail here as branch has already been deleted
 | 
			
		||||
		log.Error("DeleteBranch: %v", err)
 | 
			
		||||
 
 | 
			
		||||
@@ -117,9 +117,7 @@ func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) {
 | 
			
		||||
 | 
			
		||||
	// can not approve/reject your own PR
 | 
			
		||||
	case models.ReviewTypeApprove, models.ReviewTypeReject:
 | 
			
		||||
 | 
			
		||||
		if issue.Poster.ID == ctx.User.ID {
 | 
			
		||||
 | 
			
		||||
		if issue.IsPoster(ctx.User.ID) {
 | 
			
		||||
			var translated string
 | 
			
		||||
 | 
			
		||||
			if reviewType == models.ReviewTypeApprove {
 | 
			
		||||
 
 | 
			
		||||
@@ -600,7 +600,7 @@ func AddTeamPost(ctx *context.Context) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.Query("team")))
 | 
			
		||||
	if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
 | 
			
		||||
	if len(name) == 0 {
 | 
			
		||||
		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -65,27 +65,20 @@ type PageMeta struct {
 | 
			
		||||
 | 
			
		||||
// findEntryForFile finds the tree entry for a target filepath.
 | 
			
		||||
func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
 | 
			
		||||
	entries, err := commit.ListEntries()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	entry, err := commit.GetTreeEntryByPath(target)
 | 
			
		||||
	if err != nil && !git.IsErrNotExist(err) {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	// The longest name should be checked first
 | 
			
		||||
	for _, entry := range entries {
 | 
			
		||||
		if entry.IsRegular() && entry.Name() == target {
 | 
			
		||||
			return entry, nil
 | 
			
		||||
		}
 | 
			
		||||
	if entry != nil {
 | 
			
		||||
		return entry, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Then the unescaped, shortest alternative
 | 
			
		||||
	var unescapedTarget string
 | 
			
		||||
	if unescapedTarget, err = url.QueryUnescape(target); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, entry := range entries {
 | 
			
		||||
		if entry.IsRegular() && entry.Name() == unescapedTarget {
 | 
			
		||||
			return entry, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, nil
 | 
			
		||||
	return commit.GetTreeEntryByPath(unescapedTarget)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
 | 
			
		||||
@@ -122,10 +115,9 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
 | 
			
		||||
// wikiContentsByName returns the contents of a wiki page, along with a boolean
 | 
			
		||||
// indicating whether the page exists. Writes to ctx if an error occurs.
 | 
			
		||||
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
 | 
			
		||||
	var entry *git.TreeEntry
 | 
			
		||||
	var err error
 | 
			
		||||
	pageFilename := models.WikiNameToFilename(wikiName)
 | 
			
		||||
	if entry, err = findEntryForFile(commit, pageFilename); err != nil {
 | 
			
		||||
	entry, err := findEntryForFile(commit, pageFilename)
 | 
			
		||||
	if err != nil && !git.IsErrNotExist(err) {
 | 
			
		||||
		ctx.ServerError("findEntryForFile", err)
 | 
			
		||||
		return nil, nil, "", false
 | 
			
		||||
	} else if entry == nil {
 | 
			
		||||
@@ -517,7 +509,7 @@ func WikiRaw(ctx *context.Context) {
 | 
			
		||||
	if commit != nil {
 | 
			
		||||
		// Try to find a file with that name
 | 
			
		||||
		entry, err = findEntryForFile(commit, providedPath)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
		if err != nil && !git.IsErrNotExist(err) {
 | 
			
		||||
			ctx.ServerError("findFile", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
@@ -530,7 +522,7 @@ func WikiRaw(ctx *context.Context) {
 | 
			
		||||
 | 
			
		||||
			wikiPath := models.WikiNameToFilename(providedPath)
 | 
			
		||||
			entry, err = findEntryForFile(commit, wikiPath)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
			if err != nil && !git.IsErrNotExist(err) {
 | 
			
		||||
				ctx.ServerError("findFile", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -191,6 +191,7 @@ func TestDeleteWikiPagePost(t *testing.T) {
 | 
			
		||||
func TestWikiRaw(t *testing.T) {
 | 
			
		||||
	for filepath, filetype := range map[string]string{
 | 
			
		||||
		"jpeg.jpg":                 "image/jpeg",
 | 
			
		||||
		"images/jpeg.jpg":          "image/jpeg",
 | 
			
		||||
		"Page With Spaced Name":    "text/plain; charset=utf-8",
 | 
			
		||||
		"Page-With-Spaced-Name":    "text/plain; charset=utf-8",
 | 
			
		||||
		"Page With Spaced Name.md": "text/plain; charset=utf-8",
 | 
			
		||||
 
 | 
			
		||||
@@ -457,7 +457,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
			m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
 | 
			
		||||
			m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
 | 
			
		||||
			m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
 | 
			
		||||
			m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
 | 
			
		||||
			m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		m.Group("/auths", func() {
 | 
			
		||||
@@ -532,18 +532,12 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
	reqRepoReleaseReader := context.RequireRepoReader(models.UnitTypeReleases)
 | 
			
		||||
	reqRepoWikiWriter := context.RequireRepoWriter(models.UnitTypeWiki)
 | 
			
		||||
	reqRepoIssueReader := context.RequireRepoReader(models.UnitTypeIssues)
 | 
			
		||||
	reqRepoIssueWriter := context.RequireRepoWriter(models.UnitTypeIssues)
 | 
			
		||||
	reqRepoPullsWriter := context.RequireRepoWriter(models.UnitTypePullRequests)
 | 
			
		||||
	reqRepoPullsReader := context.RequireRepoReader(models.UnitTypePullRequests)
 | 
			
		||||
	reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(models.UnitTypeIssues, models.UnitTypePullRequests)
 | 
			
		||||
	reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(models.UnitTypeIssues, models.UnitTypePullRequests)
 | 
			
		||||
 | 
			
		||||
	reqRepoIssueWriter := func(ctx *context.Context) {
 | 
			
		||||
		if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
 | 
			
		||||
			ctx.Error(403)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ***** START: Organization *****
 | 
			
		||||
	m.Group("/org", func() {
 | 
			
		||||
		m.Group("", func() {
 | 
			
		||||
@@ -590,6 +584,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
					m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
 | 
			
		||||
					m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
 | 
			
		||||
					m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost)
 | 
			
		||||
					m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
 | 
			
		||||
					m.Get("/:id", repo.WebHooksEdit)
 | 
			
		||||
					m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
 | 
			
		||||
					m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost)
 | 
			
		||||
@@ -597,6 +592,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
					m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
 | 
			
		||||
					m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
 | 
			
		||||
					m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
 | 
			
		||||
					m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
 | 
			
		||||
				})
 | 
			
		||||
 | 
			
		||||
				m.Route("/delete", "GET,POST", org.SettingsDelete)
 | 
			
		||||
 
 | 
			
		||||
@@ -1282,7 +1282,7 @@ func ForgotPasswdPost(ctx *context.Context) {
 | 
			
		||||
	ctx.HTML(200, tplForgotPassword)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func commonResetPassword(ctx *context.Context) *models.User {
 | 
			
		||||
func commonResetPassword(ctx *context.Context) (*models.User, *models.TwoFactor) {
 | 
			
		||||
	code := ctx.Query("code")
 | 
			
		||||
 | 
			
		||||
	ctx.Data["Title"] = ctx.Tr("auth.reset_password")
 | 
			
		||||
@@ -1294,14 +1294,25 @@ func commonResetPassword(ctx *context.Context) *models.User {
 | 
			
		||||
 | 
			
		||||
	if len(code) == 0 {
 | 
			
		||||
		ctx.Flash.Error(ctx.Tr("auth.invalid_code"))
 | 
			
		||||
		return nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Fail early, don't frustrate the user
 | 
			
		||||
	u := models.VerifyUserActiveCode(code)
 | 
			
		||||
	if u == nil {
 | 
			
		||||
		ctx.Flash.Error(ctx.Tr("auth.invalid_code"))
 | 
			
		||||
		return nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	twofa, err := models.GetTwoFactorByUID(u.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if !models.IsErrTwoFactorNotEnrolled(err) {
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, "CommonResetPassword", err.Error())
 | 
			
		||||
			return nil, nil
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		ctx.Data["has_two_factor"] = true
 | 
			
		||||
		ctx.Data["scratch_code"] = ctx.QueryBool("scratch_code")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Show the user that they are affecting the account that they intended to
 | 
			
		||||
@@ -1309,10 +1320,10 @@ func commonResetPassword(ctx *context.Context) *models.User {
 | 
			
		||||
 | 
			
		||||
	if nil != ctx.User && u.ID != ctx.User.ID {
 | 
			
		||||
		ctx.Flash.Error(ctx.Tr("auth.reset_password_wrong_user", ctx.User.Email, u.Email))
 | 
			
		||||
		return nil
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return u
 | 
			
		||||
	return u, twofa
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResetPasswd render the account recovery page
 | 
			
		||||
@@ -1320,13 +1331,19 @@ func ResetPasswd(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["IsResetForm"] = true
 | 
			
		||||
 | 
			
		||||
	commonResetPassword(ctx)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.HTML(200, tplResetPassword)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResetPasswdPost response from account recovery request
 | 
			
		||||
func ResetPasswdPost(ctx *context.Context) {
 | 
			
		||||
	u := commonResetPassword(ctx)
 | 
			
		||||
	u, twofa := commonResetPassword(ctx)
 | 
			
		||||
	if ctx.Written() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if u == nil {
 | 
			
		||||
		// Flash error has been set
 | 
			
		||||
@@ -1348,6 +1365,39 @@ func ResetPasswdPost(ctx *context.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle two-factor
 | 
			
		||||
	regenerateScratchToken := false
 | 
			
		||||
	if twofa != nil {
 | 
			
		||||
		if ctx.QueryBool("scratch_code") {
 | 
			
		||||
			if !twofa.VerifyScratchToken(ctx.Query("token")) {
 | 
			
		||||
				ctx.Data["IsResetForm"] = true
 | 
			
		||||
				ctx.Data["Err_Token"] = true
 | 
			
		||||
				ctx.RenderWithErr(ctx.Tr("auth.twofa_scratch_token_incorrect"), tplResetPassword, nil)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			regenerateScratchToken = true
 | 
			
		||||
		} else {
 | 
			
		||||
			passcode := ctx.Query("passcode")
 | 
			
		||||
			ok, err := twofa.ValidateTOTP(passcode)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ctx.Error(http.StatusInternalServerError, "ValidateTOTP", err.Error())
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if !ok || twofa.LastUsedPasscode == passcode {
 | 
			
		||||
				ctx.Data["IsResetForm"] = true
 | 
			
		||||
				ctx.Data["Err_Passcode"] = true
 | 
			
		||||
				ctx.RenderWithErr(ctx.Tr("auth.twofa_passcode_incorrect"), tplResetPassword, nil)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			twofa.LastUsedPasscode = passcode
 | 
			
		||||
			if err = models.UpdateTwoFactor(twofa); err != nil {
 | 
			
		||||
				ctx.ServerError("ResetPasswdPost: UpdateTwoFactor", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	if u.Rands, err = models.GetUserSalt(); err != nil {
 | 
			
		||||
		ctx.ServerError("UpdateUser", err)
 | 
			
		||||
@@ -1357,7 +1407,6 @@ func ResetPasswdPost(ctx *context.Context) {
 | 
			
		||||
		ctx.ServerError("UpdateUser", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u.HashPassword(passwd)
 | 
			
		||||
	u.MustChangePassword = false
 | 
			
		||||
	if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil {
 | 
			
		||||
@@ -1366,9 +1415,27 @@ func ResetPasswdPost(ctx *context.Context) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Trace("User password reset: %s", u.Name)
 | 
			
		||||
 | 
			
		||||
	ctx.Data["IsResetFailed"] = true
 | 
			
		||||
	remember := len(ctx.Query("remember")) != 0
 | 
			
		||||
 | 
			
		||||
	if regenerateScratchToken {
 | 
			
		||||
		// Invalidate the scratch token.
 | 
			
		||||
		_, err = twofa.GenerateScratchToken()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.ServerError("UserSignIn", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if err = models.UpdateTwoFactor(twofa); err != nil {
 | 
			
		||||
			ctx.ServerError("UserSignIn", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		handleSignInFull(ctx, u, remember, false)
 | 
			
		||||
		ctx.Flash.Info(ctx.Tr("auth.twofa_scratch_used"))
 | 
			
		||||
		ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	handleSignInFull(ctx, u, remember, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ package user
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
	"code.gitea.io/gitea/modules/context"
 | 
			
		||||
@@ -23,14 +24,19 @@ func Avatar(ctx *context.Context) {
 | 
			
		||||
 | 
			
		||||
	log.Debug("Asked avatar for user %v and size %v", userName, size)
 | 
			
		||||
 | 
			
		||||
	user, err := models.GetUserByName(userName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if models.IsErrUserNotExist(err) {
 | 
			
		||||
			ctx.ServerError("Requested avatar for invalid user", err)
 | 
			
		||||
		} else {
 | 
			
		||||
			ctx.ServerError("Retrieving user by name", err)
 | 
			
		||||
	var user *models.User
 | 
			
		||||
	if strings.ToLower(userName) != "ghost" {
 | 
			
		||||
		user, err = models.GetUserByName(userName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if models.IsErrUserNotExist(err) {
 | 
			
		||||
				ctx.ServerError("Requested avatar for invalid user", err)
 | 
			
		||||
			} else {
 | 
			
		||||
				ctx.ServerError("Retrieving user by name", err)
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	} else {
 | 
			
		||||
		user = models.NewGhostUser()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.Redirect(user.RealSizedAvatarLink(size))
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,9 @@ func retrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) {
 | 
			
		||||
		if act.ActUser != nil {
 | 
			
		||||
			userCache[act.ActUserID] = act.ActUser
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, act := range actions {
 | 
			
		||||
		repoOwner, ok := userCache[act.Repo.OwnerID]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			repoOwner, err = models.GetUserByID(act.Repo.OwnerID)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import (
 | 
			
		||||
	"code.gitea.io/gitea/modules/git"
 | 
			
		||||
	"code.gitea.io/gitea/modules/log"
 | 
			
		||||
	"code.gitea.io/gitea/modules/process"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	api "code.gitea.io/gitea/modules/structs"
 | 
			
		||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
			
		||||
)
 | 
			
		||||
@@ -37,6 +38,54 @@ func createTag(gitRepo *git.Repository, rel *models.Release) error {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			rel.LowerTagName = strings.ToLower(rel.TagName)
 | 
			
		||||
			// Prepare Notify
 | 
			
		||||
			if err := rel.LoadAttributes(); err != nil {
 | 
			
		||||
				log.Error("LoadAttributes: %v", err)
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			refName := git.TagPrefix + rel.TagName
 | 
			
		||||
			apiPusher := rel.Publisher.APIFormat()
 | 
			
		||||
			apiRepo := rel.Repo.APIFormat(models.AccessModeNone)
 | 
			
		||||
			apiCommits, err := models.NewPushCommits().ToAPIPayloadCommits(rel.Repo.RepoPath(), rel.Repo.HTMLURL())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error("commits.ToAPIPayloadCommits failed: %v", err)
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := models.PrepareWebhooks(rel.Repo, models.HookEventPush, &api.PushPayload{
 | 
			
		||||
				Ref:        refName,
 | 
			
		||||
				Before:     git.EmptySHA,
 | 
			
		||||
				After:      commit.ID.String(),
 | 
			
		||||
				CompareURL: setting.AppURL + models.NewPushCommits().CompareURL,
 | 
			
		||||
				Commits:    apiCommits,
 | 
			
		||||
				Repo:       apiRepo,
 | 
			
		||||
				Pusher:     apiPusher,
 | 
			
		||||
				Sender:     apiPusher,
 | 
			
		||||
			}); err != nil {
 | 
			
		||||
				log.Error("PrepareWebhooks: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			gitRepo, err := git.OpenRepository(rel.Repo.RepoPath())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Error("OpenRepository[%s]: %v", rel.Repo.RepoPath(), err)
 | 
			
		||||
			}
 | 
			
		||||
			shaSum, err := gitRepo.GetTagCommitID(refName)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				gitRepo.Close()
 | 
			
		||||
				log.Error("GetTagCommitID[%s]: %v", refName, err)
 | 
			
		||||
			}
 | 
			
		||||
			gitRepo.Close()
 | 
			
		||||
 | 
			
		||||
			if err = models.PrepareWebhooks(rel.Repo, models.HookEventCreate, &api.CreatePayload{
 | 
			
		||||
				Ref:     git.RefEndName(refName),
 | 
			
		||||
				Sha:     shaSum,
 | 
			
		||||
				RefType: "tag",
 | 
			
		||||
				Repo:    apiRepo,
 | 
			
		||||
				Sender:  apiPusher,
 | 
			
		||||
			}); err != nil {
 | 
			
		||||
				return fmt.Errorf("PrepareWebhooks: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		commit, err := gitRepo.GetTagCommit(rel.TagName)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -112,6 +112,12 @@
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					{{end}}
 | 
			
		||||
					<div class="inline field">
 | 
			
		||||
						<div class="ui checkbox">
 | 
			
		||||
							<label for="allow_deactivate_all"><strong>{{.i18n.Tr "admin.auths.allow_deactivate_all"}}</strong></label>
 | 
			
		||||
							<input id="allow_deactivate_all" name="allow_deactivate_all" type="checkbox" {{if $cfg.AllowDeactivateAll}}checked{{end}}>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				{{end}}
 | 
			
		||||
 | 
			
		||||
				<!-- SMTP -->
 | 
			
		||||
 
 | 
			
		||||
@@ -73,21 +73,27 @@
 | 
			
		||||
										</div>
 | 
			
		||||
										{{end}}
 | 
			
		||||
									</td>
 | 
			
		||||
									<td class="two wide right aligned">
 | 
			
		||||
									<td class="three wide right aligned">
 | 
			
		||||
										{{if not .LatestPullRequest}}
 | 
			
		||||
											{{if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
 | 
			
		||||
											<a href="{{$.RepoLink}}/compare/{{$.DefaultBranch | EscapePound}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | EscapePound}}">
 | 
			
		||||
												<button id="new-pull-request" class="ui compact basic button">{{$.i18n.Tr "repo.pulls.compare_changes"}}</button>
 | 
			
		||||
											</a>
 | 
			
		||||
											{{end}}
 | 
			
		||||
										{{else if and .LatestPullRequest.HasMerged .MergeMovedOn}}
 | 
			
		||||
											{{if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
 | 
			
		||||
											<a href="{{$.RepoLink}}/compare/{{$.DefaultBranch | EscapePound}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | EscapePound}}">
 | 
			
		||||
												<button id="new-pull-request" class="ui compact basic button">{{$.i18n.Tr "repo.pulls.compare_changes"}}</button>
 | 
			
		||||
											</a>
 | 
			
		||||
											{{end}}
 | 
			
		||||
										{{else}}
 | 
			
		||||
											<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}">#{{.LatestPullRequest.Issue.Index}}</a>
 | 
			
		||||
											<a href="{{.LatestPullRequest.Issue.HTMLURL}}">{{if ne .LatestPullRequest.BaseRepoID .LatestPullRequest.HeadRepoID}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
 | 
			
		||||
											{{if .LatestPullRequest.HasMerged}}
 | 
			
		||||
												<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui purple small label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a>
 | 
			
		||||
												<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui purple mini label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a>
 | 
			
		||||
											{{else if .LatestPullRequest.Issue.IsClosed}}
 | 
			
		||||
												<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui red small label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a>
 | 
			
		||||
												<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui red mini label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a>
 | 
			
		||||
											{{else}}
 | 
			
		||||
												<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui green small label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a>
 | 
			
		||||
												<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui green mini label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a>
 | 
			
		||||
											{{end}}
 | 
			
		||||
										{{end}}
 | 
			
		||||
									</td>
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,8 @@
 | 
			
		||||
			<span class="markdown-info"><i class="octicon octicon-markdown"></i> {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span>
 | 
			
		||||
			<div class="ui right floated">
 | 
			
		||||
				{{if $.reply}}
 | 
			
		||||
					<button name="reply" value="{{$.reply}}" class="ui submit green tiny button btn-reply">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button>
 | 
			
		||||
					<input type="hidden" name="reply" value="{{$.reply}}">
 | 
			
		||||
					<button class="ui submit green tiny button btn-reply" onclick="submitReply(this);">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button>
 | 
			
		||||
				{{else}}
 | 
			
		||||
					{{if $.root.CurrentReview}}
 | 
			
		||||
						<button name="is_review" value="true" type="submit"
 | 
			
		||||
 
 | 
			
		||||
@@ -154,7 +154,7 @@
 | 
			
		||||
						<div class="menu">
 | 
			
		||||
							{{range .Labels}}
 | 
			
		||||
								<div class="item issue-action has-emoji" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
 | 
			
		||||
									<span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
 | 
			
		||||
									<span class="octicon {{if contain $.SelLabelIDs .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
 | 
			
		||||
								</div>
 | 
			
		||||
							{{end}}
 | 
			
		||||
						</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
						<div class="menu">
 | 
			
		||||
							<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a>
 | 
			
		||||
							{{range .Labels}}
 | 
			
		||||
								<a class="item has-emoji" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.ID}}&assignee={{$.AssigneeID}}"><span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
 | 
			
		||||
								<a class="item has-emoji" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.ID}}&assignee={{$.AssigneeID}}"><span class="octicon {{if contain $.SelLabelIDs .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
 | 
			
		||||
							{{end}}
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
@@ -148,7 +148,7 @@
 | 
			
		||||
						<div class="menu">
 | 
			
		||||
							{{range .Labels}}
 | 
			
		||||
								<div class="item issue-action has-emoji" data-action="toggle" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/labels">
 | 
			
		||||
									<span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
 | 
			
		||||
									<span class="octicon {{if contain $.SelLabelIDs .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}
 | 
			
		||||
								</div>
 | 
			
		||||
							{{end}}
 | 
			
		||||
						</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,7 @@
 | 
			
		||||
				{{ template "repo/issue/view_content/pull". }}
 | 
			
		||||
			{{end}}
 | 
			
		||||
			{{if .IsSigned}}
 | 
			
		||||
				{{ if and (or .IsRepoAdmin .IsRepoIssuesWriter (or (not .Issue.IsLocked))) (not .Repository.IsArchived) }}
 | 
			
		||||
				{{ if and (or .IsRepoAdmin .IsIssuesWriter (or (not .Issue.IsLocked))) (not .Repository.IsArchived) }}
 | 
			
		||||
				<div class="comment form">
 | 
			
		||||
					<a class="avatar" href="{{.SignedUser.HomeLink}}">
 | 
			
		||||
						<img src="{{.SignedUser.RelAvatarLink}}">
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,7 @@
 | 
			
		||||
						{{$.i18n.Tr "repo.pulls.reopen_to_merge"}}
 | 
			
		||||
					{{end}}
 | 
			
		||||
				</div>
 | 
			
		||||
				{{if .IsPullBranchDeletable}}
 | 
			
		||||
				{{if and .IsPullBranchDeletable ( not .IsPullRequestBroken )}}
 | 
			
		||||
					<div class="ui divider"></div>
 | 
			
		||||
					<div>
 | 
			
		||||
						<a class="delete-button ui red button" href="" data-url="{{.DeleteBranchLink}}">{{$.i18n.Tr "repo.branch.delete" .HeadTarget}}</a>
 | 
			
		||||
 
 | 
			
		||||
@@ -426,6 +426,7 @@
 | 
			
		||||
	<input type="hidden" id="repolink" value="{{$.RepoRelPath}}">
 | 
			
		||||
	<!-- I know, there is probably a better way to do this -->
 | 
			
		||||
	<input type="hidden" id="issueIndex" value="{{.Issue.Index}}"/>
 | 
			
		||||
	<input type="hidden" id="type" value="{{.IssueType}}">
 | 
			
		||||
 | 
			
		||||
	<div class="ui basic modal remove-dependency">
 | 
			
		||||
		<div class="ui icon header">
 | 
			
		||||
 
 | 
			
		||||
@@ -2777,6 +2777,12 @@
 | 
			
		||||
            "description": "search string",
 | 
			
		||||
            "name": "q",
 | 
			
		||||
            "in": "query"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "type": "string",
 | 
			
		||||
            "description": "filter by type (issues / pulls) if set",
 | 
			
		||||
            "name": "type",
 | 
			
		||||
            "in": "query"
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
@@ -5284,8 +5290,8 @@
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "responses": {
 | 
			
		||||
          "200": {
 | 
			
		||||
            "$ref": "#/responses/StatusList"
 | 
			
		||||
          "201": {
 | 
			
		||||
            "$ref": "#/responses/Status"
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
					{{end}}
 | 
			
		||||
					{{if .IsResetForm}}
 | 
			
		||||
						<div class="required inline field {{if .Err_Password}}error{{end}}">
 | 
			
		||||
							<label for="password">{{.i18n.Tr "password"}}</label>
 | 
			
		||||
							<label for="password">{{.i18n.Tr "settings.new_password"}}</label>
 | 
			
		||||
							<input id="password" name="password" type="password"  value="{{.password}}" autocomplete="off" autofocus required>
 | 
			
		||||
						</div>
 | 
			
		||||
						{{if not .user_signed_in}}
 | 
			
		||||
@@ -30,10 +30,31 @@
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
						{{end}}
 | 
			
		||||
						{{if .has_two_factor}}
 | 
			
		||||
						<h4 class="ui dividing header">
 | 
			
		||||
							{{.i18n.Tr "twofa"}}
 | 
			
		||||
						</h4>
 | 
			
		||||
						<div class="ui warning visible message">{{.i18n.Tr "settings.twofa_is_enrolled" | Str2html }}</div>
 | 
			
		||||
						{{if .scratch_code}}
 | 
			
		||||
						<div class="required inline field {{if .Err_Token}}error{{end}}">
 | 
			
		||||
							<label for="token">{{.i18n.Tr "auth.scratch_code"}}</label>
 | 
			
		||||
							<input id="token" name="token" type="text" autocomplete="off" autofocus required>
 | 
			
		||||
						</div>
 | 
			
		||||
						<input type="hidden" name="scratch_code" value="true">
 | 
			
		||||
						{{else}}
 | 
			
		||||
						<div class="required inline field {{if .Err_Passcode}}error{{end}}">
 | 
			
		||||
							<label for="passcode">{{.i18n.Tr "passcode"}}</label>
 | 
			
		||||
							<input id="passcode" name="passcode" type="number" autocomplete="off" autofocus required>
 | 
			
		||||
						</div>
 | 
			
		||||
						{{end}}
 | 
			
		||||
						{{end}}
 | 
			
		||||
						<div class="ui divider"></div>
 | 
			
		||||
						<div class="inline field">
 | 
			
		||||
							<label></label>
 | 
			
		||||
							<button class="ui blue button">{{.i18n.Tr "auth.reset_password_helper"}}</button>
 | 
			
		||||
							{{if and .has_two_factor (not .scratch_code)}}
 | 
			
		||||
								<a href="{{.Link}}?code={{.Code}}&scratch_code=true">{{.i18n.Tr "auth.use_scratch_code" | Str2html}}</a>
 | 
			
		||||
							{{end}}
 | 
			
		||||
						</div>
 | 
			
		||||
					{{else}}
 | 
			
		||||
						<p class="center">{{.i18n.Tr "auth.invalid_code"}}</p>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user