Multiple Escaping Improvements (#17551)
There are multiple places where Gitea does not properly escape URLs that it is building and there are multiple places where it builds urls when there is already a simpler function available to use this.
    
This is an extensive PR attempting to fix these issues.
1. The first commit in this PR looks through all href, src and links in the Gitea codebase and has attempted to catch all the places where there is potentially incomplete escaping.
2. Whilst doing this we will prefer to use functions that create URLs over recreating them by hand.
3. All uses of strings should be directly escaped - even if they are not currently expected to contain escaping characters. The main benefit to doing this will be that we can consider relaxing the constraints on user names and reponames in future. 
4. The next commit looks at escaping in the wiki and re-considers the urls that are used there. Using the improved escaping here wiki files containing '/'. (This implementation will currently still place all of the wiki files the root directory of the repo but this would not be difficult to change.)
5. The title generation in feeds is now properly escaped.
6. EscapePound is no longer needed - urls should be PathEscaped / QueryEscaped as necessary but then re-escaped with Escape when creating html with locales Signed-off-by: Andrew Thornton <art27@cantab.net>
Signed-off-by: Andrew Thornton <art27@cantab.net>
			
			
This commit is contained in:
		
							parent
							
								
									7e1ae38097
								
							
						
					
					
						commit
						bbffcc3aec
					
				
					 153 changed files with 891 additions and 712 deletions
				
			
		| 
						 | 
					@ -65,7 +65,7 @@ func TestViewIssuesSortByType(t *testing.T) {
 | 
				
			||||||
	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
						repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, user.Name)
 | 
						session := loginUser(t, user.Name)
 | 
				
			||||||
	req := NewRequest(t, "GET", repo.RelLink()+"/issues?type=created_by")
 | 
						req := NewRequest(t, "GET", repo.Link()+"/issues?type=created_by")
 | 
				
			||||||
	resp := session.MakeRequest(t, req, http.StatusOK)
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	htmlDoc := NewHTMLParser(t, resp.Body)
 | 
						htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
| 
						 | 
					@ -97,7 +97,7 @@ func TestViewIssuesKeyword(t *testing.T) {
 | 
				
			||||||
	issues.UpdateIssueIndexer(issue)
 | 
						issues.UpdateIssueIndexer(issue)
 | 
				
			||||||
	time.Sleep(time.Second * 1)
 | 
						time.Sleep(time.Second * 1)
 | 
				
			||||||
	const keyword = "first"
 | 
						const keyword = "first"
 | 
				
			||||||
	req := NewRequestf(t, "GET", "%s/issues?q=%s", repo.RelLink(), keyword)
 | 
						req := NewRequestf(t, "GET", "%s/issues?q=%s", repo.Link(), keyword)
 | 
				
			||||||
	resp := MakeRequest(t, req, http.StatusOK)
 | 
						resp := MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	htmlDoc := NewHTMLParser(t, resp.Body)
 | 
						htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -156,7 +156,7 @@ func testLinksAsUser(userName string, t *testing.T) {
 | 
				
			||||||
		"/releases",
 | 
							"/releases",
 | 
				
			||||||
		"/releases/new",
 | 
							"/releases/new",
 | 
				
			||||||
		//"/wiki/_pages",
 | 
							//"/wiki/_pages",
 | 
				
			||||||
		"/wiki/_new",
 | 
							"/wiki/?action=_new",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, repo := range apiRepos {
 | 
						for _, repo := range apiRepos {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,17 +63,17 @@ func TestNonasciiBranches(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "ГлавнаяВетка",
 | 
								from:   "ГлавнаяВетка",
 | 
				
			||||||
			to:     "branch/%d0%93%d0%bb%d0%b0%d0%b2%d0%bd%d0%b0%d1%8f%d0%92%d0%b5%d1%82%d0%ba%d0%b0",
 | 
								to:     "branch/%D0%93%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F%D0%92%D0%B5%D1%82%D0%BA%D0%B0",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "а/б/в",
 | 
								from:   "а/б/в",
 | 
				
			||||||
			to:     "branch/%d0%b0/%d0%b1/%d0%b2",
 | 
								to:     "branch/%D0%B0/%D0%B1/%D0%B2",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "Grüßen/README.md",
 | 
								from:   "Grüßen/README.md",
 | 
				
			||||||
			to:     "branch/Gr%c3%bc%c3%9fen/README.md",
 | 
								to:     "branch/Gr%C3%BC%C3%9Fen/README.md",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -83,7 +83,7 @@ func TestNonasciiBranches(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "Plus+Is+Not+Space/Файл.md",
 | 
								from:   "Plus+Is+Not+Space/Файл.md",
 | 
				
			||||||
			to:     "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
 | 
								to:     "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -93,28 +93,28 @@ func TestNonasciiBranches(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "ブランチ",
 | 
								from:   "ブランチ",
 | 
				
			||||||
			to:     "branch/%e3%83%96%e3%83%a9%e3%83%b3%e3%83%81",
 | 
								to:     "branch/%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		// Tags
 | 
							// Tags
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "Тэг",
 | 
								from:   "Тэг",
 | 
				
			||||||
			to:     "tag/%d0%a2%d1%8d%d0%b3",
 | 
								to:     "tag/%D0%A2%D1%8D%D0%B3",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "Ё/人",
 | 
								from:   "Ё/人",
 | 
				
			||||||
			to:     "tag/%d0%81/%e4%ba%ba",
 | 
								to:     "tag/%D0%81/%E4%BA%BA",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "タグ",
 | 
								from:   "タグ",
 | 
				
			||||||
			to:     "tag/%e3%82%bf%e3%82%b0",
 | 
								to:     "tag/%E3%82%BF%E3%82%B0",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "タグ/ファイル.md",
 | 
								from:   "タグ/ファイル.md",
 | 
				
			||||||
			to:     "tag/%e3%82%bf%e3%82%b0/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
 | 
								to:     "tag/%E3%82%BF%E3%82%B0/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		// Files
 | 
							// Files
 | 
				
			||||||
| 
						 | 
					@ -125,38 +125,38 @@ func TestNonasciiBranches(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "Файл.md",
 | 
								from:   "Файл.md",
 | 
				
			||||||
			to:     "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
 | 
								to:     "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "ファイル.md",
 | 
								from:   "ファイル.md",
 | 
				
			||||||
			to:     "branch/Plus+Is+Not+Space/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
 | 
								to:     "branch/Plus+Is+Not+Space/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
 | 
				
			||||||
			status: http.StatusNotFound, // it's not on default branch
 | 
								status: http.StatusNotFound, // it's not on default branch
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		// Same but url-encoded (few tests)
 | 
							// Same but url-encoded (few tests)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81",
 | 
								from:   "%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81",
 | 
				
			||||||
			to:     "branch/%e3%83%96%e3%83%a9%e3%83%b3%e3%83%81",
 | 
								to:     "branch/%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "%E3%82%BF%E3%82%b0",
 | 
								from:   "%E3%82%BF%E3%82%b0",
 | 
				
			||||||
			to:     "tag/%e3%82%bf%e3%82%b0",
 | 
								to:     "tag/%E3%82%BF%E3%82%B0",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "%D0%A4%D0%B0%D0%B9%D0%BB.md",
 | 
								from:   "%D0%A4%D0%B0%D0%B9%D0%BB.md",
 | 
				
			||||||
			to:     "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
 | 
								to:     "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "%D0%81%2F%E4%BA%BA",
 | 
								from:   "%D0%81%2F%E4%BA%BA",
 | 
				
			||||||
			to:     "tag/%d0%81/%e4%ba%ba",
 | 
								to:     "tag/%D0%81/%E4%BA%BA",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			from:   "Ё%2F%E4%BA%BA",
 | 
								from:   "Ё%2F%E4%BA%BA",
 | 
				
			||||||
			to:     "tag/%d0%81/%e4%ba%ba",
 | 
								to:     "tag/%D0%81/%E4%BA%BA",
 | 
				
			||||||
			status: http.StatusOK,
 | 
								status: http.StatusOK,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
| 
						 | 
					@ -185,10 +186,8 @@ func (a *Action) ShortRepoPath() string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetRepoLink returns relative link to action repository.
 | 
					// GetRepoLink returns relative link to action repository.
 | 
				
			||||||
func (a *Action) GetRepoLink() string {
 | 
					func (a *Action) GetRepoLink() string {
 | 
				
			||||||
	if len(setting.AppSubURL) > 0 {
 | 
						// path.Join will skip empty strings
 | 
				
			||||||
		return path.Join(setting.AppSubURL, a.GetRepoPath())
 | 
						return path.Join(setting.AppSubURL, "/", url.PathEscape(a.GetRepoUserName()), url.PathEscape(a.GetRepoName()))
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return "/" + a.GetRepoPath()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetRepositoryFromMatch returns a *Repository from a username and repo strings
 | 
					// GetRepositoryFromMatch returns a *Repository from a username and repo strings
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package models
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
| 
						 | 
					@ -59,7 +60,7 @@ func (a *Attachment) RelativePath() string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DownloadURL returns the download url of the attached file
 | 
					// DownloadURL returns the download url of the attached file
 | 
				
			||||||
func (a *Attachment) DownloadURL() string {
 | 
					func (a *Attachment) DownloadURL() string {
 | 
				
			||||||
	return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID)
 | 
						return setting.AppURL + "attachments/" + url.PathEscape(a.UUID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LinkedRepository returns the linked repo if any
 | 
					// LinkedRepository returns the linked repo if any
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,15 +112,15 @@ func GenerateUserAvatarFastLink(userName string, size int) string {
 | 
				
			||||||
	if size < 0 {
 | 
						if size < 0 {
 | 
				
			||||||
		size = 0
 | 
							size = 0
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return setting.AppSubURL + "/user/avatar/" + userName + "/" + strconv.Itoa(size)
 | 
						return setting.AppSubURL + "/user/avatar/" + url.PathEscape(userName) + "/" + strconv.Itoa(size)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateUserAvatarImageLink returns a link for `User.Avatar` image file: "/avatars/${User.Avatar}"
 | 
					// GenerateUserAvatarImageLink returns a link for `User.Avatar` image file: "/avatars/${User.Avatar}"
 | 
				
			||||||
func GenerateUserAvatarImageLink(userAvatar string, size int) string {
 | 
					func GenerateUserAvatarImageLink(userAvatar string, size int) string {
 | 
				
			||||||
	if size > 0 {
 | 
						if size > 0 {
 | 
				
			||||||
		return setting.AppSubURL + "/avatars/" + userAvatar + "?size=" + strconv.Itoa(size)
 | 
							return setting.AppSubURL + "/avatars/" + url.PathEscape(userAvatar) + "?size=" + strconv.Itoa(size)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return setting.AppSubURL + "/avatars/" + userAvatar
 | 
						return setting.AppSubURL + "/avatars/" + url.PathEscape(userAvatar)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// generateRecognizedAvatarURL generate a recognized avatar (Gravatar/Libravatar) URL, it modifies the URL so the parameter is passed by a copy
 | 
					// generateRecognizedAvatarURL generate a recognized avatar (Gravatar/Libravatar) URL, it modifies the URL so the parameter is passed by a copy
 | 
				
			||||||
| 
						 | 
					@ -155,7 +155,7 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
 | 
				
			||||||
			return generateRecognizedAvatarURL(*avatarURL, size)
 | 
								return generateRecognizedAvatarURL(*avatarURL, size)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// for non-final link, we should return fast (use a 302 redirection link)
 | 
							// for non-final link, we should return fast (use a 302 redirection link)
 | 
				
			||||||
		urlStr := setting.AppSubURL + "/avatar/" + emailHash
 | 
							urlStr := setting.AppSubURL + "/avatar/" + url.PathEscape(emailHash)
 | 
				
			||||||
		if size > 0 {
 | 
							if size > 0 {
 | 
				
			||||||
			urlStr += "?size=" + strconv.Itoa(size)
 | 
								urlStr += "?size=" + strconv.Itoa(size)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package models
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/sha1"
 | 
						"crypto/sha1"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -137,8 +138,7 @@ func (status *CommitStatus) loadAttributes(e db.Engine) (err error) {
 | 
				
			||||||
// APIURL returns the absolute APIURL to this commit-status.
 | 
					// APIURL returns the absolute APIURL to this commit-status.
 | 
				
			||||||
func (status *CommitStatus) APIURL() string {
 | 
					func (status *CommitStatus) APIURL() string {
 | 
				
			||||||
	_ = status.loadAttributes(db.GetEngine(db.DefaultContext))
 | 
						_ = status.loadAttributes(db.GetEngine(db.DefaultContext))
 | 
				
			||||||
	return fmt.Sprintf("%sapi/v1/repos/%s/statuses/%s",
 | 
						return status.Repo.APIURL() + "/statuses/" + url.PathEscape(status.SHA)
 | 
				
			||||||
		setting.AppURL, status.Repo.FullName(), status.SHA)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
 | 
					// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -372,6 +372,17 @@ func (issue *Issue) HTMLURL() string {
 | 
				
			||||||
	return fmt.Sprintf("%s/%s/%d", issue.Repo.HTMLURL(), path, issue.Index)
 | 
						return fmt.Sprintf("%s/%s/%d", issue.Repo.HTMLURL(), path, issue.Index)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Link returns the Link URL to this issue.
 | 
				
			||||||
 | 
					func (issue *Issue) Link() string {
 | 
				
			||||||
 | 
						var path string
 | 
				
			||||||
 | 
						if issue.IsPull {
 | 
				
			||||||
 | 
							path = "pulls"
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							path = "issues"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Sprintf("%s/%s/%d", issue.Repo.Link(), path, issue.Index)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DiffURL returns the absolute URL to this diff
 | 
					// DiffURL returns the absolute URL to this diff
 | 
				
			||||||
func (issue *Issue) DiffURL() string {
 | 
					func (issue *Issue) DiffURL() string {
 | 
				
			||||||
	if issue.IsPull {
 | 
						if issue.IsPull {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
| 
						 | 
					@ -475,7 +476,7 @@ func (n *Notification) HTMLURL() string {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return n.Issue.HTMLURL()
 | 
							return n.Issue.HTMLURL()
 | 
				
			||||||
	case NotificationSourceCommit:
 | 
						case NotificationSourceCommit:
 | 
				
			||||||
		return n.Repository.HTMLURL() + "/commit/" + n.CommitID
 | 
							return n.Repository.HTMLURL() + "/commit/" + url.PathEscape(n.CommitID)
 | 
				
			||||||
	case NotificationSourceRepository:
 | 
						case NotificationSourceRepository:
 | 
				
			||||||
		return n.Repository.HTMLURL()
 | 
							return n.Repository.HTMLURL()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,10 +10,10 @@ import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/structs"
 | 
						"code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
| 
						 | 
					@ -78,23 +78,22 @@ func (r *Release) LoadAttributes() error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// APIURL the api url for a release. release must have attributes loaded
 | 
					// APIURL the api url for a release. release must have attributes loaded
 | 
				
			||||||
func (r *Release) APIURL() string {
 | 
					func (r *Release) APIURL() string {
 | 
				
			||||||
	return fmt.Sprintf("%sapi/v1/repos/%s/releases/%d",
 | 
						return r.Repo.APIURL() + "/releases/" + strconv.FormatInt(r.ID, 10)
 | 
				
			||||||
		setting.AppURL, r.Repo.FullName(), r.ID)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ZipURL the zip url for a release. release must have attributes loaded
 | 
					// ZipURL the zip url for a release. release must have attributes loaded
 | 
				
			||||||
func (r *Release) ZipURL() string {
 | 
					func (r *Release) ZipURL() string {
 | 
				
			||||||
	return fmt.Sprintf("%s/archive/%s.zip", r.Repo.HTMLURL(), r.TagName)
 | 
						return r.Repo.HTMLURL() + "/archive/" + util.PathEscapeSegments(r.TagName) + ".zip"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TarURL the tar.gz url for a release. release must have attributes loaded
 | 
					// TarURL the tar.gz url for a release. release must have attributes loaded
 | 
				
			||||||
func (r *Release) TarURL() string {
 | 
					func (r *Release) TarURL() string {
 | 
				
			||||||
	return fmt.Sprintf("%s/archive/%s.tar.gz", r.Repo.HTMLURL(), r.TagName)
 | 
						return r.Repo.HTMLURL() + "/archive/" + util.PathEscapeSegments(r.TagName) + ".tar.gz"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HTMLURL the url for a release on the web UI. release must have attributes loaded
 | 
					// HTMLURL the url for a release on the web UI. release must have attributes loaded
 | 
				
			||||||
func (r *Release) HTMLURL() string {
 | 
					func (r *Release) HTMLURL() string {
 | 
				
			||||||
	return fmt.Sprintf("%s/releases/tag/%s", r.Repo.HTMLURL(), r.TagName)
 | 
						return r.Repo.HTMLURL() + "/releases/tag/" + util.PathEscapeSegments(r.TagName)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsReleaseExist returns true if release with given tag name already exists.
 | 
					// IsReleaseExist returns true if release with given tag name already exists.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -314,7 +314,7 @@ func (repo *Repository) FullName() string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HTMLURL returns the repository HTML URL
 | 
					// HTMLURL returns the repository HTML URL
 | 
				
			||||||
func (repo *Repository) HTMLURL() string {
 | 
					func (repo *Repository) HTMLURL() string {
 | 
				
			||||||
	return setting.AppURL + repo.FullName()
 | 
						return setting.AppURL + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommitLink make link to by commit full ID
 | 
					// CommitLink make link to by commit full ID
 | 
				
			||||||
| 
						 | 
					@ -323,14 +323,14 @@ func (repo *Repository) CommitLink(commitID string) (result string) {
 | 
				
			||||||
	if commitID == "" || commitID == "0000000000000000000000000000000000000000" {
 | 
						if commitID == "" || commitID == "0000000000000000000000000000000000000000" {
 | 
				
			||||||
		result = ""
 | 
							result = ""
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		result = repo.HTMLURL() + "/commit/" + commitID
 | 
							result = repo.HTMLURL() + "/commit/" + url.PathEscape(commitID)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// APIURL returns the repository API URL
 | 
					// APIURL returns the repository API URL
 | 
				
			||||||
func (repo *Repository) APIURL() string {
 | 
					func (repo *Repository) APIURL() string {
 | 
				
			||||||
	return setting.AppURL + "api/v1/repos/" + repo.FullName()
 | 
						return setting.AppURL + "api/v1/repos/" + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCommitsCountCacheKey returns cache key used for commits count caching.
 | 
					// GetCommitsCountCacheKey returns cache key used for commits count caching.
 | 
				
			||||||
| 
						 | 
					@ -709,19 +709,14 @@ func (repo *Repository) GitConfigPath() string {
 | 
				
			||||||
	return GitConfigPath(repo.RepoPath())
 | 
						return GitConfigPath(repo.RepoPath())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RelLink returns the repository relative link
 | 
					 | 
				
			||||||
func (repo *Repository) RelLink() string {
 | 
					 | 
				
			||||||
	return "/" + repo.FullName()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Link returns the repository link
 | 
					// Link returns the repository link
 | 
				
			||||||
func (repo *Repository) Link() string {
 | 
					func (repo *Repository) Link() string {
 | 
				
			||||||
	return setting.AppSubURL + "/" + repo.FullName()
 | 
						return setting.AppSubURL + "/" + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ComposeCompareURL returns the repository comparison URL
 | 
					// ComposeCompareURL returns the repository comparison URL
 | 
				
			||||||
func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
 | 
					func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
 | 
				
			||||||
	return fmt.Sprintf("%s/compare/%s...%s", repo.FullName(), oldCommitID, newCommitID)
 | 
						return fmt.Sprintf("%s/%s/compare/%s...%s", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name), util.PathEscapeSegments(oldCommitID), util.PathEscapeSegments(newCommitID))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateDefaultBranch updates the default branch
 | 
					// UpdateDefaultBranch updates the default branch
 | 
				
			||||||
| 
						 | 
					@ -930,11 +925,11 @@ func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if setting.SSH.Port != 22 {
 | 
						if setting.SSH.Port != 22 {
 | 
				
			||||||
		cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, net.JoinHostPort(setting.SSH.Domain, strconv.Itoa(setting.SSH.Port)), repo.OwnerName, repoName)
 | 
							cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, net.JoinHostPort(setting.SSH.Domain, strconv.Itoa(setting.SSH.Port)), url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
 | 
				
			||||||
	} else if setting.Repository.UseCompatSSHURI {
 | 
						} else if setting.Repository.UseCompatSSHURI {
 | 
				
			||||||
		cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshDomain, repo.OwnerName, repoName)
 | 
							cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshDomain, url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshDomain, repo.OwnerName, repoName)
 | 
							cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshDomain, url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cl.HTTPS = ComposeHTTPSCloneURL(repo.OwnerName, repoName)
 | 
						cl.HTTPS = ComposeHTTPSCloneURL(repo.OwnerName, repoName)
 | 
				
			||||||
	return cl
 | 
						return cl
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"image/png"
 | 
						"image/png"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -96,7 +97,7 @@ func (repo *Repository) relAvatarLink(e db.Engine) string {
 | 
				
			||||||
			return ""
 | 
								return ""
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return setting.AppSubURL + "/repo-avatars/" + repo.Avatar
 | 
						return setting.AppSubURL + "/repo-avatars/" + url.PathEscape(repo.Avatar)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AvatarLink returns a link to the repository's avatar.
 | 
					// AvatarLink returns a link to the repository's avatar.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@ import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	_ "image/jpeg" // Needed for jpeg support
 | 
						_ "image/jpeg" // Needed for jpeg support
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
| 
						 | 
					@ -315,17 +316,17 @@ func (u *User) DashboardLink() string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HomeLink returns the user or organization home page link.
 | 
					// HomeLink returns the user or organization home page link.
 | 
				
			||||||
func (u *User) HomeLink() string {
 | 
					func (u *User) HomeLink() string {
 | 
				
			||||||
	return setting.AppSubURL + "/" + u.Name
 | 
						return setting.AppSubURL + "/" + url.PathEscape(u.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HTMLURL returns the user or organization's full link.
 | 
					// HTMLURL returns the user or organization's full link.
 | 
				
			||||||
func (u *User) HTMLURL() string {
 | 
					func (u *User) HTMLURL() string {
 | 
				
			||||||
	return setting.AppURL + u.Name
 | 
						return setting.AppURL + url.PathEscape(u.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// OrganisationLink returns the organization sub page link.
 | 
					// OrganisationLink returns the organization sub page link.
 | 
				
			||||||
func (u *User) OrganisationLink() string {
 | 
					func (u *User) OrganisationLink() string {
 | 
				
			||||||
	return setting.AppSubURL + "/org/" + u.Name
 | 
						return setting.AppSubURL + "/org/" + url.PathEscape(u.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
 | 
					// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,16 @@ type Context struct {
 | 
				
			||||||
	Org  *Organization
 | 
						Org  *Organization
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TrHTMLEscapeArgs runs Tr but pre-escapes all arguments with html.EscapeString.
 | 
				
			||||||
 | 
					// This is useful if the locale message is intended to only produce HTML content.
 | 
				
			||||||
 | 
					func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
 | 
				
			||||||
 | 
						trArgs := make([]interface{}, len(args))
 | 
				
			||||||
 | 
						for i, arg := range args {
 | 
				
			||||||
 | 
							trArgs[i] = html.EscapeString(arg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ctx.Tr(msg, trArgs...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetData returns the data
 | 
					// GetData returns the data
 | 
				
			||||||
func (ctx *Context) GetData() map[string]interface{} {
 | 
					func (ctx *Context) GetData() map[string]interface{} {
 | 
				
			||||||
	return ctx.Data
 | 
						return ctx.Data
 | 
				
			||||||
| 
						 | 
					@ -120,9 +130,9 @@ func RedirectToUser(ctx *Context, userName string, redirectUserID int64) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	redirectPath := strings.Replace(
 | 
						redirectPath := strings.Replace(
 | 
				
			||||||
		ctx.Req.URL.Path,
 | 
							ctx.Req.URL.EscapedPath(),
 | 
				
			||||||
		userName,
 | 
							url.PathEscape(userName),
 | 
				
			||||||
		user.Name,
 | 
							url.PathEscape(user.Name),
 | 
				
			||||||
		1,
 | 
							1,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if ctx.Req.URL.RawQuery != "" {
 | 
						if ctx.Req.URL.RawQuery != "" {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,10 +41,10 @@ var IssueTemplateDirCandidates = []string{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PullRequest contains information to make a pull request
 | 
					// PullRequest contains information to make a pull request
 | 
				
			||||||
type PullRequest struct {
 | 
					type PullRequest struct {
 | 
				
			||||||
	BaseRepo *models.Repository
 | 
						BaseRepo       *models.Repository
 | 
				
			||||||
	Allowed  bool
 | 
						Allowed        bool
 | 
				
			||||||
	SameRepo bool
 | 
						SameRepo       bool
 | 
				
			||||||
	HeadInfo string // [<user>:]<branch>
 | 
						HeadInfoSubURL string // [<user>:]<branch> url segment
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Repository contains information to operate a repository
 | 
					// Repository contains information to operate a repository
 | 
				
			||||||
| 
						 | 
					@ -189,11 +189,11 @@ func (r *Repository) GetCommitGraphsCount(hidePRRefs bool, branches []string, fi
 | 
				
			||||||
func (r *Repository) BranchNameSubURL() string {
 | 
					func (r *Repository) BranchNameSubURL() string {
 | 
				
			||||||
	switch {
 | 
						switch {
 | 
				
			||||||
	case r.IsViewBranch:
 | 
						case r.IsViewBranch:
 | 
				
			||||||
		return "branch/" + r.BranchName
 | 
							return "branch/" + util.PathEscapeSegments(r.BranchName)
 | 
				
			||||||
	case r.IsViewTag:
 | 
						case r.IsViewTag:
 | 
				
			||||||
		return "tag/" + r.BranchName
 | 
							return "tag/" + util.PathEscapeSegments(r.BranchName)
 | 
				
			||||||
	case r.IsViewCommit:
 | 
						case r.IsViewCommit:
 | 
				
			||||||
		return "commit/" + r.BranchName
 | 
							return "commit/" + util.PathEscapeSegments(r.BranchName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	log.Error("Unknown view type for repo: %v", r)
 | 
						log.Error("Unknown view type for repo: %v", r)
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
| 
						 | 
					@ -321,9 +321,9 @@ func RedirectToRepo(ctx *Context, redirectRepoID int64) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	redirectPath := strings.Replace(
 | 
						redirectPath := strings.Replace(
 | 
				
			||||||
		ctx.Req.URL.Path,
 | 
							ctx.Req.URL.EscapedPath(),
 | 
				
			||||||
		fmt.Sprintf("%s/%s", ownerName, previousRepoName),
 | 
							url.PathEscape(ownerName)+"/"+url.PathEscape(previousRepoName),
 | 
				
			||||||
		repo.FullName(),
 | 
							url.PathEscape(repo.OwnerName)+"/"+url.PathEscape(repo.Name),
 | 
				
			||||||
		1,
 | 
							1,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if ctx.Req.URL.RawQuery != "" {
 | 
						if ctx.Req.URL.RawQuery != "" {
 | 
				
			||||||
| 
						 | 
					@ -588,7 +588,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 | 
				
			||||||
		ctx.Data["BaseRepo"] = repo.BaseRepo
 | 
							ctx.Data["BaseRepo"] = repo.BaseRepo
 | 
				
			||||||
		ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
 | 
							ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
 | 
				
			||||||
		ctx.Repo.PullRequest.Allowed = canPush
 | 
							ctx.Repo.PullRequest.Allowed = canPush
 | 
				
			||||||
		ctx.Repo.PullRequest.HeadInfo = ctx.Repo.Owner.Name + ":" + ctx.Repo.BranchName
 | 
							ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.Repo.Owner.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName)
 | 
				
			||||||
	} else if repo.AllowsPulls() {
 | 
						} else if repo.AllowsPulls() {
 | 
				
			||||||
		// Or, this is repository accepts pull requests between branches.
 | 
							// Or, this is repository accepts pull requests between branches.
 | 
				
			||||||
		canCompare = true
 | 
							canCompare = true
 | 
				
			||||||
| 
						 | 
					@ -596,7 +596,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 | 
				
			||||||
		ctx.Repo.PullRequest.BaseRepo = repo
 | 
							ctx.Repo.PullRequest.BaseRepo = repo
 | 
				
			||||||
		ctx.Repo.PullRequest.Allowed = canPush
 | 
							ctx.Repo.PullRequest.Allowed = canPush
 | 
				
			||||||
		ctx.Repo.PullRequest.SameRepo = true
 | 
							ctx.Repo.PullRequest.SameRepo = true
 | 
				
			||||||
		ctx.Repo.PullRequest.HeadInfo = ctx.Repo.BranchName
 | 
							ctx.Repo.PullRequest.HeadInfoSubURL = util.PathEscapeSegments(ctx.Repo.BranchName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["CanCompareOrPull"] = canCompare
 | 
						ctx.Data["CanCompareOrPull"] = canCompare
 | 
				
			||||||
	ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
 | 
						ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
 | 
				
			||||||
| 
						 | 
					@ -621,7 +621,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.FormString("go-get") == "1" {
 | 
						if ctx.FormString("go-get") == "1" {
 | 
				
			||||||
		ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name)
 | 
							ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name)
 | 
				
			||||||
		prefix := setting.AppURL + path.Join(owner.Name, repo.Name, "src", "branch", ctx.Repo.BranchName)
 | 
							prefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
 | 
				
			||||||
		ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
 | 
							ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
 | 
				
			||||||
		ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
 | 
							ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -810,7 +810,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 | 
				
			||||||
			if isRenamedBranch && has {
 | 
								if isRenamedBranch && has {
 | 
				
			||||||
				renamedBranchName := ctx.Data["RenamedBranchName"].(string)
 | 
									renamedBranchName := ctx.Data["RenamedBranchName"].(string)
 | 
				
			||||||
				ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName))
 | 
									ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName))
 | 
				
			||||||
				link := strings.Replace(ctx.Req.RequestURI, refName, renamedBranchName, 1)
 | 
									link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refName), util.PathEscapeSegments(renamedBranchName), 1)
 | 
				
			||||||
				ctx.Redirect(link)
 | 
									ctx.Redirect(link)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -845,7 +845,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 | 
				
			||||||
				// If short commit ID add canonical link header
 | 
									// If short commit ID add canonical link header
 | 
				
			||||||
				if len(refName) < 40 {
 | 
									if len(refName) < 40 {
 | 
				
			||||||
					ctx.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
 | 
										ctx.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
 | 
				
			||||||
						util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), refName, ctx.Repo.Commit.ID.String(), 1))))
 | 
											util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))))
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				if len(ignoreNotExistErr) > 0 && ignoreNotExistErr[0] {
 | 
									if len(ignoreNotExistErr) > 0 && ignoreNotExistErr[0] {
 | 
				
			||||||
| 
						 | 
					@ -857,11 +857,13 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if refType == RepoRefLegacy {
 | 
								if refType == RepoRefLegacy {
 | 
				
			||||||
				// redirect from old URL scheme to new URL scheme
 | 
									// redirect from old URL scheme to new URL scheme
 | 
				
			||||||
 | 
									prefix := strings.TrimPrefix(setting.AppSubURL+strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")), ctx.Repo.RepoLink)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				ctx.Redirect(path.Join(
 | 
									ctx.Redirect(path.Join(
 | 
				
			||||||
					setting.AppSubURL,
 | 
										ctx.Repo.RepoLink,
 | 
				
			||||||
					strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")),
 | 
										util.PathEscapeSegments(prefix),
 | 
				
			||||||
					ctx.Repo.BranchNameSubURL(),
 | 
										ctx.Repo.BranchNameSubURL(),
 | 
				
			||||||
					ctx.Repo.TreePath))
 | 
										util.PathEscapeSegments(ctx.Repo.TreePath)))
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@
 | 
				
			||||||
package convert
 | 
					package convert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
| 
						 | 
					@ -126,7 +127,7 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
 | 
				
			||||||
	for i := 0; i < commit.ParentCount(); i++ {
 | 
						for i := 0; i < commit.ParentCount(); i++ {
 | 
				
			||||||
		sha, _ := commit.ParentID(i)
 | 
							sha, _ := commit.ParentID(i)
 | 
				
			||||||
		apiParents[i] = &api.CommitMeta{
 | 
							apiParents[i] = &api.CommitMeta{
 | 
				
			||||||
			URL: repo.APIURL() + "/git/commits/" + sha.String(),
 | 
								URL: repo.APIURL() + "/git/commits/" + url.PathEscape(sha.String()),
 | 
				
			||||||
			SHA: sha.String(),
 | 
								SHA: sha.String(),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -147,13 +148,13 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &api.Commit{
 | 
						return &api.Commit{
 | 
				
			||||||
		CommitMeta: &api.CommitMeta{
 | 
							CommitMeta: &api.CommitMeta{
 | 
				
			||||||
			URL:     repo.APIURL() + "/git/commits/" + commit.ID.String(),
 | 
								URL:     repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()),
 | 
				
			||||||
			SHA:     commit.ID.String(),
 | 
								SHA:     commit.ID.String(),
 | 
				
			||||||
			Created: commit.Committer.When,
 | 
								Created: commit.Committer.When,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		HTMLURL: repo.HTMLURL() + "/commit/" + commit.ID.String(),
 | 
							HTMLURL: repo.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()),
 | 
				
			||||||
		RepoCommit: &api.RepoCommit{
 | 
							RepoCommit: &api.RepoCommit{
 | 
				
			||||||
			URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
 | 
								URL: repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()),
 | 
				
			||||||
			Author: &api.CommitUser{
 | 
								Author: &api.CommitUser{
 | 
				
			||||||
				Identity: api.Identity{
 | 
									Identity: api.Identity{
 | 
				
			||||||
					Name:  commit.Author.Name,
 | 
										Name:  commit.Author.Name,
 | 
				
			||||||
| 
						 | 
					@ -170,7 +171,7 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Message: commit.Message(),
 | 
								Message: commit.Message(),
 | 
				
			||||||
			Tree: &api.CommitMeta{
 | 
								Tree: &api.CommitMeta{
 | 
				
			||||||
				URL:     repo.APIURL() + "/git/trees/" + commit.ID.String(),
 | 
									URL:     repo.APIURL() + "/git/trees/" + url.PathEscape(commit.ID.String()),
 | 
				
			||||||
				SHA:     commit.ID.String(),
 | 
									SHA:     commit.ID.String(),
 | 
				
			||||||
				Created: commit.Committer.When,
 | 
									Created: commit.Committer.When,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package convert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
| 
						 | 
					@ -191,7 +192,7 @@ func ToLabel(label *models.Label, repo *models.Repository, org *models.User) *ap
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else { // BelongsToOrg
 | 
						} else { // BelongsToOrg
 | 
				
			||||||
		if org != nil {
 | 
							if org != nil {
 | 
				
			||||||
			result.URL = fmt.Sprintf("%sapi/v1/orgs/%s/labels/%d", setting.AppURL, org.Name, label.ID)
 | 
								result.URL = fmt.Sprintf("%sapi/v1/orgs/%s/labels/%d", setting.AppURL, url.PathEscape(org.Name), label.ID)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			log.Error("ToLabel did not get org to calculate url for label with id '%d'", label.ID)
 | 
								log.Error("ToLabel did not get org to calculate url for label with id '%d'", label.ID)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,8 @@
 | 
				
			||||||
package convert
 | 
					package convert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -58,7 +60,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case models.NotificationSourceCommit:
 | 
						case models.NotificationSourceCommit:
 | 
				
			||||||
		url := n.Repository.HTMLURL() + "/commit/" + n.CommitID
 | 
							url := n.Repository.HTMLURL() + "/commit/" + url.PathEscape(n.CommitID)
 | 
				
			||||||
		result.Subject = &api.NotificationSubject{
 | 
							result.Subject = &api.NotificationSubject{
 | 
				
			||||||
			Type:    api.NotifySubjectCommit,
 | 
								Type:    api.NotifySubjectCommit,
 | 
				
			||||||
			Title:   n.CommitID,
 | 
								Title:   n.CommitID,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,8 @@ import (
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ObjectCache provides thread-safe cache operations.
 | 
					// ObjectCache provides thread-safe cache operations.
 | 
				
			||||||
| 
						 | 
					@ -92,7 +94,7 @@ func RefEndName(refStr string) string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RefURL returns the absolute URL for a ref in a repository
 | 
					// RefURL returns the absolute URL for a ref in a repository
 | 
				
			||||||
func RefURL(repoURL, ref string) string {
 | 
					func RefURL(repoURL, ref string) string {
 | 
				
			||||||
	refName := RefEndName(ref)
 | 
						refName := util.PathEscapeSegments(RefEndName(ref))
 | 
				
			||||||
	switch {
 | 
						switch {
 | 
				
			||||||
	case strings.HasPrefix(ref, BranchPrefix):
 | 
						case strings.HasPrefix(ref, BranchPrefix):
 | 
				
			||||||
		return repoURL + "/src/branch/" + refName
 | 
							return repoURL + "/src/branch/" + refName
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package repofiles
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"html"
 | 
						"html"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
| 
						 | 
					@ -175,7 +176,7 @@ func UpdateIssuesCommit(doer *models.User, repo *models.Repository, commits []*r
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, html.EscapeString(strings.SplitN(c.Message, "\n", 2)[0]))
 | 
								message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, html.EscapeString(repo.Link()), html.EscapeString(url.PathEscape(c.Sha1)), html.EscapeString(strings.SplitN(c.Message, "\n", 2)[0]))
 | 
				
			||||||
			if err = models.CreateRefComment(doer, refRepo, refIssue, message, c.Sha1); err != nil {
 | 
								if err = models.CreateRefComment(doer, refRepo, refIssue, message, c.Sha1); err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,8 @@
 | 
				
			||||||
package repofiles
 | 
					package repofiles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
| 
						 | 
					@ -31,7 +33,7 @@ func GetBlobBySHA(repo *models.Repository, sha string) (*api.GitBlobResponse, er
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &api.GitBlobResponse{
 | 
						return &api.GitBlobResponse{
 | 
				
			||||||
		SHA:      gitBlob.ID.String(),
 | 
							SHA:      gitBlob.ID.String(),
 | 
				
			||||||
		URL:      repo.APIURL() + "/git/blobs/" + gitBlob.ID.String(),
 | 
							URL:      repo.APIURL() + "/git/blobs/" + url.PathEscape(gitBlob.ID.String()),
 | 
				
			||||||
		Size:     gitBlob.Size(),
 | 
							Size:     gitBlob.Size(),
 | 
				
			||||||
		Encoding: "base64",
 | 
							Encoding: "base64",
 | 
				
			||||||
		Content:  content,
 | 
							Content:  content,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,19 +36,19 @@ func GetFileCommitResponse(repo *models.Repository, commit *git.Commit) (*api.Fi
 | 
				
			||||||
	if commit == nil {
 | 
						if commit == nil {
 | 
				
			||||||
		return nil, fmt.Errorf("commit cannot be nil")
 | 
							return nil, fmt.Errorf("commit cannot be nil")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	commitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + commit.ID.String())
 | 
						commitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()))
 | 
				
			||||||
	commitTreeURL, _ := url.Parse(repo.APIURL() + "/git/trees/" + commit.Tree.ID.String())
 | 
						commitTreeURL, _ := url.Parse(repo.APIURL() + "/git/trees/" + url.PathEscape(commit.Tree.ID.String()))
 | 
				
			||||||
	parents := make([]*api.CommitMeta, commit.ParentCount())
 | 
						parents := make([]*api.CommitMeta, commit.ParentCount())
 | 
				
			||||||
	for i := 0; i <= commit.ParentCount(); i++ {
 | 
						for i := 0; i <= commit.ParentCount(); i++ {
 | 
				
			||||||
		if parent, err := commit.Parent(i); err == nil && parent != nil {
 | 
							if parent, err := commit.Parent(i); err == nil && parent != nil {
 | 
				
			||||||
			parentCommitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + parent.ID.String())
 | 
								parentCommitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(parent.ID.String()))
 | 
				
			||||||
			parents[i] = &api.CommitMeta{
 | 
								parents[i] = &api.CommitMeta{
 | 
				
			||||||
				SHA: parent.ID.String(),
 | 
									SHA: parent.ID.String(),
 | 
				
			||||||
				URL: parentCommitURL.String(),
 | 
									URL: parentCommitURL.String(),
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	commitHTMLURL, _ := url.Parse(repo.HTMLURL() + "/commit/" + commit.ID.String())
 | 
						commitHTMLURL, _ := url.Parse(repo.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()))
 | 
				
			||||||
	fileCommit := &api.FileCommitResponse{
 | 
						fileCommit := &api.FileCommitResponse{
 | 
				
			||||||
		CommitMeta: api.CommitMeta{
 | 
							CommitMeta: api.CommitMeta{
 | 
				
			||||||
			SHA: commit.ID.String(),
 | 
								SHA: commit.ID.String(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package repofiles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
| 
						 | 
					@ -28,7 +29,7 @@ func GetTreeBySHA(repo *models.Repository, sha string, page, perPage int, recurs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tree := new(api.GitTreeResponse)
 | 
						tree := new(api.GitTreeResponse)
 | 
				
			||||||
	tree.SHA = gitTree.ResolvedID.String()
 | 
						tree.SHA = gitTree.ResolvedID.String()
 | 
				
			||||||
	tree.URL = repo.APIURL() + "/git/trees/" + tree.SHA
 | 
						tree.URL = repo.APIURL() + "/git/trees/" + url.PathEscape(tree.SHA)
 | 
				
			||||||
	var entries git.Entries
 | 
						var entries git.Entries
 | 
				
			||||||
	if recursive {
 | 
						if recursive {
 | 
				
			||||||
		entries, err = gitTree.ListEntriesRecursive()
 | 
							entries, err = gitTree.ListEntriesRecursive()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
| 
						 | 
					@ -81,7 +82,7 @@ func (pc *PushCommits) toAPIPayloadCommit(repoPath, repoLink string, commit *Pus
 | 
				
			||||||
	return &api.PayloadCommit{
 | 
						return &api.PayloadCommit{
 | 
				
			||||||
		ID:      commit.Sha1,
 | 
							ID:      commit.Sha1,
 | 
				
			||||||
		Message: commit.Message,
 | 
							Message: commit.Message,
 | 
				
			||||||
		URL:     fmt.Sprintf("%s/commit/%s", repoLink, commit.Sha1),
 | 
							URL:     fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(commit.Sha1)),
 | 
				
			||||||
		Author: &api.PayloadUser{
 | 
							Author: &api.PayloadUser{
 | 
				
			||||||
			Name:     commit.AuthorName,
 | 
								Name:     commit.AuthorName,
 | 
				
			||||||
			Email:    commit.AuthorEmail,
 | 
								Email:    commit.AuthorEmail,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,17 +139,14 @@ func NewFuncMap() []template.FuncMap {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return str[start:end]
 | 
								return str[start:end]
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"EllipsisString":        base.EllipsisString,
 | 
							"EllipsisString":                 base.EllipsisString,
 | 
				
			||||||
		"DiffTypeToStr":         DiffTypeToStr,
 | 
							"DiffTypeToStr":                  DiffTypeToStr,
 | 
				
			||||||
		"DiffLineTypeToStr":     DiffLineTypeToStr,
 | 
							"DiffLineTypeToStr":              DiffLineTypeToStr,
 | 
				
			||||||
		"Sha1":                  Sha1,
 | 
							"Sha1":                           Sha1,
 | 
				
			||||||
		"ShortSha":              base.ShortSha,
 | 
							"ShortSha":                       base.ShortSha,
 | 
				
			||||||
		"MD5":                   base.EncodeMD5,
 | 
							"MD5":                            base.EncodeMD5,
 | 
				
			||||||
		"ActionContent2Commits": ActionContent2Commits,
 | 
							"ActionContent2Commits":          ActionContent2Commits,
 | 
				
			||||||
		"PathEscape":            url.PathEscape,
 | 
							"PathEscape":                     url.PathEscape,
 | 
				
			||||||
		"EscapePound": func(str string) string {
 | 
					 | 
				
			||||||
			return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str)
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		"PathEscapeSegments":             util.PathEscapeSegments,
 | 
							"PathEscapeSegments":             util.PathEscapeSegments,
 | 
				
			||||||
		"URLJoin":                        util.URLJoin,
 | 
							"URLJoin":                        util.URLJoin,
 | 
				
			||||||
		"RenderCommitMessage":            RenderCommitMessage,
 | 
							"RenderCommitMessage":            RenderCommitMessage,
 | 
				
			||||||
| 
						 | 
					@ -742,7 +739,7 @@ func ReactionToEmoji(reaction string) template.HTML {
 | 
				
			||||||
	if val != nil {
 | 
						if val != nil {
 | 
				
			||||||
		return template.HTML(val.Emoji)
 | 
							return template.HTML(val.Emoji)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return template.HTML(fmt.Sprintf(`<img alt=":%s:" src="%s/assets/img/emoji/%s.png"></img>`, reaction, setting.StaticURLPrefix, reaction))
 | 
						return template.HTML(fmt.Sprintf(`<img alt=":%s:" src="%s/assets/img/emoji/%s.png"></img>`, reaction, setting.StaticURLPrefix, url.PathEscape(reaction)))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RenderNote renders the contents of a git-notes file as a commit message.
 | 
					// RenderNote renders the contents of a git-notes file as a commit message.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package upload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
| 
						 | 
					@ -83,7 +84,7 @@ func AddUploadContext(ctx *context.Context, uploadType string) {
 | 
				
			||||||
		ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
 | 
							ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
 | 
				
			||||||
		ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove"
 | 
							ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove"
 | 
				
			||||||
		if len(ctx.Params(":index")) > 0 {
 | 
							if len(ctx.Params(":index")) > 0 {
 | 
				
			||||||
			ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/" + ctx.Params(":index") + "/attachments"
 | 
								ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/" + url.PathEscape(ctx.Params(":index")) + "/attachments"
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
 | 
								ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1409,7 +1409,7 @@ pulls.filter_branch = Filter branch
 | 
				
			||||||
pulls.no_results = No results found.
 | 
					pulls.no_results = No results found.
 | 
				
			||||||
pulls.nothing_to_compare = These branches are equal. There is no need to create a pull request.
 | 
					pulls.nothing_to_compare = These branches are equal. There is no need to create a pull request.
 | 
				
			||||||
pulls.nothing_to_compare_and_allow_empty_pr = These branches are equal. This PR will be empty.
 | 
					pulls.nothing_to_compare_and_allow_empty_pr = These branches are equal. This PR will be empty.
 | 
				
			||||||
pulls.has_pull_request = `A pull request between these branches already exists: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
 | 
					pulls.has_pull_request = `A pull request between these branches already exists: <a href="%[1]s">%[2]s#%[3]d</a>`
 | 
				
			||||||
pulls.create = Create Pull Request
 | 
					pulls.create = Create Pull Request
 | 
				
			||||||
pulls.title_desc = wants to merge %[1]d commits from <code>%[2]s</code> into <code id="branch_target">%[3]s</code>
 | 
					pulls.title_desc = wants to merge %[1]d commits from <code>%[2]s</code> into <code id="branch_target">%[3]s</code>
 | 
				
			||||||
pulls.merged_title_desc = merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s
 | 
					pulls.merged_title_desc = merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s
 | 
				
			||||||
| 
						 | 
					@ -2761,32 +2761,32 @@ notices.delete_success = The system notices have been deleted.
 | 
				
			||||||
[action]
 | 
					[action]
 | 
				
			||||||
create_repo = created repository <a href="%s">%s</a>
 | 
					create_repo = created repository <a href="%s">%s</a>
 | 
				
			||||||
rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
 | 
					rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
 | 
				
			||||||
commit_repo = pushed to <a href="%[1]s/src/branch/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a>
 | 
					commit_repo = pushed to <a href="%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a>
 | 
				
			||||||
create_issue = `opened issue <a href="%s/issues/%s">%s#%[2]s</a>`
 | 
					create_issue = `opened issue <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
close_issue = `closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
 | 
					close_issue = `closed issue <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
reopen_issue = `reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
 | 
					reopen_issue = `reopened issue <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
create_pull_request = `created pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
 | 
					create_pull_request = `created pull request <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
close_pull_request = `closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
 | 
					close_pull_request = `closed pull request <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
reopen_pull_request = `reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
 | 
					reopen_pull_request = `reopened pull request <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
comment_issue = `commented on issue <a href="%s/issues/%s">%s#%[2]s</a>`
 | 
					comment_issue = `commented on issue <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
comment_pull = `commented on pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
 | 
					comment_pull = `commented on pull request <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
merge_pull_request = `merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
 | 
					merge_pull_request = `merged pull request <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
transfer_repo = transferred repository <code>%s</code> to <a href="%s">%s</a>
 | 
					transfer_repo = transferred repository <code>%s</code> to <a href="%s">%s</a>
 | 
				
			||||||
push_tag = pushed tag <a href="%s/src/tag/%s">%[4]s</a> to <a href="%[1]s">%[3]s</a>
 | 
					push_tag = pushed tag <a href="%[2]s">%[3]s</a> to <a href="%[1]s">%[4]s</a>
 | 
				
			||||||
delete_tag = deleted tag %[2]s from <a href="%[1]s">%[3]s</a>
 | 
					delete_tag = deleted tag %[2]s from <a href="%[1]s">%[3]s</a>
 | 
				
			||||||
delete_branch = deleted branch %[2]s from <a href="%[1]s">%[3]s</a>
 | 
					delete_branch = deleted branch %[2]s from <a href="%[1]s">%[3]s</a>
 | 
				
			||||||
compare_branch = Compare
 | 
					compare_branch = Compare
 | 
				
			||||||
compare_commits = Compare %d commits
 | 
					compare_commits = Compare %d commits
 | 
				
			||||||
compare_commits_general = Compare commits
 | 
					compare_commits_general = Compare commits
 | 
				
			||||||
mirror_sync_push = synced commits to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a> from mirror
 | 
					mirror_sync_push = synced commits to <a href="%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a> from mirror
 | 
				
			||||||
mirror_sync_create = synced new reference <a href="%s/src/%s">%[2]s</a> to <a href="%[1]s">%[3]s</a> from mirror
 | 
					mirror_sync_create = synced new reference <a href="%[2]s">%[3]s</a> to <a href="%[1]s">%[4]s</a> from mirror
 | 
				
			||||||
mirror_sync_delete = synced and deleted reference <code>%[2]s</code> at <a href="%[1]s">%[3]s</a> from mirror
 | 
					mirror_sync_delete = synced and deleted reference <code>%[2]s</code> at <a href="%[1]s">%[3]s</a> from mirror
 | 
				
			||||||
approve_pull_request = `approved <a href="%s/pulls/%s">%s#%[2]s</a>`
 | 
					approve_pull_request = `approved <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
reject_pull_request = `suggested changes for <a href="%s/pulls/%s">%s#%[2]s</a>`
 | 
					reject_pull_request = `suggested changes for <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
publish_release  = `released <a href="%s/releases/tag/%s"> "%[4]s" </a> at <a href="%[1]s">%[3]s</a>`
 | 
					publish_release  = `released <a href="%[2]s"> "%[4]s" </a> at <a href="%[1]s">%[3]s</a>`
 | 
				
			||||||
review_dismissed = `dismissed review from <b>%[4]s</b> for <a href="%[1]s/pulls/%[2]s">%[3]s#%[2]s</a>`
 | 
					review_dismissed = `dismissed review from <b>%[4]s</b> for <a href="%[1]s">%[3]s#%[2]s</a>`
 | 
				
			||||||
review_dismissed_reason = Reason:
 | 
					review_dismissed_reason = Reason:
 | 
				
			||||||
create_branch = created branch <a href="%[1]s/src/branch/%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>
 | 
					create_branch = created branch <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>
 | 
				
			||||||
starred_repo = starred <a href="%[1]s">%[2]s</a>
 | 
					starred_repo = starred <a href="%[1]s">%[2]s</a>
 | 
				
			||||||
watched_repo = started watching <a href="%[1]s">%[2]s</a>
 | 
					watched_repo = started watching <a href="%[1]s">%[2]s</a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package org
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
| 
						 | 
					@ -159,7 +160,7 @@ func IsMember(ctx *context.APIContext) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	redirectURL := setting.AppSubURL + "/api/v1/orgs/" + ctx.Org.Organization.Name + "/public_members/" + userToCheck.Name
 | 
						redirectURL := setting.AppSubURL + "/api/v1/orgs/" + url.PathEscape(ctx.Org.Organization.Name) + "/public_members/" + url.PathEscape(userToCheck.Name)
 | 
				
			||||||
	ctx.Redirect(redirectURL, 302)
 | 
						ctx.Redirect(redirectURL, 302)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,11 @@ package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/routers/api/v1/utils"
 | 
						"code.gitea.io/gitea/routers/api/v1/utils"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -89,11 +91,11 @@ func getGitRefsInternal(ctx *context.APIContext, filter string) {
 | 
				
			||||||
	for i := range refs {
 | 
						for i := range refs {
 | 
				
			||||||
		apiRefs[i] = &api.Reference{
 | 
							apiRefs[i] = &api.Reference{
 | 
				
			||||||
			Ref: refs[i].Name,
 | 
								Ref: refs[i].Name,
 | 
				
			||||||
			URL: ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Name,
 | 
								URL: ctx.Repo.Repository.APIURL() + "/git/" + util.PathEscapeSegments(refs[i].Name),
 | 
				
			||||||
			Object: &api.GitObject{
 | 
								Object: &api.GitObject{
 | 
				
			||||||
				SHA:  refs[i].Object.String(),
 | 
									SHA:  refs[i].Object.String(),
 | 
				
			||||||
				Type: refs[i].Type,
 | 
									Type: refs[i].Type,
 | 
				
			||||||
				URL:  ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Type + "s/" + refs[i].Object.String(),
 | 
									URL:  ctx.Repo.Repository.APIURL() + "/git/" + url.PathEscape(refs[i].Type) + "s/" + url.PathEscape(refs[i].Object.String()),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ package repo
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
| 
						 | 
					@ -33,8 +34,8 @@ func appendPrivateInformation(apiKey *api.DeployKey, key *models.DeployKey, repo
 | 
				
			||||||
	return apiKey, nil
 | 
						return apiKey, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func composeDeployKeysAPILink(repoPath string) string {
 | 
					func composeDeployKeysAPILink(owner, name string) string {
 | 
				
			||||||
	return setting.AppURL + "api/v1/repos/" + repoPath + "/keys/"
 | 
						return setting.AppURL + "api/v1/repos/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/keys/"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListDeployKeys list all the deploy keys of a repository
 | 
					// ListDeployKeys list all the deploy keys of a repository
 | 
				
			||||||
| 
						 | 
					@ -94,7 +95,7 @@ func ListDeployKeys(ctx *context.APIContext) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
 | 
						apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
				
			||||||
	apiKeys := make([]*api.DeployKey, len(keys))
 | 
						apiKeys := make([]*api.DeployKey, len(keys))
 | 
				
			||||||
	for i := range keys {
 | 
						for i := range keys {
 | 
				
			||||||
		if err := keys[i].GetContent(); err != nil {
 | 
							if err := keys[i].GetContent(); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -154,7 +155,7 @@ func GetDeployKey(ctx *context.APIContext) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
 | 
						apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
				
			||||||
	apiKey := convert.ToDeployKey(apiLink, key)
 | 
						apiKey := convert.ToDeployKey(apiLink, key)
 | 
				
			||||||
	if ctx.User.IsAdmin || ((ctx.Repo.Repository.ID == key.RepoID) && (ctx.User.ID == ctx.Repo.Owner.ID)) {
 | 
						if ctx.User.IsAdmin || ((ctx.Repo.Repository.ID == key.RepoID) && (ctx.User.ID == ctx.Repo.Owner.ID)) {
 | 
				
			||||||
		apiKey, _ = appendPrivateInformation(apiKey, key, ctx.Repo.Repository)
 | 
							apiKey, _ = appendPrivateInformation(apiKey, key, ctx.Repo.Repository)
 | 
				
			||||||
| 
						 | 
					@ -233,7 +234,7 @@ func CreateDeployKey(ctx *context.APIContext) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key.Content = content
 | 
						key.Content = content
 | 
				
			||||||
	apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
 | 
						apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
				
			||||||
	ctx.JSON(http.StatusCreated, convert.ToDeployKey(apiLink, key))
 | 
						ctx.JSON(http.StatusCreated, convert.ToDeployKey(apiLink, key))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,9 @@ import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/login"
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/auth/pam"
 | 
						"code.gitea.io/gitea/modules/auth/pam"
 | 
				
			||||||
| 
						 | 
					@ -396,7 +398,7 @@ func EditAuthSourcePost(ctx *context.Context) {
 | 
				
			||||||
	log.Trace("Authentication changed by admin(%s): %d", ctx.User.Name, source.ID)
 | 
						log.Trace("Authentication changed by admin(%s): %d", ctx.User.Name, source.ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Flash.Success(ctx.Tr("admin.auths.update_success"))
 | 
						ctx.Flash.Success(ctx.Tr("admin.auths.update_success"))
 | 
				
			||||||
	ctx.Redirect(setting.AppSubURL + "/admin/auths/" + fmt.Sprint(form.ID))
 | 
						ctx.Redirect(setting.AppSubURL + "/admin/auths/" + strconv.FormatInt(form.ID, 10))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteAuthSource response for deleting an auth source
 | 
					// DeleteAuthSource response for deleting an auth source
 | 
				
			||||||
| 
						 | 
					@ -414,7 +416,7 @@ func DeleteAuthSource(ctx *context.Context) {
 | 
				
			||||||
			ctx.Flash.Error(fmt.Sprintf("DeleteLoginSource: %v", err))
 | 
								ctx.Flash.Error(fmt.Sprintf("DeleteLoginSource: %v", err))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
							ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
				
			||||||
			"redirect": setting.AppSubURL + "/admin/auths/" + ctx.Params(":authid"),
 | 
								"redirect": setting.AppSubURL + "/admin/auths/" + url.PathEscape(ctx.Params(":authid")),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,7 @@ func DeleteRepo(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
 | 
						ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
 | 
				
			||||||
	ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
						ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
				
			||||||
		"redirect": setting.AppSubURL + "/admin/repos?page=" + ctx.FormString("page") + "&sort=" + ctx.FormString("sort"),
 | 
							"redirect": setting.AppSubURL + "/admin/repos?page=" + url.QueryEscape(ctx.FormString("page")) + "&sort=" + url.QueryEscape(ctx.FormString("sort")),
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,5 +161,5 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.Flash.Success(ctx.Tr("repo.delete_preexisting_success", dir))
 | 
							ctx.Flash.Success(ctx.Tr("repo.delete_preexisting_success", dir))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Redirect(setting.AppSubURL + "/admin/repos/unadopted?search=true&q=" + url.QueryEscape(q) + "&page=" + page)
 | 
						ctx.Redirect(setting.AppSubURL + "/admin/repos/unadopted?search=true&q=" + url.QueryEscape(q) + "&page=" + url.QueryEscape(page))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,8 @@
 | 
				
			||||||
package admin
 | 
					package admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -188,7 +188,7 @@ func NewUserPost(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name))
 | 
						ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name))
 | 
				
			||||||
	ctx.Redirect(setting.AppSubURL + "/admin/users/" + fmt.Sprint(u.ID))
 | 
						ctx.Redirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func prepareUserInfo(ctx *context.Context) *models.User {
 | 
					func prepareUserInfo(ctx *context.Context) *models.User {
 | 
				
			||||||
| 
						 | 
					@ -366,7 +366,7 @@ func EditUserPost(ctx *context.Context) {
 | 
				
			||||||
	log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name)
 | 
						log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success"))
 | 
						ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success"))
 | 
				
			||||||
	ctx.Redirect(setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"))
 | 
						ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteUser response for deleting a user
 | 
					// DeleteUser response for deleting a user
 | 
				
			||||||
| 
						 | 
					@ -382,12 +382,12 @@ func DeleteUser(ctx *context.Context) {
 | 
				
			||||||
		case models.IsErrUserOwnRepos(err):
 | 
							case models.IsErrUserOwnRepos(err):
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo"))
 | 
								ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo"))
 | 
				
			||||||
			ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
								ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
				
			||||||
				"redirect": setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"),
 | 
									"redirect": setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")),
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		case models.IsErrUserHasOrgs(err):
 | 
							case models.IsErrUserHasOrgs(err):
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("admin.users.still_has_org"))
 | 
								ctx.Flash.Error(ctx.Tr("admin.users.still_has_org"))
 | 
				
			||||||
			ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
								ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
				
			||||||
				"redirect": setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"),
 | 
									"redirect": setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")),
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			ctx.ServerError("DeleteUser", err)
 | 
								ctx.ServerError("DeleteUser", err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,10 +15,35 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/templates"
 | 
						"code.gitea.io/gitea/modules/templates"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gorilla/feeds"
 | 
						"github.com/gorilla/feeds"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toBranchLink(act *models.Action) string {
 | 
				
			||||||
 | 
						return act.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toTagLink(act *models.Action) string {
 | 
				
			||||||
 | 
						return act.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toIssueLink(act *models.Action) string {
 | 
				
			||||||
 | 
						return act.GetRepoLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toPullLink(act *models.Action) string {
 | 
				
			||||||
 | 
						return act.GetRepoLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toSrcLink(act *models.Action) string {
 | 
				
			||||||
 | 
						return act.GetRepoLink() + "/src/" + util.PathEscapeSegments(act.GetBranch())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func toReleaseLink(act *models.Action) string {
 | 
				
			||||||
 | 
						return act.GetRepoLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// feedActionsToFeedItems convert gitea's Action feed to feeds Item
 | 
					// feedActionsToFeedItems convert gitea's Action feed to feeds Item
 | 
				
			||||||
func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (items []*feeds.Item, err error) {
 | 
					func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (items []*feeds.Item, err error) {
 | 
				
			||||||
	for _, act := range actions {
 | 
						for _, act := range actions {
 | 
				
			||||||
| 
						 | 
					@ -32,62 +57,111 @@ func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (ite
 | 
				
			||||||
		title = act.ActUser.DisplayName() + " "
 | 
							title = act.ActUser.DisplayName() + " "
 | 
				
			||||||
		switch act.OpType {
 | 
							switch act.OpType {
 | 
				
			||||||
		case models.ActionCreateRepo:
 | 
							case models.ActionCreateRepo:
 | 
				
			||||||
			title += ctx.Tr("action.create_repo", act.GetRepoLink(), act.ShortRepoPath())
 | 
								title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoLink(), act.ShortRepoPath())
 | 
				
			||||||
 | 
								link.Href = act.GetRepoLink()
 | 
				
			||||||
		case models.ActionRenameRepo:
 | 
							case models.ActionRenameRepo:
 | 
				
			||||||
			title += ctx.Tr("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
 | 
								title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
 | 
				
			||||||
 | 
								link.Href = act.GetRepoLink()
 | 
				
			||||||
		case models.ActionCommitRepo:
 | 
							case models.ActionCommitRepo:
 | 
				
			||||||
			branchLink := act.GetBranch()
 | 
								link.Href = toBranchLink(act)
 | 
				
			||||||
			if len(act.Content) != 0 {
 | 
								if len(act.Content) != 0 {
 | 
				
			||||||
				title += ctx.Tr("action.commit_repo", act.GetRepoLink(), branchLink, act.GetBranch(), act.ShortRepoPath())
 | 
									title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				title += ctx.Tr("action.create_branch", act.GetRepoLink(), branchLink, act.GetBranch(), act.ShortRepoPath())
 | 
									title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case models.ActionCreateIssue:
 | 
							case models.ActionCreateIssue:
 | 
				
			||||||
			title += ctx.Tr("action.create_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								link.Href = toIssueLink(act)
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.create_issue", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionCreatePullRequest:
 | 
							case models.ActionCreatePullRequest:
 | 
				
			||||||
			title += ctx.Tr("action.create_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								link.Href = toPullLink(act)
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionTransferRepo:
 | 
							case models.ActionTransferRepo:
 | 
				
			||||||
			title += ctx.Tr("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
 | 
								link.Href = act.GetRepoLink()
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionPushTag:
 | 
							case models.ActionPushTag:
 | 
				
			||||||
			title += ctx.Tr("action.push_tag", act.GetRepoLink(), url.QueryEscape(act.GetTag()), act.ShortRepoPath())
 | 
								link.Href = toTagLink(act)
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoLink(), link.Href, act.GetTag(), act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionCommentIssue:
 | 
							case models.ActionCommentIssue:
 | 
				
			||||||
			title += ctx.Tr("action.comment_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								issueLink := toIssueLink(act)
 | 
				
			||||||
 | 
								if link.Href == "#" {
 | 
				
			||||||
 | 
									link.Href = issueLink
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.comment_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionMergePullRequest:
 | 
							case models.ActionMergePullRequest:
 | 
				
			||||||
			title += ctx.Tr("action.merge_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								pullLink := toPullLink(act)
 | 
				
			||||||
 | 
								if link.Href == "#" {
 | 
				
			||||||
 | 
									link.Href = pullLink
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.merge_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionCloseIssue:
 | 
							case models.ActionCloseIssue:
 | 
				
			||||||
			title += ctx.Tr("action.close_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								issueLink := toIssueLink(act)
 | 
				
			||||||
 | 
								if link.Href == "#" {
 | 
				
			||||||
 | 
									link.Href = issueLink
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.close_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionReopenIssue:
 | 
							case models.ActionReopenIssue:
 | 
				
			||||||
			title += ctx.Tr("action.reopen_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								issueLink := toIssueLink(act)
 | 
				
			||||||
 | 
								if link.Href == "#" {
 | 
				
			||||||
 | 
									link.Href = issueLink
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.reopen_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionClosePullRequest:
 | 
							case models.ActionClosePullRequest:
 | 
				
			||||||
			title += ctx.Tr("action.close_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								pullLink := toPullLink(act)
 | 
				
			||||||
 | 
								if link.Href == "#" {
 | 
				
			||||||
 | 
									link.Href = pullLink
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.close_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionReopenPullRequest:
 | 
							case models.ActionReopenPullRequest:
 | 
				
			||||||
			title += ctx.Tr("action.reopen_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath)
 | 
								pullLink := toPullLink(act)
 | 
				
			||||||
 | 
								if link.Href == "#" {
 | 
				
			||||||
 | 
									link.Href = pullLink
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionDeleteTag:
 | 
							case models.ActionDeleteTag:
 | 
				
			||||||
			title += ctx.Tr("action.delete_tag", act.GetRepoLink(), html.EscapeString(act.GetTag()), act.ShortRepoPath())
 | 
								link.Href = act.GetRepoLink()
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoLink(), act.GetTag(), act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionDeleteBranch:
 | 
							case models.ActionDeleteBranch:
 | 
				
			||||||
			title += ctx.Tr("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
 | 
								link.Href = act.GetRepoLink()
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionMirrorSyncPush:
 | 
							case models.ActionMirrorSyncPush:
 | 
				
			||||||
			title += ctx.Tr("action.mirror_sync_push", act.GetRepoLink(), url.QueryEscape(act.GetBranch()), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
 | 
								srcLink := toSrcLink(act)
 | 
				
			||||||
 | 
								if link.Href == "#" {
 | 
				
			||||||
 | 
									link.Href = srcLink
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionMirrorSyncCreate:
 | 
							case models.ActionMirrorSyncCreate:
 | 
				
			||||||
			title += ctx.Tr("action.mirror_sync_create", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
 | 
								srcLink := toSrcLink(act)
 | 
				
			||||||
 | 
								if link.Href == "#" {
 | 
				
			||||||
 | 
									link.Href = srcLink
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionMirrorSyncDelete:
 | 
							case models.ActionMirrorSyncDelete:
 | 
				
			||||||
			title += ctx.Tr("action.mirror_sync_delete", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
 | 
								link.Href = act.GetRepoLink()
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoLink(), act.GetBranch(), act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionApprovePullRequest:
 | 
							case models.ActionApprovePullRequest:
 | 
				
			||||||
			title += ctx.Tr("action.approve_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								pullLink := toPullLink(act)
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionRejectPullRequest:
 | 
							case models.ActionRejectPullRequest:
 | 
				
			||||||
			title += ctx.Tr("action.reject_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								pullLink := toPullLink(act)
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.reject_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionCommentPull:
 | 
							case models.ActionCommentPull:
 | 
				
			||||||
			title += ctx.Tr("action.comment_pull", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
								pullLink := toPullLink(act)
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.comment_pull", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
 | 
				
			||||||
		case models.ActionPublishRelease:
 | 
							case models.ActionPublishRelease:
 | 
				
			||||||
			title += ctx.Tr("action.publish_release", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath(), act.Content)
 | 
								releaseLink := toReleaseLink(act)
 | 
				
			||||||
 | 
								if link.Href == "#" {
 | 
				
			||||||
 | 
									link.Href = releaseLink
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoLink(), releaseLink, act.ShortRepoPath(), act.Content)
 | 
				
			||||||
		case models.ActionPullReviewDismissed:
 | 
							case models.ActionPullReviewDismissed:
 | 
				
			||||||
			title += ctx.Tr("action.review_dismissed", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
 | 
								pullLink := toPullLink(act)
 | 
				
			||||||
 | 
								title += ctx.TrHTMLEscapeArgs("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
 | 
				
			||||||
		case models.ActionStarRepo:
 | 
							case models.ActionStarRepo:
 | 
				
			||||||
			title += ctx.Tr("action.starred_repo", act.GetRepoLink(), act.GetRepoPath())
 | 
								link.Href = act.GetRepoLink()
 | 
				
			||||||
			link = &feeds.Link{Href: act.GetRepoLink()}
 | 
								title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoLink(), act.GetRepoPath())
 | 
				
			||||||
		case models.ActionWatchRepo:
 | 
							case models.ActionWatchRepo:
 | 
				
			||||||
			title += ctx.Tr("action.watched_repo", act.GetRepoLink(), act.GetRepoPath())
 | 
								link.Href = act.GetRepoLink()
 | 
				
			||||||
			link = &feeds.Link{Href: act.GetRepoLink()}
 | 
								title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoLink(), act.GetRepoPath())
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			return nil, fmt.Errorf("unknown action type: %v", act.OpType)
 | 
								return nil, fmt.Errorf("unknown action type: %v", act.OpType)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -104,7 +178,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (ite
 | 
				
			||||||
						desc += "\n\n"
 | 
											desc += "\n\n"
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
 | 
										desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
 | 
				
			||||||
						fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), commit.Sha1),
 | 
											html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), commit.Sha1)),
 | 
				
			||||||
						commit.Sha1,
 | 
											commit.Sha1,
 | 
				
			||||||
						templates.RenderCommitMessage(commit.Message, repoLink, nil),
 | 
											templates.RenderCommitMessage(commit.Message, repoLink, nil),
 | 
				
			||||||
					)
 | 
										)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package org
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
| 
						 | 
					@ -76,7 +77,7 @@ func SettingsPost(ctx *context.Context) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// reset ctx.org.OrgLink with new name
 | 
							// reset ctx.org.OrgLink with new name
 | 
				
			||||||
		ctx.Org.OrgLink = setting.AppSubURL + "/org/" + form.Name
 | 
							ctx.Org.OrgLink = setting.AppSubURL + "/org/" + url.PathEscape(form.Name)
 | 
				
			||||||
		log.Trace("Organization name changed: %s -> %s", org.Name, form.Name)
 | 
							log.Trace("Organization name changed: %s -> %s", org.Name, form.Name)
 | 
				
			||||||
		nameChanged = false
 | 
							nameChanged = false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package org
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,7 +106,7 @@ func TeamsAction(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.JSON(http.StatusOK,
 | 
							ctx.JSON(http.StatusOK,
 | 
				
			||||||
			map[string]interface{}{
 | 
								map[string]interface{}{
 | 
				
			||||||
				"redirect": ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName,
 | 
									"redirect": ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName),
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	case "add":
 | 
						case "add":
 | 
				
			||||||
| 
						 | 
					@ -119,7 +120,7 @@ func TeamsAction(ctx *context.Context) {
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if models.IsErrUserNotExist(err) {
 | 
								if models.IsErrUserNotExist(err) {
 | 
				
			||||||
				ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
 | 
									ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
 | 
				
			||||||
				ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
 | 
									ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				ctx.ServerError(" GetUserByName", err)
 | 
									ctx.ServerError(" GetUserByName", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -128,7 +129,7 @@ func TeamsAction(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if u.IsOrganization() {
 | 
							if u.IsOrganization() {
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("form.cannot_add_org_to_team"))
 | 
								ctx.Flash.Error(ctx.Tr("form.cannot_add_org_to_team"))
 | 
				
			||||||
			ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
 | 
								ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -156,7 +157,7 @@ func TeamsAction(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch page {
 | 
						switch page {
 | 
				
			||||||
	case "team":
 | 
						case "team":
 | 
				
			||||||
		ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
 | 
							ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
 | 
				
			||||||
	case "home":
 | 
						case "home":
 | 
				
			||||||
		ctx.Redirect(ctx.Org.Organization.HomeLink())
 | 
							ctx.Redirect(ctx.Org.Organization.HomeLink())
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
| 
						 | 
					@ -181,7 +182,7 @@ func TeamsRepoAction(ctx *context.Context) {
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if models.IsErrRepoNotExist(err) {
 | 
								if models.IsErrRepoNotExist(err) {
 | 
				
			||||||
				ctx.Flash.Error(ctx.Tr("org.teams.add_nonexistent_repo"))
 | 
									ctx.Flash.Error(ctx.Tr("org.teams.add_nonexistent_repo"))
 | 
				
			||||||
				ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")
 | 
									ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.ServerError("GetRepositoryByName", err)
 | 
								ctx.ServerError("GetRepositoryByName", err)
 | 
				
			||||||
| 
						 | 
					@ -204,11 +205,11 @@ func TeamsRepoAction(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if action == "addall" || action == "removeall" {
 | 
						if action == "addall" || action == "removeall" {
 | 
				
			||||||
		ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
							ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
				
			||||||
			"redirect": ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories",
 | 
								"redirect": ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories",
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")
 | 
						ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTeam render create new team page
 | 
					// NewTeam render create new team page
 | 
				
			||||||
| 
						 | 
					@ -273,7 +274,7 @@ func NewTeamPost(ctx *context.Context) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	log.Trace("Team created: %s/%s", ctx.Org.Organization.Name, t.Name)
 | 
						log.Trace("Team created: %s/%s", ctx.Org.Organization.Name, t.Name)
 | 
				
			||||||
	ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName)
 | 
						ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TeamMembers render team members page
 | 
					// TeamMembers render team members page
 | 
				
			||||||
| 
						 | 
					@ -375,7 +376,7 @@ func EditTeamPost(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName)
 | 
						ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteTeam response for the delete team request
 | 
					// DeleteTeam response for the delete team request
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	gotemplate "html/template"
 | 
						gotemplate "html/template"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
| 
						 | 
					@ -17,6 +18,7 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/highlight"
 | 
						"code.gitea.io/gitea/modules/highlight"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/templates"
 | 
						"code.gitea.io/gitea/modules/templates"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
| 
						 | 
					@ -54,7 +56,7 @@ func RefBlame(ctx *context.Context) {
 | 
				
			||||||
	rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
 | 
						rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(ctx.Repo.TreePath) > 0 {
 | 
						if len(ctx.Repo.TreePath) > 0 {
 | 
				
			||||||
		treeLink += "/" + ctx.Repo.TreePath
 | 
							treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var treeNames []string
 | 
						var treeNames []string
 | 
				
			||||||
| 
						 | 
					@ -85,7 +87,7 @@ func RefBlame(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["TreeNames"] = treeNames
 | 
						ctx.Data["TreeNames"] = treeNames
 | 
				
			||||||
	ctx.Data["BranchLink"] = branchLink
 | 
						ctx.Data["BranchLink"] = branchLink
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath
 | 
						ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
 | 
				
			||||||
	ctx.Data["PageIsViewCode"] = true
 | 
						ctx.Data["PageIsViewCode"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["IsBlame"] = true
 | 
						ctx.Data["IsBlame"] = true
 | 
				
			||||||
| 
						 | 
					@ -236,8 +238,8 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
 | 
				
			||||||
				br.RepoLink = repoLink
 | 
									br.RepoLink = repoLink
 | 
				
			||||||
				br.PartSha = part.Sha
 | 
									br.PartSha = part.Sha
 | 
				
			||||||
				br.PreviousSha = previousSha
 | 
									br.PreviousSha = previousSha
 | 
				
			||||||
				br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, previousSha, ctx.Repo.TreePath)
 | 
									br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(previousSha), util.PathEscapeSegments(ctx.Repo.TreePath))
 | 
				
			||||||
				br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, part.Sha)
 | 
									br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(part.Sha))
 | 
				
			||||||
				br.CommitMessage = commit.CommitMessage
 | 
									br.CommitMessage = commit.CommitMessage
 | 
				
			||||||
				br.CommitSince = commitSince
 | 
									br.CommitSince = commitSince
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,6 @@ package repo
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"path"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
| 
						 | 
					@ -323,8 +322,7 @@ func Diff(ctx *context.Context) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	headTarget := path.Join(userName, repoName)
 | 
						setCompareContext(ctx, parentCommit, commit, userName, repoName)
 | 
				
			||||||
	setCompareContext(ctx, parentCommit, commit, headTarget)
 | 
					 | 
				
			||||||
	ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
 | 
						ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
 | 
				
			||||||
	ctx.Data["Commit"] = commit
 | 
						ctx.Data["Commit"] = commit
 | 
				
			||||||
	ctx.Data["Diff"] = diff
 | 
						ctx.Data["Diff"] = diff
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ import (
 | 
				
			||||||
	"html"
 | 
						"html"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"path"
 | 
						"net/url"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,7 @@ const (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// setCompareContext sets context data.
 | 
					// setCompareContext sets context data.
 | 
				
			||||||
func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headTarget string) {
 | 
					func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headOwner, headName string) {
 | 
				
			||||||
	ctx.Data["BaseCommit"] = base
 | 
						ctx.Data["BaseCommit"] = base
 | 
				
			||||||
	ctx.Data["HeadCommit"] = head
 | 
						ctx.Data["HeadCommit"] = head
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,22 +54,28 @@ func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit,
 | 
				
			||||||
		return blob
 | 
							return blob
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	setPathsCompareContext(ctx, base, head, headTarget)
 | 
						setPathsCompareContext(ctx, base, head, headOwner, headName)
 | 
				
			||||||
	setImageCompareContext(ctx)
 | 
						setImageCompareContext(ctx)
 | 
				
			||||||
	setCsvCompareContext(ctx)
 | 
						setCsvCompareContext(ctx)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// setPathsCompareContext sets context data for source and raw paths
 | 
					// SourceCommitURL creates a relative URL for a commit in the given repository
 | 
				
			||||||
func setPathsCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headTarget string) {
 | 
					func SourceCommitURL(owner, name string, commit *git.Commit) string {
 | 
				
			||||||
	sourcePath := setting.AppSubURL + "/%s/src/commit/%s"
 | 
						return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/src/commit/" + url.PathEscape(commit.ID.String())
 | 
				
			||||||
	rawPath := setting.AppSubURL + "/%s/raw/commit/%s"
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["SourcePath"] = fmt.Sprintf(sourcePath, headTarget, head.ID)
 | 
					// RawCommitURL creates a relative URL for the raw commit in the given repository
 | 
				
			||||||
	ctx.Data["RawPath"] = fmt.Sprintf(rawPath, headTarget, head.ID)
 | 
					func RawCommitURL(owner, name string, commit *git.Commit) string {
 | 
				
			||||||
 | 
						return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/raw/commit/" + url.PathEscape(commit.ID.String())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// setPathsCompareContext sets context data for source and raw paths
 | 
				
			||||||
 | 
					func setPathsCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headOwner, headName string) {
 | 
				
			||||||
 | 
						ctx.Data["SourcePath"] = SourceCommitURL(headOwner, headName, head)
 | 
				
			||||||
 | 
						ctx.Data["RawPath"] = RawCommitURL(headOwner, headName, head)
 | 
				
			||||||
	if base != nil {
 | 
						if base != nil {
 | 
				
			||||||
		baseTarget := path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
							ctx.Data["BeforeSourcePath"] = SourceCommitURL(headOwner, headName, head)
 | 
				
			||||||
		ctx.Data["BeforeSourcePath"] = fmt.Sprintf(sourcePath, baseTarget, base.ID)
 | 
							ctx.Data["BeforeRawPath"] = RawCommitURL(headOwner, headName, head)
 | 
				
			||||||
		ctx.Data["BeforeRawPath"] = fmt.Sprintf(rawPath, baseTarget, base.ID)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -619,8 +625,7 @@ func PrepareCompareDiff(
 | 
				
			||||||
	ctx.Data["Username"] = ci.HeadUser.Name
 | 
						ctx.Data["Username"] = ci.HeadUser.Name
 | 
				
			||||||
	ctx.Data["Reponame"] = ci.HeadRepo.Name
 | 
						ctx.Data["Reponame"] = ci.HeadRepo.Name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	headTarget := path.Join(ci.HeadUser.Name, repo.Name)
 | 
						setCompareContext(ctx, baseCommit, headCommit, ci.HeadUser.Name, repo.Name)
 | 
				
			||||||
	setCompareContext(ctx, baseCommit, headCommit, headTarget)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -204,7 +204,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
 | 
				
			||||||
	ctx.Data["TreePath"] = form.TreePath
 | 
						ctx.Data["TreePath"] = form.TreePath
 | 
				
			||||||
	ctx.Data["TreeNames"] = treeNames
 | 
						ctx.Data["TreeNames"] = treeNames
 | 
				
			||||||
	ctx.Data["TreePaths"] = treePaths
 | 
						ctx.Data["TreePaths"] = treePaths
 | 
				
			||||||
	ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + ctx.Repo.BranchName
 | 
						ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
 | 
				
			||||||
	ctx.Data["FileContent"] = form.Content
 | 
						ctx.Data["FileContent"] = form.Content
 | 
				
			||||||
	ctx.Data["commit_summary"] = form.CommitSummary
 | 
						ctx.Data["commit_summary"] = form.CommitSummary
 | 
				
			||||||
	ctx.Data["commit_message"] = form.CommitMessage
 | 
						ctx.Data["commit_message"] = form.CommitMessage
 | 
				
			||||||
| 
						 | 
					@ -299,9 +299,9 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
 | 
				
			||||||
				ctx.Error(http.StatusInternalServerError, err.Error())
 | 
									ctx.Error(http.StatusInternalServerError, err.Error())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if models.IsErrCommitIDDoesNotMatch(err) {
 | 
							} else if models.IsErrCommitIDDoesNotMatch(err) {
 | 
				
			||||||
			ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplEditFile, &form)
 | 
								ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplEditFile, &form)
 | 
				
			||||||
		} else if git.IsErrPushOutOfDate(err) {
 | 
							} else if git.IsErrPushOutOfDate(err) {
 | 
				
			||||||
			ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
 | 
								ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
 | 
				
			||||||
		} else if git.IsErrPushRejected(err) {
 | 
							} else if git.IsErrPushRejected(err) {
 | 
				
			||||||
			errPushRej := err.(*git.ErrPushRejected)
 | 
								errPushRej := err.(*git.ErrPushRejected)
 | 
				
			||||||
			if len(errPushRej.Message) == 0 {
 | 
								if len(errPushRej.Message) == 0 {
 | 
				
			||||||
| 
						 | 
					@ -495,7 +495,7 @@ func DeleteFilePost(ctx *context.Context) {
 | 
				
			||||||
				ctx.Error(http.StatusInternalServerError, err.Error())
 | 
									ctx.Error(http.StatusInternalServerError, err.Error())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if models.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
 | 
							} else if models.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
 | 
				
			||||||
			ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplDeleteFile, &form)
 | 
								ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
 | 
				
			||||||
		} else if git.IsErrPushRejected(err) {
 | 
							} else if git.IsErrPushRejected(err) {
 | 
				
			||||||
			errPushRej := err.(*git.ErrPushRejected)
 | 
								errPushRej := err.(*git.ErrPushRejected)
 | 
				
			||||||
			if len(errPushRej.Message) == 0 {
 | 
								if len(errPushRej.Message) == 0 {
 | 
				
			||||||
| 
						 | 
					@ -602,7 +602,7 @@ func UploadFilePost(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["TreePath"] = form.TreePath
 | 
						ctx.Data["TreePath"] = form.TreePath
 | 
				
			||||||
	ctx.Data["TreeNames"] = treeNames
 | 
						ctx.Data["TreeNames"] = treeNames
 | 
				
			||||||
	ctx.Data["TreePaths"] = treePaths
 | 
						ctx.Data["TreePaths"] = treePaths
 | 
				
			||||||
	ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + branchName
 | 
						ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName)
 | 
				
			||||||
	ctx.Data["commit_summary"] = form.CommitSummary
 | 
						ctx.Data["commit_summary"] = form.CommitSummary
 | 
				
			||||||
	ctx.Data["commit_message"] = form.CommitMessage
 | 
						ctx.Data["commit_message"] = form.CommitMessage
 | 
				
			||||||
	ctx.Data["commit_choice"] = form.CommitChoice
 | 
						ctx.Data["commit_choice"] = form.CommitChoice
 | 
				
			||||||
| 
						 | 
					@ -698,7 +698,7 @@ func UploadFilePost(ctx *context.Context) {
 | 
				
			||||||
			branchErr := err.(models.ErrBranchAlreadyExists)
 | 
								branchErr := err.(models.ErrBranchAlreadyExists)
 | 
				
			||||||
			ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplUploadFile, &form)
 | 
								ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplUploadFile, &form)
 | 
				
			||||||
		} else if git.IsErrPushOutOfDate(err) {
 | 
							} else if git.IsErrPushOutOfDate(err) {
 | 
				
			||||||
			ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+ctx.Repo.CommitID+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
 | 
								ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(ctx.Repo.CommitID)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
 | 
				
			||||||
		} else if git.IsErrPushRejected(err) {
 | 
							} else if git.IsErrPushRejected(err) {
 | 
				
			||||||
			errPushRej := err.(*git.ErrPushRejected)
 | 
								errPushRej := err.(*git.ErrPushRejected)
 | 
				
			||||||
			if len(errPushRej.Message) == 0 {
 | 
								if len(errPushRej.Message) == 0 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
| 
						 | 
					@ -106,7 +107,7 @@ func MustAllowPulls(ctx *context.Context) {
 | 
				
			||||||
	// User can send pull request if owns a forked repository.
 | 
						// User can send pull request if owns a forked repository.
 | 
				
			||||||
	if ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID) {
 | 
						if ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID) {
 | 
				
			||||||
		ctx.Repo.PullRequest.Allowed = true
 | 
							ctx.Repo.PullRequest.Allowed = true
 | 
				
			||||||
		ctx.Repo.PullRequest.HeadInfo = ctx.User.Name + ":" + ctx.Repo.BranchName
 | 
							ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.User.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -764,7 +765,7 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleDirs [
 | 
				
			||||||
					for _, repoLabel := range repoLabels {
 | 
										for _, repoLabel := range repoLabels {
 | 
				
			||||||
						if strings.EqualFold(repoLabel.Name, metaLabel) {
 | 
											if strings.EqualFold(repoLabel.Name, metaLabel) {
 | 
				
			||||||
							repoLabel.IsChecked = true
 | 
												repoLabel.IsChecked = true
 | 
				
			||||||
							labelIDs = append(labelIDs, fmt.Sprintf("%d", repoLabel.ID))
 | 
												labelIDs = append(labelIDs, strconv.FormatInt(repoLabel.ID, 10))
 | 
				
			||||||
							break
 | 
												break
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
| 
						 | 
					@ -983,6 +984,7 @@ func NewIssuePost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issue := &models.Issue{
 | 
						issue := &models.Issue{
 | 
				
			||||||
		RepoID:      repo.ID,
 | 
							RepoID:      repo.ID,
 | 
				
			||||||
 | 
							Repo:        repo,
 | 
				
			||||||
		Title:       form.Title,
 | 
							Title:       form.Title,
 | 
				
			||||||
		PosterID:    ctx.User.ID,
 | 
							PosterID:    ctx.User.ID,
 | 
				
			||||||
		Poster:      ctx.User,
 | 
							Poster:      ctx.User,
 | 
				
			||||||
| 
						 | 
					@ -1009,9 +1011,9 @@ func NewIssuePost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
 | 
						log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
 | 
				
			||||||
	if ctx.FormString("redirect_after_creation") == "project" {
 | 
						if ctx.FormString("redirect_after_creation") == "project" {
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/projects/" + fmt.Sprint(form.ProjectID))
 | 
							ctx.Redirect(ctx.Repo.RepoLink + "/projects/" + strconv.FormatInt(form.ProjectID, 10))
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + fmt.Sprint(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1097,13 +1099,16 @@ func ViewIssue(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if issue.Repo == nil {
 | 
				
			||||||
 | 
							issue.Repo = ctx.Repo.Repository
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Make sure type and URL matches.
 | 
						// Make sure type and URL matches.
 | 
				
			||||||
	if ctx.Params(":type") == "issues" && issue.IsPull {
 | 
						if ctx.Params(":type") == "issues" && issue.IsPull {
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	} else if ctx.Params(":type") == "pulls" && !issue.IsPull {
 | 
						} else if ctx.Params(":type") == "pulls" && !issue.IsPull {
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + fmt.Sprint(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1496,7 +1501,7 @@ func ViewIssue(ctx *context.Context) {
 | 
				
			||||||
						log.Error("IsProtectedBranch: %v", err)
 | 
											log.Error("IsProtectedBranch: %v", err)
 | 
				
			||||||
					} else if !protected {
 | 
										} else if !protected {
 | 
				
			||||||
						canDelete = true
 | 
											canDelete = true
 | 
				
			||||||
						ctx.Data["DeleteBranchLink"] = ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index) + "/cleanup"
 | 
											ctx.Data["DeleteBranchLink"] = issue.Link() + "/cleanup"
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -1624,7 +1629,7 @@ func ViewIssue(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["NumParticipants"] = len(participants)
 | 
						ctx.Data["NumParticipants"] = len(participants)
 | 
				
			||||||
	ctx.Data["Issue"] = issue
 | 
						ctx.Data["Issue"] = issue
 | 
				
			||||||
	ctx.Data["ReadOnly"] = false
 | 
						ctx.Data["ReadOnly"] = false
 | 
				
			||||||
	ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + ctx.Data["Link"].(string)
 | 
						ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + url.QueryEscape(ctx.Data["Link"].(string))
 | 
				
			||||||
	ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID)
 | 
						ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID)
 | 
				
			||||||
	ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
 | 
						ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
 | 
				
			||||||
	ctx.Data["HasProjectsWritePermission"] = ctx.Repo.CanWrite(unit.TypeProjects)
 | 
						ctx.Data["HasProjectsWritePermission"] = ctx.Repo.CanWrite(unit.TypeProjects)
 | 
				
			||||||
| 
						 | 
					@ -1773,7 +1778,7 @@ func UpdateIssueContent(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	content, err := markdown.RenderString(&markup.RenderContext{
 | 
						content, err := markdown.RenderString(&markup.RenderContext{
 | 
				
			||||||
		URLPrefix: ctx.FormString("context"),
 | 
							URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
 | 
				
			||||||
		Metas:     ctx.Repo.Repository.ComposeMetas(),
 | 
							Metas:     ctx.Repo.Repository.ComposeMetas(),
 | 
				
			||||||
		GitRepo:   ctx.Repo.GitRepo,
 | 
							GitRepo:   ctx.Repo.GitRepo,
 | 
				
			||||||
		Ctx:       ctx,
 | 
							Ctx:       ctx,
 | 
				
			||||||
| 
						 | 
					@ -2205,7 +2210,7 @@ func UpdateCommentContent(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	content, err := markdown.RenderString(&markup.RenderContext{
 | 
						content, err := markdown.RenderString(&markup.RenderContext{
 | 
				
			||||||
		URLPrefix: ctx.FormString("context"),
 | 
							URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
 | 
				
			||||||
		Metas:     ctx.Repo.Repository.ComposeMetas(),
 | 
							Metas:     ctx.Repo.Repository.ComposeMetas(),
 | 
				
			||||||
		GitRepo:   ctx.Repo.GitRepo,
 | 
							GitRepo:   ctx.Repo.GitRepo,
 | 
				
			||||||
		Ctx:       ctx,
 | 
							Ctx:       ctx,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,6 +94,7 @@ func GetActiveStopwatch(c *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.Data["ActiveStopwatch"] = StopwatchTmplInfo{
 | 
						c.Data["ActiveStopwatch"] = StopwatchTmplInfo{
 | 
				
			||||||
 | 
							issue.Link(),
 | 
				
			||||||
		issue.Repo.FullName(),
 | 
							issue.Repo.FullName(),
 | 
				
			||||||
		issue.Index,
 | 
							issue.Index,
 | 
				
			||||||
		sw.Seconds() + 1, // ensure time is never zero in ui
 | 
							sw.Seconds() + 1, // ensure time is never zero in ui
 | 
				
			||||||
| 
						 | 
					@ -102,6 +103,7 @@ func GetActiveStopwatch(c *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// StopwatchTmplInfo is a view on a stopwatch specifically for template rendering
 | 
					// StopwatchTmplInfo is a view on a stopwatch specifically for template rendering
 | 
				
			||||||
type StopwatchTmplInfo struct {
 | 
					type StopwatchTmplInfo struct {
 | 
				
			||||||
 | 
						IssueLink  string
 | 
				
			||||||
	RepoSlug   string
 | 
						RepoSlug   string
 | 
				
			||||||
	IssueIndex int64
 | 
						IssueIndex int64
 | 
				
			||||||
	Seconds    int64
 | 
						Seconds    int64
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ import (
 | 
				
			||||||
	gotemplate "html/template"
 | 
						gotemplate "html/template"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
| 
						 | 
					@ -285,7 +286,7 @@ func LFSFileGet(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fileSize := meta.Size
 | 
						fileSize := meta.Size
 | 
				
			||||||
	ctx.Data["FileSize"] = meta.Size
 | 
						ctx.Data["FileSize"] = meta.Size
 | 
				
			||||||
	ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s.git/info/lfs/objects/%s/%s", setting.AppURL, ctx.Repo.Repository.FullName(), meta.Oid, "direct")
 | 
						ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s/%s.git/info/lfs/objects/%s/%s", setting.AppURL, url.PathEscape(ctx.Repo.Repository.OwnerName), url.PathEscape(ctx.Repo.Repository.Name), url.PathEscape(meta.Oid), "direct")
 | 
				
			||||||
	switch {
 | 
						switch {
 | 
				
			||||||
	case isRepresentableAsText:
 | 
						case isRepresentableAsText:
 | 
				
			||||||
		if st.IsSvgImage() {
 | 
							if st.IsSvgImage() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
| 
						 | 
					@ -237,7 +238,7 @@ func MigratePost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = task.MigrateRepository(ctx.User, ctxUser, opts)
 | 
						err = task.MigrateRepository(ctx.User, ctxUser, opts)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		ctx.Redirect(ctxUser.HomeLink() + "/" + opts.RepoName)
 | 
							ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(opts.RepoName))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
| 
						 | 
					@ -244,7 +245,7 @@ func ChangeMilestoneStatus(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=" + ctx.Params(":action"))
 | 
						ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=" + url.QueryEscape(ctx.Params(":action")))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteMilestone delete a milestone
 | 
					// DeleteMilestone delete a milestone
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ package repo
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
| 
						 | 
					@ -173,7 +174,7 @@ func ChangeProjectStatus(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Redirect(ctx.Repo.RepoLink + "/projects?state=" + ctx.Params(":action"))
 | 
						ctx.Redirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action")))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteProject delete a project
 | 
					// DeleteProject delete a project
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,8 +10,10 @@ import (
 | 
				
			||||||
	"crypto/subtle"
 | 
						"crypto/subtle"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"html"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"path"
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +36,6 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/services/gitdiff"
 | 
						"code.gitea.io/gitea/services/gitdiff"
 | 
				
			||||||
	pull_service "code.gitea.io/gitea/services/pull"
 | 
						pull_service "code.gitea.io/gitea/services/pull"
 | 
				
			||||||
	repo_service "code.gitea.io/gitea/services/repository"
 | 
						repo_service "code.gitea.io/gitea/services/repository"
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
| 
						 | 
					@ -109,8 +110,7 @@ func getForkRepository(ctx *context.Context) *models.Repository {
 | 
				
			||||||
	ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate
 | 
						ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate
 | 
				
			||||||
	canForkToUser := forkRepo.OwnerID != ctx.User.ID && !ctx.User.HasForkedRepo(forkRepo.ID)
 | 
						canForkToUser := forkRepo.OwnerID != ctx.User.ID && !ctx.User.HasForkedRepo(forkRepo.ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name
 | 
						ctx.Data["ForkRepo"] = forkRepo
 | 
				
			||||||
	ctx.Data["ForkFromOwnerID"] = forkRepo.Owner.ID
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := ctx.User.GetOwnedOrganizations(); err != nil {
 | 
						if err := ctx.User.GetOwnedOrganizations(); err != nil {
 | 
				
			||||||
		ctx.ServerError("GetOwnedOrganizations", err)
 | 
							ctx.ServerError("GetOwnedOrganizations", err)
 | 
				
			||||||
| 
						 | 
					@ -202,7 +202,7 @@ func ForkPost(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		repo, has := models.HasForkedRepo(ctxUser.ID, traverseParentRepo.ID)
 | 
							repo, has := models.HasForkedRepo(ctxUser.ID, traverseParentRepo.ID)
 | 
				
			||||||
		if has {
 | 
							if has {
 | 
				
			||||||
			ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
 | 
								ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !traverseParentRepo.IsFork {
 | 
							if !traverseParentRepo.IsFork {
 | 
				
			||||||
| 
						 | 
					@ -248,7 +248,7 @@ func ForkPost(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name)
 | 
						log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name)
 | 
				
			||||||
	ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
 | 
						ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func checkPullInfo(ctx *context.Context) *models.Issue {
 | 
					func checkPullInfo(ctx *context.Context) *models.Issue {
 | 
				
			||||||
| 
						 | 
					@ -682,8 +682,7 @@ func ViewPullFiles(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	headTarget := path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
						setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
				
			||||||
	setCompareContext(ctx, baseCommit, commit, headTarget)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["RequireHighlightJS"] = true
 | 
						ctx.Data["RequireHighlightJS"] = true
 | 
				
			||||||
	ctx.Data["RequireSimpleMDE"] = true
 | 
						ctx.Data["RequireSimpleMDE"] = true
 | 
				
			||||||
| 
						 | 
					@ -746,7 +745,7 @@ func UpdatePullRequest(ctx *context.Context) {
 | 
				
			||||||
	// ToDo: add check if maintainers are allowed to change branch ... (need migration & co)
 | 
						// ToDo: add check if maintainers are allowed to change branch ... (need migration & co)
 | 
				
			||||||
	if (!allowedUpdateByMerge && !rebase) || (rebase && !allowedUpdateByRebase) {
 | 
						if (!allowedUpdateByMerge && !rebase) || (rebase && !allowedUpdateByRebase) {
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
 | 
							ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -766,7 +765,7 @@ func UpdatePullRequest(ctx *context.Context) {
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Flash.Error(flashError)
 | 
								ctx.Flash.Error(flashError)
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		} else if models.IsErrRebaseConflicts(err) {
 | 
							} else if models.IsErrRebaseConflicts(err) {
 | 
				
			||||||
			conflictError := err.(models.ErrRebaseConflicts)
 | 
								conflictError := err.(models.ErrRebaseConflicts)
 | 
				
			||||||
| 
						 | 
					@ -780,19 +779,19 @@ func UpdatePullRequest(ctx *context.Context) {
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Flash.Error(flashError)
 | 
								ctx.Flash.Error(flashError)
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.Flash.Error(err.Error())
 | 
							ctx.Flash.Error(err.Error())
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	time.Sleep(1 * time.Second)
 | 
						time.Sleep(1 * time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Flash.Success(ctx.Tr("repo.pulls.update_branch_success"))
 | 
						ctx.Flash.Success(ctx.Tr("repo.pulls.update_branch_success"))
 | 
				
			||||||
	ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
 | 
						ctx.Redirect(issue.Link())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MergePullRequest response for merging pull request
 | 
					// MergePullRequest response for merging pull request
 | 
				
			||||||
| 
						 | 
					@ -805,11 +804,11 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
	if issue.IsClosed {
 | 
						if issue.IsClosed {
 | 
				
			||||||
		if issue.IsPull {
 | 
							if issue.IsPull {
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("repo.pulls.is_closed"))
 | 
								ctx.Flash.Error(ctx.Tr("repo.pulls.is_closed"))
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.issues.closed_title"))
 | 
							ctx.Flash.Error(ctx.Tr("repo.issues.closed_title"))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + fmt.Sprint(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -822,13 +821,13 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !allowedMerge {
 | 
						if !allowedMerge {
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
 | 
							ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if pr.HasMerged {
 | 
						if pr.HasMerged {
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.pulls.has_merged"))
 | 
							ctx.Flash.Error(ctx.Tr("repo.pulls.has_merged"))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -837,11 +836,11 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
		if err = pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
 | 
							if err = pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
 | 
				
			||||||
			if models.IsErrInvalidMergeStyle(err) {
 | 
								if models.IsErrInvalidMergeStyle(err) {
 | 
				
			||||||
				ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
 | 
									ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
 | 
				
			||||||
				ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
 | 
									ctx.Redirect(issue.Link())
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			} else if strings.Contains(err.Error(), "Wrong commit ID") {
 | 
								} else if strings.Contains(err.Error(), "Wrong commit ID") {
 | 
				
			||||||
				ctx.Flash.Error(ctx.Tr("repo.pulls.wrong_commit_id"))
 | 
									ctx.Flash.Error(ctx.Tr("repo.pulls.wrong_commit_id"))
 | 
				
			||||||
				ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
 | 
									ctx.Redirect(issue.Link())
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -849,19 +848,19 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !pr.CanAutoMerge() {
 | 
						if !pr.CanAutoMerge() {
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
 | 
							ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if pr.IsWorkInProgress() {
 | 
						if pr.IsWorkInProgress() {
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_wip"))
 | 
							ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_wip"))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -875,14 +874,14 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		} else if !isRepoAdmin {
 | 
							} else if !isRepoAdmin {
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
 | 
								ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.HasError() {
 | 
						if ctx.HasError() {
 | 
				
			||||||
		ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
 | 
							ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -914,14 +913,14 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !noDeps {
 | 
						if !noDeps {
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
 | 
							ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
							ctx.Redirect(issue.Link())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
 | 
						if err = pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
 | 
				
			||||||
		if models.IsErrInvalidMergeStyle(err) {
 | 
							if models.IsErrInvalidMergeStyle(err) {
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
 | 
								ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		} else if models.IsErrMergeConflicts(err) {
 | 
							} else if models.IsErrMergeConflicts(err) {
 | 
				
			||||||
			conflictError := err.(models.ErrMergeConflicts)
 | 
								conflictError := err.(models.ErrMergeConflicts)
 | 
				
			||||||
| 
						 | 
					@ -935,7 +934,7 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Flash.Error(flashError)
 | 
								ctx.Flash.Error(flashError)
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		} else if models.IsErrRebaseConflicts(err) {
 | 
							} else if models.IsErrRebaseConflicts(err) {
 | 
				
			||||||
			conflictError := err.(models.ErrRebaseConflicts)
 | 
								conflictError := err.(models.ErrRebaseConflicts)
 | 
				
			||||||
| 
						 | 
					@ -949,17 +948,17 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Flash.Error(flashError)
 | 
								ctx.Flash.Error(flashError)
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		} else if models.IsErrMergeUnrelatedHistories(err) {
 | 
							} else if models.IsErrMergeUnrelatedHistories(err) {
 | 
				
			||||||
			log.Debug("MergeUnrelatedHistories error: %v", err)
 | 
								log.Debug("MergeUnrelatedHistories error: %v", err)
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories"))
 | 
								ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories"))
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		} else if git.IsErrPushOutOfDate(err) {
 | 
							} else if git.IsErrPushOutOfDate(err) {
 | 
				
			||||||
			log.Debug("MergePushOutOfDate error: %v", err)
 | 
								log.Debug("MergePushOutOfDate error: %v", err)
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date"))
 | 
								ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date"))
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		} else if git.IsErrPushRejected(err) {
 | 
							} else if git.IsErrPushRejected(err) {
 | 
				
			||||||
			log.Debug("MergePushRejected error: %v", err)
 | 
								log.Debug("MergePushRejected error: %v", err)
 | 
				
			||||||
| 
						 | 
					@ -979,7 +978,7 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				ctx.Flash.Error(flashError)
 | 
									ctx.Flash.Error(flashError)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
								ctx.Redirect(issue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.ServerError("Merge", err)
 | 
							ctx.ServerError("Merge", err)
 | 
				
			||||||
| 
						 | 
					@ -1008,7 +1007,7 @@ func MergePullRequest(ctx *context.Context) {
 | 
				
			||||||
		deleteBranch(ctx, pr, headRepo)
 | 
							deleteBranch(ctx, pr, headRepo)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
 | 
						ctx.Redirect(issue.Link())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func stopTimerIfAvailable(user *models.User, issue *models.Issue) error {
 | 
					func stopTimerIfAvailable(user *models.User, issue *models.Issue) error {
 | 
				
			||||||
| 
						 | 
					@ -1097,6 +1096,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pullIssue := &models.Issue{
 | 
						pullIssue := &models.Issue{
 | 
				
			||||||
		RepoID:      repo.ID,
 | 
							RepoID:      repo.ID,
 | 
				
			||||||
 | 
							Repo:        repo,
 | 
				
			||||||
		Title:       form.Title,
 | 
							Title:       form.Title,
 | 
				
			||||||
		PosterID:    ctx.User.ID,
 | 
							PosterID:    ctx.User.ID,
 | 
				
			||||||
		Poster:      ctx.User,
 | 
							Poster:      ctx.User,
 | 
				
			||||||
| 
						 | 
					@ -1138,7 +1138,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				ctx.Flash.Error(flashError)
 | 
									ctx.Flash.Error(flashError)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pullIssue.Index))
 | 
								ctx.Redirect(pullIssue.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.ServerError("NewPullRequest", err)
 | 
							ctx.ServerError("NewPullRequest", err)
 | 
				
			||||||
| 
						 | 
					@ -1146,7 +1146,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
 | 
						log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
 | 
				
			||||||
	ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pullIssue.Index))
 | 
						ctx.Redirect(pullIssue.Link())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TriggerTask response for a trigger task request
 | 
					// TriggerTask response for a trigger task request
 | 
				
			||||||
| 
						 | 
					@ -1261,7 +1261,7 @@ func CleanUpPullRequest(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
							ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
				
			||||||
			"redirect": pr.BaseRepo.Link() + "/pulls/" + fmt.Sprint(issue.Index),
 | 
								"redirect": issue.Link(),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1369,7 +1369,7 @@ func UpdatePullRequestTarget(ctx *context.Context) {
 | 
				
			||||||
			err := err.(models.ErrPullRequestAlreadyExists)
 | 
								err := err.(models.ErrPullRequestAlreadyExists)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			RepoRelPath := ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
 | 
								RepoRelPath := ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
 | 
				
			||||||
			errorMessage := ctx.Tr("repo.pulls.has_pull_request", ctx.Repo.RepoLink, RepoRelPath, err.IssueID)
 | 
								errorMessage := ctx.Tr("repo.pulls.has_pull_request", html.EscapeString(ctx.Repo.RepoLink+"/pulls/"+strconv.FormatInt(err.IssueID, 10)), html.EscapeString(RepoRelPath), err.IssueID) // FIXME: Creates url insidde locale string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Flash.Error(errorMessage)
 | 
								ctx.Flash.Error(errorMessage)
 | 
				
			||||||
			ctx.JSON(http.StatusConflict, map[string]interface{}{
 | 
								ctx.JSON(http.StatusConflict, map[string]interface{}{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup/markdown"
 | 
						"code.gitea.io/gitea/modules/markup/markdown"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/upload"
 | 
						"code.gitea.io/gitea/modules/upload"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web"
 | 
						"code.gitea.io/gitea/modules/web"
 | 
				
			||||||
	"code.gitea.io/gitea/services/forms"
 | 
						"code.gitea.io/gitea/services/forms"
 | 
				
			||||||
	releaseservice "code.gitea.io/gitea/services/release"
 | 
						releaseservice "code.gitea.io/gitea/services/release"
 | 
				
			||||||
| 
						 | 
					@ -350,7 +351,7 @@ func NewReleasePost(ctx *context.Context) {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
 | 
								ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
 | 
				
			||||||
			ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + form.TagName)
 | 
								ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -244,7 +244,7 @@ func CreatePost(ctx *context.Context) {
 | 
				
			||||||
		repo, err = repo_service.GenerateRepository(ctx.User, ctxUser, templateRepo, opts)
 | 
							repo, err = repo_service.GenerateRepository(ctx.User, ctxUser, templateRepo, opts)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			log.Trace("Repository generated [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
 | 
								log.Trace("Repository generated [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
 | 
				
			||||||
			ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
 | 
								ctx.Redirect(repo.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					@ -263,7 +263,7 @@ func CreatePost(ctx *context.Context) {
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
 | 
								log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
 | 
				
			||||||
			ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
 | 
								ctx.Redirect(repo.Link())
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -615,7 +615,7 @@ func SettingsPost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
 | 
							log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
 | 
				
			||||||
		ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName()))
 | 
							ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName()))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.Owner.HomeLink() + "/" + repo.Name + "/settings")
 | 
							ctx.Redirect(repo.Link() + "/settings")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case "cancel_transfer":
 | 
						case "cancel_transfer":
 | 
				
			||||||
		if !ctx.Repo.IsOwner() {
 | 
							if !ctx.Repo.IsOwner() {
 | 
				
			||||||
| 
						 | 
					@ -627,7 +627,7 @@ func SettingsPost(ctx *context.Context) {
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if models.IsErrNoPendingTransfer(err) {
 | 
								if models.IsErrNoPendingTransfer(err) {
 | 
				
			||||||
				ctx.Flash.Error("repo.settings.transfer_abort_invalid")
 | 
									ctx.Flash.Error("repo.settings.transfer_abort_invalid")
 | 
				
			||||||
				ctx.Redirect(ctx.User.HomeLink() + "/" + repo.Name + "/settings")
 | 
									ctx.Redirect(repo.Link() + "/settings")
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				ctx.ServerError("GetPendingRepositoryTransfer", err)
 | 
									ctx.ServerError("GetPendingRepositoryTransfer", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -647,7 +647,7 @@ func SettingsPost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name)
 | 
							log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name)
 | 
				
			||||||
		ctx.Flash.Success(ctx.Tr("repo.settings.transfer_abort_success", repoTransfer.Recipient.Name))
 | 
							ctx.Flash.Success(ctx.Tr("repo.settings.transfer_abort_success", repoTransfer.Recipient.Name))
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.Owner.HomeLink() + "/" + repo.Name + "/settings")
 | 
							ctx.Redirect(repo.Link() + "/settings")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case "delete":
 | 
						case "delete":
 | 
				
			||||||
		if !ctx.Repo.IsOwner() {
 | 
							if !ctx.Repo.IsOwner() {
 | 
				
			||||||
| 
						 | 
					@ -796,7 +796,7 @@ func Collaboration(ctx *context.Context) {
 | 
				
			||||||
func CollaborationPost(ctx *context.Context) {
 | 
					func CollaborationPost(ctx *context.Context) {
 | 
				
			||||||
	name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("collaborator")))
 | 
						name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("collaborator")))
 | 
				
			||||||
	if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
 | 
						if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
 | 
				
			||||||
		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
 | 
							ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -804,7 +804,7 @@ func CollaborationPost(ctx *context.Context) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrUserNotExist(err) {
 | 
							if models.IsErrUserNotExist(err) {
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
 | 
								ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
 | 
				
			||||||
			ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
 | 
								ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.ServerError("GetUserByName", err)
 | 
								ctx.ServerError("GetUserByName", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -813,14 +813,14 @@ func CollaborationPost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !u.IsActive {
 | 
						if !u.IsActive {
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_inactive_user"))
 | 
							ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_inactive_user"))
 | 
				
			||||||
		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
 | 
							ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Organization is not allowed to be added as a collaborator.
 | 
						// Organization is not allowed to be added as a collaborator.
 | 
				
			||||||
	if u.IsOrganization() {
 | 
						if u.IsOrganization() {
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("repo.settings.org_not_allowed_to_be_collaborator"))
 | 
							ctx.Flash.Error(ctx.Tr("repo.settings.org_not_allowed_to_be_collaborator"))
 | 
				
			||||||
		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
 | 
							ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -840,7 +840,7 @@ func CollaborationPost(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
 | 
						ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
 | 
				
			||||||
	ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
 | 
						ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ChangeCollaborationAccessMode response for changing access of a collaboration
 | 
					// ChangeCollaborationAccessMode response for changing access of a collaboration
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,7 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web"
 | 
						"code.gitea.io/gitea/modules/web"
 | 
				
			||||||
	"code.gitea.io/gitea/services/forms"
 | 
						"code.gitea.io/gitea/services/forms"
 | 
				
			||||||
	pull_service "code.gitea.io/gitea/services/pull"
 | 
						pull_service "code.gitea.io/gitea/services/pull"
 | 
				
			||||||
| 
						 | 
					@ -89,7 +90,7 @@ func ProtectedBranchPost(ctx *context.Context) {
 | 
				
			||||||
		log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
 | 
							log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
 | 
							ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
 | 
				
			||||||
		ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
 | 
							ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		ctx.NotFound("", nil)
 | 
							ctx.NotFound("", nil)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -197,7 +198,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if f.RequiredApprovals < 0 {
 | 
							if f.RequiredApprovals < 0 {
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min"))
 | 
								ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min"))
 | 
				
			||||||
			ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch))
 | 
								ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch)))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64
 | 
							var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64
 | 
				
			||||||
| 
						 | 
					@ -274,7 +275,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.Flash.Success(ctx.Tr("repo.settings.update_protect_branch_success", branch))
 | 
							ctx.Flash.Success(ctx.Tr("repo.settings.update_protect_branch_success", branch))
 | 
				
			||||||
		ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch))
 | 
							ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch)))
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if protectBranch != nil {
 | 
							if protectBranch != nil {
 | 
				
			||||||
			if err := ctx.Repo.Repository.DeleteProtectedBranch(protectBranch.ID); err != nil {
 | 
								if err := ctx.Repo.Repository.DeleteProtectedBranch(protectBranch.ID); err != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,7 @@ func NewProtectedTagPost(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
 | 
						ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
 | 
				
			||||||
	ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
 | 
						ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// EditProtectedTag render the page to edit a protect tag
 | 
					// EditProtectedTag render the page to edit a protect tag
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -232,7 +232,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if readmeFile != nil {
 | 
								if readmeFile != nil {
 | 
				
			||||||
				readmeFile.name = entry.Name() + "/" + readmeFile.name
 | 
									readmeFile.name = entry.Name() + "/" + readmeFile.name
 | 
				
			||||||
				readmeTreelink = treeLink + "/" + entry.GetSubJumpablePathName()
 | 
									readmeTreelink = treeLink + "/" + util.PathEscapeSegments(entry.GetSubJumpablePathName())
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -301,7 +301,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
 | 
				
			||||||
					fileSize = meta.Size
 | 
										fileSize = meta.Size
 | 
				
			||||||
					ctx.Data["FileSize"] = meta.Size
 | 
										ctx.Data["FileSize"] = meta.Size
 | 
				
			||||||
					filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.name))
 | 
										filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.name))
 | 
				
			||||||
					ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s.git/info/lfs/objects/%s/%s", setting.AppURL, ctx.Repo.Repository.FullName(), meta.Oid, filenameBase64)
 | 
										ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.HTMLURL(), url.PathEscape(meta.Oid), url.PathEscape(filenameBase64))
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -376,7 +376,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 | 
				
			||||||
	fileSize := blob.Size()
 | 
						fileSize := blob.Size()
 | 
				
			||||||
	ctx.Data["FileIsSymlink"] = entry.IsLink()
 | 
						ctx.Data["FileIsSymlink"] = entry.IsLink()
 | 
				
			||||||
	ctx.Data["FileName"] = blob.Name()
 | 
						ctx.Data["FileName"] = blob.Name()
 | 
				
			||||||
	ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath
 | 
						ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	buf := make([]byte, 1024)
 | 
						buf := make([]byte, 1024)
 | 
				
			||||||
	n, _ := util.ReadAtMost(dataRc, buf)
 | 
						n, _ := util.ReadAtMost(dataRc, buf)
 | 
				
			||||||
| 
						 | 
					@ -422,7 +422,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 | 
				
			||||||
				isTextFile = st.IsText()
 | 
									isTextFile = st.IsText()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				fileSize = meta.Size
 | 
									fileSize = meta.Size
 | 
				
			||||||
				ctx.Data["RawFileLink"] = fmt.Sprintf("%s/media/%s/%s", ctx.Repo.RepoLink, ctx.Repo.BranchNameSubURL(), ctx.Repo.TreePath)
 | 
									ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -628,7 +628,7 @@ func checkHomeCodeViewable(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if firstUnit != nil {
 | 
							if firstUnit != nil {
 | 
				
			||||||
			ctx.Redirect(fmt.Sprintf("%s/%s%s", setting.AppSubURL, ctx.Repo.Repository.FullName(), firstUnit.URI))
 | 
								ctx.Redirect(fmt.Sprintf("%s%s", ctx.Repo.Repository.Link(), firstUnit.URI))
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -684,7 +684,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["LastCommitLoaderURL"] = ctx.Repo.RepoLink + "/lastcommit/" + ctx.Repo.CommitID + "/" + ctx.Repo.TreePath
 | 
						ctx.Data["LastCommitLoaderURL"] = ctx.Repo.RepoLink + "/lastcommit/" + url.PathEscape(ctx.Repo.CommitID) + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get current entry user currently looking at.
 | 
						// Get current entry user currently looking at.
 | 
				
			||||||
	entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
 | 
						entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
 | 
				
			||||||
| 
						 | 
					@ -766,7 +766,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
 | 
				
			||||||
	treeLink := branchLink
 | 
						treeLink := branchLink
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(ctx.Repo.TreePath) > 0 {
 | 
						if len(ctx.Repo.TreePath) > 0 {
 | 
				
			||||||
		treeLink += "/" + ctx.Repo.TreePath
 | 
							treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["TreeLink"] = treeLink
 | 
						ctx.Data["TreeLink"] = treeLink
 | 
				
			||||||
| 
						 | 
					@ -815,7 +815,7 @@ func renderCode(ctx *context.Context) {
 | 
				
			||||||
	rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
 | 
						rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(ctx.Repo.TreePath) > 0 {
 | 
						if len(ctx.Repo.TreePath) > 0 {
 | 
				
			||||||
		treeLink += "/" + ctx.Repo.TreePath
 | 
							treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get Topics of this repo
 | 
						// Get Topics of this repo
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -414,7 +415,7 @@ func TelegramHooksNewPost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	w := &webhook.Webhook{
 | 
						w := &webhook.Webhook{
 | 
				
			||||||
		RepoID:          orCtx.RepoID,
 | 
							RepoID:          orCtx.RepoID,
 | 
				
			||||||
		URL:             fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID),
 | 
							URL:             fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID)),
 | 
				
			||||||
		ContentType:     webhook.ContentTypeJSON,
 | 
							ContentType:     webhook.ContentTypeJSON,
 | 
				
			||||||
		HookEvent:       ParseHookEvent(form.WebhookForm),
 | 
							HookEvent:       ParseHookEvent(form.WebhookForm),
 | 
				
			||||||
		IsActive:        form.Active,
 | 
							IsActive:        form.Active,
 | 
				
			||||||
| 
						 | 
					@ -468,7 +469,7 @@ func MatrixHooksNewPost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	w := &webhook.Webhook{
 | 
						w := &webhook.Webhook{
 | 
				
			||||||
		RepoID:          orCtx.RepoID,
 | 
							RepoID:          orCtx.RepoID,
 | 
				
			||||||
		URL:             fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID),
 | 
							URL:             fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
 | 
				
			||||||
		ContentType:     webhook.ContentTypeJSON,
 | 
							ContentType:     webhook.ContentTypeJSON,
 | 
				
			||||||
		HTTPMethod:      "PUT",
 | 
							HTTPMethod:      "PUT",
 | 
				
			||||||
		HookEvent:       ParseHookEvent(form.WebhookForm),
 | 
							HookEvent:       ParseHookEvent(form.WebhookForm),
 | 
				
			||||||
| 
						 | 
					@ -976,7 +977,7 @@ func TelegramHooksEditPost(ctx *context.Context) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	w.Meta = string(meta)
 | 
						w.Meta = string(meta)
 | 
				
			||||||
	w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID)
 | 
						w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID))
 | 
				
			||||||
	w.HookEvent = ParseHookEvent(form.WebhookForm)
 | 
						w.HookEvent = ParseHookEvent(form.WebhookForm)
 | 
				
			||||||
	w.IsActive = form.Active
 | 
						w.IsActive = form.Active
 | 
				
			||||||
	if err := w.UpdateEvent(); err != nil {
 | 
						if err := w.UpdateEvent(); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -1020,7 +1021,7 @@ func MatrixHooksEditPost(ctx *context.Context) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	w.Meta = string(meta)
 | 
						w.Meta = string(meta)
 | 
				
			||||||
	w.URL = fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID)
 | 
						w.URL = fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	w.HookEvent = ParseHookEvent(form.WebhookForm)
 | 
						w.HookEvent = ParseHookEvent(form.WebhookForm)
 | 
				
			||||||
	w.IsActive = form.Active
 | 
						w.IsActive = form.Active
 | 
				
			||||||
| 
						 | 
					@ -1162,7 +1163,7 @@ func TestWebhook(ctx *context.Context) {
 | 
				
			||||||
	apiCommit := &api.PayloadCommit{
 | 
						apiCommit := &api.PayloadCommit{
 | 
				
			||||||
		ID:      commit.ID.String(),
 | 
							ID:      commit.ID.String(),
 | 
				
			||||||
		Message: commit.Message(),
 | 
							Message: commit.Message(),
 | 
				
			||||||
		URL:     ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
 | 
							URL:     ctx.Repo.Repository.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()),
 | 
				
			||||||
		Author: &api.PayloadUser{
 | 
							Author: &api.PayloadUser{
 | 
				
			||||||
			Name:  commit.Author.Name,
 | 
								Name:  commit.Author.Name,
 | 
				
			||||||
			Email: commit.Author.Email,
 | 
								Email: commit.Author.Email,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -180,7 +180,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
 | 
				
			||||||
	ctx.Data["Pages"] = pages
 | 
						ctx.Data["Pages"] = pages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get requested pagename
 | 
						// get requested pagename
 | 
				
			||||||
	pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
 | 
						pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
 | 
				
			||||||
	if len(pageName) == 0 {
 | 
						if len(pageName) == 0 {
 | 
				
			||||||
		pageName = "Home"
 | 
							pageName = "Home"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -193,7 +193,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
 | 
				
			||||||
	//lookup filename in wiki - get filecontent, gitTree entry , real filename
 | 
						//lookup filename in wiki - get filecontent, gitTree entry , real filename
 | 
				
			||||||
	data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
 | 
						data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
 | 
				
			||||||
	if noEntry {
 | 
						if noEntry {
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
 | 
							ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if entry == nil || ctx.Written() {
 | 
						if entry == nil || ctx.Written() {
 | 
				
			||||||
		if wikiRepo != nil {
 | 
							if wikiRepo != nil {
 | 
				
			||||||
| 
						 | 
					@ -276,7 +276,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get requested pagename
 | 
						// get requested pagename
 | 
				
			||||||
	pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
 | 
						pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
 | 
				
			||||||
	if len(pageName) == 0 {
 | 
						if len(pageName) == 0 {
 | 
				
			||||||
		pageName = "Home"
 | 
							pageName = "Home"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -291,7 +291,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
 | 
				
			||||||
	//lookup filename in wiki - get filecontent, gitTree entry , real filename
 | 
						//lookup filename in wiki - get filecontent, gitTree entry , real filename
 | 
				
			||||||
	data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
 | 
						data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
 | 
				
			||||||
	if noEntry {
 | 
						if noEntry {
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
 | 
							ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if entry == nil || ctx.Written() {
 | 
						if entry == nil || ctx.Written() {
 | 
				
			||||||
		if wikiRepo != nil {
 | 
							if wikiRepo != nil {
 | 
				
			||||||
| 
						 | 
					@ -352,7 +352,7 @@ func renderEditPage(ctx *context.Context) {
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// get requested pagename
 | 
						// get requested pagename
 | 
				
			||||||
	pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
 | 
						pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
 | 
				
			||||||
	if len(pageName) == 0 {
 | 
						if len(pageName) == 0 {
 | 
				
			||||||
		pageName = "Home"
 | 
							pageName = "Home"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -365,7 +365,7 @@ func renderEditPage(ctx *context.Context) {
 | 
				
			||||||
	//lookup filename in wiki - get filecontent, gitTree entry , real filename
 | 
						//lookup filename in wiki - get filecontent, gitTree entry , real filename
 | 
				
			||||||
	data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
 | 
						data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
 | 
				
			||||||
	if noEntry {
 | 
						if noEntry {
 | 
				
			||||||
		ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
 | 
							ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if entry == nil || ctx.Written() {
 | 
						if entry == nil || ctx.Written() {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -378,6 +378,32 @@ func renderEditPage(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["footerContent"] = ""
 | 
						ctx.Data["footerContent"] = ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WikiPost renders post of wiki page
 | 
				
			||||||
 | 
					func WikiPost(ctx *context.Context) {
 | 
				
			||||||
 | 
						switch ctx.FormString("action") {
 | 
				
			||||||
 | 
						case "_new":
 | 
				
			||||||
 | 
							if !ctx.Repo.CanWrite(unit.TypeWiki) {
 | 
				
			||||||
 | 
								ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							NewWikiPost(ctx)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						case "_delete":
 | 
				
			||||||
 | 
							if !ctx.Repo.CanWrite(unit.TypeWiki) {
 | 
				
			||||||
 | 
								ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							DeleteWikiPagePost(ctx)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !ctx.Repo.CanWrite(unit.TypeWiki) {
 | 
				
			||||||
 | 
							ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						EditWikiPost(ctx)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Wiki renders single wiki page
 | 
					// Wiki renders single wiki page
 | 
				
			||||||
func Wiki(ctx *context.Context) {
 | 
					func Wiki(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["PageIsWiki"] = true
 | 
						ctx.Data["PageIsWiki"] = true
 | 
				
			||||||
| 
						 | 
					@ -389,6 +415,29 @@ func Wiki(ctx *context.Context) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch ctx.FormString("action") {
 | 
				
			||||||
 | 
						case "_pages":
 | 
				
			||||||
 | 
							WikiPages(ctx)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						case "_revision":
 | 
				
			||||||
 | 
							WikiRevision(ctx)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						case "_edit":
 | 
				
			||||||
 | 
							if !ctx.Repo.CanWrite(unit.TypeWiki) {
 | 
				
			||||||
 | 
								ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							EditWiki(ctx)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						case "_new":
 | 
				
			||||||
 | 
							if !ctx.Repo.CanWrite(unit.TypeWiki) {
 | 
				
			||||||
 | 
								ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							NewWiki(ctx)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wikiRepo, entry := renderViewPage(ctx)
 | 
						wikiRepo, entry := renderViewPage(ctx)
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if wikiRepo != nil {
 | 
							if wikiRepo != nil {
 | 
				
			||||||
| 
						 | 
					@ -652,7 +701,7 @@ func EditWikiPost(ctx *context.Context) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	oldWikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
 | 
						oldWikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
 | 
				
			||||||
	newWikiName := wiki_service.NormalizeWikiName(form.Title)
 | 
						newWikiName := wiki_service.NormalizeWikiName(form.Title)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(form.Message) == 0 {
 | 
						if len(form.Message) == 0 {
 | 
				
			||||||
| 
						 | 
					@ -669,7 +718,7 @@ func EditWikiPost(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteWikiPagePost delete wiki page
 | 
					// DeleteWikiPagePost delete wiki page
 | 
				
			||||||
func DeleteWikiPagePost(ctx *context.Context) {
 | 
					func DeleteWikiPagePost(ctx *context.Context) {
 | 
				
			||||||
	wikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
 | 
						wikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
 | 
				
			||||||
	if len(wikiName) == 0 {
 | 
						if len(wikiName) == 0 {
 | 
				
			||||||
		wikiName = "Home"
 | 
							wikiName = "Home"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,8 +76,8 @@ func assertPagesMetas(t *testing.T, expectedNames []string, metas interface{}) {
 | 
				
			||||||
func TestWiki(t *testing.T) {
 | 
					func TestWiki(t *testing.T) {
 | 
				
			||||||
	unittest.PrepareTestEnv(t)
 | 
						unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
 | 
						ctx := test.MockContext(t, "user2/repo1/wiki/?action=_pages")
 | 
				
			||||||
	ctx.SetParams(":page", "Home")
 | 
						ctx.SetParams("*", "Home")
 | 
				
			||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	Wiki(ctx)
 | 
						Wiki(ctx)
 | 
				
			||||||
	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
						assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
				
			||||||
| 
						 | 
					@ -88,7 +88,7 @@ func TestWiki(t *testing.T) {
 | 
				
			||||||
func TestWikiPages(t *testing.T) {
 | 
					func TestWikiPages(t *testing.T) {
 | 
				
			||||||
	unittest.PrepareTestEnv(t)
 | 
						unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
 | 
						ctx := test.MockContext(t, "user2/repo1/wiki/?action=_pages")
 | 
				
			||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	WikiPages(ctx)
 | 
						WikiPages(ctx)
 | 
				
			||||||
	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
						assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
				
			||||||
| 
						 | 
					@ -98,7 +98,7 @@ func TestWikiPages(t *testing.T) {
 | 
				
			||||||
func TestNewWiki(t *testing.T) {
 | 
					func TestNewWiki(t *testing.T) {
 | 
				
			||||||
	unittest.PrepareTestEnv(t)
 | 
						unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := test.MockContext(t, "user2/repo1/wiki/_new")
 | 
						ctx := test.MockContext(t, "user2/repo1/wiki/?action=_new")
 | 
				
			||||||
	test.LoadUser(t, ctx, 2)
 | 
						test.LoadUser(t, ctx, 2)
 | 
				
			||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	NewWiki(ctx)
 | 
						NewWiki(ctx)
 | 
				
			||||||
| 
						 | 
					@ -113,7 +113,7 @@ func TestNewWikiPost(t *testing.T) {
 | 
				
			||||||
	} {
 | 
						} {
 | 
				
			||||||
		unittest.PrepareTestEnv(t)
 | 
							unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx := test.MockContext(t, "user2/repo1/wiki/_new")
 | 
							ctx := test.MockContext(t, "user2/repo1/wiki/?action=_new")
 | 
				
			||||||
		test.LoadUser(t, ctx, 2)
 | 
							test.LoadUser(t, ctx, 2)
 | 
				
			||||||
		test.LoadRepo(t, ctx, 1)
 | 
							test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
		web.SetForm(ctx, &forms.NewWikiForm{
 | 
							web.SetForm(ctx, &forms.NewWikiForm{
 | 
				
			||||||
| 
						 | 
					@ -131,7 +131,7 @@ func TestNewWikiPost(t *testing.T) {
 | 
				
			||||||
func TestNewWikiPost_ReservedName(t *testing.T) {
 | 
					func TestNewWikiPost_ReservedName(t *testing.T) {
 | 
				
			||||||
	unittest.PrepareTestEnv(t)
 | 
						unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := test.MockContext(t, "user2/repo1/wiki/_new")
 | 
						ctx := test.MockContext(t, "user2/repo1/wiki/?action=_new")
 | 
				
			||||||
	test.LoadUser(t, ctx, 2)
 | 
						test.LoadUser(t, ctx, 2)
 | 
				
			||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	web.SetForm(ctx, &forms.NewWikiForm{
 | 
						web.SetForm(ctx, &forms.NewWikiForm{
 | 
				
			||||||
| 
						 | 
					@ -148,8 +148,8 @@ func TestNewWikiPost_ReservedName(t *testing.T) {
 | 
				
			||||||
func TestEditWiki(t *testing.T) {
 | 
					func TestEditWiki(t *testing.T) {
 | 
				
			||||||
	unittest.PrepareTestEnv(t)
 | 
						unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := test.MockContext(t, "user2/repo1/wiki/_edit/Home")
 | 
						ctx := test.MockContext(t, "user2/repo1/wiki/Home?action=_edit")
 | 
				
			||||||
	ctx.SetParams(":page", "Home")
 | 
						ctx.SetParams("*", "Home")
 | 
				
			||||||
	test.LoadUser(t, ctx, 2)
 | 
						test.LoadUser(t, ctx, 2)
 | 
				
			||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	EditWiki(ctx)
 | 
						EditWiki(ctx)
 | 
				
			||||||
| 
						 | 
					@ -164,8 +164,8 @@ func TestEditWikiPost(t *testing.T) {
 | 
				
			||||||
		"New/<page>",
 | 
							"New/<page>",
 | 
				
			||||||
	} {
 | 
						} {
 | 
				
			||||||
		unittest.PrepareTestEnv(t)
 | 
							unittest.PrepareTestEnv(t)
 | 
				
			||||||
		ctx := test.MockContext(t, "user2/repo1/wiki/_new/Home")
 | 
							ctx := test.MockContext(t, "user2/repo1/wiki/Home?action=_new")
 | 
				
			||||||
		ctx.SetParams(":page", "Home")
 | 
							ctx.SetParams("*", "Home")
 | 
				
			||||||
		test.LoadUser(t, ctx, 2)
 | 
							test.LoadUser(t, ctx, 2)
 | 
				
			||||||
		test.LoadRepo(t, ctx, 1)
 | 
							test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
		web.SetForm(ctx, &forms.NewWikiForm{
 | 
							web.SetForm(ctx, &forms.NewWikiForm{
 | 
				
			||||||
| 
						 | 
					@ -186,7 +186,7 @@ func TestEditWikiPost(t *testing.T) {
 | 
				
			||||||
func TestDeleteWikiPagePost(t *testing.T) {
 | 
					func TestDeleteWikiPagePost(t *testing.T) {
 | 
				
			||||||
	unittest.PrepareTestEnv(t)
 | 
						unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := test.MockContext(t, "user2/repo1/wiki/Home/delete")
 | 
						ctx := test.MockContext(t, "user2/repo1/wiki/Home?action=_delete")
 | 
				
			||||||
	test.LoadUser(t, ctx, 2)
 | 
						test.LoadUser(t, ctx, 2)
 | 
				
			||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	DeleteWikiPagePost(ctx)
 | 
						DeleteWikiPagePost(ctx)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -887,5 +887,5 @@ func Email2User(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Redirect(setting.AppSubURL + "/user/" + u.Name)
 | 
						ctx.Redirect(u.HomeLink())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -167,7 +167,7 @@ func NotificationStatusPost(c *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !c.FormBool("noredirect") {
 | 
						if !c.FormBool("noredirect") {
 | 
				
			||||||
		url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, c.FormString("page"))
 | 
							url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, url.QueryEscape(c.FormString("page")))
 | 
				
			||||||
		c.Redirect(url, http.StatusSeeOther)
 | 
							c.Redirect(url, http.StatusSeeOther)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -189,6 +189,5 @@ func NotificationPurgePost(c *context.Context) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	url := fmt.Sprintf("%s/notifications", setting.AppSubURL)
 | 
						c.Redirect(setting.AppSubURL+"/notifications", http.StatusSeeOther)
 | 
				
			||||||
	c.Redirect(url, http.StatusSeeOther)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -454,7 +454,7 @@ func AuthorizeOAuth(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["State"] = form.State
 | 
						ctx.Data["State"] = form.State
 | 
				
			||||||
	ctx.Data["Scope"] = form.Scope
 | 
						ctx.Data["Scope"] = form.Scope
 | 
				
			||||||
	ctx.Data["Nonce"] = form.Nonce
 | 
						ctx.Data["Nonce"] = form.Nonce
 | 
				
			||||||
	ctx.Data["ApplicationUserLink"] = "<a href=\"" + html.EscapeString(setting.AppURL) + html.EscapeString(url.PathEscape(user.LowerName)) + "\">@" + html.EscapeString(user.Name) + "</a>"
 | 
						ctx.Data["ApplicationUserLink"] = "<a href=\"" + html.EscapeString(user.HTMLURL()) + "\">@" + html.EscapeString(user.Name) + "</a>"
 | 
				
			||||||
	ctx.Data["ApplicationRedirectDomainHTML"] = "<strong>" + html.EscapeString(form.RedirectURI) + "</strong>"
 | 
						ctx.Data["ApplicationRedirectDomainHTML"] = "<strong>" + html.EscapeString(form.RedirectURI) + "</strong>"
 | 
				
			||||||
	// TODO document SESSION <=> FORM
 | 
						// TODO document SESSION <=> FORM
 | 
				
			||||||
	err = ctx.Session.Set("client_id", app.ClientID)
 | 
						err = ctx.Session.Set("client_id", app.ClientID)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -364,6 +364,6 @@ func Action(ctx *context.Context) {
 | 
				
			||||||
		ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
 | 
							ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// FIXME: We should check this URL and make sure that it's a valid Gitea URL
 | 
				
			||||||
	ctx.RedirectToFirst(ctx.FormString("redirect_to"), u.HomeLink())
 | 
						ctx.RedirectToFirst(ctx.FormString("redirect_to"), u.HomeLink())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -895,21 +895,23 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			||||||
		}, reqRepoProjectsReader, repo.MustEnableProjects)
 | 
							}, reqRepoProjectsReader, repo.MustEnableProjects)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Group("/wiki", func() {
 | 
							m.Group("/wiki", func() {
 | 
				
			||||||
			m.Get("/", repo.Wiki)
 | 
								m.Combo("/").
 | 
				
			||||||
			m.Get("/{page}", repo.Wiki)
 | 
									Get(repo.Wiki).
 | 
				
			||||||
			m.Get("/_pages", repo.WikiPages)
 | 
									Post(context.RepoMustNotBeArchived(),
 | 
				
			||||||
			m.Get("/{page}/_revision", repo.WikiRevision)
 | 
										reqSignIn,
 | 
				
			||||||
 | 
										reqRepoWikiWriter,
 | 
				
			||||||
 | 
										bindIgnErr(forms.NewWikiForm{}),
 | 
				
			||||||
 | 
										repo.WikiPost)
 | 
				
			||||||
 | 
								m.Combo("/*").
 | 
				
			||||||
 | 
									Get(repo.Wiki).
 | 
				
			||||||
 | 
									Post(context.RepoMustNotBeArchived(),
 | 
				
			||||||
 | 
										reqSignIn,
 | 
				
			||||||
 | 
										reqRepoWikiWriter,
 | 
				
			||||||
 | 
										bindIgnErr(forms.NewWikiForm{}),
 | 
				
			||||||
 | 
										repo.WikiPost)
 | 
				
			||||||
			m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
 | 
								m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
 | 
				
			||||||
			m.Get("/commit/{sha:[a-f0-9]{7,40}}.{ext:patch|diff}", repo.RawDiff)
 | 
								m.Get("/commit/{sha:[a-f0-9]{7,40}}.{ext:patch|diff}", repo.RawDiff)
 | 
				
			||||||
 | 
							}, repo.MustEnableWiki, func(ctx *context.Context) {
 | 
				
			||||||
			m.Group("", func() {
 | 
					 | 
				
			||||||
				m.Combo("/_new").Get(repo.NewWiki).
 | 
					 | 
				
			||||||
					Post(bindIgnErr(forms.NewWikiForm{}), repo.NewWikiPost)
 | 
					 | 
				
			||||||
				m.Combo("/{page}/_edit").Get(repo.EditWiki).
 | 
					 | 
				
			||||||
					Post(bindIgnErr(forms.NewWikiForm{}), repo.EditWikiPost)
 | 
					 | 
				
			||||||
				m.Post("/{page}/delete", repo.DeleteWikiPagePost)
 | 
					 | 
				
			||||||
			}, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter)
 | 
					 | 
				
			||||||
		}, repo.MustEnableWiki, context.RepoRef(), func(ctx *context.Context) {
 | 
					 | 
				
			||||||
			ctx.Data["PageIsWiki"] = true
 | 
								ctx.Data["PageIsWiki"] = true
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
| 
						 | 
					@ -46,17 +47,17 @@ type Claims struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DownloadLink builds a URL to download the object.
 | 
					// DownloadLink builds a URL to download the object.
 | 
				
			||||||
func (rc *requestContext) DownloadLink(p lfs_module.Pointer) string {
 | 
					func (rc *requestContext) DownloadLink(p lfs_module.Pointer) string {
 | 
				
			||||||
	return setting.AppURL + path.Join(rc.User, rc.Repo+".git", "info/lfs/objects", p.Oid)
 | 
						return setting.AppURL + path.Join(url.PathEscape(rc.User), url.PathEscape(rc.Repo+".git"), "info/lfs/objects", url.PathEscape(p.Oid))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UploadLink builds a URL to upload the object.
 | 
					// UploadLink builds a URL to upload the object.
 | 
				
			||||||
func (rc *requestContext) UploadLink(p lfs_module.Pointer) string {
 | 
					func (rc *requestContext) UploadLink(p lfs_module.Pointer) string {
 | 
				
			||||||
	return setting.AppURL + path.Join(rc.User, rc.Repo+".git", "info/lfs/objects", p.Oid, strconv.FormatInt(p.Size, 10))
 | 
						return setting.AppURL + path.Join(url.PathEscape(rc.User), url.PathEscape(rc.Repo+".git"), "info/lfs/objects", url.PathEscape(p.Oid), strconv.FormatInt(p.Size, 10))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// VerifyLink builds a URL for verifying the object.
 | 
					// VerifyLink builds a URL for verifying the object.
 | 
				
			||||||
func (rc *requestContext) VerifyLink(p lfs_module.Pointer) string {
 | 
					func (rc *requestContext) VerifyLink(p lfs_module.Pointer) string {
 | 
				
			||||||
	return setting.AppURL + path.Join(rc.User, rc.Repo+".git", "info/lfs/verify")
 | 
						return setting.AppURL + path.Join(url.PathEscape(rc.User), url.PathEscape(rc.Repo+".git"), "info/lfs/verify")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CheckAcceptMediaType checks if the client accepts the LFS media type.
 | 
					// CheckAcceptMediaType checks if the client accepts the LFS media type.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dingtalk "github.com/lunny/dingtalk_webhook"
 | 
						dingtalk "github.com/lunny/dingtalk_webhook"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -41,7 +42,7 @@ func (d *DingtalkPayload) Create(p *api.CreatePayload) (api.Payloader, error) {
 | 
				
			||||||
	refName := git.RefEndName(p.Ref)
 | 
						refName := git.RefEndName(p.Ref)
 | 
				
			||||||
	title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
 | 
						title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+refName), nil
 | 
						return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Delete implements PayloadConvertor Delete method
 | 
					// Delete implements PayloadConvertor Delete method
 | 
				
			||||||
| 
						 | 
					@ -50,7 +51,7 @@ func (d *DingtalkPayload) Delete(p *api.DeletePayload) (api.Payloader, error) {
 | 
				
			||||||
	refName := git.RefEndName(p.Ref)
 | 
						refName := git.RefEndName(p.Ref)
 | 
				
			||||||
	title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
 | 
						title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+refName), nil
 | 
						return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Fork implements PayloadConvertor Fork method
 | 
					// Fork implements PayloadConvertor Fork method
 | 
				
			||||||
| 
						 | 
					@ -78,7 +79,7 @@ func (d *DingtalkPayload) Push(p *api.PushPayload) (api.Payloader, error) {
 | 
				
			||||||
		linkText = fmt.Sprintf("view commit %s...%s", p.Commits[0].ID[:7], p.Commits[len(p.Commits)-1].ID[:7])
 | 
							linkText = fmt.Sprintf("view commit %s...%s", p.Commits[0].ID[:7], p.Commits[len(p.Commits)-1].ID[:7])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if titleLink == "" {
 | 
						if titleLink == "" {
 | 
				
			||||||
		titleLink = p.Repo.HTMLURL + "/src/" + branchName
 | 
							titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc)
 | 
						title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,7 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type (
 | 
					type (
 | 
				
			||||||
| 
						 | 
					@ -115,7 +116,7 @@ func (d *DiscordPayload) Create(p *api.CreatePayload) (api.Payloader, error) {
 | 
				
			||||||
	refName := git.RefEndName(p.Ref)
 | 
						refName := git.RefEndName(p.Ref)
 | 
				
			||||||
	title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
 | 
						title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL+"/src/"+refName, greenColor), nil
 | 
						return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName), greenColor), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Delete implements PayloadConvertor Delete method
 | 
					// Delete implements PayloadConvertor Delete method
 | 
				
			||||||
| 
						 | 
					@ -124,7 +125,7 @@ func (d *DiscordPayload) Delete(p *api.DeletePayload) (api.Payloader, error) {
 | 
				
			||||||
	refName := git.RefEndName(p.Ref)
 | 
						refName := git.RefEndName(p.Ref)
 | 
				
			||||||
	title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
 | 
						title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL+"/src/"+refName, redColor), nil
 | 
						return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName), redColor), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Fork implements PayloadConvertor Fork method
 | 
					// Fork implements PayloadConvertor Fork method
 | 
				
			||||||
| 
						 | 
					@ -150,7 +151,7 @@ func (d *DiscordPayload) Push(p *api.PushPayload) (api.Payloader, error) {
 | 
				
			||||||
		titleLink = p.CompareURL
 | 
							titleLink = p.CompareURL
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if titleLink == "" {
 | 
						if titleLink == "" {
 | 
				
			||||||
		titleLink = p.Repo.HTMLURL + "/src/" + branchName
 | 
							titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc)
 | 
						title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,10 +7,12 @@ package webhook
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"html"
 | 
						"html"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type linkFormatter = func(string, string) string
 | 
					type linkFormatter = func(string, string) string
 | 
				
			||||||
| 
						 | 
					@ -22,7 +24,7 @@ func noneLinkFormatter(url string, text string) string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// htmlLinkFormatter creates a HTML link
 | 
					// htmlLinkFormatter creates a HTML link
 | 
				
			||||||
func htmlLinkFormatter(url string, text string) string {
 | 
					func htmlLinkFormatter(url string, text string) string {
 | 
				
			||||||
	return fmt.Sprintf(`<a href="%s">%s</a>`, url, html.EscapeString(text))
 | 
						return fmt.Sprintf(`<a href="%s">%s</a>`, html.EscapeString(url), html.EscapeString(text))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) {
 | 
					func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) {
 | 
				
			||||||
| 
						 | 
					@ -46,7 +48,7 @@ func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, with
 | 
				
			||||||
	case api.HookIssueAssigned:
 | 
						case api.HookIssueAssigned:
 | 
				
			||||||
		list := make([]string, len(p.Issue.Assignees))
 | 
							list := make([]string, len(p.Issue.Assignees))
 | 
				
			||||||
		for i, user := range p.Issue.Assignees {
 | 
							for i, user := range p.Issue.Assignees {
 | 
				
			||||||
			list[i] = linkFormatter(setting.AppURL+user.UserName, user.UserName)
 | 
								list[i] = linkFormatter(setting.AppURL+url.PathEscape(user.UserName), user.UserName)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		text = fmt.Sprintf("[%s] Issue assigned to %s: %s", repoLink, strings.Join(list, ", "), titleLink)
 | 
							text = fmt.Sprintf("[%s] Issue assigned to %s: %s", repoLink, strings.Join(list, ", "), titleLink)
 | 
				
			||||||
		color = greenColor
 | 
							color = greenColor
 | 
				
			||||||
| 
						 | 
					@ -66,7 +68,7 @@ func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, with
 | 
				
			||||||
		text = fmt.Sprintf("[%s] Issue milestone cleared: %s", repoLink, titleLink)
 | 
							text = fmt.Sprintf("[%s] Issue milestone cleared: %s", repoLink, titleLink)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if withSender {
 | 
						if withSender {
 | 
				
			||||||
		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
 | 
							text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var attachmentText string
 | 
						var attachmentText string
 | 
				
			||||||
| 
						 | 
					@ -139,7 +141,7 @@ func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
 | 
					func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
 | 
				
			||||||
	repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
 | 
						repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
 | 
				
			||||||
	refLink := linkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName)
 | 
						refLink := linkFormatter(p.Repository.HTMLURL+"/src/"+util.PathEscapeSegments(p.Release.TagName), p.Release.TagName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch p.Action {
 | 
						switch p.Action {
 | 
				
			||||||
	case api.HookReleasePublished:
 | 
						case api.HookReleasePublished:
 | 
				
			||||||
| 
						 | 
					@ -153,7 +155,7 @@ func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter, w
 | 
				
			||||||
		color = redColor
 | 
							color = redColor
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if withSender {
 | 
						if withSender {
 | 
				
			||||||
		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
 | 
							text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return text, color
 | 
						return text, color
 | 
				
			||||||
| 
						 | 
					@ -189,7 +191,7 @@ func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFo
 | 
				
			||||||
		color = redColor
 | 
							color = redColor
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if withSender {
 | 
						if withSender {
 | 
				
			||||||
		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
 | 
							text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return text, issueTitle, color
 | 
						return text, issueTitle, color
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"html"
 | 
						"html"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +20,7 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const matrixPayloadSizeLimit = 1024 * 64
 | 
					const matrixPayloadSizeLimit = 1024 * 64
 | 
				
			||||||
| 
						 | 
					@ -94,11 +96,11 @@ func MatrixLinkToRef(repoURL, ref string) string {
 | 
				
			||||||
	refName := git.RefEndName(ref)
 | 
						refName := git.RefEndName(ref)
 | 
				
			||||||
	switch {
 | 
						switch {
 | 
				
			||||||
	case strings.HasPrefix(ref, git.BranchPrefix):
 | 
						case strings.HasPrefix(ref, git.BranchPrefix):
 | 
				
			||||||
		return MatrixLinkFormatter(repoURL+"/src/branch/"+refName, refName)
 | 
							return MatrixLinkFormatter(repoURL+"/src/branch/"+util.PathEscapeSegments(refName), refName)
 | 
				
			||||||
	case strings.HasPrefix(ref, git.TagPrefix):
 | 
						case strings.HasPrefix(ref, git.TagPrefix):
 | 
				
			||||||
		return MatrixLinkFormatter(repoURL+"/src/tag/"+refName, refName)
 | 
							return MatrixLinkFormatter(repoURL+"/src/tag/"+util.PathEscapeSegments(refName), refName)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return MatrixLinkFormatter(repoURL+"/src/commit/"+refName, refName)
 | 
							return MatrixLinkFormatter(repoURL+"/src/commit/"+util.PathEscapeSegments(refName), refName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -186,7 +188,7 @@ func (m *MatrixPayloadUnsafe) PullRequest(p *api.PullRequestPayload) (api.Payloa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Review implements PayloadConvertor Review method
 | 
					// Review implements PayloadConvertor Review method
 | 
				
			||||||
func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 | 
					func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
 | 
				
			||||||
	senderLink := MatrixLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
 | 
						senderLink := MatrixLinkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
 | 
				
			||||||
	title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
 | 
						title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
 | 
				
			||||||
	titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
 | 
						titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
 | 
				
			||||||
	repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
 | 
						repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
 | 
				
			||||||
| 
						 | 
					@ -281,7 +283,7 @@ func getMatrixHookRequest(w *webhook_model.Webhook, t *webhook_model.HookTask) (
 | 
				
			||||||
		return nil, fmt.Errorf("getMatrixHookRequest: unable to hash payload: %+v", err)
 | 
							return nil, fmt.Errorf("getMatrixHookRequest: unable to hash payload: %+v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	url := fmt.Sprintf("%s/%s", w.URL, txnID)
 | 
						url := fmt.Sprintf("%s/%s", w.URL, url.PathEscape(txnID))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req, err := http.NewRequest(w.HTTPMethod, url, strings.NewReader(string(payload)))
 | 
						req, err := http.NewRequest(w.HTTPMethod, url, strings.NewReader(string(payload)))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type (
 | 
					type (
 | 
				
			||||||
| 
						 | 
					@ -79,7 +80,7 @@ func (m *MSTeamsPayload) Create(p *api.CreatePayload) (api.Payloader, error) {
 | 
				
			||||||
		p.Sender,
 | 
							p.Sender,
 | 
				
			||||||
		title,
 | 
							title,
 | 
				
			||||||
		"",
 | 
							"",
 | 
				
			||||||
		p.Repo.HTMLURL+"/src/"+refName,
 | 
							p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName),
 | 
				
			||||||
		greenColor,
 | 
							greenColor,
 | 
				
			||||||
		&MSTeamsFact{fmt.Sprintf("%s:", p.RefType), refName},
 | 
							&MSTeamsFact{fmt.Sprintf("%s:", p.RefType), refName},
 | 
				
			||||||
	), nil
 | 
						), nil
 | 
				
			||||||
| 
						 | 
					@ -96,7 +97,7 @@ func (m *MSTeamsPayload) Delete(p *api.DeletePayload) (api.Payloader, error) {
 | 
				
			||||||
		p.Sender,
 | 
							p.Sender,
 | 
				
			||||||
		title,
 | 
							title,
 | 
				
			||||||
		"",
 | 
							"",
 | 
				
			||||||
		p.Repo.HTMLURL+"/src/"+refName,
 | 
							p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName),
 | 
				
			||||||
		yellowColor,
 | 
							yellowColor,
 | 
				
			||||||
		&MSTeamsFact{fmt.Sprintf("%s:", p.RefType), refName},
 | 
							&MSTeamsFact{fmt.Sprintf("%s:", p.RefType), refName},
 | 
				
			||||||
	), nil
 | 
						), nil
 | 
				
			||||||
| 
						 | 
					@ -133,7 +134,7 @@ func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) {
 | 
				
			||||||
		titleLink = p.CompareURL
 | 
							titleLink = p.CompareURL
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if titleLink == "" {
 | 
						if titleLink == "" {
 | 
				
			||||||
		titleLink = p.Repo.HTMLURL + "/src/" + branchName
 | 
							titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc)
 | 
						title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ func nameAllowed(name string) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NameToSubURL converts a wiki name to its corresponding sub-URL.
 | 
					// NameToSubURL converts a wiki name to its corresponding sub-URL.
 | 
				
			||||||
func NameToSubURL(name string) string {
 | 
					func NameToSubURL(name string) string {
 | 
				
			||||||
	return url.QueryEscape(strings.ReplaceAll(name, " ", "-"))
 | 
						return url.PathEscape(strings.ReplaceAll(name, " ", "-"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NormalizeWikiName normalizes a wiki name
 | 
					// NormalizeWikiName normalizes a wiki name
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,7 @@
 | 
				
			||||||
				<tbody>
 | 
									<tbody>
 | 
				
			||||||
					{{range .Emails}}
 | 
										{{range .Emails}}
 | 
				
			||||||
						<tr>
 | 
											<tr>
 | 
				
			||||||
							<td><a href="{{AppSubUrl}}/{{.Name}}">{{.Name}}</a></td>
 | 
												<td><a href="{{AppSubUrl}}/{{.Name | PathEscape}}">{{.Name}}</a></td>
 | 
				
			||||||
							<td><span class="text truncate">{{.FullName}}</span></td>
 | 
												<td><span class="text truncate">{{.FullName}}</span></td>
 | 
				
			||||||
							<td><span class="text email">{{.Email}}</span></td>
 | 
												<td><span class="text email">{{.Email}}</span></td>
 | 
				
			||||||
							<td>{{if .IsPrimary}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
 | 
												<td>{{if .IsPrimary}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,13 +45,13 @@
 | 
				
			||||||
						<tr>
 | 
											<tr>
 | 
				
			||||||
							<td>{{.ID}}</td>
 | 
												<td>{{.ID}}</td>
 | 
				
			||||||
							<td>
 | 
												<td>
 | 
				
			||||||
								<a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
 | 
													<a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>
 | 
				
			||||||
								{{if .Owner.Visibility.IsPrivate}}
 | 
													{{if .Owner.Visibility.IsPrivate}}
 | 
				
			||||||
									<span class="text gold">{{svg "octicon-lock"}}</span>
 | 
														<span class="text gold">{{svg "octicon-lock"}}</span>
 | 
				
			||||||
								{{end}}
 | 
													{{end}}
 | 
				
			||||||
							</td>
 | 
												</td>
 | 
				
			||||||
							<td>
 | 
												<td>
 | 
				
			||||||
								<a href="{{AppSubUrl}}/{{.Owner.Name}}/{{.Name}}">{{.Name}}</a>
 | 
													<a href="{{.Link}}">{{.Name}}</a>
 | 
				
			||||||
								{{if .IsArchived}}
 | 
													{{if .IsArchived}}
 | 
				
			||||||
									<span class="ui basic mini label">{{$.i18n.Tr "repo.desc.archived"}}</span>
 | 
														<span class="ui basic mini label">{{$.i18n.Tr "repo.desc.archived"}}</span>
 | 
				
			||||||
								{{end}}
 | 
													{{end}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,7 +87,7 @@
 | 
				
			||||||
					{{range .Users}}
 | 
										{{range .Users}}
 | 
				
			||||||
						<tr>
 | 
											<tr>
 | 
				
			||||||
							<td>{{.ID}}</td>
 | 
												<td>{{.ID}}</td>
 | 
				
			||||||
							<td><a href="{{AppSubUrl}}/{{.Name}}">{{.Name}}</a></td>
 | 
												<td><a href="{{.HomeLink}}">{{.Name}}</a></td>
 | 
				
			||||||
							<td><span class="text truncate email">{{.Email}}</span></td>
 | 
												<td><span class="text truncate email">{{.Email}}</span></td>
 | 
				
			||||||
							<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
 | 
												<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
 | 
				
			||||||
							<td>{{if .IsAdmin}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
 | 
												<td>{{if .IsAdmin}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,10 +102,10 @@
 | 
				
			||||||
<meta property="og:site_name" content="{{AppName}}" />
 | 
					<meta property="og:site_name" content="{{AppName}}" />
 | 
				
			||||||
{{if .IsSigned }}
 | 
					{{if .IsSigned }}
 | 
				
			||||||
	{{ if ne .SignedUser.Theme "gitea" }}
 | 
						{{ if ne .SignedUser.Theme "gitea" }}
 | 
				
			||||||
		<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css?v={{MD5 AppVer}}">
 | 
							<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/theme-{{.SignedUser.Theme | PathEscape}}.css?v={{MD5 AppVer}}">
 | 
				
			||||||
	{{end}}
 | 
						{{end}}
 | 
				
			||||||
{{else if ne DefaultTheme "gitea"}}
 | 
					{{else if ne DefaultTheme "gitea"}}
 | 
				
			||||||
	<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}">
 | 
						<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/theme-{{DefaultTheme | PathEscape}}.css?v={{MD5 AppVer}}">
 | 
				
			||||||
{{end}}
 | 
					{{end}}
 | 
				
			||||||
{{template "custom/header" .}}
 | 
					{{template "custom/header" .}}
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,8 +63,7 @@
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	{{else if .IsSigned}}
 | 
						{{else if .IsSigned}}
 | 
				
			||||||
		<div class="right stackable menu">
 | 
							<div class="right stackable menu">
 | 
				
			||||||
			{{$issueURL := Printf "%s/%s/issues/%d" AppSubUrl .ActiveStopwatch.RepoSlug .ActiveStopwatch.IssueIndex}}
 | 
								<a class="active-stopwatch-trigger item ui label {{if not .ActiveStopwatch}}hidden{{end}}" href="{{.ActiveStopwatch.IssueLink}}">
 | 
				
			||||||
			<a class="active-stopwatch-trigger item ui label {{if not .ActiveStopwatch}}hidden{{end}}" href="{{$issueURL}}">
 | 
					 | 
				
			||||||
				<span class="text">
 | 
									<span class="text">
 | 
				
			||||||
					<span class="fitted item">
 | 
										<span class="fitted item">
 | 
				
			||||||
						{{svg "octicon-stopwatch"}}
 | 
											{{svg "octicon-stopwatch"}}
 | 
				
			||||||
| 
						 | 
					@ -75,14 +74,14 @@
 | 
				
			||||||
			</a>
 | 
								</a>
 | 
				
			||||||
			<div class="ui popup very wide">
 | 
								<div class="ui popup very wide">
 | 
				
			||||||
				<div class="df ac">
 | 
									<div class="df ac">
 | 
				
			||||||
					<a class="stopwatch-link df ac" href="{{$issueURL}}">
 | 
										<a class="stopwatch-link df ac" href="{{.ActiveStopwatch.IssueLink}}">
 | 
				
			||||||
						{{svg "octicon-issue-opened"}}
 | 
											{{svg "octicon-issue-opened"}}
 | 
				
			||||||
						<span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span>
 | 
											<span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span>
 | 
				
			||||||
						<span class="ui label blue stopwatch-time my-0 mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}">
 | 
											<span class="ui label blue stopwatch-time my-0 mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}">
 | 
				
			||||||
							{{if .ActiveStopwatch}}{{Sec2Time .ActiveStopwatch.Seconds}}{{end}}
 | 
												{{if .ActiveStopwatch}}{{Sec2Time .ActiveStopwatch.Seconds}}{{end}}
 | 
				
			||||||
						</span>
 | 
											</span>
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
					<form class="stopwatch-commit" method="POST" action="{{$issueURL}}/times/stopwatch/toggle">
 | 
										<form class="stopwatch-commit" method="POST" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/toggle">
 | 
				
			||||||
						{{.CsrfTokenHtml}}
 | 
											{{.CsrfTokenHtml}}
 | 
				
			||||||
						<button
 | 
											<button
 | 
				
			||||||
							class="ui button mini compact basic icon fitted poping up"
 | 
												class="ui button mini compact basic icon fitted poping up"
 | 
				
			||||||
| 
						 | 
					@ -90,7 +89,7 @@
 | 
				
			||||||
							data-position="top right" data-variation="small inverted"
 | 
												data-position="top right" data-variation="small inverted"
 | 
				
			||||||
						>{{svg "octicon-square-fill"}}</button>
 | 
											>{{svg "octicon-square-fill"}}</button>
 | 
				
			||||||
					</form>
 | 
										</form>
 | 
				
			||||||
					<form class="stopwatch-cancel" method="POST" action="{{$issueURL}}/times/stopwatch/cancel">
 | 
										<form class="stopwatch-cancel" method="POST" action="{{.ActiveStopwatch.IssueLink}}/times/stopwatch/cancel">
 | 
				
			||||||
						{{.CsrfTokenHtml}}
 | 
											{{.CsrfTokenHtml}}
 | 
				
			||||||
						<button
 | 
											<button
 | 
				
			||||||
							class="ui button mini compact basic icon fitted poping up"
 | 
												class="ui button mini compact basic icon fitted poping up"
 | 
				
			||||||
| 
						 | 
					@ -149,12 +148,12 @@
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					<div class="divider"></div>
 | 
										<div class="divider"></div>
 | 
				
			||||||
					<a class="item" href="{{AppSubUrl}}/{{.SignedUser.Name}}">
 | 
										<a class="item" href="{{.SignedUser.HomeLink}}">
 | 
				
			||||||
						{{svg "octicon-person"}}
 | 
											{{svg "octicon-person"}}
 | 
				
			||||||
						{{.i18n.Tr "your_profile"}}<!-- Your profile -->
 | 
											{{.i18n.Tr "your_profile"}}<!-- Your profile -->
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
					{{if not .DisableStars}}
 | 
										{{if not .DisableStars}}
 | 
				
			||||||
						<a class="item" href="{{AppSubUrl}}/{{.SignedUser.Name}}?tab=stars">
 | 
											<a class="item" href="{{.SignedUser.HomeLink}}?tab=stars">
 | 
				
			||||||
							{{svg "octicon-star"}}
 | 
												{{svg "octicon-star"}}
 | 
				
			||||||
							{{.i18n.Tr "your_starred"}}
 | 
												{{.i18n.Tr "your_starred"}}
 | 
				
			||||||
						</a>
 | 
											</a>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,8 +37,8 @@
 | 
				
			||||||
						{{$repo := (index $.RepoMaps .RepoID)}}
 | 
											{{$repo := (index $.RepoMaps .RepoID)}}
 | 
				
			||||||
						<div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
 | 
											<div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
 | 
				
			||||||
							<h4 class="ui top attached normal header">
 | 
												<h4 class="ui top attached normal header">
 | 
				
			||||||
								<span class="file"><a rel="nofollow" href="{{EscapePound $repo.HTMLURL}}">{{$repo.FullName}}</a> - {{.Filename}}</span>
 | 
													<span class="file"><a rel="nofollow" href="{{$repo.HTMLURL}}">{{$repo.FullName}}</a> - {{.Filename}}</span>
 | 
				
			||||||
								<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $repo.HTMLURL}}/src/commit/{{$result.CommitID}}/{{EscapePound .Filename}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
 | 
													<a class="ui basic tiny button" rel="nofollow" href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{.Filename | PathEscapeSegments}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
 | 
				
			||||||
							</h4>
 | 
												</h4>
 | 
				
			||||||
							<div class="ui attached table segment">
 | 
												<div class="ui attached table segment">
 | 
				
			||||||
								<div class="file-body file-code code-view">
 | 
													<div class="file-body file-code code-view">
 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,7 @@
 | 
				
			||||||
											<tr>
 | 
																<tr>
 | 
				
			||||||
												<td class="lines-num">
 | 
																	<td class="lines-num">
 | 
				
			||||||
													{{range .LineNumbers}}
 | 
																		{{range .LineNumbers}}
 | 
				
			||||||
														<a href="{{EscapePound $repo.HTMLURL}}/src/commit/{{$result.CommitID}}/{{EscapePound $result.Filename}}#L{{.}}"><span>{{.}}</span></a>
 | 
																			<a href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{$result.Filename | PathEscapeSegments}}#L{{.}}"><span>{{.}}</span></a>
 | 
				
			||||||
													{{end}}
 | 
																		{{end}}
 | 
				
			||||||
												</td>
 | 
																	</td>
 | 
				
			||||||
												<td class="lines-code chroma"><code class="code-inner">{{.FormattedLines | Safe}}</code></td>
 | 
																	<td class="lines-code chroma"><code class="code-inner">{{.FormattedLines | Safe}}</code></td>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
	<title>{{.i18n.Tr "mail.activate_account.title" .DisplayName}}</title>
 | 
						<title>{{.i18n.Tr "mail.activate_account.title" .DisplayName}}</title>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{{ $activate_url := printf "%suser/activate?code=%s" AppUrl .Code}}
 | 
					{{ $activate_url := printf "%suser/activate?code=%s" AppUrl (QueryEscape .Code)}}
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.activate_account.text_1" .DisplayName AppName | Str2html}}</p><br>
 | 
						<p>{{.i18n.Tr "mail.activate_account.text_1" .DisplayName AppName | Str2html}}</p><br>
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.activate_account.text_2" .ActiveCodeLives | Str2html}}</p><p><a href="{{$activate_url}}">{{$activate_url}}</a></p><br>
 | 
						<p>{{.i18n.Tr "mail.activate_account.text_2" .ActiveCodeLives | Str2html}}</p><p><a href="{{$activate_url}}">{{$activate_url}}</a></p><br>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
	<title>{{.i18n.Tr "mail.activate_email.title" .DisplayName}}</title>
 | 
						<title>{{.i18n.Tr "mail.activate_email.title" .DisplayName}}</title>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{{ $activate_url := printf "%suser/activate_email?code=%s&email=%s" AppUrl .Code (QueryEscape .Email)}}
 | 
					{{ $activate_url := printf "%suser/activate_email?code=%s&email=%s" AppUrl (QueryEscape .Code) (QueryEscape .Email)}}
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}</p><br>
 | 
						<p>{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}</p><br>
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.activate_email.text" .ActiveCodeLives | Str2html}}</p><p><a href="{{$activate_url}}">{{$activate_url}}</a></p><br>
 | 
						<p>{{.i18n.Tr "mail.activate_email.text" .ActiveCodeLives | Str2html}}</p><p><a href="{{$activate_url}}">{{$activate_url}}</a></p><br>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}</p><br>
 | 
						<p>{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}</p><br>
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.register_notify.text_1" AppName}}</p><br>
 | 
						<p>{{.i18n.Tr "mail.register_notify.text_1" AppName}}</p><br>
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.register_notify.text_2" .Username}}</p><p><a href="{{AppUrl}}user/login">{{AppUrl}}user/login</a></p><br>
 | 
						<p>{{.i18n.Tr "mail.register_notify.text_2" .Username}}</p><p><a href="{{AppUrl}}user/login">{{AppUrl}}user/login</a></p><br>
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.register_notify.text_3" $set_pwd_url | Str2html}}</p><br>
 | 
						<p>{{.i18n.Tr "mail.register_notify.text_3" ($set_pwd_url | Escape) | Str2html}}</p><br>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<p>© <a target="_blank" rel="noopener noreferrer" href="{{AppUrl}}">{{AppName}}</a></p>
 | 
						<p>© <a target="_blank" rel="noopener noreferrer" href="{{AppUrl}}">{{AppName}}</a></p>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
	<title>{{.i18n.Tr "mail.reset_password.title" .DisplayName}}</title>
 | 
						<title>{{.i18n.Tr "mail.reset_password.title" .DisplayName}}</title>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{{ $recover_url := printf "%suser/recover_account?code=%s" AppUrl .Code}}
 | 
					{{ $recover_url := printf "%suser/recover_account?code=%s" AppUrl (QueryEscape .Code)}}
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}</p><br>
 | 
						<p>{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}</p><br>
 | 
				
			||||||
	<p>{{.i18n.Tr "mail.reset_password.text" .ResetPwdCodeLives | Str2html}}</p><p><a href="{{$recover_url}}">{{$recover_url}}</a></p><br>
 | 
						<p>{{.i18n.Tr "mail.reset_password.text" .ResetPwdCodeLives | Str2html}}</p><p><a href="{{$recover_url}}">{{$recover_url}}</a></p><br>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,8 +8,8 @@
 | 
				
			||||||
	<title>{{.Subject}}</title>
 | 
						<title>{{.Subject}}</title>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{{$repo_url := printf "<a href='%s'>%s</a>" .Issue.Repo.HTMLURL .Issue.Repo.FullName}}
 | 
					{{$repo_url := printf "<a href='%s'>%s</a>" (Escape .Issue.Repo.HTMLURL) (Escape .Issue.Repo.FullName)}}
 | 
				
			||||||
{{$link := printf "<a href='%s'>#%d</a>" .Link .Issue.Index}}
 | 
					{{$link := printf "<a href='%s'>#%d</a>" (Escape .Link) (Escape .Issue.Index)}}
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
	<p>
 | 
						<p>
 | 
				
			||||||
		{{if .IsPull}}
 | 
							{{if .IsPull}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,13 +20,13 @@
 | 
				
			||||||
	{{if eq .ActionName "push"}}
 | 
						{{if eq .ActionName "push"}}
 | 
				
			||||||
		<p>
 | 
							<p>
 | 
				
			||||||
			{{if .Comment.IsForcePush}}
 | 
								{{if .Comment.IsForcePush}}
 | 
				
			||||||
				{{$oldCommitUrl := printf "%s%s/%s/commit/%s" AppUrl  .Comment.Issue.PullRequest.BaseRepo.OwnerName .Comment.Issue.PullRequest.BaseRepo.Name .Comment.OldCommit}}
 | 
									{{$oldCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.OldCommit}}
 | 
				
			||||||
				{{$oldShortSha := ShortSha .Comment.OldCommit}}
 | 
									{{$oldShortSha := ShortSha .Comment.OldCommit}}
 | 
				
			||||||
				{{$oldCommitLink := printf "<a href='%[1]s'><b>%[2]s</b></a>" $oldCommitUrl $oldShortSha}}
 | 
									{{$oldCommitLink := printf "<a href='%[1]s'><b>%[2]s</b></a>" (Escape $oldCommitUrl) (Escape $oldShortSha)}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				{{$newCommitUrl := printf "%s%s/%s/commit/%s" AppUrl  .Comment.Issue.PullRequest.BaseRepo.OwnerName .Comment.Issue.PullRequest.BaseRepo.Name .Comment.NewCommit}}
 | 
									{{$newCommitUrl := printf "%s/commit/%s" .Comment.Issue.PullRequest.BaseRepo.HTMLURL .Comment.NewCommit}}
 | 
				
			||||||
				{{$newShortSha := ShortSha .Comment.NewCommit}}
 | 
									{{$newShortSha := ShortSha .Comment.NewCommit}}
 | 
				
			||||||
				{{$newCommitLink := printf "<a href='%[1]s'><b>%[2]s</b></a>" $newCommitUrl $newShortSha}}
 | 
									{{$newCommitLink := printf "<a href='%[1]s'><b>%[2]s</b></a>" (Escape $newCommitUrl) (Escape $newShortSha)}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				{{.i18n.Tr "mail.issue.action.force_push" .Doer.Name .Comment.Issue.PullRequest.HeadBranch $oldCommitLink $newCommitLink | Str2html}}
 | 
									{{.i18n.Tr "mail.issue.action.force_push" .Doer.Name .Comment.Issue.PullRequest.HeadBranch $oldCommitLink $newCommitLink | Str2html}}
 | 
				
			||||||
			{{else}}
 | 
								{{else}}
 | 
				
			||||||
| 
						 | 
					@ -36,26 +36,26 @@
 | 
				
			||||||
	{{end}}
 | 
						{{end}}
 | 
				
			||||||
	<p>
 | 
						<p>
 | 
				
			||||||
		{{if eq .ActionName "close"}}
 | 
							{{if eq .ActionName "close"}}
 | 
				
			||||||
			{{.i18n.Tr "mail.issue.action.close" .Doer.Name .Issue.Index | Str2html}}
 | 
								{{.i18n.Tr "mail.issue.action.close" (Escape .Doer.Name) .Issue.Index | Str2html}}
 | 
				
			||||||
		{{else if eq .ActionName "reopen"}}
 | 
							{{else if eq .ActionName "reopen"}}
 | 
				
			||||||
			{{.i18n.Tr "mail.issue.action.reopen" .Doer.Name .Issue.Index | Str2html}}
 | 
								{{.i18n.Tr "mail.issue.action.reopen" (Escape .Doer.Name) .Issue.Index | Str2html}}
 | 
				
			||||||
		{{else if eq .ActionName "merge"}}
 | 
							{{else if eq .ActionName "merge"}}
 | 
				
			||||||
			{{.i18n.Tr "mail.issue.action.merge" .Doer.Name .Issue.Index .Issue.PullRequest.BaseBranch | Str2html}}
 | 
								{{.i18n.Tr "mail.issue.action.merge" (Escape .Doer.Name) .Issue.Index (Escape .Issue.PullRequest.BaseBranch) | Str2html}}
 | 
				
			||||||
		{{else if eq .ActionName "approve"}}
 | 
							{{else if eq .ActionName "approve"}}
 | 
				
			||||||
			{{.i18n.Tr "mail.issue.action.approve" .Doer.Name | Str2html}}
 | 
								{{.i18n.Tr "mail.issue.action.approve" (Escape .Doer.Name) | Str2html}}
 | 
				
			||||||
		{{else if eq .ActionName "reject"}}
 | 
							{{else if eq .ActionName "reject"}}
 | 
				
			||||||
			{{.i18n.Tr "mail.issue.action.reject" .Doer.Name | Str2html}}
 | 
								{{.i18n.Tr "mail.issue.action.reject" (Escape .Doer.Name) | Str2html}}
 | 
				
			||||||
		{{else if eq .ActionName "review"}}
 | 
							{{else if eq .ActionName "review"}}
 | 
				
			||||||
			{{.i18n.Tr "mail.issue.action.review" .Doer.Name | Str2html}}
 | 
								{{.i18n.Tr "mail.issue.action.review" (Escape .Doer.Name) | Str2html}}
 | 
				
			||||||
		{{else if eq .ActionName "review_dismissed"}}
 | 
							{{else if eq .ActionName "review_dismissed"}}
 | 
				
			||||||
			{{.i18n.Tr "mail.issue.action.review_dismissed" .Doer.Name .Comment.Review.Reviewer.Name | Str2html}}
 | 
								{{.i18n.Tr "mail.issue.action.review_dismissed" (Escape .Doer.Name) (Escape .Comment.Review.Reviewer.Name) | Str2html}}
 | 
				
			||||||
		{{else if eq .ActionName "ready_for_review"}}
 | 
							{{else if eq .ActionName "ready_for_review"}}
 | 
				
			||||||
			{{.i18n.Tr "mail.issue.action.ready_for_review" .Doer.Name | Str2html}}
 | 
								{{.i18n.Tr "mail.issue.action.ready_for_review" (Escape .Doer.Name) | Str2html}}
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{{- if eq .Body ""}}
 | 
							{{- if eq .Body ""}}
 | 
				
			||||||
			{{if eq .ActionName "new"}}
 | 
								{{if eq .ActionName "new"}}
 | 
				
			||||||
				{{.i18n.Tr "mail.issue.action.new" .Doer.Name .Issue.Index | Str2html}}
 | 
									{{.i18n.Tr "mail.issue.action.new" (Escape .Doer.Name) .Issue.Index | Str2html}}
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
		{{else}}
 | 
							{{else}}
 | 
				
			||||||
			{{.Body | Str2html}}
 | 
								{{.Body | Str2html}}
 | 
				
			||||||
| 
						 | 
					@ -72,7 +72,7 @@
 | 
				
			||||||
			<ul>
 | 
								<ul>
 | 
				
			||||||
			{{range .Comment.Commits}}
 | 
								{{range .Comment.Commits}}
 | 
				
			||||||
				<li>
 | 
									<li>
 | 
				
			||||||
					<a href="{{AppUrl}}{{$.Comment.Issue.PullRequest.BaseRepo.OwnerName}}/{{$.Comment.Issue.PullRequest.BaseRepo.Name}}/commit/{{.ID}}">
 | 
										<a href="{{$.Comment.Issue.PullRequest.BaseRepo.HTMLURL}}/commit/{{.ID}}">
 | 
				
			||||||
						{{ShortSha .ID.String}}
 | 
											{{ShortSha .ID.String}}
 | 
				
			||||||
					</a>  -  {{.Summary}}
 | 
										</a>  -  {{.Summary}}
 | 
				
			||||||
				</li>
 | 
									</li>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
	<title>{{.Subject}}</title>
 | 
						<title>{{.Subject}}</title>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{{$url := printf "<a href='%[1]s'>%[2]s</a>" .Link .Repo}}
 | 
					{{$url := printf "<a href='%[1]s'>%[2]s</a>" (Escape .Link) (Escape .Repo)}}
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
	<p>{{.Subject}}.
 | 
						<p>{{.Subject}}.
 | 
				
			||||||
		{{.i18n.Tr "mail.repo.transfer.body" $url | Str2html}}
 | 
							{{.i18n.Tr "mail.repo.transfer.body" $url | Str2html}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,8 +11,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{{$release_url := printf "<a href='%s'>%s</a>" .Release.HTMLURL .Release.TagName}}
 | 
					{{$release_url := printf "<a href='%s'>%s</a>" (.Release.HTMLURL | Escape) (.Release.TagName | Escape) }}
 | 
				
			||||||
{{$repo_url := printf "<a href='%s'>%s</a>" .Release.Repo.HTMLURL .Release.Repo.FullName}}
 | 
					{{$repo_url := printf "<a href='%s'>%s</a>" (.Release.Repo.HTMLURL | Escape) (.Release.Repo.FullName | Escape)}}
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
	<p>
 | 
						<p>
 | 
				
			||||||
		{{.i18n.Tr "mail.release.new.text" .Release.Publisher.Name $release_url $repo_url | Str2html}}
 | 
							{{.i18n.Tr "mail.release.new.text" .Release.Publisher.Name $release_url $repo_url | Str2html}}
 | 
				
			||||||
| 
						 | 
					@ -31,13 +31,11 @@
 | 
				
			||||||
		<br>
 | 
							<br>
 | 
				
			||||||
		{{.i18n.Tr "mail.release.downloads"}}
 | 
							{{.i18n.Tr "mail.release.downloads"}}
 | 
				
			||||||
		<ul>
 | 
							<ul>
 | 
				
			||||||
			{{$tagname := .Release.TagName | EscapePound}}
 | 
					 | 
				
			||||||
			{{$archive_url := printf "%s%s/%s/archive" AppUrl .Release.Repo.OwnerName .Release.Repo.Name}}
 | 
					 | 
				
			||||||
			<li>
 | 
								<li>
 | 
				
			||||||
				<a href="{{$archive_url}}/{{$tagname}}.zip" rel="nofollow"><strong>{{.i18n.Tr "mail.release.download.zip"}}</strong></a>
 | 
									<a href="{{.Release.Repo.Link}}/archive/{{.Release.TagName | PathEscapeSegments}}.zip" rel="nofollow"><strong>{{.i18n.Tr "mail.release.download.zip"}}</strong></a>
 | 
				
			||||||
			</li>
 | 
								</li>
 | 
				
			||||||
			<li>
 | 
								<li>
 | 
				
			||||||
				<a href="{{$archive_url}}/{{$tagname}}.tar.gz" rel="nofollow"><strong>{{.i18n.Tr "mail.release.download.targz"}}</strong></a>
 | 
									<a href="{{.Release.Repo.Link}}/archive/{{.Release.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow"><strong>{{.i18n.Tr "mail.release.download.targz"}}</strong></a>
 | 
				
			||||||
			</li>
 | 
								</li>
 | 
				
			||||||
			{{if .Release.Attachments}}
 | 
								{{if .Release.Attachments}}
 | 
				
			||||||
				{{range .Release.Attachments}}
 | 
									{{range .Release.Attachments}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,10 +68,10 @@
 | 
				
			||||||
					<div class="ui attached table segment teams">
 | 
										<div class="ui attached table segment teams">
 | 
				
			||||||
						{{range .Teams}}
 | 
											{{range .Teams}}
 | 
				
			||||||
							<div class="item">
 | 
												<div class="item">
 | 
				
			||||||
								<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong class="team-name">{{.Name}}</strong></a>
 | 
													<a href="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}"><strong class="team-name">{{.Name}}</strong></a>
 | 
				
			||||||
								<p class="text grey">
 | 
													<p class="text grey">
 | 
				
			||||||
									<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> ·
 | 
														<a href="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}"><strong>{{.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> ·
 | 
				
			||||||
									<a href="{{$.OrgLink}}/teams/{{.LowerName}}/repositories"><strong>{{.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a>
 | 
														<a href="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}/repositories"><strong>{{.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a>
 | 
				
			||||||
								</p>
 | 
													</p>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						{{end}}
 | 
											{{end}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@
 | 
				
			||||||
				{{template "org/team/navbar" .}}
 | 
									{{template "org/team/navbar" .}}
 | 
				
			||||||
				{{if .IsOrganizationOwner}}
 | 
									{{if .IsOrganizationOwner}}
 | 
				
			||||||
					<div class="ui attached segment">
 | 
										<div class="ui attached segment">
 | 
				
			||||||
						<form class="ui form" id="add-member-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/add" method="post">
 | 
											<form class="ui form" id="add-member-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/add" method="post">
 | 
				
			||||||
							{{.CsrfTokenHtml}}
 | 
												{{.CsrfTokenHtml}}
 | 
				
			||||||
							<input type="hidden" name="uid" value="{{.SignedUser.ID}}">
 | 
												<input type="hidden" name="uid" value="{{.SignedUser.ID}}">
 | 
				
			||||||
							<div class="inline field ui left">
 | 
												<div class="inline field ui left">
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@
 | 
				
			||||||
							{{if and $.IsOrganizationOwner (not (eq $.SignedUser.ID .ID))}}
 | 
												{{if and $.IsOrganizationOwner (not (eq $.SignedUser.ID .ID))}}
 | 
				
			||||||
								<form>
 | 
													<form>
 | 
				
			||||||
									<button class="ui red button delete-button right" data-modal-id="remove-team-member"
 | 
														<button class="ui red button delete-button right" data-modal-id="remove-team-member"
 | 
				
			||||||
										data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/remove" data-datauid="{{.ID}}"
 | 
															data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/remove" data-datauid="{{.ID}}"
 | 
				
			||||||
										data-name="{{.DisplayName}}"
 | 
															data-name="{{.DisplayName}}"
 | 
				
			||||||
										data-data-team-name="{{$.Team.Name}}">{{$.i18n.Tr "org.members.remove"}}</button>
 | 
															data-data-team-name="{{$.Team.Name}}">{{$.i18n.Tr "org.members.remove"}}</button>
 | 
				
			||||||
								</form>
 | 
													</form>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
<div class="ui top attached tabular menu">
 | 
					<div class="ui top attached tabular menu">
 | 
				
			||||||
	<a class="item{{if .PageIsOrgTeamMembers}} active{{end}}" href="{{.OrgLink}}/teams/{{.Team.LowerName}}">{{svg "octicon-person"}} <strong>{{.Team.NumMembers}}</strong>  {{$.i18n.Tr "org.lower_members"}}</a>
 | 
						<a class="item{{if .PageIsOrgTeamMembers}} active{{end}}" href="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}">{{svg "octicon-person"}} <strong>{{.Team.NumMembers}}</strong>  {{$.i18n.Tr "org.lower_members"}}</a>
 | 
				
			||||||
	<a class="item{{if .PageIsOrgTeamRepos}} active{{end}}" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/repositories">{{svg "octicon-repo"}} <strong>{{.Team.NumRepos}}</strong>  {{$.i18n.Tr "org.lower_repositories"}}</a>
 | 
						<a class="item{{if .PageIsOrgTeamRepos}} active{{end}}" href="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/repositories">{{svg "octicon-repo"}} <strong>{{.Team.NumRepos}}</strong>  {{$.i18n.Tr "org.lower_repositories"}}</a>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
	{{template "org/header" .}}
 | 
						{{template "org/header" .}}
 | 
				
			||||||
	<div class="ui middle very relaxed page grid">
 | 
						<div class="ui middle very relaxed page grid">
 | 
				
			||||||
		<div class="column">
 | 
							<div class="column">
 | 
				
			||||||
			<form class="ui form" action="{{if .PageIsOrgTeamsNew}}{{.OrgLink}}/teams/new{{else}}{{.OrgLink}}/teams/{{.Team.LowerName}}/edit{{end}}" data-delete-url="{{.OrgLink}}/teams/{{.Team.LowerName}}/delete" method="post">
 | 
								<form class="ui form" action="{{if .PageIsOrgTeamsNew}}{{.OrgLink}}/teams/new{{else}}{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/edit{{end}}" data-delete-url="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/delete" method="post">
 | 
				
			||||||
				{{.CsrfTokenHtml}}
 | 
									{{.CsrfTokenHtml}}
 | 
				
			||||||
				<h3 class="ui top attached header">
 | 
									<h3 class="ui top attached header">
 | 
				
			||||||
					{{if .PageIsOrgTeamsNew}}{{.i18n.Tr "org.create_new_team"}}{{else}}{{.i18n.Tr "org.teams.settings"}}{{end}}
 | 
										{{if .PageIsOrgTeamsNew}}{{.i18n.Tr "org.create_new_team"}}{{else}}{{.i18n.Tr "org.teams.settings"}}{{end}}
 | 
				
			||||||
| 
						 | 
					@ -104,7 +104,7 @@
 | 
				
			||||||
						{{else}}
 | 
											{{else}}
 | 
				
			||||||
							<button class="ui green button">{{.i18n.Tr "org.teams.update_settings"}}</button>
 | 
												<button class="ui green button">{{.i18n.Tr "org.teams.update_settings"}}</button>
 | 
				
			||||||
							{{if not (eq .Team.LowerName "owners")}}
 | 
												{{if not (eq .Team.LowerName "owners")}}
 | 
				
			||||||
								<button class="ui red button delete-button" data-url="{{.OrgLink}}/teams/{{.team_name}}/delete">{{.i18n.Tr "org.teams.delete_team"}}</button>
 | 
													<button class="ui red button delete-button" data-url="{{.OrgLink}}/teams/{{.team_name | PathEscape}}/delete">{{.i18n.Tr "org.teams.delete_team"}}</button>
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
						{{end}}
 | 
											{{end}}
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@
 | 
				
			||||||
				{{if $canAddRemove}}
 | 
									{{if $canAddRemove}}
 | 
				
			||||||
					<div class="ui attached segment" id="repo-top-segment">
 | 
										<div class="ui attached segment" id="repo-top-segment">
 | 
				
			||||||
						<div class="inline ui field left">
 | 
											<div class="inline ui field left">
 | 
				
			||||||
							<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post">
 | 
												<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
 | 
				
			||||||
								{{.CsrfTokenHtml}}
 | 
													{{.CsrfTokenHtml}}
 | 
				
			||||||
								<div class="inline field ui left">
 | 
													<div class="inline field ui left">
 | 
				
			||||||
									<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
 | 
														<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
 | 
				
			||||||
| 
						 | 
					@ -24,9 +24,9 @@
 | 
				
			||||||
							</form>
 | 
												</form>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
						<div class="inline ui field right">
 | 
											<div class="inline ui field right">
 | 
				
			||||||
							<form class="ui form" id="repo-multiple-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/repositories" method="post">
 | 
												<form class="ui form" id="repo-multiple-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/repositories" method="post">
 | 
				
			||||||
								<button class="ui red button delete-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/removeall">{{.i18n.Tr "remove_all"}}</button>
 | 
													<button class="ui red button delete-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/removeall">{{.i18n.Tr "remove_all"}}</button>
 | 
				
			||||||
								<button class="ui green button add-all-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/addall">{{.i18n.Tr "add_all"}}</button>
 | 
													<button class="ui green button add-all-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/addall">{{.i18n.Tr "add_all"}}</button>
 | 
				
			||||||
							</form>
 | 
												</form>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
| 
						 | 
					@ -35,12 +35,12 @@
 | 
				
			||||||
					{{range .Team.Repos}}
 | 
										{{range .Team.Repos}}
 | 
				
			||||||
						<div class="item">
 | 
											<div class="item">
 | 
				
			||||||
							{{if $canAddRemove}}
 | 
												{{if $canAddRemove}}
 | 
				
			||||||
								<form method="post" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove">
 | 
													<form method="post" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/remove">
 | 
				
			||||||
									{{$.CsrfTokenHtml}}
 | 
														{{$.CsrfTokenHtml}}
 | 
				
			||||||
									<button type="submit" class="ui red small button right" name="repoid" value="{{.ID}}">{{$.i18n.Tr "remove"}}</button>
 | 
														<button type="submit" class="ui red small button right" name="repoid" value="{{.ID}}">{{$.i18n.Tr "remove"}}</button>
 | 
				
			||||||
								</form>
 | 
													</form>
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
							<a class="member" href="{{AppSubUrl}}/{{$.Org.Name}}/{{.Name}}">
 | 
												<a class="member" href="{{$.Org.HomeLink}}/{{.Name | PathEscape}}">
 | 
				
			||||||
								{{if .IsPrivate}}
 | 
													{{if .IsPrivate}}
 | 
				
			||||||
									{{svg "octicon-lock"}}
 | 
														{{svg "octicon-lock"}}
 | 
				
			||||||
								{{else if .IsFork}}
 | 
													{{else if .IsFork}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,11 +5,11 @@
 | 
				
			||||||
			{{if .Team.IsMember $.SignedUser.ID}}
 | 
								{{if .Team.IsMember $.SignedUser.ID}}
 | 
				
			||||||
				<form>
 | 
									<form>
 | 
				
			||||||
					<button class="ui red tiny button delete-button" data-modal-id="leave-team-sidebar"
 | 
										<button class="ui red tiny button delete-button" data-modal-id="leave-team-sidebar"
 | 
				
			||||||
						data-url="{{.OrgLink}}/teams/{{.Team.LowerName}}/action/leave" data-datauid="{{$.SignedUser.ID}}"
 | 
											data-url="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/action/leave" data-datauid="{{$.SignedUser.ID}}"
 | 
				
			||||||
						data-name="{{.Team.Name}}">{{$.i18n.Tr "org.teams.leave"}}</button>
 | 
											data-name="{{.Team.Name}}">{{$.i18n.Tr "org.teams.leave"}}</button>
 | 
				
			||||||
				</form>
 | 
									</form>
 | 
				
			||||||
			{{else if .IsOrganizationOwner}}
 | 
								{{else if .IsOrganizationOwner}}
 | 
				
			||||||
				<form method="post" action="{{.OrgLink}}/teams/{{.Team.LowerName}}/action/join">
 | 
									<form method="post" action="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/action/join">
 | 
				
			||||||
					{{$.CsrfTokenHtml}}
 | 
										{{$.CsrfTokenHtml}}
 | 
				
			||||||
					<input type="hidden" name="page" value="team"/>
 | 
										<input type="hidden" name="page" value="team"/>
 | 
				
			||||||
					<button type="submit" class="ui blue tiny button" name="uid" value="{{$.SignedUser.ID}}">{{$.i18n.Tr "org.teams.join"}}</button>
 | 
										<button type="submit" class="ui blue tiny button" name="uid" value="{{$.SignedUser.ID}}">{{$.i18n.Tr "org.teams.join"}}</button>
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,7 @@
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	{{if .IsOrganizationOwner}}
 | 
						{{if .IsOrganizationOwner}}
 | 
				
			||||||
		<div class="ui bottom attached segment">
 | 
							<div class="ui bottom attached segment">
 | 
				
			||||||
			<a class="ui teal small button" href="{{.OrgLink}}/teams/{{.Team.LowerName}}/edit">{{svg "octicon-gear"}} {{$.i18n.Tr "org.teams.settings"}}</a>
 | 
								<a class="ui teal small button" href="{{.OrgLink}}/teams/{{.Team.LowerName | PathEscape}}/edit">{{svg "octicon-gear"}} {{$.i18n.Tr "org.teams.settings"}}</a>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	{{end}}
 | 
						{{end}}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,16 +14,16 @@
 | 
				
			||||||
			{{range .Teams}}
 | 
								{{range .Teams}}
 | 
				
			||||||
				<div class="column">
 | 
									<div class="column">
 | 
				
			||||||
					<div class="ui top attached header">
 | 
										<div class="ui top attached header">
 | 
				
			||||||
						<a class="text black" href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.Name}}</strong></a>
 | 
											<a class="text black" href="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}"><strong>{{.Name}}</strong></a>
 | 
				
			||||||
						<div class="ui right">
 | 
											<div class="ui right">
 | 
				
			||||||
							{{if .IsMember $.SignedUser.ID}}
 | 
												{{if .IsMember $.SignedUser.ID}}
 | 
				
			||||||
								<form>
 | 
													<form>
 | 
				
			||||||
									<button class="ui red tiny button delete-button" data-modal-id="leave-team"
 | 
														<button class="ui red tiny button delete-button" data-modal-id="leave-team"
 | 
				
			||||||
										data-url="{{$.OrgLink}}/teams/{{.LowerName}}/action/leave" data-datauid="{{$.SignedUser.ID}}"
 | 
															data-url="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}/action/leave" data-datauid="{{$.SignedUser.ID}}"
 | 
				
			||||||
										data-name="{{.Name}}">{{$.i18n.Tr "org.teams.leave"}}</button>
 | 
															data-name="{{.Name}}">{{$.i18n.Tr "org.teams.leave"}}</button>
 | 
				
			||||||
								</form>
 | 
													</form>
 | 
				
			||||||
							{{else if $.IsOrganizationOwner}}
 | 
												{{else if $.IsOrganizationOwner}}
 | 
				
			||||||
								<form method="post" action="{{$.OrgLink}}/teams/{{.LowerName}}/action/join">
 | 
													<form method="post" action="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}/action/join">
 | 
				
			||||||
									{{$.CsrfTokenHtml}}
 | 
														{{$.CsrfTokenHtml}}
 | 
				
			||||||
									<button type="submit" class="ui blue small button" name="uid" value="{{$.SignedUser.ID}}">{{$.i18n.Tr "org.teams.join"}}</button>
 | 
														<button type="submit" class="ui blue small button" name="uid" value="{{$.SignedUser.ID}}">{{$.i18n.Tr "org.teams.join"}}</button>
 | 
				
			||||||
								</form>
 | 
													</form>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,7 +126,7 @@
 | 
				
			||||||
						<span class="ui green label">{{$.i18n.Tr "repo.activity.published_release_label"}}</span>
 | 
											<span class="ui green label">{{$.i18n.Tr "repo.activity.published_release_label"}}</span>
 | 
				
			||||||
						{{.TagName}}
 | 
											{{.TagName}}
 | 
				
			||||||
						{{if not .IsTag}}
 | 
											{{if not .IsTag}}
 | 
				
			||||||
							<a class="title" href="{{$.RepoLink}}/src/{{.TagName | EscapePound}}">{{.Title | RenderEmoji}}</a>
 | 
												<a class="title" href="{{$.RepoLink}}/src/{{.TagName | PathEscapeSegments}}">{{.Title | RenderEmoji}}</a>
 | 
				
			||||||
						{{end}}
 | 
											{{end}}
 | 
				
			||||||
						{{TimeSinceUnix .CreatedUnix $.Lang}}
 | 
											{{TimeSinceUnix .CreatedUnix $.Lang}}
 | 
				
			||||||
					</p>
 | 
										</p>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,12 +10,12 @@
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div class="file-header-right file-actions df ac">
 | 
							<div class="file-header-right file-actions df ac">
 | 
				
			||||||
			<div class="ui buttons">
 | 
								<div class="ui buttons">
 | 
				
			||||||
				<a class="ui tiny button" href="{{EscapePound $.RawFileLink}}">{{.i18n.Tr "repo.file_raw"}}</a>
 | 
									<a class="ui tiny button" href="{{$.RawFileLink}}">{{.i18n.Tr "repo.file_raw"}}</a>
 | 
				
			||||||
				{{if not .IsViewCommit}}
 | 
									{{if not .IsViewCommit}}
 | 
				
			||||||
					<a class="ui tiny button" href="{{.RepoLink}}/src/commit/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_permalink"}}</a>
 | 
										<a class="ui tiny button" href="{{.RepoLink}}/src/commit/{{.CommitID | PathEscape}}/{{.TreePath | PathEscapeSegments}}">{{.i18n.Tr "repo.file_permalink"}}</a>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
				<a class="ui tiny button" href="{{.RepoLink}}/src/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.normal_view"}}</a>
 | 
									<a class="ui tiny button" href="{{.RepoLink}}/src/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{.i18n.Tr "repo.normal_view"}}</a>
 | 
				
			||||||
				<a class="ui tiny button" href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_history"}}</a>
 | 
									<a class="ui tiny button" href="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/{{.TreePath | PathEscapeSegments}}">{{.i18n.Tr "repo.file_history"}}</a>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</h4>
 | 
						</h4>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,22 +18,22 @@
 | 
				
			||||||
								{{if .IsProtected}}
 | 
													{{if .IsProtected}}
 | 
				
			||||||
									{{svg "octicon-shield-lock"}}
 | 
														{{svg "octicon-shield-lock"}}
 | 
				
			||||||
								{{end}}
 | 
													{{end}}
 | 
				
			||||||
								<a href="{{$.RepoLink}}/src/branch/{{$.DefaultBranch | EscapePound}}">{{$.DefaultBranch}}</a>
 | 
													<a href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments $.DefaultBranch}}">{{$.DefaultBranch}}</a>
 | 
				
			||||||
								<p class="info df ac my-2">{{svg "octicon-git-commit" 16 "mr-2"}}<a href="{{$.RepoLink}}/commit/{{.Commit.ID.String}}">{{ShortSha .Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage .Commit.CommitMessage $.RepoLink $.Repository.ComposeMetas}}</span> · {{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Commit.Committer.When $.i18n.Lang}}</p>
 | 
													<p class="info df ac my-2">{{svg "octicon-git-commit" 16 "mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .Commit.ID.String}}">{{ShortSha .Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage .Commit.CommitMessage $.RepoLink $.Repository.ComposeMetas}}</span> · {{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Commit.Committer.When $.i18n.Lang}}</p>
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
						{{end}}
 | 
											{{end}}
 | 
				
			||||||
						</td>
 | 
											</td>
 | 
				
			||||||
						<td class="right aligned overflow-visible">
 | 
											<td class="right aligned overflow-visible">
 | 
				
			||||||
							{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
 | 
												{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
 | 
				
			||||||
								<div class="ui basic jump button icon poping up show-create-branch-modal" data-content="{{$.i18n.Tr "repo.branch.new_branch_from" ($.DefaultBranch)}}" data-variation="tiny inverted" data-branch-from="{{EscapePound $.DefaultBranch}}" data-modal="#create-branch-modal" data-position="top right">
 | 
													<div class="ui basic jump button icon poping up show-create-branch-modal" data-content="{{$.i18n.Tr "repo.branch.new_branch_from" ($.DefaultBranch)}}" data-variation="tiny inverted" data-branch-from="{{$.DefaultBranch}}" data-branch-from-urlcomponent="{{PathEscapeSegments $.DefaultBranch}}" data-modal="#create-branch-modal" data-position="top right">
 | 
				
			||||||
									{{svg "octicon-git-branch"}}
 | 
														{{svg "octicon-git-branch"}}
 | 
				
			||||||
								</div>
 | 
													</div>
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
							<div class="ui basic jump dropdown icon button poping up" data-content="{{$.i18n.Tr "repo.branch.download" ($.DefaultBranch)}}" data-variation="tiny inverted" data-position="top right">
 | 
												<div class="ui basic jump dropdown icon button poping up" data-content="{{$.i18n.Tr "repo.branch.download" ($.DefaultBranch)}}" data-variation="tiny inverted" data-position="top right">
 | 
				
			||||||
								{{svg "octicon-download"}}
 | 
													{{svg "octicon-download"}}
 | 
				
			||||||
								<div class="menu">
 | 
													<div class="menu">
 | 
				
			||||||
									<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.DefaultBranch}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
														<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{PathEscapeSegments $.DefaultBranch}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
				
			||||||
									<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.DefaultBranch}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
														<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{PathEscapeSegments $.DefaultBranch}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
				
			||||||
								</div>
 | 
													</div>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</td>
 | 
											</td>
 | 
				
			||||||
| 
						 | 
					@ -54,14 +54,14 @@
 | 
				
			||||||
								<tr>
 | 
													<tr>
 | 
				
			||||||
									<td class="six wide">
 | 
														<td class="six wide">
 | 
				
			||||||
									{{if .IsDeleted}}
 | 
														{{if .IsDeleted}}
 | 
				
			||||||
										<s><a href="{{$.RepoLink}}/src/branch/{{.Name | EscapePound}}">{{.Name}}</a></s>
 | 
															<s><a href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .Name}}">{{.Name}}</a></s>
 | 
				
			||||||
										<p class="info">{{$.i18n.Tr "repo.branch.deleted_by" .DeletedBranch.DeletedBy.Name}} {{TimeSinceUnix .DeletedBranch.DeletedUnix $.i18n.Lang}}</p>
 | 
															<p class="info">{{$.i18n.Tr "repo.branch.deleted_by" .DeletedBranch.DeletedBy.Name}} {{TimeSinceUnix .DeletedBranch.DeletedUnix $.i18n.Lang}}</p>
 | 
				
			||||||
									{{else}}
 | 
														{{else}}
 | 
				
			||||||
										{{if .IsProtected}}
 | 
															{{if .IsProtected}}
 | 
				
			||||||
											{{svg "octicon-shield-lock"}}
 | 
																{{svg "octicon-shield-lock"}}
 | 
				
			||||||
										{{end}}
 | 
															{{end}}
 | 
				
			||||||
										<a href="{{$.RepoLink}}/src/branch/{{.Name | EscapePound}}">{{.Name}}</a>
 | 
															<a href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .Name}}">{{.Name}}</a>
 | 
				
			||||||
										<p class="info df ac my-2">{{svg "octicon-git-commit" 16 "mr-2"}}<a href="{{$.RepoLink}}/commit/{{.Commit.ID.String}}">{{ShortSha .Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage .Commit.CommitMessage $.RepoLink $.Repository.ComposeMetas}}</span> · {{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Commit.Committer.When $.i18n.Lang}}</p>
 | 
															<p class="info df ac my-2">{{svg "octicon-git-commit" 16 "mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .Commit.ID.String}}">{{ShortSha .Commit.ID.String}}</a> · <span class="commit-message">{{RenderCommitMessage .Commit.CommitMessage $.RepoLink $.Repository.ComposeMetas}}</span> · {{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Commit.Committer.When $.i18n.Lang}}</p>
 | 
				
			||||||
									{{end}}
 | 
														{{end}}
 | 
				
			||||||
									</td>
 | 
														</td>
 | 
				
			||||||
									<td class="three wide ui">
 | 
														<td class="three wide ui">
 | 
				
			||||||
| 
						 | 
					@ -85,13 +85,13 @@
 | 
				
			||||||
													{{svg "octicon-git-pull-request"}} {{$.i18n.Tr "repo.branch.included"}}
 | 
																		{{svg "octicon-git-pull-request"}} {{$.i18n.Tr "repo.branch.included"}}
 | 
				
			||||||
												</a>
 | 
																	</a>
 | 
				
			||||||
											{{else if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
 | 
																{{else 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}}">
 | 
																<a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranch}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{PathEscape $.Owner.Name}}:{{end}}{{PathEscapeSegments .Name}}">
 | 
				
			||||||
												<button id="new-pull-request" class="ui compact basic button mr-0">{{if $.CanPull}}{{$.i18n.Tr "repo.pulls.compare_changes"}}{{else}}{{$.i18n.Tr "action.compare_branch"}}{{end}}</button>
 | 
																	<button id="new-pull-request" class="ui compact basic button mr-0">{{if $.CanPull}}{{$.i18n.Tr "repo.pulls.compare_changes"}}{{else}}{{$.i18n.Tr "action.compare_branch"}}{{end}}</button>
 | 
				
			||||||
											</a>
 | 
																</a>
 | 
				
			||||||
											{{end}}
 | 
																{{end}}
 | 
				
			||||||
										{{else if and .LatestPullRequest.HasMerged .MergeMovedOn}}
 | 
															{{else if and .LatestPullRequest.HasMerged .MergeMovedOn}}
 | 
				
			||||||
											{{if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
 | 
																{{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}}">
 | 
																<a href="{{$.RepoLink}}/compare/{{PathEscapeSegments $.DefaultBranch}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | PathEscapeSegments}}">
 | 
				
			||||||
												<button id="new-pull-request" class="ui compact basic button mr-0">{{if $.CanPull}}{{$.i18n.Tr "repo.pulls.compare_changes"}}{{else}}{{$.i18n.Tr "action.compare_branch"}}{{end}}</button>
 | 
																	<button id="new-pull-request" class="ui compact basic button mr-0">{{if $.CanPull}}{{$.i18n.Tr "repo.pulls.compare_changes"}}{{else}}{{$.i18n.Tr "action.compare_branch"}}{{end}}</button>
 | 
				
			||||||
											</a>
 | 
																</a>
 | 
				
			||||||
											{{end}}
 | 
																{{end}}
 | 
				
			||||||
| 
						 | 
					@ -108,7 +108,7 @@
 | 
				
			||||||
									</td>
 | 
														</td>
 | 
				
			||||||
									<td class="two wide right aligned overflow-visible">
 | 
														<td class="two wide right aligned overflow-visible">
 | 
				
			||||||
										{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
 | 
															{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
 | 
				
			||||||
											<div class="ui basic jump button icon poping up show-create-branch-modal" data-branch-from="{{EscapePound .Name}}" data-content="{{$.i18n.Tr "repo.branch.new_branch_from" .Name}}" data-variation="tiny inverted" data-position="top right" data-modal="#create-branch-modal" data-name="{{.Name}}">
 | 
																<div class="ui basic jump button icon poping up show-create-branch-modal" data-branch-from="{{.Name}}" data-branch-from-urlcomponent="{{PathEscapeSegments .Name}}" data-content="{{$.i18n.Tr "repo.branch.new_branch_from" .Name}}" data-variation="tiny inverted" data-position="top right" data-modal="#create-branch-modal" data-name="{{.Name}}">
 | 
				
			||||||
												{{svg "octicon-git-branch"}}
 | 
																	{{svg "octicon-git-branch"}}
 | 
				
			||||||
											</div>
 | 
																</div>
 | 
				
			||||||
										{{end}}
 | 
															{{end}}
 | 
				
			||||||
| 
						 | 
					@ -116,16 +116,16 @@
 | 
				
			||||||
											<div class="ui basic jump dropdown icon button poping up" data-content="{{$.i18n.Tr "repo.branch.download" (.Name)}}" data-variation="tiny inverted" data-position="top right">
 | 
																<div class="ui basic jump dropdown icon button poping up" data-content="{{$.i18n.Tr "repo.branch.download" (.Name)}}" data-variation="tiny inverted" data-position="top right">
 | 
				
			||||||
												{{svg "octicon-download"}}
 | 
																	{{svg "octicon-download"}}
 | 
				
			||||||
												<div class="menu">
 | 
																	<div class="menu">
 | 
				
			||||||
													<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound .Name}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
																		<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{PathEscapeSegments .Name}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
				
			||||||
													<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound .Name}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
																		<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{PathEscapeSegments .Name}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
				
			||||||
												</div>
 | 
																	</div>
 | 
				
			||||||
											</div>
 | 
																</div>
 | 
				
			||||||
										{{end}}
 | 
															{{end}}
 | 
				
			||||||
										{{if and $.IsWriter (not $.IsMirror) (not $.Repository.IsArchived) (not .IsProtected)}}
 | 
															{{if and $.IsWriter (not $.IsMirror) (not $.Repository.IsArchived) (not .IsProtected)}}
 | 
				
			||||||
											{{if .IsDeleted}}
 | 
																{{if .IsDeleted}}
 | 
				
			||||||
												<a class="ui basic jump button icon poping up undo-button" href data-url="{{$.Link}}/restore?branch_id={{.DeletedBranch.ID | urlquery}}&name={{.DeletedBranch.Name | urlquery}}" data-content="{{$.i18n.Tr "repo.branch.restore" (.Name)}}" data-variation="tiny inverted" data-position="top right"><span class="text blue">{{svg "octicon-reply"}}</span></a>
 | 
																	<a class="ui basic jump button icon poping up undo-button" href data-url="{{$.Link}}/restore?branch_id={{.DeletedBranch.ID}}&name={{PathEscapeSegments .DeletedBranch.Name}}" data-content="{{$.i18n.Tr "repo.branch.restore" (.Name)}}" data-variation="tiny inverted" data-position="top right"><span class="text blue">{{svg "octicon-reply"}}</span></a>
 | 
				
			||||||
											{{else}}
 | 
																{{else}}
 | 
				
			||||||
												<a class="ui basic jump button icon poping up delete-button delete-branch-button" href data-url="{{$.Link}}/delete?name={{.Name | urlquery}}" data-content="{{$.i18n.Tr "repo.branch.delete" (.Name)}}" data-variation="tiny inverted" data-position="top right" data-name="{{.Name}}">
 | 
																	<a class="ui basic jump button icon poping up delete-button delete-branch-button" href data-url="{{$.Link}}/delete?name={{PathEscapeSegments .Name}}" data-content="{{$.i18n.Tr "repo.branch.delete" (.Name)}}" data-variation="tiny inverted" data-position="top right" data-name="{{.Name}}">
 | 
				
			||||||
													{{svg "octicon-trash"}}
 | 
																		{{svg "octicon-trash"}}
 | 
				
			||||||
												</a>
 | 
																	</a>
 | 
				
			||||||
											{{end}}
 | 
																{{end}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,14 +17,14 @@
 | 
				
			||||||
		<div class="data" style="display: none" data-mode="{{if .root.IsViewTag}}tags{{else}}branches{{end}}">
 | 
							<div class="data" style="display: none" data-mode="{{if .root.IsViewTag}}tags{{else}}branches{{end}}">
 | 
				
			||||||
			{{if $showBranchesInDropdown}}
 | 
								{{if $showBranchesInDropdown}}
 | 
				
			||||||
				{{range .root.Branches}}
 | 
									{{range .root.Branches}}
 | 
				
			||||||
					<div class="item branch {{if eq $.root.BranchName .}}selected{{end}}" data-url="{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/branch/{{EscapePound .}}{{if $.root.TreePath}}/{{EscapePound $.root.TreePath}}{{end}}">{{.}}</div>
 | 
										<div class="item branch {{if eq $.root.BranchName .}}selected{{end}}" data-url="{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/branch/{{PathEscapeSegments .}}{{if $.root.TreePath}}/{{PathEscapeSegments $.root.TreePath}}{{end}}">{{.}}</div>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
			{{range .root.Tags}}
 | 
								{{range .root.Tags}}
 | 
				
			||||||
				{{if $release}}
 | 
									{{if $release}}
 | 
				
			||||||
					<div class="item tag {{if eq $release.TagName .}}selected{{end}}" data-url="{{$.root.RepoLink}}/compare/{{EscapePound .}}...{{if $release.IsDraft}}{{EscapePound $release.Target}}{{else}}{{if $release.TagName}}{{EscapePound $release.TagName}}{{else}}{{EscapePound $release.Sha1}}{{end}}{{end}}">{{.}}</div>
 | 
										<div class="item tag {{if eq $release.TagName .}}selected{{end}}" data-url="{{$.root.RepoLink}}/compare/{{PathEscapeSegments .}}...{{if $release.IsDraft}}{{PathEscapeSegments $release.Target}}{{else}}{{if $release.TagName}}{{PathEscapeSegments $release.TagName}}{{else}}{{PathEscapeSegments $release.Sha1}}{{end}}{{end}}">{{.}}</div>
 | 
				
			||||||
				{{else}}
 | 
									{{else}}
 | 
				
			||||||
					<div class="item tag {{if eq $.root.BranchName .}}selected{{end}}" data-url="{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/tag/{{EscapePound .}}{{if $.root.TreePath}}/{{EscapePound $.root.TreePath}}{{end}}">{{.}}</div>
 | 
										<div class="item tag {{if eq $.root.BranchName .}}selected{{end}}" data-url="{{$.root.RepoLink}}/{{if $.root.PageIsCommits}}commits{{else}}src{{end}}/tag/{{PathEscapeSegments .}}{{if $.root.TreePath}}/{{PathEscapeSegments $.root.TreePath}}{{end}}">{{.}}</div>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
					<form ref="newBranchForm" action="{{.root.RepoLink}}/branches/_new/{{EscapePound .root.BranchNameSubURL}}" method="post">
 | 
										<form ref="newBranchForm" action="{{.root.RepoLink}}/branches/_new/{{.root.BranchNameSubURL}}" method="post">
 | 
				
			||||||
						{{.root.CsrfTokenHtml}}
 | 
											{{.root.CsrfTokenHtml}}
 | 
				
			||||||
						<input type="hidden" name="new_branch_name" v-model="searchTerm">
 | 
											<input type="hidden" name="new_branch_name" v-model="searchTerm">
 | 
				
			||||||
						<input type="hidden" name="create_tag" v-model="createTag">
 | 
											<input type="hidden" name="create_tag" v-model="createTag">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
		<div class="ui top attached info clearing segment {{$class}}">
 | 
							<div class="ui top attached info clearing segment {{$class}}">
 | 
				
			||||||
			{{if not $.PageIsWiki}}
 | 
								{{if not $.PageIsWiki}}
 | 
				
			||||||
			<a class="ui floated right blue tiny button" href="{{EscapePound .SourcePath}}">
 | 
								<a class="ui floated right blue tiny button" href="{{.SourcePath}}">
 | 
				
			||||||
				{{.i18n.Tr "repo.diff.browse_source"}}
 | 
									{{.i18n.Tr "repo.diff.browse_source"}}
 | 
				
			||||||
			</a>
 | 
								</a>
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
| 
						 | 
					@ -72,9 +72,9 @@
 | 
				
			||||||
							<div class="item">
 | 
												<div class="item">
 | 
				
			||||||
								{{range .Parents}}
 | 
													{{range .Parents}}
 | 
				
			||||||
									{{if $.PageIsWiki}}
 | 
														{{if $.PageIsWiki}}
 | 
				
			||||||
										<a class="ui blue sha label" href="{{$.RepoLink}}/wiki/commit/{{.}}">{{ShortSha .}}</a>
 | 
															<a class="ui blue sha label" href="{{$.RepoLink}}/wiki/commit/{{PathEscape .}}">{{ShortSha .}}</a>
 | 
				
			||||||
									{{else}}
 | 
														{{else}}
 | 
				
			||||||
										<a class="ui blue sha label" href="{{$.RepoLink}}/commit/{{.}}">{{ShortSha .}}</a>
 | 
															<a class="ui blue sha label" href="{{$.RepoLink}}/commit/{{PathEscape .}}">{{ShortSha .}}</a>
 | 
				
			||||||
									{{end}}
 | 
														{{end}}
 | 
				
			||||||
								{{end}}
 | 
													{{end}}
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@
 | 
				
			||||||
								{{if .User.FullName}}
 | 
													{{if .User.FullName}}
 | 
				
			||||||
									{{$userName = .User.FullName}}
 | 
														{{$userName = .User.FullName}}
 | 
				
			||||||
								{{end}}
 | 
													{{end}}
 | 
				
			||||||
								{{avatar .User 28 "mr-2"}}<a href="{{AppSubUrl}}/{{.User.Name}}">{{$userName}}</a>
 | 
													{{avatar .User 28 "mr-2"}}<a href="{{.User.HomeLink}}">{{$userName}}</a>
 | 
				
			||||||
							{{else}}
 | 
												{{else}}
 | 
				
			||||||
								{{avatarByEmail .Author.Email .Author.Name 28 "mr-2"}}
 | 
													{{avatarByEmail .Author.Email .Author.Name 28 "mr-2"}}
 | 
				
			||||||
								{{$userName}}
 | 
													{{$userName}}
 | 
				
			||||||
| 
						 | 
					@ -40,9 +40,9 @@
 | 
				
			||||||
								{{end}}
 | 
													{{end}}
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
							{{if $.PageIsWiki}}
 | 
												{{if $.PageIsWiki}}
 | 
				
			||||||
								<a href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/wiki/commit/{{.ID}}" rel="nofollow" class="{{$class}}">
 | 
													<a href="{{$.RepoLink}}/wiki/commit/{{.ID}}" rel="nofollow" class="{{$class}}">
 | 
				
			||||||
							{{else if $.Reponame}}
 | 
												{{else if $.Reponame}}
 | 
				
			||||||
								<a href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}" rel="nofollow" class="{{$class}}">
 | 
													<a href="{{$.RepoLink}}/commit/{{.ID}}" rel="nofollow" class="{{$class}}">
 | 
				
			||||||
							{{else}}
 | 
												{{else}}
 | 
				
			||||||
								<span class="{{$class}}">
 | 
													<span class="{{$class}}">
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,7 @@
 | 
				
			||||||
							{{if $.PageIsWiki}}
 | 
												{{if $.PageIsWiki}}
 | 
				
			||||||
								<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | RenderEmoji}}</span>
 | 
													<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | RenderEmoji}}</span>
 | 
				
			||||||
							{{else }}
 | 
												{{else }}
 | 
				
			||||||
								{{ $commitLink:= printf "%s/%s/%s/commit/%s" AppSubUrl $.Username $.Reponame .ID }}
 | 
													{{ $commitLink:= printf "%s/commit/%s" $.RepoLink (PathEscape .ID.String) }}
 | 
				
			||||||
								<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject .Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span>
 | 
													<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject .Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span>
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
							</span>
 | 
												</span>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@
 | 
				
			||||||
	<div class="singular-commit" id="{{$tag}}">
 | 
						<div class="singular-commit" id="{{$tag}}">
 | 
				
			||||||
		<span class="badge badge-commit">{{svg "octicon-git-commit"}}</span>
 | 
							<span class="badge badge-commit">{{svg "octicon-git-commit"}}</span>
 | 
				
			||||||
		{{if .User}}
 | 
							{{if .User}}
 | 
				
			||||||
			<a href="{{AppSubUrl}}/{{.User.Name}}">
 | 
								<a href="{.User.HomeLink}}">
 | 
				
			||||||
				{{avatar .User}}
 | 
									{{avatar .User}}
 | 
				
			||||||
			</a>
 | 
								</a>
 | 
				
			||||||
		{{else}}
 | 
							{{else}}
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
			{{if $.comment.Issue.PullRequest.BaseRepo.Name}}
 | 
								{{if $.comment.Issue.PullRequest.BaseRepo.Name}}
 | 
				
			||||||
				<a href="{{AppSubUrl}}/{{$.comment.Issue.PullRequest.BaseRepo.OwnerName}}/{{$.comment.Issue.PullRequest.BaseRepo.Name}}/commit/{{.ID}}" rel="nofollow" class="{{$class}}">
 | 
									<a href="{{$.comment.Issue.PullRequest.BaseRepo.Link}}/commit/{{PathEscape .ID.String}}" rel="nofollow" class="{{$class}}">
 | 
				
			||||||
			{{else}}
 | 
								{{else}}
 | 
				
			||||||
				<span class="{{$class}}">
 | 
									<span class="{{$class}}">
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
| 
						 | 
					@ -46,7 +46,7 @@
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
		</span>
 | 
							</span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{{ $commitLink:= printf "%s/%s/%s/commit/%s" AppSubUrl  $.comment.Issue.PullRequest.BaseRepo.OwnerName $.comment.Issue.PullRequest.BaseRepo.Name .ID }}
 | 
							{{ $commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String) }}
 | 
				
			||||||
		<span class="mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $commitLink $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</span>
 | 
							<span class="mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $commitLink $.comment.Issue.PullRequest.BaseRepo.ComposeMetas}}</span>
 | 
				
			||||||
		{{if IsMultilineCommitMessage .Message}}
 | 
							{{if IsMultilineCommitMessage .Message}}
 | 
				
			||||||
			<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button>
 | 
								<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div class="commits-table-right df ac">
 | 
						<div class="commits-table-right df ac">
 | 
				
			||||||
		{{if .PageIsCommits}}
 | 
							{{if .PageIsCommits}}
 | 
				
			||||||
			<form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.BranchNameSubURL | EscapePound}}/search">
 | 
								<form class="ignore-dirty" action="{{.RepoLink}}/commits/{{.BranchNameSubURL}}/search">
 | 
				
			||||||
				<div class="ui tiny search input">
 | 
									<div class="ui tiny search input">
 | 
				
			||||||
					<input name="q" placeholder="{{.i18n.Tr "repo.commits.search"}}" value="{{.Keyword}}" autofocus>
 | 
										<input name="q" placeholder="{{.i18n.Tr "repo.commits.search"}}" value="{{.Keyword}}" autofocus>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
| 
						 | 
					@ -23,9 +23,9 @@
 | 
				
			||||||
				<button class="ui primary tiny button mr-0 poping up" data-panel="#add-deploy-key-panel" data-content={{.i18n.Tr "repo.commits.search.tooltip"}}>{{.i18n.Tr "repo.commits.find"}}</button>
 | 
									<button class="ui primary tiny button mr-0 poping up" data-panel="#add-deploy-key-panel" data-content={{.i18n.Tr "repo.commits.search.tooltip"}}>{{.i18n.Tr "repo.commits.find"}}</button>
 | 
				
			||||||
			</form>
 | 
								</form>
 | 
				
			||||||
		{{else if .IsDiffCompare}}
 | 
							{{else if .IsDiffCompare}}
 | 
				
			||||||
			<a href="{{$.CommitRepoLink}}/commit/{{.BeforeCommitID}}" class="ui green sha label">{{if not .BaseIsCommit}}{{if .BaseIsBranch}}{{svg "octicon-git-branch"}}{{else if .BaseIsTag}}{{svg "octicon-tag"}}{{end}}{{.BaseBranch}}{{else}}{{ShortSha .BaseBranch}}{{end}}</a>
 | 
								<a href="{{$.CommitRepoLink}}/commit/{{.BeforeCommitID | PathEscape}}" class="ui green sha label">{{if not .BaseIsCommit}}{{if .BaseIsBranch}}{{svg "octicon-git-branch"}}{{else if .BaseIsTag}}{{svg "octicon-tag"}}{{end}}{{.BaseBranch}}{{else}}{{ShortSha .BaseBranch}}{{end}}</a>
 | 
				
			||||||
			...
 | 
								...
 | 
				
			||||||
			<a href="{{$.CommitRepoLink}}/commit/{{.AfterCommitID}}" class="ui green sha label">{{if not .HeadIsCommit}}{{if .HeadIsBranch}}{{svg "octicon-git-branch"}}{{else if .HeadIsTag}}{{svg "octicon-tag"}}{{end}}{{.HeadBranch}}{{else}}{{ShortSha .HeadBranch}}{{end}}</a>
 | 
								<a href="{{$.CommitRepoLink}}/commit/{{.AfterCommitID | PathEscape}}" class="ui green sha label">{{if not .HeadIsCommit}}{{if .HeadIsBranch}}{{svg "octicon-git-branch"}}{{else if .HeadIsTag}}{{svg "octicon-tag"}}{{end}}{{.HeadBranch}}{{else}}{{ShortSha .HeadBranch}}{{end}}</a>
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</h4>
 | 
					</h4>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@
 | 
				
			||||||
					{{template "base/alert" .}}
 | 
										{{template "base/alert" .}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					{{if not $.DisableMigrations}}
 | 
										{{if not $.DisableMigrations}}
 | 
				
			||||||
						<p class="ui center">{{.i18n.Tr "repo.new_repo_helper" (printf "%s%s" AppSubUrl "/repo/migrate") | Safe}}</p>
 | 
											<p class="ui center">{{.i18n.Tr "repo.new_repo_helper" ((printf "%s%s" AppSubUrl "/repo/migrate")|Escape) | Safe}}</p>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					{{if not .CanCreateRepo}}
 | 
										{{if not .CanCreateRepo}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,17 +4,17 @@
 | 
				
			||||||
		{{if eq .GetType 4}}
 | 
							{{if eq .GetType 4}}
 | 
				
			||||||
			<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}">
 | 
								<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}">
 | 
				
			||||||
				{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
 | 
									{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
 | 
				
			||||||
					<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=down" data-anchor="{{$.Anchor}}">
 | 
										<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=down" data-anchor="{{$.Anchor}}">
 | 
				
			||||||
						{{svg "octicon-fold-down"}}
 | 
											{{svg "octicon-fold-down"}}
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
				{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }}
 | 
									{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }}
 | 
				
			||||||
					<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=up" data-anchor="{{$.Anchor}}">
 | 
										<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=up" data-anchor="{{$.Anchor}}">
 | 
				
			||||||
						{{svg "octicon-fold-up"}}
 | 
											{{svg "octicon-fold-up"}}
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
				{{if eq $line.GetExpandDirection 2}}
 | 
									{{if eq $line.GetExpandDirection 2}}
 | 
				
			||||||
					<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=" data-anchor="{{$.Anchor}}">
 | 
										<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=" data-anchor="{{$.Anchor}}">
 | 
				
			||||||
						{{svg "octicon-fold"}}
 | 
											{{svg "octicon-fold"}}
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
| 
						 | 
					@ -36,17 +36,17 @@
 | 
				
			||||||
		{{if eq .GetType 4}}
 | 
							{{if eq .GetType 4}}
 | 
				
			||||||
			<td colspan="2" class="lines-num">
 | 
								<td colspan="2" class="lines-num">
 | 
				
			||||||
				{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
 | 
									{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
 | 
				
			||||||
					<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=down" data-anchor="{{$.Anchor}}">
 | 
										<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=down" data-anchor="{{$.Anchor}}">
 | 
				
			||||||
						{{svg "octicon-fold-down"}}
 | 
											{{svg "octicon-fold-down"}}
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
				{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }}
 | 
									{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }}
 | 
				
			||||||
					<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=up" data-anchor="{{$.Anchor}}">
 | 
										<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=up" data-anchor="{{$.Anchor}}">
 | 
				
			||||||
						{{svg "octicon-fold-up"}}
 | 
											{{svg "octicon-fold-up"}}
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
				{{if eq $line.GetExpandDirection 2}}
 | 
									{{if eq $line.GetExpandDirection 2}}
 | 
				
			||||||
					<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=" data-anchor="{{$.Anchor}}">
 | 
										<a role="button" class="blob-excerpt" data-url="{{$.RepoLink}}/blob_excerpt/{{PathEscape $.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=" data-anchor="{{$.Anchor}}">
 | 
				
			||||||
						{{svg "octicon-fold"}}
 | 
											{{svg "octicon-fold"}}
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -96,9 +96,9 @@
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
							{{if and (not $file.IsSubmodule) (not $.PageIsWiki)}}
 | 
												{{if and (not $file.IsSubmodule) (not $.PageIsWiki)}}
 | 
				
			||||||
								{{if $file.IsDeleted}}
 | 
													{{if $file.IsDeleted}}
 | 
				
			||||||
									<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
 | 
														<a class="ui basic tiny button" rel="nofollow" href="{{$.BeforeSourcePath}}/{{PathEscapeSegments .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
 | 
				
			||||||
								{{else}}
 | 
													{{else}}
 | 
				
			||||||
									<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
 | 
														<a class="ui basic tiny button" rel="nofollow" href="{{$.SourcePath}}/{{PathEscapeSegments .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
 | 
				
			||||||
								{{end}}
 | 
													{{end}}
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue