mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	Compare commits
	
		
			42 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					abc159637c | ||
| 
						 | 
					e35d7ae1fa | ||
| 
						 | 
					40c6eb0d85 | ||
| 
						 | 
					2996573976 | ||
| 
						 | 
					d0a9957c32 | ||
| 
						 | 
					adbf576a6e | ||
| 
						 | 
					15cdb19d77 | ||
| 
						 | 
					9be48f04cb | ||
| 
						 | 
					24dd77eca6 | ||
| 
						 | 
					af7779daf8 | ||
| 
						 | 
					31c0a338d6 | ||
| 
						 | 
					2ec85e0b70 | ||
| 
						 | 
					251e1b68b4 | ||
| 
						 | 
					641d481c38 | ||
| 
						 | 
					6c6d1ff08c | ||
| 
						 | 
					d9ad876d97 | ||
| 
						 | 
					832e2ebe91 | ||
| 
						 | 
					68134e6441 | ||
| 
						 | 
					3022681432 | ||
| 
						 | 
					c0e0fb7d39 | ||
| 
						 | 
					0c612124f9 | ||
| 
						 | 
					5d0c9872a9 | ||
| 
						 | 
					92a3061753 | ||
| 
						 | 
					efc5a7171b | ||
| 
						 | 
					93f34fd8a2 | ||
| 
						 | 
					dd784396ce | ||
| 
						 | 
					4a0ce6896b | ||
| 
						 | 
					d87fb0a6fd | ||
| 
						 | 
					e55eaa8545 | ||
| 
						 | 
					423c642fdb | ||
| 
						 | 
					ed2ba84525 | ||
| 
						 | 
					e8015a59bb | ||
| 
						 | 
					8327300809 | ||
| 
						 | 
					ade183957d | ||
| 
						 | 
					c503eac206 | ||
| 
						 | 
					221e502297 | ||
| 
						 | 
					1c3d712fe7 | ||
| 
						 | 
					85f90187f3 | ||
| 
						 | 
					c0675ef6c2 | ||
| 
						 | 
					4e27cc4813 | ||
| 
						 | 
					f61ef28f26 | ||
| 
						 | 
					b27e10d6e4 | 
							
								
								
									
										34
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -4,11 +4,32 @@ This changelog goes through all the changes that have been made in each release
 | 
				
			|||||||
without substantial changes to our git log; to see the highlights of what has
 | 
					without substantial changes to our git log; to see the highlights of what has
 | 
				
			||||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
					been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [1.4.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.4.0-rc1) - 2018-01-31
 | 
					## [1.4.1](https://github.com/go-gitea/gitea/releases/tag/v1.4.1) - 2018-05-03
 | 
				
			||||||
 | 
					* BREAKING
 | 
				
			||||||
 | 
					  * Add "error" as reserved username (#3882) (#3886)
 | 
				
			||||||
 | 
					* SECURITY
 | 
				
			||||||
 | 
					  * Do not allow inactive users to access repositories using private key (#3887) (#3889)
 | 
				
			||||||
 | 
					  * Fix path cleanup in file editor, when initilizing new repository and LFS oids  (#3871) (#3873)
 | 
				
			||||||
 | 
					  * Remove unnecessary allowed safe HTML (#3778) (#3779)
 | 
				
			||||||
 | 
					  * Correctly check http git access rights for reverse proxy authorized users (#3721) (#3743)
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Fix to use only needed columns from tables to get repository git paths (#3870) (#3883)
 | 
				
			||||||
 | 
					  * Fix GPG expire time display when time is zero (#3584) (#3884)
 | 
				
			||||||
 | 
					  * Fix to update only issue last update time when adding a comment (#3855) (#3860)
 | 
				
			||||||
 | 
					  * Fix repository star count after deleting user (#3781) (#3783)
 | 
				
			||||||
 | 
					  * Use the active branch for the code tab (#3720) (#3776)
 | 
				
			||||||
 | 
					  * Set default branch name on first push (#3715) (#3723)
 | 
				
			||||||
 | 
					  * Show clipboard button if disable HTTP of git protocol (#3773) (#3774)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.4.0](https://github.com/go-gitea/gitea/releases/tag/v1.4.0) - 2018-03-25
 | 
				
			||||||
* BREAKING
 | 
					* BREAKING
 | 
				
			||||||
  * Drop deprecated GOGS\_WORK\_DIR use (#2946)
 | 
					  * Drop deprecated GOGS\_WORK\_DIR use (#2946)
 | 
				
			||||||
  * Fix API status code for hook creation (#2814)
 | 
					  * Fix API status code for hook creation (#2814)
 | 
				
			||||||
* SECURITY
 | 
					* SECURITY
 | 
				
			||||||
 | 
					  * Escape branch name in dropdown menu (#3691) (#3692)
 | 
				
			||||||
 | 
					  * Refactor and simplify to correctly validate redirect to URL (#3674) (#3676)
 | 
				
			||||||
 | 
					  * Fix escaping changed title in comments (#3530) (#3534)
 | 
				
			||||||
 | 
					  * Escape search query (#3486) (#3488)
 | 
				
			||||||
  * Sanitize logs for mirror sync (#3057)
 | 
					  * Sanitize logs for mirror sync (#3057)
 | 
				
			||||||
* FEATURE
 | 
					* FEATURE
 | 
				
			||||||
  * Serve .patch and .diff for pull requests (#3305, #3293)
 | 
					  * Serve .patch and .diff for pull requests (#3305, #3293)
 | 
				
			||||||
@@ -24,6 +45,17 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Add dingtalk webhook  (#2777)
 | 
					  * Add dingtalk webhook  (#2777)
 | 
				
			||||||
  * Responsive view (#2750)
 | 
					  * Responsive view (#2750)
 | 
				
			||||||
* BUGFIXES
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Fix wiki inter-links with spaces (#3560) (#3632)
 | 
				
			||||||
 | 
					  * Fix query protected branch bug (#3563) (#3571)
 | 
				
			||||||
 | 
					  * Fix remove team member issue (#3566) (#3570)
 | 
				
			||||||
 | 
					  * Fix the protected branch panic issue (#3567) (#3569)
 | 
				
			||||||
 | 
					  * If Mirrors repository no content is fetched, updated time should not be changed (#3551) (#3565)
 | 
				
			||||||
 | 
					  * Bug fix for mirrored repository releases sorted (#3522) (#3555)
 | 
				
			||||||
 | 
					  * Add issue closed time column to fix activity closed issues list (#3537) (#3540)
 | 
				
			||||||
 | 
					  * Update markbates/goth library to support OAuth2 with new dropbox API (#3533) (#3539)
 | 
				
			||||||
 | 
					  * Fixes missing avatars in offline mode (#3471) (#3477)
 | 
				
			||||||
 | 
					  * Fix synchronization bug in repo indexer (#3455) (#3461)
 | 
				
			||||||
 | 
					  * Fix rendering of wiki page list if wiki repo contains other files (#3454) (#3463)
 | 
				
			||||||
  * Fix webhook X-GitHub-* headers casing for better compatibility (#3429)
 | 
					  * Fix webhook X-GitHub-* headers casing for better compatibility (#3429)
 | 
				
			||||||
  * Add content type and doctype to requests made with go-get (#3426, #3423)
 | 
					  * Add content type and doctype to requests made with go-get (#3426, #3423)
 | 
				
			||||||
  * Fix SQL type error for webhooks (#3424)
 | 
					  * Fix SQL type error for webhooks (#3424)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -230,6 +230,12 @@ func runServ(c *cli.Context) error {
 | 
				
			|||||||
				fail("internal error", "Failed to get user by key ID(%d): %v", keyID, err)
 | 
									fail("internal error", "Failed to get user by key ID(%d): %v", keyID, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !user.IsActive || user.ProhibitLogin {
 | 
				
			||||||
 | 
									fail("Your account is not active or has been disabled by Administrator",
 | 
				
			||||||
 | 
										"User %s is disabled and have no access to repository %s",
 | 
				
			||||||
 | 
										user.Name, repoPath)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			mode, err := models.AccessLevel(user.ID, repo)
 | 
								mode, err := models.AccessLevel(user.ID, repo)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				fail("Internal error", "Failed to check access: %v", err)
 | 
									fail("Internal error", "Failed to check access: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,7 +128,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 | 
				
			|||||||
- `REPO_INDEXER_ENABLED`: **false**: Enables code search (uses a lot of disk space).
 | 
					- `REPO_INDEXER_ENABLED`: **false**: Enables code search (uses a lot of disk space).
 | 
				
			||||||
- `REPO_INDEXER_PATH`: **indexers/repos.bleve**: Index file used for code search.
 | 
					- `REPO_INDEXER_PATH`: **indexers/repos.bleve**: Index file used for code search.
 | 
				
			||||||
- `UPDATE_BUFFER_LEN`: **20**: Buffer length of index request.
 | 
					- `UPDATE_BUFFER_LEN`: **20**: Buffer length of index request.
 | 
				
			||||||
- `MAX_FILE_SIZE`: **1048576**: Maximum size in bytes of each index files.
 | 
					- `MAX_FILE_SIZE`: **1048576**: Maximum size in bytes of files to be indexed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Security (`security`)
 | 
					## Security (`security`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
date: "2016-11-08T16:00:00+02:00"
 | 
					date: "2016-11-08T16:00:00+02:00"
 | 
				
			||||||
title: "Documentation"
 | 
					title: "Documentation"
 | 
				
			||||||
slug: "documentation"
 | 
					slug: "documentation"
 | 
				
			||||||
 | 
					url: "/en-us/"
 | 
				
			||||||
weight: 10
 | 
					weight: 10
 | 
				
			||||||
toc: true
 | 
					toc: true
 | 
				
			||||||
draft: false
 | 
					draft: false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
date: "2017-08-23T09:00:00+02:00"
 | 
					date: "2017-08-23T09:00:00+02:00"
 | 
				
			||||||
title: "Documentation"
 | 
					title: "Documentation"
 | 
				
			||||||
slug: "documentation"
 | 
					slug: "documentation"
 | 
				
			||||||
 | 
					url: "/fr-fr/"
 | 
				
			||||||
weight: 10
 | 
					weight: 10
 | 
				
			||||||
toc: true
 | 
					toc: true
 | 
				
			||||||
draft: false
 | 
					draft: false
 | 
				
			||||||
@@ -48,7 +49,7 @@ Le but de ce projet est de fournir de la manière la plus simple, la plus rapide
 | 
				
			|||||||
    - Migré
 | 
					    - Migré
 | 
				
			||||||
- Notifications (courriel et web)
 | 
					- Notifications (courriel et web)
 | 
				
			||||||
    - Lu
 | 
					    - Lu
 | 
				
			||||||
    - Non lu 
 | 
					    - Non lu
 | 
				
			||||||
    - Épinglé
 | 
					    - Épinglé
 | 
				
			||||||
- Page d'exploration
 | 
					- Page d'exploration
 | 
				
			||||||
    - Utilisateurs
 | 
					    - Utilisateurs
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
date: "2016-11-08T16:00:00+02:00"
 | 
					date: "2016-11-08T16:00:00+02:00"
 | 
				
			||||||
title: "文档"
 | 
					title: "文档"
 | 
				
			||||||
slug: "documentation"
 | 
					slug: "documentation"
 | 
				
			||||||
 | 
					url: "/zh-cn/"
 | 
				
			||||||
weight: 10
 | 
					weight: 10
 | 
				
			||||||
toc: true
 | 
					toc: true
 | 
				
			||||||
draft: false
 | 
					draft: false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@
 | 
				
			|||||||
date: "2016-11-08T16:00:00+02:00"
 | 
					date: "2016-11-08T16:00:00+02:00"
 | 
				
			||||||
title: "文件"
 | 
					title: "文件"
 | 
				
			||||||
slug: "documentation"
 | 
					slug: "documentation"
 | 
				
			||||||
 | 
					url: "/zh-tw/"
 | 
				
			||||||
weight: 10
 | 
					weight: 10
 | 
				
			||||||
toc: true
 | 
					toc: true
 | 
				
			||||||
draft: false
 | 
					draft: false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,8 @@ for SOURCE in $(find ${ROOT}/content -type f -iname *.en-us.md); do
 | 
				
			|||||||
    if [[ ! -f ${DEST} ]]; then
 | 
					    if [[ ! -f ${DEST} ]]; then
 | 
				
			||||||
      echo "Creating fallback for ${DEST#${ROOT}/content/}"
 | 
					      echo "Creating fallback for ${DEST#${ROOT}/content/}"
 | 
				
			||||||
      cp ${SOURCE} ${DEST}
 | 
					      cp ${SOURCE} ${DEST}
 | 
				
			||||||
 | 
					      sed -i.bak "s/en\-us/${LOCALE}/g" ${DEST}
 | 
				
			||||||
 | 
					      rm ${DEST}.bak
 | 
				
			||||||
    fi
 | 
					    fi
 | 
				
			||||||
  done
 | 
					  done
 | 
				
			||||||
done
 | 
					done
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ import (
 | 
				
			|||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func testPullCreate(t *testing.T, session *TestSession, user, repo, branch string) *httptest.ResponseRecorder {
 | 
					func testPullCreate(t *testing.T, session *TestSession, user, repo, branch, title string) *httptest.ResponseRecorder {
 | 
				
			||||||
	req := NewRequest(t, "GET", path.Join(user, repo))
 | 
						req := NewRequest(t, "GET", path.Join(user, repo))
 | 
				
			||||||
	resp := session.MakeRequest(t, req, http.StatusOK)
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,7 +35,7 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch strin
 | 
				
			|||||||
	assert.True(t, exists, "The template has changed")
 | 
						assert.True(t, exists, "The template has changed")
 | 
				
			||||||
	req = NewRequestWithValues(t, "POST", link, map[string]string{
 | 
						req = NewRequestWithValues(t, "POST", link, map[string]string{
 | 
				
			||||||
		"_csrf": htmlDoc.GetCSRF(),
 | 
							"_csrf": htmlDoc.GetCSRF(),
 | 
				
			||||||
		"title": "This is a pull title",
 | 
							"title": title,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	resp = session.MakeRequest(t, req, http.StatusFound)
 | 
						resp = session.MakeRequest(t, req, http.StatusFound)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,7 +47,7 @@ func TestPullCreate(t *testing.T) {
 | 
				
			|||||||
	session := loginUser(t, "user1")
 | 
						session := loginUser(t, "user1")
 | 
				
			||||||
	testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
						testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
	testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
						testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
	resp := testPullCreate(t, session, "user1", "repo1", "master")
 | 
						resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// check the redirected URL
 | 
						// check the redirected URL
 | 
				
			||||||
	url := resp.HeaderMap.Get("Location")
 | 
						url := resp.HeaderMap.Get("Location")
 | 
				
			||||||
@@ -68,3 +68,38 @@ func TestPullCreate(t *testing.T) {
 | 
				
			|||||||
	assert.Regexp(t, `Subject: \[PATCH\] Update 'README.md'`, resp.Body)
 | 
						assert.Regexp(t, `Subject: \[PATCH\] Update 'README.md'`, resp.Body)
 | 
				
			||||||
	assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one
 | 
						assert.NotRegexp(t, "diff.*diff", resp.Body) // not two diffs, just one
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPullCreate_TitleEscape(t *testing.T) {
 | 
				
			||||||
 | 
						prepareTestEnv(t)
 | 
				
			||||||
 | 
						session := loginUser(t, "user1")
 | 
				
			||||||
 | 
						testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
 | 
						testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
 | 
						resp := testPullCreate(t, session, "user1", "repo1", "master", "<i>XSS PR</i>")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check the redirected URL
 | 
				
			||||||
 | 
						url := resp.HeaderMap.Get("Location")
 | 
				
			||||||
 | 
						assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Edit title
 | 
				
			||||||
 | 
						req := NewRequest(t, "GET", url)
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
						editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url")
 | 
				
			||||||
 | 
						assert.True(t, exists, "The template has changed")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{
 | 
				
			||||||
 | 
							"_csrf": htmlDoc.GetCSRF(),
 | 
				
			||||||
 | 
							"title": "<u>XSS PR</u>",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", url)
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						htmlDoc = NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
						titleHTML, err := htmlDoc.doc.Find(".comments .event .text b").First().Html()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Equal(t, "<i>XSS PR</i>", titleHTML)
 | 
				
			||||||
 | 
						titleHTML, err = htmlDoc.doc.Find(".comments .event .text b").Next().Html()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Equal(t, "<u>XSS PR</u>", titleHTML)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,7 +56,7 @@ func TestPullMerge(t *testing.T) {
 | 
				
			|||||||
	testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
						testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
	testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
						testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := testPullCreate(t, session, "user1", "repo1", "master")
 | 
						resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	elem := strings.Split(test.RedirectURL(resp), "/")
 | 
						elem := strings.Split(test.RedirectURL(resp), "/")
 | 
				
			||||||
	assert.EqualValues(t, "pulls", elem[3])
 | 
						assert.EqualValues(t, "pulls", elem[3])
 | 
				
			||||||
@@ -69,7 +69,7 @@ func TestPullRebase(t *testing.T) {
 | 
				
			|||||||
	testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
						testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
	testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
						testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := testPullCreate(t, session, "user1", "repo1", "master")
 | 
						resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	elem := strings.Split(test.RedirectURL(resp), "/")
 | 
						elem := strings.Split(test.RedirectURL(resp), "/")
 | 
				
			||||||
	assert.EqualValues(t, "pulls", elem[3])
 | 
						assert.EqualValues(t, "pulls", elem[3])
 | 
				
			||||||
@@ -83,7 +83,7 @@ func TestPullSquash(t *testing.T) {
 | 
				
			|||||||
	testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
						testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
	testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n")
 | 
						testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := testPullCreate(t, session, "user1", "repo1", "master")
 | 
						resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	elem := strings.Split(test.RedirectURL(resp), "/")
 | 
						elem := strings.Split(test.RedirectURL(resp), "/")
 | 
				
			||||||
	assert.EqualValues(t, "pulls", elem[3])
 | 
						assert.EqualValues(t, "pulls", elem[3])
 | 
				
			||||||
@@ -96,7 +96,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
 | 
				
			|||||||
	testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
						testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
	testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
 | 
						testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp := testPullCreate(t, session, "user1", "repo1", "feature/test")
 | 
						resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	elem := strings.Split(test.RedirectURL(resp), "/")
 | 
						elem := strings.Split(test.RedirectURL(resp), "/")
 | 
				
			||||||
	assert.EqualValues(t, "pulls", elem[3])
 | 
						assert.EqualValues(t, "pulls", elem[3])
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,16 +22,16 @@ func TestRepoActivity(t *testing.T) {
 | 
				
			|||||||
	// Create PRs (1 merged & 2 proposed)
 | 
						// Create PRs (1 merged & 2 proposed)
 | 
				
			||||||
	testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
						testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
	testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
						testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
	resp := testPullCreate(t, session, "user1", "repo1", "master")
 | 
						resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
 | 
				
			||||||
	elem := strings.Split(test.RedirectURL(resp), "/")
 | 
						elem := strings.Split(test.RedirectURL(resp), "/")
 | 
				
			||||||
	assert.EqualValues(t, "pulls", elem[3])
 | 
						assert.EqualValues(t, "pulls", elem[3])
 | 
				
			||||||
	testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
 | 
						testPullMerge(t, session, elem[1], elem[2], elem[4], models.MergeStyleMerge)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n")
 | 
						testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n")
 | 
				
			||||||
	testPullCreate(t, session, "user1", "repo1", "feat/better_readme")
 | 
						testPullCreate(t, session, "user1", "repo1", "feat/better_readme", "This is a pull title")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/much_better_readme", "README.md", "Hello, World (Edited More)\n")
 | 
						testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/much_better_readme", "README.md", "Hello, World (Edited More)\n")
 | 
				
			||||||
	testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme")
 | 
						testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme", "This is a pull title")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create issues (3 new issues)
 | 
						// Create issues (3 new issues)
 | 
				
			||||||
	testNewIssue(t, session, "user2", "repo1", "Issue 1", "Description 1")
 | 
						testNewIssue(t, session, "user2", "repo1", "Issue 1", "Description 1")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -523,6 +523,11 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
 | 
				
			|||||||
		return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err)
 | 
							return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						refName := git.RefEndName(opts.RefFullName)
 | 
				
			||||||
 | 
						if repo.IsBare && refName != repo.DefaultBranch {
 | 
				
			||||||
 | 
							repo.DefaultBranch = refName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Change repository bare status and update last updated time.
 | 
						// Change repository bare status and update last updated time.
 | 
				
			||||||
	repo.IsBare = repo.IsBare && opts.Commits.Len <= 0
 | 
						repo.IsBare = repo.IsBare && opts.Commits.Len <= 0
 | 
				
			||||||
	if err = UpdateRepository(repo, false); err != nil {
 | 
						if err = UpdateRepository(repo, false); err != nil {
 | 
				
			||||||
@@ -563,7 +568,6 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
 | 
				
			|||||||
		return fmt.Errorf("Marshal: %v", err)
 | 
							return fmt.Errorf("Marshal: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	refName := git.RefEndName(opts.RefFullName)
 | 
					 | 
				
			||||||
	if err = NotifyWatchers(&Action{
 | 
						if err = NotifyWatchers(&Action{
 | 
				
			||||||
		ActUserID: pusher.ID,
 | 
							ActUserID: pusher.ID,
 | 
				
			||||||
		ActUser:   pusher,
 | 
							ActUser:   pusher,
 | 
				
			||||||
@@ -742,5 +746,14 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	actions := make([]*Action, 0, 20)
 | 
						actions := make([]*Action, 0, 20)
 | 
				
			||||||
	return actions, x.Limit(20).Desc("id").Where(cond).Find(&actions)
 | 
					
 | 
				
			||||||
 | 
						if err := x.Limit(20).Desc("id").Where(cond).Find(&actions); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("Find: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := ActionList(actions).LoadAttributes(); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("LoadAttributes: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return actions, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										98
									
								
								models/action_list.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								models/action_list.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ActionList defines a list of actions
 | 
				
			||||||
 | 
					type ActionList []*Action
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (actions ActionList) getUserIDs() []int64 {
 | 
				
			||||||
 | 
						userIDs := make(map[int64]struct{}, len(actions))
 | 
				
			||||||
 | 
						for _, action := range actions {
 | 
				
			||||||
 | 
							if _, ok := userIDs[action.ActUserID]; !ok {
 | 
				
			||||||
 | 
								userIDs[action.ActUserID] = struct{}{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return keysInt64(userIDs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (actions ActionList) loadUsers(e Engine) ([]*User, error) {
 | 
				
			||||||
 | 
						if len(actions) == 0 {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						userIDs := actions.getUserIDs()
 | 
				
			||||||
 | 
						userMaps := make(map[int64]*User, len(userIDs))
 | 
				
			||||||
 | 
						err := e.
 | 
				
			||||||
 | 
							In("id", userIDs).
 | 
				
			||||||
 | 
							Find(&userMaps)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("find user: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, action := range actions {
 | 
				
			||||||
 | 
							action.ActUser = userMaps[action.ActUserID]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return valuesUser(userMaps), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoadUsers loads actions' all users
 | 
				
			||||||
 | 
					func (actions ActionList) LoadUsers() ([]*User, error) {
 | 
				
			||||||
 | 
						return actions.loadUsers(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (actions ActionList) getRepoIDs() []int64 {
 | 
				
			||||||
 | 
						repoIDs := make(map[int64]struct{}, len(actions))
 | 
				
			||||||
 | 
						for _, action := range actions {
 | 
				
			||||||
 | 
							if _, ok := repoIDs[action.RepoID]; !ok {
 | 
				
			||||||
 | 
								repoIDs[action.RepoID] = struct{}{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return keysInt64(repoIDs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (actions ActionList) loadRepositories(e Engine) ([]*Repository, error) {
 | 
				
			||||||
 | 
						if len(actions) == 0 {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repoIDs := actions.getRepoIDs()
 | 
				
			||||||
 | 
						repoMaps := make(map[int64]*Repository, len(repoIDs))
 | 
				
			||||||
 | 
						err := e.
 | 
				
			||||||
 | 
							In("id", repoIDs).
 | 
				
			||||||
 | 
							Find(&repoMaps)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("find repository: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, action := range actions {
 | 
				
			||||||
 | 
							action.Repo = repoMaps[action.RepoID]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return valuesRepository(repoMaps), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoadRepositories loads actions' all repositories
 | 
				
			||||||
 | 
					func (actions ActionList) LoadRepositories() ([]*Repository, error) {
 | 
				
			||||||
 | 
						return actions.loadRepositories(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// loadAttributes loads all attributes
 | 
				
			||||||
 | 
					func (actions ActionList) loadAttributes(e Engine) (err error) {
 | 
				
			||||||
 | 
						if _, err = actions.loadUsers(e); err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err = actions.loadRepositories(e); err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoadAttributes loads attributes of the actions
 | 
				
			||||||
 | 
					func (actions ActionList) LoadAttributes() error {
 | 
				
			||||||
 | 
						return actions.loadAttributes(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,6 @@ package models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
@@ -70,7 +69,7 @@ func GetProtectedBranchByRepoID(RepoID int64) ([]*ProtectedBranch, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetProtectedBranchBy getting protected branch by ID/Name
 | 
					// GetProtectedBranchBy getting protected branch by ID/Name
 | 
				
			||||||
func GetProtectedBranchBy(repoID int64, BranchName string) (*ProtectedBranch, error) {
 | 
					func GetProtectedBranchBy(repoID int64, BranchName string) (*ProtectedBranch, error) {
 | 
				
			||||||
	rel := &ProtectedBranch{RepoID: repoID, BranchName: strings.ToLower(BranchName)}
 | 
						rel := &ProtectedBranch{RepoID: repoID, BranchName: BranchName}
 | 
				
			||||||
	has, err := x.Get(rel)
 | 
						has, err := x.Get(rel)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -156,6 +155,10 @@ func (repo *Repository) GetProtectedBranches() ([]*ProtectedBranch, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IsProtectedBranch checks if branch is protected
 | 
					// IsProtectedBranch checks if branch is protected
 | 
				
			||||||
func (repo *Repository) IsProtectedBranch(branchName string, doer *User) (bool, error) {
 | 
					func (repo *Repository) IsProtectedBranch(branchName string, doer *User) (bool, error) {
 | 
				
			||||||
 | 
						if doer == nil {
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protectedBranch := &ProtectedBranch{
 | 
						protectedBranch := &ProtectedBranch{
 | 
				
			||||||
		RepoID:     repo.ID,
 | 
							RepoID:     repo.ID,
 | 
				
			||||||
		BranchName: branchName,
 | 
							BranchName: branchName,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -216,6 +216,21 @@ func (err ErrWikiReservedName) Error() string {
 | 
				
			|||||||
	return fmt.Sprintf("wiki title is reserved: %s", err.Title)
 | 
						return fmt.Sprintf("wiki title is reserved: %s", err.Title)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrWikiInvalidFileName represents an invalid wiki file name.
 | 
				
			||||||
 | 
					type ErrWikiInvalidFileName struct {
 | 
				
			||||||
 | 
						FileName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrWikiInvalidFileName checks if an error is an ErrWikiInvalidFileName.
 | 
				
			||||||
 | 
					func IsErrWikiInvalidFileName(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrWikiInvalidFileName)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrWikiInvalidFileName) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("Invalid wiki filename: %s", err.FileName)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// __________     ___.   .__  .__          ____  __.
 | 
					// __________     ___.   .__  .__          ____  __.
 | 
				
			||||||
// \______   \__ _\_ |__ |  | |__| ____   |    |/ _|____ ___.__.
 | 
					// \______   \__ _\_ |__ |  | |__| ____   |    |/ _|____ ___.__.
 | 
				
			||||||
//  |     ___/  |  \ __ \|  | |  |/ ___\  |      <_/ __ <   |  |
 | 
					//  |     ___/  |  \ __ \|  | |  |/ ___\  |      <_/ __ <   |  |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,16 +21,16 @@ func assertLineEqual(t *testing.T, d1 *DiffLine, d2 *DiffLine) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestDiffToHTML(t *testing.T) {
 | 
					func TestDiffToHTML(t *testing.T) {
 | 
				
			||||||
	assertEqual(t, "+foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
 | 
						assertEqual(t, "+foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
 | 
				
			||||||
		{dmp.DiffEqual, "foo "},
 | 
							{Type: dmp.DiffEqual, Text: "foo "},
 | 
				
			||||||
		{dmp.DiffInsert, "bar"},
 | 
							{Type: dmp.DiffInsert, Text: "bar"},
 | 
				
			||||||
		{dmp.DiffDelete, " baz"},
 | 
							{Type: dmp.DiffDelete, Text: " baz"},
 | 
				
			||||||
		{dmp.DiffEqual, " biz"},
 | 
							{Type: dmp.DiffEqual, Text: " biz"},
 | 
				
			||||||
	}, DiffLineAdd))
 | 
						}, DiffLineAdd))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
 | 
						assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
 | 
				
			||||||
		{dmp.DiffEqual, "foo "},
 | 
							{Type: dmp.DiffEqual, Text: "foo "},
 | 
				
			||||||
		{dmp.DiffDelete, "bar"},
 | 
							{Type: dmp.DiffDelete, Text: "bar"},
 | 
				
			||||||
		{dmp.DiffInsert, " baz"},
 | 
							{Type: dmp.DiffInsert, Text: " baz"},
 | 
				
			||||||
		{dmp.DiffEqual, " biz"},
 | 
							{Type: dmp.DiffEqual, Text: " biz"},
 | 
				
			||||||
	}, DiffLineDel))
 | 
						}, DiffLineDel))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,6 +49,7 @@ type Issue struct {
 | 
				
			|||||||
	DeadlineUnix util.TimeStamp `xorm:"INDEX"`
 | 
						DeadlineUnix util.TimeStamp `xorm:"INDEX"`
 | 
				
			||||||
	CreatedUnix  util.TimeStamp `xorm:"INDEX created"`
 | 
						CreatedUnix  util.TimeStamp `xorm:"INDEX created"`
 | 
				
			||||||
	UpdatedUnix  util.TimeStamp `xorm:"INDEX updated"`
 | 
						UpdatedUnix  util.TimeStamp `xorm:"INDEX updated"`
 | 
				
			||||||
 | 
						ClosedUnix   util.TimeStamp `xorm:"INDEX"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Attachments []*Attachment `xorm:"-"`
 | 
						Attachments []*Attachment `xorm:"-"`
 | 
				
			||||||
	Comments    []*Comment    `xorm:"-"`
 | 
						Comments    []*Comment    `xorm:"-"`
 | 
				
			||||||
@@ -612,8 +613,13 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository,
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	issue.IsClosed = isClosed
 | 
						issue.IsClosed = isClosed
 | 
				
			||||||
 | 
						if isClosed {
 | 
				
			||||||
 | 
							issue.ClosedUnix = util.TimeStampNow()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							issue.ClosedUnix = 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = updateIssueCols(e, issue, "is_closed"); err != nil {
 | 
						if err = updateIssueCols(e, issue, "is_closed", "closed_unix"); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -418,7 +418,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// update the issue's updated_unix column
 | 
						// update the issue's updated_unix column
 | 
				
			||||||
	if err = updateIssueCols(e, opts.Issue); err != nil {
 | 
						if err = updateIssueCols(e, opts.Issue, "updated_unix"); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -214,13 +214,15 @@ func TestChangeMilestoneIssueStats(t *testing.T) {
 | 
				
			|||||||
		"is_closed=0").(*Issue)
 | 
							"is_closed=0").(*Issue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issue.IsClosed = true
 | 
						issue.IsClosed = true
 | 
				
			||||||
	_, err := x.Cols("is_closed").Update(issue)
 | 
						issue.ClosedUnix = util.TimeStampNow()
 | 
				
			||||||
 | 
						_, err := x.Cols("is_closed", "closed_unix").Update(issue)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
 | 
						assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
 | 
				
			||||||
	CheckConsistencyFor(t, &Milestone{})
 | 
						CheckConsistencyFor(t, &Milestone{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issue.IsClosed = false
 | 
						issue.IsClosed = false
 | 
				
			||||||
	_, err = x.Cols("is_closed").Update(issue)
 | 
						issue.ClosedUnix = 0
 | 
				
			||||||
 | 
						_, err = x.Cols("is_closed", "closed_unix").Update(issue)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
 | 
						assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
 | 
				
			||||||
	CheckConsistencyFor(t, &Milestone{})
 | 
						CheckConsistencyFor(t, &Milestone{})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -166,6 +166,8 @@ var migrations = []Migration{
 | 
				
			|||||||
	NewMigration("add writable deploy keys", addModeToDeploKeys),
 | 
						NewMigration("add writable deploy keys", addModeToDeploKeys),
 | 
				
			||||||
	// v56 -> v57
 | 
						// v56 -> v57
 | 
				
			||||||
	NewMigration("remove is_owner, num_teams columns from org_user", removeIsOwnerColumnFromOrgUser),
 | 
						NewMigration("remove is_owner, num_teams columns from org_user", removeIsOwnerColumnFromOrgUser),
 | 
				
			||||||
 | 
						// v57 -> v58
 | 
				
			||||||
 | 
						NewMigration("add closed_unix column for issues", addIssueClosedTime),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Migrate database to current version
 | 
					// Migrate database to current version
 | 
				
			||||||
@@ -215,6 +217,66 @@ Please try to upgrade to a lower version (>= v0.6.0) first, then upgrade to curr
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func dropTableColumns(x *xorm.Engine, tableName string, columnNames ...string) (err error) {
 | 
				
			||||||
 | 
						if tableName == "" || len(columnNames) == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case setting.UseSQLite3:
 | 
				
			||||||
 | 
							log.Warn("Unable to drop columns in SQLite")
 | 
				
			||||||
 | 
						case setting.UseMySQL, setting.UseTiDB, setting.UsePostgreSQL:
 | 
				
			||||||
 | 
							cols := ""
 | 
				
			||||||
 | 
							for _, col := range columnNames {
 | 
				
			||||||
 | 
								if cols != "" {
 | 
				
			||||||
 | 
									cols += ", "
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								cols += "DROP COLUMN `" + col + "`"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case setting.UseMSSQL:
 | 
				
			||||||
 | 
							sess := x.NewSession()
 | 
				
			||||||
 | 
							defer sess.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err = sess.Begin(); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cols := ""
 | 
				
			||||||
 | 
							for _, col := range columnNames {
 | 
				
			||||||
 | 
								if cols != "" {
 | 
				
			||||||
 | 
									cols += ", "
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								cols += "`" + strings.ToLower(col) + "`"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							sql := fmt.Sprintf("SELECT Name FROM SYS.DEFAULT_CONSTRAINTS WHERE PARENT_OBJECT_ID = OBJECT_ID('%[1]s') AND PARENT_COLUMN_ID IN (SELECT column_id FROM sys.columns WHERE lower(NAME) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))",
 | 
				
			||||||
 | 
								tableName, strings.Replace(cols, "`", "'", -1))
 | 
				
			||||||
 | 
							constraints := make([]string, 0)
 | 
				
			||||||
 | 
							if err := sess.SQL(sql).Find(&constraints); err != nil {
 | 
				
			||||||
 | 
								sess.Rollback()
 | 
				
			||||||
 | 
								return fmt.Errorf("Find constraints: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, constraint := range constraints {
 | 
				
			||||||
 | 
								if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP CONSTRAINT `%s`", tableName, constraint)); err != nil {
 | 
				
			||||||
 | 
									sess.Rollback()
 | 
				
			||||||
 | 
									return fmt.Errorf("Drop table `%s` constraint `%s`: %v", tableName, constraint, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN %s", tableName, cols)); err != nil {
 | 
				
			||||||
 | 
								sess.Rollback()
 | 
				
			||||||
 | 
								return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return sess.Commit()
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							log.Fatal(4, "Unrecognized DB")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func fixLocaleFileLoadPanic(_ *xorm.Engine) error {
 | 
					func fixLocaleFileLoadPanic(_ *xorm.Engine) error {
 | 
				
			||||||
	cfg, err := ini.Load(setting.CustomConf)
 | 
						cfg, err := ini.Load(setting.CustomConf)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,29 +5,9 @@
 | 
				
			|||||||
package migrations
 | 
					package migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/go-xorm/xorm"
 | 
						"github.com/go-xorm/xorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func removeIsOwnerColumnFromOrgUser(x *xorm.Engine) (err error) {
 | 
					func removeIsOwnerColumnFromOrgUser(x *xorm.Engine) (err error) {
 | 
				
			||||||
	switch {
 | 
						return dropTableColumns(x, "org_user", "is_owner", "num_teams")
 | 
				
			||||||
	case setting.UseSQLite3:
 | 
					 | 
				
			||||||
		log.Warn("Unable to drop columns in SQLite")
 | 
					 | 
				
			||||||
	case setting.UseMySQL, setting.UseTiDB, setting.UsePostgreSQL:
 | 
					 | 
				
			||||||
		if _, err := x.Exec("ALTER TABLE org_user DROP COLUMN is_owner, DROP COLUMN num_teams"); err != nil {
 | 
					 | 
				
			||||||
			return fmt.Errorf("DROP COLUMN org_user.is_owner, org_user.num_teams: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	case setting.UseMSSQL:
 | 
					 | 
				
			||||||
		if _, err := x.Exec("ALTER TABLE org_user DROP COLUMN is_owner, num_teams"); err != nil {
 | 
					 | 
				
			||||||
			return fmt.Errorf("DROP COLUMN org_user.is_owner, org_user.num_teams: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		log.Fatal(4, "Unrecognized DB")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								models/migrations/v57.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								models/migrations/v57.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					// Copyright 2017 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-xorm/xorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func addIssueClosedTime(x *xorm.Engine) error {
 | 
				
			||||||
 | 
						// Issue see models/issue.go
 | 
				
			||||||
 | 
						type Issue struct {
 | 
				
			||||||
 | 
							ClosedUnix util.TimeStamp `xorm:"INDEX"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := x.Sync2(new(Issue)); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("Sync2: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := x.Exec("UPDATE `issue` SET `closed_unix` = `updated_unix` WHERE `is_closed` = ?", true); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -436,8 +436,7 @@ func AddOrgUser(orgID, uid int64) error {
 | 
				
			|||||||
	return sess.Commit()
 | 
						return sess.Commit()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RemoveOrgUser removes user from given organization.
 | 
					func removeOrgUser(sess *xorm.Session, orgID, userID int64) error {
 | 
				
			||||||
func RemoveOrgUser(orgID, userID int64) error {
 | 
					 | 
				
			||||||
	ou := new(OrgUser)
 | 
						ou := new(OrgUser)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	has, err := x.
 | 
						has, err := x.
 | 
				
			||||||
@@ -473,12 +472,6 @@ func RemoveOrgUser(orgID, userID int64) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sess := x.NewSession()
 | 
					 | 
				
			||||||
	defer sess.Close()
 | 
					 | 
				
			||||||
	if err := sess.Begin(); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if _, err := sess.ID(ou.ID).Delete(ou); err != nil {
 | 
						if _, err := sess.ID(ou.ID).Delete(ou); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	} else if _, err = sess.Exec("UPDATE `user` SET num_members=num_members-1 WHERE id=?", orgID); err != nil {
 | 
						} else if _, err = sess.Exec("UPDATE `user` SET num_members=num_members-1 WHERE id=?", orgID); err != nil {
 | 
				
			||||||
@@ -520,6 +513,19 @@ func RemoveOrgUser(orgID, userID int64) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RemoveOrgUser removes user from given organization.
 | 
				
			||||||
 | 
					func RemoveOrgUser(orgID, userID int64) error {
 | 
				
			||||||
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := removeOrgUser(sess, orgID, userID); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return sess.Commit()
 | 
						return sess.Commit()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,8 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-xorm/xorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ownerTeamName = "Owners"
 | 
					const ownerTeamName = "Owners"
 | 
				
			||||||
@@ -521,7 +523,7 @@ func AddTeamMember(team *Team, userID int64) error {
 | 
				
			|||||||
	return sess.Commit()
 | 
						return sess.Commit()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func removeTeamMember(e Engine, team *Team, userID int64) error {
 | 
					func removeTeamMember(e *xorm.Session, team *Team, userID int64) error {
 | 
				
			||||||
	isMember, err := isTeamMember(e, team.OrgID, team.ID, userID)
 | 
						isMember, err := isTeamMember(e, team.OrgID, team.ID, userID)
 | 
				
			||||||
	if err != nil || !isMember {
 | 
						if err != nil || !isMember {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -558,6 +560,16 @@ func removeTeamMember(e Engine, team *Team, userID int64) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check if the user is a member of any team in the organization.
 | 
				
			||||||
 | 
						if count, err := e.Count(&TeamUser{
 | 
				
			||||||
 | 
							UID:   userID,
 | 
				
			||||||
 | 
							OrgID: team.OrgID,
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else if count == 0 {
 | 
				
			||||||
 | 
							return removeOrgUser(e, team.OrgID, userID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@ type Release struct {
 | 
				
			|||||||
	IsPrerelease     bool           `xorm:"NOT NULL DEFAULT false"`
 | 
						IsPrerelease     bool           `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
	IsTag            bool           `xorm:"NOT NULL DEFAULT false"`
 | 
						IsTag            bool           `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
	Attachments      []*Attachment  `xorm:"-"`
 | 
						Attachments      []*Attachment  `xorm:"-"`
 | 
				
			||||||
	CreatedUnix      util.TimeStamp `xorm:"created INDEX"`
 | 
						CreatedUnix      util.TimeStamp `xorm:"INDEX"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *Release) loadAttributes(e Engine) error {
 | 
					func (r *Release) loadAttributes(e Engine) error {
 | 
				
			||||||
@@ -134,6 +134,8 @@ func createTag(gitRepo *git.Repository, rel *Release) error {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return fmt.Errorf("CommitsCount: %v", err)
 | 
								return fmt.Errorf("CommitsCount: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							rel.CreatedUnix = util.TimeStampNow()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -163,6 +163,7 @@ func NewRepoContext() {
 | 
				
			|||||||
type Repository struct {
 | 
					type Repository struct {
 | 
				
			||||||
	ID            int64  `xorm:"pk autoincr"`
 | 
						ID            int64  `xorm:"pk autoincr"`
 | 
				
			||||||
	OwnerID       int64  `xorm:"UNIQUE(s)"`
 | 
						OwnerID       int64  `xorm:"UNIQUE(s)"`
 | 
				
			||||||
 | 
						OwnerName     string `xorm:"-"`
 | 
				
			||||||
	Owner         *User  `xorm:"-"`
 | 
						Owner         *User  `xorm:"-"`
 | 
				
			||||||
	LowerName     string `xorm:"UNIQUE(s) INDEX NOT NULL"`
 | 
						LowerName     string `xorm:"UNIQUE(s) INDEX NOT NULL"`
 | 
				
			||||||
	Name          string `xorm:"INDEX NOT NULL"`
 | 
						Name          string `xorm:"INDEX NOT NULL"`
 | 
				
			||||||
@@ -223,9 +224,17 @@ func (repo *Repository) MustOwner() *User {
 | 
				
			|||||||
	return repo.mustOwner(x)
 | 
						return repo.mustOwner(x)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MustOwnerName always returns valid owner name to avoid
 | 
				
			||||||
 | 
					// conceptually impossible error handling.
 | 
				
			||||||
 | 
					// It returns "error" and logs error details when error
 | 
				
			||||||
 | 
					// occurs.
 | 
				
			||||||
 | 
					func (repo *Repository) MustOwnerName() string {
 | 
				
			||||||
 | 
						return repo.mustOwnerName(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FullName returns the repository full name
 | 
					// FullName returns the repository full name
 | 
				
			||||||
func (repo *Repository) FullName() string {
 | 
					func (repo *Repository) FullName() string {
 | 
				
			||||||
	return repo.MustOwner().Name + "/" + repo.Name
 | 
						return repo.MustOwnerName() + "/" + repo.Name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HTMLURL returns the repository HTML URL
 | 
					// HTMLURL returns the repository HTML URL
 | 
				
			||||||
@@ -477,6 +486,41 @@ func (repo *Repository) mustOwner(e Engine) *User {
 | 
				
			|||||||
	return repo.Owner
 | 
						return repo.Owner
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (repo *Repository) getOwnerName(e Engine) error {
 | 
				
			||||||
 | 
						if len(repo.OwnerName) > 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if repo.Owner != nil {
 | 
				
			||||||
 | 
							repo.OwnerName = repo.Owner.Name
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u := new(User)
 | 
				
			||||||
 | 
						has, err := e.ID(repo.OwnerID).Cols("name").Get(u)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else if !has {
 | 
				
			||||||
 | 
							return ErrUserNotExist{repo.OwnerID, "", 0}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repo.OwnerName = u.Name
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetOwnerName returns the repository owner name
 | 
				
			||||||
 | 
					func (repo *Repository) GetOwnerName() error {
 | 
				
			||||||
 | 
						return repo.getOwnerName(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (repo *Repository) mustOwnerName(e Engine) string {
 | 
				
			||||||
 | 
						if err := repo.getOwnerName(e); err != nil {
 | 
				
			||||||
 | 
							log.Error(4, "Error loading repository owner name: %v", err)
 | 
				
			||||||
 | 
							return "error"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return repo.OwnerName
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ComposeMetas composes a map of metas for rendering external issue tracker URL.
 | 
					// ComposeMetas composes a map of metas for rendering external issue tracker URL.
 | 
				
			||||||
func (repo *Repository) ComposeMetas() map[string]string {
 | 
					func (repo *Repository) ComposeMetas() map[string]string {
 | 
				
			||||||
	unit, err := repo.GetUnit(UnitTypeExternalTracker)
 | 
						unit, err := repo.GetUnit(UnitTypeExternalTracker)
 | 
				
			||||||
@@ -588,7 +632,7 @@ func (repo *Repository) GetBaseRepo() (err error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (repo *Repository) repoPath(e Engine) string {
 | 
					func (repo *Repository) repoPath(e Engine) string {
 | 
				
			||||||
	return RepoPath(repo.mustOwner(e).Name, repo.Name)
 | 
						return RepoPath(repo.mustOwnerName(e), repo.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RepoPath returns the repository path
 | 
					// RepoPath returns the repository path
 | 
				
			||||||
@@ -1131,7 +1175,7 @@ type CreateRepoOptions struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getRepoInitFile(tp, name string) ([]byte, error) {
 | 
					func getRepoInitFile(tp, name string) ([]byte, error) {
 | 
				
			||||||
	cleanedName := strings.TrimLeft(name, "./")
 | 
						cleanedName := strings.TrimLeft(path.Clean("/"+name), "/")
 | 
				
			||||||
	relPath := path.Join("options", tp, cleanedName)
 | 
						relPath := path.Join("options", tp, cleanedName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Use custom file when available.
 | 
						// Use custom file when available.
 | 
				
			||||||
@@ -2133,7 +2177,7 @@ func ReinitMissingRepositories() error {
 | 
				
			|||||||
// SyncRepositoryHooks rewrites all repositories' pre-receive, update and post-receive hooks
 | 
					// SyncRepositoryHooks rewrites all repositories' pre-receive, update and post-receive hooks
 | 
				
			||||||
// to make sure the binary and custom conf path are up-to-date.
 | 
					// to make sure the binary and custom conf path are up-to-date.
 | 
				
			||||||
func SyncRepositoryHooks() error {
 | 
					func SyncRepositoryHooks() error {
 | 
				
			||||||
	return x.Where("id > 0").Iterate(new(Repository),
 | 
						return x.Cols("owner_id", "name").Where("id > 0").Iterate(new(Repository),
 | 
				
			||||||
		func(idx int, bean interface{}) error {
 | 
							func(idx int, bean interface{}) error {
 | 
				
			||||||
			if err := createDelegateHooks(bean.(*Repository).RepoPath()); err != nil {
 | 
								if err := createDelegateHooks(bean.(*Repository).RepoPath()); err != nil {
 | 
				
			||||||
				return fmt.Errorf("SyncRepositoryHook: %v", err)
 | 
									return fmt.Errorf("SyncRepositoryHook: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -176,7 +176,7 @@ func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Closed issues
 | 
						// Closed issues
 | 
				
			||||||
	sess := issuesForActivityStatement(repoID, fromTime, true, false)
 | 
						sess := issuesForActivityStatement(repoID, fromTime, true, false)
 | 
				
			||||||
	sess.OrderBy("issue.updated_unix DESC")
 | 
						sess.OrderBy("issue.closed_unix DESC")
 | 
				
			||||||
	stats.ClosedIssues = make(IssueList, 0)
 | 
						stats.ClosedIssues = make(IssueList, 0)
 | 
				
			||||||
	if err = sess.Find(&stats.ClosedIssues); err != nil {
 | 
						if err = sess.Find(&stats.ClosedIssues); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -228,7 +228,11 @@ func issuesForActivityStatement(repoID int64, fromTime time.Time, closed, unreso
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if !unresolved {
 | 
						if !unresolved {
 | 
				
			||||||
		sess.And("issue.is_pull = ?", false)
 | 
							sess.And("issue.is_pull = ?", false)
 | 
				
			||||||
		sess.And("issue.created_unix >= ?", fromTime.Unix())
 | 
							if closed {
 | 
				
			||||||
 | 
								sess.And("issue.closed_unix >= ?", fromTime.Unix())
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								sess.And("issue.created_unix >= ?", fromTime.Unix())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		sess.And("issue.created_unix < ?", fromTime.Unix())
 | 
							sess.And("issue.created_unix < ?", fromTime.Unix())
 | 
				
			||||||
		sess.And("issue.updated_unix >= ?", fromTime.Unix())
 | 
							sess.And("issue.updated_unix >= ?", fromTime.Unix())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,9 +5,7 @@
 | 
				
			|||||||
package models
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"io/ioutil"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"path"
 | 
					 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -16,8 +14,6 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/indexer"
 | 
						"code.gitea.io/gitea/modules/indexer"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/Unknwon/com"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RepoIndexerStatus status of a repo's entry in the repo indexer
 | 
					// RepoIndexerStatus status of a repo's entry in the repo indexer
 | 
				
			||||||
@@ -132,7 +128,11 @@ func populateRepoIndexer(maxRepoID int64) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func updateRepoIndexer(repo *Repository) error {
 | 
					func updateRepoIndexer(repo *Repository) error {
 | 
				
			||||||
	changes, err := getRepoChanges(repo)
 | 
						sha, err := getDefaultBranchSha(repo)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						changes, err := getRepoChanges(repo, sha)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	} else if changes == nil {
 | 
						} else if changes == nil {
 | 
				
			||||||
@@ -140,12 +140,12 @@ func updateRepoIndexer(repo *Repository) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	batch := indexer.RepoIndexerBatch()
 | 
						batch := indexer.RepoIndexerBatch()
 | 
				
			||||||
	for _, filename := range changes.UpdatedFiles {
 | 
						for _, update := range changes.Updates {
 | 
				
			||||||
		if err := addUpdate(filename, repo, batch); err != nil {
 | 
							if err := addUpdate(update, repo, batch); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, filename := range changes.RemovedFiles {
 | 
						for _, filename := range changes.RemovedFilenames {
 | 
				
			||||||
		if err := addDelete(filename, repo, batch); err != nil {
 | 
							if err := addDelete(filename, repo, batch); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -153,56 +153,61 @@ func updateRepoIndexer(repo *Repository) error {
 | 
				
			|||||||
	if err = batch.Flush(); err != nil {
 | 
						if err = batch.Flush(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return updateLastIndexSync(repo)
 | 
						return repo.updateIndexerStatus(sha)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// repoChanges changes (file additions/updates/removals) to a repo
 | 
					// repoChanges changes (file additions/updates/removals) to a repo
 | 
				
			||||||
type repoChanges struct {
 | 
					type repoChanges struct {
 | 
				
			||||||
	UpdatedFiles []string
 | 
						Updates          []fileUpdate
 | 
				
			||||||
	RemovedFiles []string
 | 
						RemovedFilenames []string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type fileUpdate struct {
 | 
				
			||||||
 | 
						Filename string
 | 
				
			||||||
 | 
						BlobSha  string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getDefaultBranchSha(repo *Repository) (string, error) {
 | 
				
			||||||
 | 
						stdout, err := git.NewCommand("show-ref", "-s", repo.DefaultBranch).RunInDir(repo.RepoPath())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.TrimSpace(stdout), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getRepoChanges returns changes to repo since last indexer update
 | 
					// getRepoChanges returns changes to repo since last indexer update
 | 
				
			||||||
func getRepoChanges(repo *Repository) (*repoChanges, error) {
 | 
					func getRepoChanges(repo *Repository, revision string) (*repoChanges, error) {
 | 
				
			||||||
	repoWorkingPool.CheckIn(com.ToStr(repo.ID))
 | 
						if err := repo.getIndexerStatus(); err != nil {
 | 
				
			||||||
	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := repo.UpdateLocalCopyBranch(""); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	} else if !git.IsBranchExist(repo.LocalCopyPath(), repo.DefaultBranch) {
 | 
					 | 
				
			||||||
		// repo does not have any commits yet, so nothing to update
 | 
					 | 
				
			||||||
		return nil, nil
 | 
					 | 
				
			||||||
	} else if err = repo.UpdateLocalCopyBranch(repo.DefaultBranch); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	} else if err = repo.getIndexerStatus(); err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(repo.IndexerStatus.CommitSha) == 0 {
 | 
						if len(repo.IndexerStatus.CommitSha) == 0 {
 | 
				
			||||||
		return genesisChanges(repo)
 | 
							return genesisChanges(repo, revision)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nonGenesisChanges(repo)
 | 
						return nonGenesisChanges(repo, revision)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func addUpdate(filename string, repo *Repository, batch *indexer.Batch) error {
 | 
					func addUpdate(update fileUpdate, repo *Repository, batch *indexer.Batch) error {
 | 
				
			||||||
	filepath := path.Join(repo.LocalCopyPath(), filename)
 | 
						stdout, err := git.NewCommand("cat-file", "-s", update.BlobSha).
 | 
				
			||||||
	if stat, err := os.Stat(filepath); err != nil {
 | 
							RunInDir(repo.RepoPath())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	} else if stat.Size() > setting.Indexer.MaxIndexerFileSize {
 | 
						}
 | 
				
			||||||
		return nil
 | 
						if size, err := strconv.Atoi(strings.TrimSpace(stdout)); err != nil {
 | 
				
			||||||
	} else if stat.IsDir() {
 | 
							return fmt.Errorf("Misformatted git cat-file output: %v", err)
 | 
				
			||||||
		// file could actually be a directory, if it is the root of a submodule.
 | 
						} else if int64(size) > setting.Indexer.MaxIndexerFileSize {
 | 
				
			||||||
		// We do not index submodule contents, so don't do anything.
 | 
					 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fileContents, err := ioutil.ReadFile(filepath)
 | 
					
 | 
				
			||||||
 | 
						fileContents, err := git.NewCommand("cat-file", "blob", update.BlobSha).
 | 
				
			||||||
 | 
							RunInDirBytes(repo.RepoPath())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	} else if !base.IsTextFile(fileContents) {
 | 
						} else if !base.IsTextFile(fileContents) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return batch.Add(indexer.RepoIndexerUpdate{
 | 
						return batch.Add(indexer.RepoIndexerUpdate{
 | 
				
			||||||
		Filepath: filename,
 | 
							Filepath: update.Filename,
 | 
				
			||||||
		Op:       indexer.RepoIndexerOpUpdate,
 | 
							Op:       indexer.RepoIndexerOpUpdate,
 | 
				
			||||||
		Data: &indexer.RepoIndexerData{
 | 
							Data: &indexer.RepoIndexerData{
 | 
				
			||||||
			RepoID:  repo.ID,
 | 
								RepoID:  repo.ID,
 | 
				
			||||||
@@ -221,42 +226,76 @@ func addDelete(filename string, repo *Repository, batch *indexer.Batch) error {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// genesisChanges get changes to add repo to the indexer for the first time
 | 
					// parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command
 | 
				
			||||||
func genesisChanges(repo *Repository) (*repoChanges, error) {
 | 
					func parseGitLsTreeOutput(stdout string) ([]fileUpdate, error) {
 | 
				
			||||||
	var changes repoChanges
 | 
						lines := strings.Split(stdout, "\n")
 | 
				
			||||||
	stdout, err := git.NewCommand("ls-files").RunInDir(repo.LocalCopyPath())
 | 
						updates := make([]fileUpdate, 0, len(lines))
 | 
				
			||||||
	if err != nil {
 | 
						for _, line := range lines {
 | 
				
			||||||
		return nil, err
 | 
							// expect line to be "<mode> <object-type> <object-sha>\t<filename>"
 | 
				
			||||||
	}
 | 
							line = strings.TrimSpace(line)
 | 
				
			||||||
	for _, line := range strings.Split(stdout, "\n") {
 | 
							if len(line) == 0 {
 | 
				
			||||||
		filename := strings.TrimSpace(line)
 | 
					 | 
				
			||||||
		if len(filename) == 0 {
 | 
					 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		} else if filename[0] == '"' {
 | 
							}
 | 
				
			||||||
 | 
							firstSpaceIndex := strings.IndexByte(line, ' ')
 | 
				
			||||||
 | 
							if firstSpaceIndex < 0 {
 | 
				
			||||||
 | 
								log.Error(4, "Misformatted git ls-tree output: %s", line)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							tabIndex := strings.IndexByte(line, '\t')
 | 
				
			||||||
 | 
							if tabIndex < 42+firstSpaceIndex || tabIndex == len(line)-1 {
 | 
				
			||||||
 | 
								log.Error(4, "Misformatted git ls-tree output: %s", line)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if objectType := line[firstSpaceIndex+1 : tabIndex-41]; objectType != "blob" {
 | 
				
			||||||
 | 
								// submodules appear as commit objects, we do not index submodules
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							blobSha := line[tabIndex-40 : tabIndex]
 | 
				
			||||||
 | 
							filename := line[tabIndex+1:]
 | 
				
			||||||
 | 
							if filename[0] == '"' {
 | 
				
			||||||
 | 
								var err error
 | 
				
			||||||
			filename, err = strconv.Unquote(filename)
 | 
								filename, err = strconv.Unquote(filename)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		changes.UpdatedFiles = append(changes.UpdatedFiles, filename)
 | 
							updates = append(updates, fileUpdate{
 | 
				
			||||||
 | 
								Filename: filename,
 | 
				
			||||||
 | 
								BlobSha:  blobSha,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &changes, nil
 | 
						return updates, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// genesisChanges get changes to add repo to the indexer for the first time
 | 
				
			||||||
 | 
					func genesisChanges(repo *Repository, revision string) (*repoChanges, error) {
 | 
				
			||||||
 | 
						var changes repoChanges
 | 
				
			||||||
 | 
						stdout, err := git.NewCommand("ls-tree", "--full-tree", "-r", revision).
 | 
				
			||||||
 | 
							RunInDir(repo.RepoPath())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						changes.Updates, err = parseGitLsTreeOutput(stdout)
 | 
				
			||||||
 | 
						return &changes, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// nonGenesisChanges get changes since the previous indexer update
 | 
					// nonGenesisChanges get changes since the previous indexer update
 | 
				
			||||||
func nonGenesisChanges(repo *Repository) (*repoChanges, error) {
 | 
					func nonGenesisChanges(repo *Repository, revision string) (*repoChanges, error) {
 | 
				
			||||||
	diffCmd := git.NewCommand("diff", "--name-status",
 | 
						diffCmd := git.NewCommand("diff", "--name-status",
 | 
				
			||||||
		repo.IndexerStatus.CommitSha, "HEAD")
 | 
							repo.IndexerStatus.CommitSha, revision)
 | 
				
			||||||
	stdout, err := diffCmd.RunInDir(repo.LocalCopyPath())
 | 
						stdout, err := diffCmd.RunInDir(repo.RepoPath())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		// previous commit sha may have been removed by a force push, so
 | 
							// previous commit sha may have been removed by a force push, so
 | 
				
			||||||
		// try rebuilding from scratch
 | 
							// try rebuilding from scratch
 | 
				
			||||||
 | 
							log.Warn("git diff: %v", err)
 | 
				
			||||||
		if err = indexer.DeleteRepoFromIndexer(repo.ID); err != nil {
 | 
							if err = indexer.DeleteRepoFromIndexer(repo.ID); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return genesisChanges(repo)
 | 
							return genesisChanges(repo, revision)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var changes repoChanges
 | 
						var changes repoChanges
 | 
				
			||||||
 | 
						updatedFilenames := make([]string, 0, 10)
 | 
				
			||||||
	for _, line := range strings.Split(stdout, "\n") {
 | 
						for _, line := range strings.Split(stdout, "\n") {
 | 
				
			||||||
		line = strings.TrimSpace(line)
 | 
							line = strings.TrimSpace(line)
 | 
				
			||||||
		if len(line) == 0 {
 | 
							if len(line) == 0 {
 | 
				
			||||||
@@ -274,23 +313,22 @@ func nonGenesisChanges(repo *Repository) (*repoChanges, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		switch status := line[0]; status {
 | 
							switch status := line[0]; status {
 | 
				
			||||||
		case 'M', 'A':
 | 
							case 'M', 'A':
 | 
				
			||||||
			changes.UpdatedFiles = append(changes.UpdatedFiles, filename)
 | 
								updatedFilenames = append(updatedFilenames, filename)
 | 
				
			||||||
		case 'D':
 | 
							case 'D':
 | 
				
			||||||
			changes.RemovedFiles = append(changes.RemovedFiles, filename)
 | 
								changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			log.Warn("Unrecognized status: %c (line=%s)", status, line)
 | 
								log.Warn("Unrecognized status: %c (line=%s)", status, line)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &changes, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func updateLastIndexSync(repo *Repository) error {
 | 
						cmd := git.NewCommand("ls-tree", "--full-tree", revision, "--")
 | 
				
			||||||
	stdout, err := git.NewCommand("rev-parse", "HEAD").RunInDir(repo.LocalCopyPath())
 | 
						cmd.AddArguments(updatedFilenames...)
 | 
				
			||||||
 | 
						stdout, err = cmd.RunInDir(repo.RepoPath())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sha := strings.TrimSpace(stdout)
 | 
						changes.Updates, err = parseGitLsTreeOutput(stdout)
 | 
				
			||||||
	return repo.updateIndexerStatus(sha)
 | 
						return &changes, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func processRepoIndexerOperationQueue() {
 | 
					func processRepoIndexerOperationQueue() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -244,6 +244,8 @@ func MirrorUpdate() {
 | 
				
			|||||||
// SyncMirrors checks and syncs mirrors.
 | 
					// SyncMirrors checks and syncs mirrors.
 | 
				
			||||||
// TODO: sync more mirrors at same time.
 | 
					// TODO: sync more mirrors at same time.
 | 
				
			||||||
func SyncMirrors() {
 | 
					func SyncMirrors() {
 | 
				
			||||||
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
	// Start listening on new sync requests.
 | 
						// Start listening on new sync requests.
 | 
				
			||||||
	for repoID := range MirrorQueue.Queue() {
 | 
						for repoID := range MirrorQueue.Queue() {
 | 
				
			||||||
		log.Trace("SyncMirrors [repo_id: %v]", repoID)
 | 
							log.Trace("SyncMirrors [repo_id: %v]", repoID)
 | 
				
			||||||
@@ -260,10 +262,22 @@ func SyncMirrors() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.ScheduleNextUpdate()
 | 
							m.ScheduleNextUpdate()
 | 
				
			||||||
		if err = UpdateMirror(m); err != nil {
 | 
							if err = updateMirror(sess, m); err != nil {
 | 
				
			||||||
			log.Error(4, "UpdateMirror [%s]: %v", repoID, err)
 | 
								log.Error(4, "UpdateMirror [%s]: %v", repoID, err)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Get latest commit date and update to current repository updated time
 | 
				
			||||||
 | 
							commitDate, err := git.GetLatestCommitTime(m.Repo.RepoPath())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Error(2, "GetLatestCommitDate [%s]: %v", m.RepoID, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if _, err = sess.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", commitDate.Unix(), m.RepoID); err != nil {
 | 
				
			||||||
 | 
								log.Error(2, "Update repository 'updated_unix' [%s]: %v", m.RepoID, err)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -72,6 +73,18 @@ func createTestEngine(fixturesDir string) error {
 | 
				
			|||||||
	return InitFixtures(&testfixtures.SQLite{}, fixturesDir)
 | 
						return InitFixtures(&testfixtures.SQLite{}, fixturesDir)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func removeAllWithRetry(dir string) error {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						for i := 0; i < 20; i++ {
 | 
				
			||||||
 | 
							err = os.RemoveAll(dir)
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							time.Sleep(100 * time.Millisecond)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PrepareTestDatabase load test fixtures into test database
 | 
					// PrepareTestDatabase load test fixtures into test database
 | 
				
			||||||
func PrepareTestDatabase() error {
 | 
					func PrepareTestDatabase() error {
 | 
				
			||||||
	return LoadFixtures()
 | 
						return LoadFixtures()
 | 
				
			||||||
@@ -81,7 +94,7 @@ func PrepareTestDatabase() error {
 | 
				
			|||||||
// by tests that use the above MainTest(..) function.
 | 
					// by tests that use the above MainTest(..) function.
 | 
				
			||||||
func PrepareTestEnv(t testing.TB) {
 | 
					func PrepareTestEnv(t testing.TB) {
 | 
				
			||||||
	assert.NoError(t, PrepareTestDatabase())
 | 
						assert.NoError(t, PrepareTestDatabase())
 | 
				
			||||||
	assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
 | 
						assert.NoError(t, removeAllWithRetry(setting.RepoRootPath))
 | 
				
			||||||
	metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
 | 
						metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
 | 
				
			||||||
	assert.NoError(t, com.CopyDir(metaPath, setting.RepoRootPath))
 | 
						assert.NoError(t, com.CopyDir(metaPath, setting.RepoRootPath))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -299,7 +299,9 @@ func (u *User) generateRandomAvatar(e Engine) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	// NOTICE for random avatar, it still uses id as avatar name, but custom avatar use md5
 | 
						// NOTICE for random avatar, it still uses id as avatar name, but custom avatar use md5
 | 
				
			||||||
	// since random image is not a user's photo, there is no security for enumable
 | 
						// since random image is not a user's photo, there is no security for enumable
 | 
				
			||||||
	u.Avatar = fmt.Sprintf("%d", u.ID)
 | 
						if u.Avatar == "" {
 | 
				
			||||||
 | 
							u.Avatar = fmt.Sprintf("%d", u.ID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if err = os.MkdirAll(filepath.Dir(u.CustomAvatarPath()), os.ModePerm); err != nil {
 | 
						if err = os.MkdirAll(filepath.Dir(u.CustomAvatarPath()), os.ModePerm); err != nil {
 | 
				
			||||||
		return fmt.Errorf("MkdirAll: %v", err)
 | 
							return fmt.Errorf("MkdirAll: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -649,7 +651,7 @@ func NewGhostUser() *User {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	reservedUsernames    = []string{"assets", "css", "explore", "img", "js", "less", "plugins", "debug", "raw", "install", "api", "avatars", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new", ".", ".."}
 | 
						reservedUsernames    = []string{"assets", "css", "explore", "img", "js", "less", "plugins", "debug", "raw", "install", "api", "avatars", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "error", "new", ".", ".."}
 | 
				
			||||||
	reservedUserPatterns = []string{"*.keys"}
 | 
						reservedUserPatterns = []string{"*.keys"}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -932,7 +934,7 @@ func deleteUser(e *xorm.Session, u *User) error {
 | 
				
			|||||||
	if err = e.Table("star").Cols("star.repo_id").
 | 
						if err = e.Table("star").Cols("star.repo_id").
 | 
				
			||||||
		Where("star.uid = ?", u.ID).Find(&starredRepoIDs); err != nil {
 | 
							Where("star.uid = ?", u.ID).Find(&starredRepoIDs); err != nil {
 | 
				
			||||||
		return fmt.Errorf("get all stars: %v", err)
 | 
							return fmt.Errorf("get all stars: %v", err)
 | 
				
			||||||
	} else if _, err = e.Decr("num_watches").In("id", starredRepoIDs).Update(new(Repository)); err != nil {
 | 
						} else if _, err = e.Decr("num_stars").In("id", starredRepoIDs).Update(new(Repository)); err != nil {
 | 
				
			||||||
		return fmt.Errorf("decrease repository num_stars: %v", err)
 | 
							return fmt.Errorf("decrease repository num_stars: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// ***** END: Star *****
 | 
						// ***** END: Star *****
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,7 +45,7 @@ func WikiNameToFilename(name string) string {
 | 
				
			|||||||
// WikiFilenameToName converts a wiki filename to its corresponding page name.
 | 
					// WikiFilenameToName converts a wiki filename to its corresponding page name.
 | 
				
			||||||
func WikiFilenameToName(filename string) (string, error) {
 | 
					func WikiFilenameToName(filename string) (string, error) {
 | 
				
			||||||
	if !strings.HasSuffix(filename, ".md") {
 | 
						if !strings.HasSuffix(filename, ".md") {
 | 
				
			||||||
		return "", fmt.Errorf("Invalid wiki filename: %s", filename)
 | 
							return "", ErrWikiInvalidFileName{filename}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	basename := filename[:len(filename)-3]
 | 
						basename := filename[:len(filename)-3]
 | 
				
			||||||
	unescaped, err := url.QueryUnescape(basename)
 | 
						unescaped, err := url.QueryUnescape(basename)
 | 
				
			||||||
@@ -67,7 +67,7 @@ func WikiPath(userName, repoName string) string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// WikiPath returns wiki data path for given repository.
 | 
					// WikiPath returns wiki data path for given repository.
 | 
				
			||||||
func (repo *Repository) WikiPath() string {
 | 
					func (repo *Repository) WikiPath() string {
 | 
				
			||||||
	return WikiPath(repo.MustOwner().Name, repo.Name)
 | 
						return WikiPath(repo.MustOwnerName(), repo.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HasWiki returns true if repository has wiki.
 | 
					// HasWiki returns true if repository has wiki.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,11 +77,14 @@ func TestWikiFilenameToName(t *testing.T) {
 | 
				
			|||||||
	for _, badFilename := range []string{
 | 
						for _, badFilename := range []string{
 | 
				
			||||||
		"nofileextension",
 | 
							"nofileextension",
 | 
				
			||||||
		"wrongfileextension.txt",
 | 
							"wrongfileextension.txt",
 | 
				
			||||||
		"badescaping%%.md",
 | 
					 | 
				
			||||||
	} {
 | 
						} {
 | 
				
			||||||
		_, err := WikiFilenameToName(badFilename)
 | 
							_, err := WikiFilenameToName(badFilename)
 | 
				
			||||||
		assert.Error(t, err)
 | 
							assert.Error(t, err)
 | 
				
			||||||
 | 
							assert.True(t, IsErrWikiInvalidFileName(err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						_, err := WikiFilenameToName("badescaping%%.md")
 | 
				
			||||||
 | 
						assert.Error(t, err)
 | 
				
			||||||
 | 
						assert.False(t, IsErrWikiInvalidFileName(err))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestWikiNameToFilenameToName(t *testing.T) {
 | 
					func TestWikiNameToFilenameToName(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
	"html/template"
 | 
						"html/template"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@@ -75,6 +76,26 @@ func (ctx *Context) HasValue(name string) bool {
 | 
				
			|||||||
	return ok
 | 
						return ok
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RedirectToFirst redirects to first not empty URL
 | 
				
			||||||
 | 
					func (ctx *Context) RedirectToFirst(location ...string) {
 | 
				
			||||||
 | 
						for _, loc := range location {
 | 
				
			||||||
 | 
							if len(loc) == 0 {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							u, err := url.Parse(loc)
 | 
				
			||||||
 | 
							if err != nil || (u.Scheme != "" && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ctx.Redirect(loc)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Redirect(setting.AppSubURL + "/")
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// HTML calls Context.HTML and converts template name to string.
 | 
					// HTML calls Context.HTML and converts template name to string.
 | 
				
			||||||
func (ctx *Context) HTML(status int, name base.TplName) {
 | 
					func (ctx *Context) HTML(status int, name base.TplName) {
 | 
				
			||||||
	log.Debug("Template: %s", name)
 | 
						log.Debug("Template: %s", name)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,6 +83,8 @@ type link struct {
 | 
				
			|||||||
	ExpiresAt time.Time         `json:"expires_at,omitempty"`
 | 
						ExpiresAt time.Time         `json:"expires_at,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var oidRegExp = regexp.MustCompile(`^[A-Fa-f0-9]+$`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ObjectOidHandler is the main request routing entry point into LFS server functions
 | 
					// ObjectOidHandler is the main request routing entry point into LFS server functions
 | 
				
			||||||
func ObjectOidHandler(ctx *context.Context) {
 | 
					func ObjectOidHandler(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -217,6 +219,12 @@ func PostHandler(ctx *context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if !authenticate(ctx, repository, rv.Authorization, true) {
 | 
						if !authenticate(ctx, repository, rv.Authorization, true) {
 | 
				
			||||||
		requireAuth(ctx)
 | 
							requireAuth(ctx)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !oidRegExp.MatchString(rv.Oid) {
 | 
				
			||||||
 | 
							writeStatus(ctx, 404)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	meta, err := models.NewLFSMetaObject(&models.LFSMetaObject{Oid: rv.Oid, Size: rv.Size, RepositoryID: repository.ID})
 | 
						meta, err := models.NewLFSMetaObject(&models.LFSMetaObject{Oid: rv.Oid, Size: rv.Size, RepositoryID: repository.ID})
 | 
				
			||||||
@@ -284,10 +292,12 @@ func BatchHandler(ctx *context.Context) {
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Object is not found
 | 
							if oidRegExp.MatchString(object.Oid) {
 | 
				
			||||||
		meta, err = models.NewLFSMetaObject(&models.LFSMetaObject{Oid: object.Oid, Size: object.Size, RepositoryID: repository.ID})
 | 
								// Object is not found
 | 
				
			||||||
		if err == nil {
 | 
								meta, err = models.NewLFSMetaObject(&models.LFSMetaObject{Oid: object.Oid, Size: object.Size, RepositoryID: repository.ID})
 | 
				
			||||||
			responseObjects = append(responseObjects, Represent(object, meta, meta.Existing, !contentStore.Exists(meta)))
 | 
								if err == nil {
 | 
				
			||||||
 | 
									responseObjects = append(responseObjects, Represent(object, meta, meta.Existing, !contentStore.Exists(meta)))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -391,7 +391,11 @@ func RenderShortLinks(rawBytes []byte, urlPrefix string, noLink bool, isWikiMark
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		absoluteLink := isLink([]byte(link))
 | 
							absoluteLink := isLink([]byte(link))
 | 
				
			||||||
		if !absoluteLink {
 | 
							if !absoluteLink {
 | 
				
			||||||
			link = strings.Replace(link, " ", "+", -1)
 | 
								if image {
 | 
				
			||||||
 | 
									link = strings.Replace(link, " ", "+", -1)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									link = strings.Replace(link, " ", "-", -1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if image {
 | 
							if image {
 | 
				
			||||||
			if !absoluteLink {
 | 
								if !absoluteLink {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ import (
 | 
				
			|||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"html"
 | 
				
			||||||
	"html/template"
 | 
						"html/template"
 | 
				
			||||||
	"mime"
 | 
						"mime"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
@@ -179,6 +180,7 @@ func NewFuncMap() []template.FuncMap {
 | 
				
			|||||||
			return dict, nil
 | 
								return dict, nil
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"Printf": fmt.Sprintf,
 | 
							"Printf": fmt.Sprintf,
 | 
				
			||||||
 | 
							"Escape": Escape,
 | 
				
			||||||
	}}
 | 
						}}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -197,6 +199,11 @@ func Str2html(raw string) template.HTML {
 | 
				
			|||||||
	return template.HTML(markup.Sanitize(raw))
 | 
						return template.HTML(markup.Sanitize(raw))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Escape escapes a HTML string
 | 
				
			||||||
 | 
					func Escape(raw string) string {
 | 
				
			||||||
 | 
						return html.EscapeString(raw)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// List traversings the list
 | 
					// List traversings the list
 | 
				
			||||||
func List(l *list.List) chan interface{} {
 | 
					func List(l *list.List) chan interface{} {
 | 
				
			||||||
	e := l.Front()
 | 
						e := l.Front()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,3 +59,8 @@ func (ts TimeStamp) FormatLong() string {
 | 
				
			|||||||
func (ts TimeStamp) FormatShort() string {
 | 
					func (ts TimeStamp) FormatShort() string {
 | 
				
			||||||
	return ts.Format("Jan 02, 2006")
 | 
						return ts.Format("Jan 02, 2006")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsZero is zero time
 | 
				
			||||||
 | 
					func (ts TimeStamp) IsZero() bool {
 | 
				
			||||||
 | 
						return ts.AsTime().IsZero()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -163,7 +163,7 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo
 | 
				
			|||||||
		branchName = form.NewBranchName
 | 
							branchName = form.NewBranchName
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	form.TreePath = strings.Trim(form.TreePath, " /")
 | 
						form.TreePath = strings.Trim(path.Clean("/"+form.TreePath), " /")
 | 
				
			||||||
	treeNames, treePaths := getParentTreeFields(form.TreePath)
 | 
						treeNames, treePaths := getParentTreeFields(form.TreePath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["TreePath"] = form.TreePath
 | 
						ctx.Data["TreePath"] = form.TreePath
 | 
				
			||||||
@@ -477,7 +477,7 @@ func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) {
 | 
				
			|||||||
		branchName = form.NewBranchName
 | 
							branchName = form.NewBranchName
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	form.TreePath = strings.Trim(form.TreePath, " /")
 | 
						form.TreePath = strings.Trim(path.Clean("/"+form.TreePath), " /")
 | 
				
			||||||
	treeNames, treePaths := getParentTreeFields(form.TreePath)
 | 
						treeNames, treePaths := getParentTreeFields(form.TreePath)
 | 
				
			||||||
	if len(treeNames) == 0 {
 | 
						if len(treeNames) == 0 {
 | 
				
			||||||
		// We must at least have one element for user to input.
 | 
							// We must at least have one element for user to input.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -184,33 +184,33 @@ func HTTP(ctx *context.Context) {
 | 
				
			|||||||
					return
 | 
										return
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !isPublicPull {
 | 
							if !isPublicPull {
 | 
				
			||||||
				has, err := models.HasAccess(authUser.ID, repo, accessMode)
 | 
								has, err := models.HasAccess(authUser.ID, repo, accessMode)
 | 
				
			||||||
				if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
					ctx.ServerError("HasAccess", err)
 | 
									ctx.ServerError("HasAccess", err)
 | 
				
			||||||
					return
 | 
									return
 | 
				
			||||||
				} else if !has {
 | 
								} else if !has {
 | 
				
			||||||
					if accessMode == models.AccessModeRead {
 | 
									if accessMode == models.AccessModeRead {
 | 
				
			||||||
						has, err = models.HasAccess(authUser.ID, repo, models.AccessModeWrite)
 | 
										has, err = models.HasAccess(authUser.ID, repo, models.AccessModeWrite)
 | 
				
			||||||
						if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
							ctx.ServerError("HasAccess2", err)
 | 
											ctx.ServerError("HasAccess2", err)
 | 
				
			||||||
							return
 | 
											return
 | 
				
			||||||
						} else if !has {
 | 
										} else if !has {
 | 
				
			||||||
							ctx.HandleText(http.StatusForbidden, "User permission denied")
 | 
					 | 
				
			||||||
							return
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						ctx.HandleText(http.StatusForbidden, "User permission denied")
 | 
											ctx.HandleText(http.StatusForbidden, "User permission denied")
 | 
				
			||||||
						return
 | 
											return
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									} else {
 | 
				
			||||||
 | 
										ctx.HandleText(http.StatusForbidden, "User permission denied")
 | 
				
			||||||
				if !isPull && repo.IsMirror {
 | 
					 | 
				
			||||||
					ctx.HandleText(http.StatusForbidden, "mirror repository is read-only")
 | 
					 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !isPull && repo.IsMirror {
 | 
				
			||||||
 | 
									ctx.HandleText(http.StatusForbidden, "mirror repository is read-only")
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if !repo.CheckUnitUser(authUser.ID, authUser.IsAdmin, unitType) {
 | 
							if !repo.CheckUnitUser(authUser.ID, authUser.IsAdmin, unitType) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ func TestInitializeLabels(t *testing.T) {
 | 
				
			|||||||
	ctx := test.MockContext(t, "user2/repo1/labels/initialize")
 | 
						ctx := test.MockContext(t, "user2/repo1/labels/initialize")
 | 
				
			||||||
	test.LoadUser(t, ctx, 2)
 | 
						test.LoadUser(t, ctx, 2)
 | 
				
			||||||
	test.LoadRepo(t, ctx, 2)
 | 
						test.LoadRepo(t, ctx, 2)
 | 
				
			||||||
	InitializeLabels(ctx, auth.InitializeLabelsForm{"Default"})
 | 
						InitializeLabels(ctx, auth.InitializeLabelsForm{TemplateName: "Default"})
 | 
				
			||||||
	assert.EqualValues(t, http.StatusFound, ctx.Resp.Status())
 | 
						assert.EqualValues(t, http.StatusFound, ctx.Resp.Status())
 | 
				
			||||||
	models.AssertExistsAndLoadBean(t, &models.Label{
 | 
						models.AssertExistsAndLoadBean(t, &models.Label{
 | 
				
			||||||
		RepoID: 2,
 | 
							RepoID: 2,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -307,11 +307,7 @@ func Action(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	redirectTo := ctx.Query("redirect_to")
 | 
						ctx.RedirectToFirst(ctx.Query("redirect_to"), ctx.Repo.RepoLink)
 | 
				
			||||||
	if len(redirectTo) == 0 {
 | 
					 | 
				
			||||||
		redirectTo = ctx.Repo.RepoLink
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ctx.Redirect(redirectTo)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Download download an archive of a repository
 | 
					// Download download an archive of a repository
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,6 +128,9 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			wikiName, err := models.WikiFilenameToName(entry.Name())
 | 
								wikiName, err := models.WikiFilenameToName(entry.Name())
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if models.IsErrWikiInvalidFileName(err) {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				ctx.ServerError("WikiFilenameToName", err)
 | 
									ctx.ServerError("WikiFilenameToName", err)
 | 
				
			||||||
				return nil, nil
 | 
									return nil, nil
 | 
				
			||||||
			} else if wikiName == "_Sidebar" || wikiName == "_Footer" {
 | 
								} else if wikiName == "_Sidebar" || wikiName == "_Footer" {
 | 
				
			||||||
@@ -262,6 +265,9 @@ func WikiPages(ctx *context.Context) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		wikiName, err := models.WikiFilenameToName(entry.Name())
 | 
							wikiName, err := models.WikiFilenameToName(entry.Name())
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if models.IsErrWikiInvalidFileName(err) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			ctx.ServerError("WikiFilenameToName", err)
 | 
								ctx.ServerError("WikiFilenameToName", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,36 +7,52 @@ package repo
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/git"
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/auth"
 | 
						"code.gitea.io/gitea/modules/auth"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/test"
 | 
						"code.gitea.io/gitea/modules/test"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/Unknwon/com"
 | 
					 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const content = "Wiki contents for unit tests"
 | 
					const content = "Wiki contents for unit tests"
 | 
				
			||||||
const message = "Wiki commit message for unit tests"
 | 
					const message = "Wiki commit message for unit tests"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func wikiPath(repo *models.Repository, wikiName string) string {
 | 
					func wikiEntry(t *testing.T, repo *models.Repository, wikiName string) *git.TreeEntry {
 | 
				
			||||||
	return filepath.Join(repo.LocalWikiPath(), models.WikiNameToFilename(wikiName))
 | 
						wikiRepo, err := git.OpenRepository(repo.WikiPath())
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						commit, err := wikiRepo.GetBranchCommit("master")
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						entries, err := commit.ListEntries()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						for _, entry := range entries {
 | 
				
			||||||
 | 
							if entry.Name() == models.WikiNameToFilename(wikiName) {
 | 
				
			||||||
 | 
								return entry
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func wikiContent(t *testing.T, repo *models.Repository, wikiName string) string {
 | 
					func wikiContent(t *testing.T, repo *models.Repository, wikiName string) string {
 | 
				
			||||||
	bytes, err := ioutil.ReadFile(wikiPath(repo, wikiName))
 | 
						entry := wikiEntry(t, repo, wikiName)
 | 
				
			||||||
 | 
						if !assert.NotNil(t, entry) {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						reader, err := entry.Blob().Data()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						bytes, err := ioutil.ReadAll(reader)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	return string(bytes)
 | 
						return string(bytes)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func assertWikiExists(t *testing.T, repo *models.Repository, wikiName string) {
 | 
					func assertWikiExists(t *testing.T, repo *models.Repository, wikiName string) {
 | 
				
			||||||
	assert.True(t, com.IsExist(wikiPath(repo, wikiName)))
 | 
						assert.NotNil(t, wikiEntry(t, repo, wikiName))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func assertWikiNotExists(t *testing.T, repo *models.Repository, wikiName string) {
 | 
					func assertWikiNotExists(t *testing.T, repo *models.Repository, wikiName string) {
 | 
				
			||||||
	assert.False(t, com.IsExist(wikiPath(repo, wikiName)))
 | 
						assert.Nil(t, wikiEntry(t, repo, wikiName))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func assertPagesMetas(t *testing.T, expectedNames []string, metas interface{}) {
 | 
					func assertPagesMetas(t *testing.T, expectedNames []string, metas interface{}) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,12 +93,8 @@ func checkAutoLogin(ctx *context.Context) bool {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if isSucceed {
 | 
						if isSucceed {
 | 
				
			||||||
		if len(redirectTo) > 0 {
 | 
							ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
 | 
				
			||||||
			ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
 | 
							ctx.RedirectToFirst(redirectTo, setting.AppSubURL+string(setting.LandingPageURL))
 | 
				
			||||||
			ctx.Redirect(redirectTo)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -350,7 +346,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
 | 
				
			|||||||
	if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
 | 
						if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
 | 
				
			||||||
		ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
 | 
							ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
 | 
				
			||||||
		if obeyRedirect {
 | 
							if obeyRedirect {
 | 
				
			||||||
			ctx.Redirect(redirectTo)
 | 
								ctx.RedirectToFirst(redirectTo)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -439,7 +435,7 @@ func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
 | 
								if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 {
 | 
				
			||||||
				ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
 | 
									ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
 | 
				
			||||||
				ctx.Redirect(redirectTo)
 | 
									ctx.RedirectToFirst(redirectTo)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,12 +49,8 @@ func SignInOpenID(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if isSucceed {
 | 
						if isSucceed {
 | 
				
			||||||
		if len(redirectTo) > 0 {
 | 
							ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
 | 
				
			||||||
			ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL)
 | 
							ctx.RedirectToFirst(redirectTo)
 | 
				
			||||||
			ctx.Redirect(redirectTo)
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			ctx.Redirect(setting.AppSubURL + "/")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,12 +66,14 @@ func retrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) {
 | 
				
			|||||||
	if ctx.User != nil {
 | 
						if ctx.User != nil {
 | 
				
			||||||
		userCache[ctx.User.ID] = ctx.User
 | 
							userCache[ctx.User.ID] = ctx.User
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	repoCache := map[int64]*models.Repository{}
 | 
					 | 
				
			||||||
	for _, act := range actions {
 | 
						for _, act := range actions {
 | 
				
			||||||
		// Cache results to reduce queries.
 | 
							if act.ActUser != nil {
 | 
				
			||||||
		u, ok := userCache[act.ActUserID]
 | 
								userCache[act.ActUserID] = act.ActUser
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							repoOwner, ok := userCache[act.Repo.OwnerID]
 | 
				
			||||||
		if !ok {
 | 
							if !ok {
 | 
				
			||||||
			u, err = models.GetUserByID(act.ActUserID)
 | 
								repoOwner, err = models.GetUserByID(act.Repo.OwnerID)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				if models.IsErrUserNotExist(err) {
 | 
									if models.IsErrUserNotExist(err) {
 | 
				
			||||||
					continue
 | 
										continue
 | 
				
			||||||
@@ -79,35 +81,9 @@ func retrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) {
 | 
				
			|||||||
				ctx.ServerError("GetUserByID", err)
 | 
									ctx.ServerError("GetUserByID", err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			userCache[act.ActUserID] = u
 | 
								userCache[repoOwner.ID] = repoOwner
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		act.ActUser = u
 | 
							act.Repo.Owner = repoOwner
 | 
				
			||||||
 | 
					 | 
				
			||||||
		repo, ok := repoCache[act.RepoID]
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			repo, err = models.GetRepositoryByID(act.RepoID)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				if models.IsErrRepoNotExist(err) {
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				ctx.ServerError("GetRepositoryByID", err)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		act.Repo = repo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		repoOwner, ok := userCache[repo.OwnerID]
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			repoOwner, err = models.GetUserByID(repo.OwnerID)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				if models.IsErrUserNotExist(err) {
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				ctx.ServerError("GetUserByID", err)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		repo.Owner = repoOwner
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["Feeds"] = actions
 | 
						ctx.Data["Feeds"] = actions
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -154,7 +130,8 @@ func Dashboard(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["MirrorCount"] = len(mirrors)
 | 
						ctx.Data["MirrorCount"] = len(mirrors)
 | 
				
			||||||
	ctx.Data["Mirrors"] = mirrors
 | 
						ctx.Data["Mirrors"] = mirrors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	retrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser,
 | 
						retrieveFeeds(ctx, models.GetFeedsOptions{
 | 
				
			||||||
 | 
							RequestedUser:   ctxUser,
 | 
				
			||||||
		IncludePrivate:  true,
 | 
							IncludePrivate:  true,
 | 
				
			||||||
		OnlyPerformedBy: false,
 | 
							OnlyPerformedBy: false,
 | 
				
			||||||
		IncludeDeleted:  false,
 | 
							IncludeDeleted:  false,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -271,9 +271,5 @@ func Action(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	redirectTo := ctx.Query("redirect_to")
 | 
						ctx.RedirectToFirst(ctx.Query("redirect_to"), u.HomeLink())
 | 
				
			||||||
	if len(redirectTo) == 0 {
 | 
					 | 
				
			||||||
		redirectTo = u.HomeLink()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ctx.Redirect(redirectTo)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,7 +134,7 @@
 | 
				
			|||||||
					<p class="desc">
 | 
										<p class="desc">
 | 
				
			||||||
						<div class="ui red label">{{$.i18n.Tr "repo.activity.closed_issue_label"}}</div>
 | 
											<div class="ui red label">{{$.i18n.Tr "repo.activity.closed_issue_label"}}</div>
 | 
				
			||||||
						#{{.Index}} <a class="title has-emoji" href="{{$.Repository.HTMLURL}}/issues/{{.Index}}">{{.Title}}</a>
 | 
											#{{.Index}} <a class="title has-emoji" href="{{$.Repository.HTMLURL}}/issues/{{.Index}}">{{.Title}}</a>
 | 
				
			||||||
						{{TimeSinceUnix .UpdatedUnix $.Lang}}
 | 
											{{TimeSinceUnix .ClosedUnix $.Lang}}
 | 
				
			||||||
					</p>
 | 
										</p>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,12 +69,12 @@
 | 
				
			|||||||
<div class="ui small basic delete modal">
 | 
					<div class="ui small basic delete modal">
 | 
				
			||||||
	<div class="ui icon header">
 | 
						<div class="ui icon header">
 | 
				
			||||||
		<i class="trash icon"></i>
 | 
							<i class="trash icon"></i>
 | 
				
			||||||
		{{.i18n.Tr "repo.branch.delete_html"| Safe}} <span class="branch-name"></span>
 | 
							{{.i18n.Tr "repo.branch.delete_html"}} <span class="branch-name"></span>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div class="content">
 | 
						<div class="content">
 | 
				
			||||||
		<p>{{.i18n.Tr "repo.branch.delete_desc" | Safe}}</p>
 | 
							<p>{{.i18n.Tr "repo.branch.delete_desc"}}</p>
 | 
				
			||||||
		{{.i18n.Tr "repo.branch.delete_notices_1" | Safe}}<br>
 | 
							{{.i18n.Tr "repo.branch.delete_notices_1" | Safe}}<br>
 | 
				
			||||||
		{{.i18n.Tr "repo.branch.delete_notices_html" | Safe}} <span class="branch-name"></span><br>
 | 
							{{.i18n.Tr "repo.branch.delete_notices_html"}} <span class="branch-name"></span><br>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	{{template "base/delete_modal_actions" .}}
 | 
						{{template "base/delete_modal_actions" .}}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,9 +47,9 @@
 | 
				
			|||||||
						</div>
 | 
											</div>
 | 
				
			||||||
						<div class="text small">
 | 
											<div class="text small">
 | 
				
			||||||
							{{if .IsViewBranch}}
 | 
												{{if .IsViewBranch}}
 | 
				
			||||||
								{{.i18n.Tr "repo.branch.create_from" .BranchName | Safe}}
 | 
													{{.i18n.Tr "repo.branch.create_from" .BranchName}}
 | 
				
			||||||
							{{else}}
 | 
												{{else}}
 | 
				
			||||||
								{{.i18n.Tr "repo.branch.create_from" (ShortSha .BranchName) | Safe}}
 | 
													{{.i18n.Tr "repo.branch.create_from" (ShortSha .BranchName)}}
 | 
				
			||||||
							{{end}}
 | 
												{{end}}
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</a>
 | 
										</a>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,7 @@
 | 
				
			|||||||
					<div class="inline required field {{if .Err_RepoName}}error{{end}}">
 | 
										<div class="inline required field {{if .Err_RepoName}}error{{end}}">
 | 
				
			||||||
						<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
 | 
											<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
 | 
				
			||||||
						<input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required>
 | 
											<input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required>
 | 
				
			||||||
						<span class="help">{{.i18n.Tr "repo.repo_name_helper" | Safe}}</span>
 | 
											<span class="help">{{.i18n.Tr "repo.repo_name_helper"}}</span>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
					<div class="inline field">
 | 
										<div class="inline field">
 | 
				
			||||||
						<label>{{.i18n.Tr "repo.visibility"}}</label>
 | 
											<label>{{.i18n.Tr "repo.visibility"}}</label>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,8 +14,7 @@
 | 
				
			|||||||
					<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct" {{if eq .commit_choice "direct"}}checked{{end}}>
 | 
										<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct" {{if eq .commit_choice "direct"}}checked{{end}}>
 | 
				
			||||||
					<label>
 | 
										<label>
 | 
				
			||||||
						<i class="octicon octicon-git-commit" height="16" width="14"></i>
 | 
											<i class="octicon octicon-git-commit" height="16" width="14"></i>
 | 
				
			||||||
						{{$branchName := .BranchName | Str2html}}
 | 
											{{.i18n.Tr "repo.editor.commit_directly_to_this_branch" (.BranchName|Escape) | Safe}}
 | 
				
			||||||
						{{.i18n.Tr "repo.editor.commit_directly_to_this_branch" $branchName | Safe}}
 | 
					 | 
				
			||||||
					</label>
 | 
										</label>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@
 | 
				
			|||||||
	<div class="ui tabs container">
 | 
						<div class="ui tabs container">
 | 
				
			||||||
		<div class="ui tabular stackable menu navbar">
 | 
							<div class="ui tabular stackable menu navbar">
 | 
				
			||||||
			{{if .Repository.UnitEnabled $.UnitTypeCode}}
 | 
								{{if .Repository.UnitEnabled $.UnitTypeCode}}
 | 
				
			||||||
			<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}">
 | 
								<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/branch/{{.BranchName}}{{end}}">
 | 
				
			||||||
				<i class="octicon octicon-code"></i> {{.i18n.Tr "repo.code"}}
 | 
									<i class="octicon octicon-code"></i> {{.i18n.Tr "repo.code"}}
 | 
				
			||||||
			</a>
 | 
								</a>
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,7 +73,7 @@
 | 
				
			|||||||
						{{else if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}}
 | 
											{{else if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}}
 | 
				
			||||||
							<input id="repo-clone-url" value="{{$.CloneLink.SSH}}" readonly>
 | 
												<input id="repo-clone-url" value="{{$.CloneLink.SSH}}" readonly>
 | 
				
			||||||
						{{end}}
 | 
											{{end}}
 | 
				
			||||||
						{{if or ((not $.DisableHTTP) (and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)))}}
 | 
											{{if or (not $.DisableHTTP) (and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH))}}
 | 
				
			||||||
							<button class="ui basic icon button poping up clipboard" id="clipboard-btn" data-original="{{.i18n.Tr "repo.copy_link"}}" data-success="{{.i18n.Tr "repo.copy_link_success"}}" data-error="{{.i18n.Tr "repo.copy_link_error"}}" data-content="{{.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-target="#repo-clone-url">
 | 
												<button class="ui basic icon button poping up clipboard" id="clipboard-btn" data-original="{{.i18n.Tr "repo.copy_link"}}" data-success="{{.i18n.Tr "repo.copy_link_success"}}" data-error="{{.i18n.Tr "repo.copy_link_error"}}" data-content="{{.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-target="#repo-clone-url">
 | 
				
			||||||
								<i class="octicon octicon-clippy"></i>
 | 
													<i class="octicon octicon-clippy"></i>
 | 
				
			||||||
							</button>
 | 
												</button>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,12 +134,12 @@
 | 
				
			|||||||
<div class="ui small basic delete modal">
 | 
					<div class="ui small basic delete modal">
 | 
				
			||||||
	<div class="ui icon header">
 | 
						<div class="ui icon header">
 | 
				
			||||||
		<i class="trash icon"></i>
 | 
							<i class="trash icon"></i>
 | 
				
			||||||
		{{.i18n.Tr "repo.branch.delete" .HeadTarget | Safe}}
 | 
							{{.i18n.Tr "repo.branch.delete" .HeadTarget }}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div class="content">
 | 
						<div class="content">
 | 
				
			||||||
		<p>{{.i18n.Tr "repo.branch.delete_desc" | Safe}}</p>
 | 
							<p>{{.i18n.Tr "repo.branch.delete_desc"}}</p>
 | 
				
			||||||
		{{.i18n.Tr "repo.branch.delete_notices_1" | Safe}}<br>
 | 
							{{.i18n.Tr "repo.branch.delete_notices_1" | Safe}}<br>
 | 
				
			||||||
		{{.i18n.Tr "repo.branch.delete_notices_2" .HeadTarget | Safe}}<br>
 | 
							{{.i18n.Tr "repo.branch.delete_notices_2" .HeadTarget}}<br>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	{{template "base/delete_modal_actions" .}}
 | 
						{{template "base/delete_modal_actions" .}}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,7 +103,7 @@
 | 
				
			|||||||
					<img src="{{.Poster.RelAvatarLink}}">
 | 
										<img src="{{.Poster.RelAvatarLink}}">
 | 
				
			||||||
				</a>
 | 
									</a>
 | 
				
			||||||
				<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
 | 
									<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
 | 
				
			||||||
				{{if .Content}}{{$.i18n.Tr "repo.issues.add_label_at" .Label.ForegroundColor .Label.Color .Label.Name $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_label_at" .Label.ForegroundColor .Label.Color .Label.Name $createdStr | Safe}}{{end}}</span>
 | 
									{{if .Content}}{{$.i18n.Tr "repo.issues.add_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|Escape) $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|Escape) $createdStr | Safe}}{{end}}</span>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
	{{else if eq .Type 8}}
 | 
						{{else if eq .Type 8}}
 | 
				
			||||||
@@ -113,7 +113,7 @@
 | 
				
			|||||||
				<img src="{{.Poster.RelAvatarLink}}">
 | 
									<img src="{{.Poster.RelAvatarLink}}">
 | 
				
			||||||
			</a>
 | 
								</a>
 | 
				
			||||||
			<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
 | 
								<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
 | 
				
			||||||
			{{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.change_milestone_at" .OldMilestone.Name .Milestone.Name $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_milestone_at" .OldMilestone.Name $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.add_milestone_at" .Milestone.Name $createdStr | Safe}}{{end}}</span>
 | 
								{{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.change_milestone_at" (.OldMilestone.Name|Escape) (.Milestone.Name|Escape) $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_milestone_at" (.OldMilestone.Name|Escape) $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.add_milestone_at" (.Milestone.Name|Escape) $createdStr | Safe}}{{end}}</span>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	{{else if eq .Type 9}}
 | 
						{{else if eq .Type 9}}
 | 
				
			||||||
		<div class="event">
 | 
							<div class="event">
 | 
				
			||||||
@@ -131,23 +131,23 @@
 | 
				
			|||||||
	{{else if eq .Type 10}}
 | 
						{{else if eq .Type 10}}
 | 
				
			||||||
		<div class="event">
 | 
							<div class="event">
 | 
				
			||||||
			<span class="octicon octicon-primitive-dot"></span>
 | 
								<span class="octicon octicon-primitive-dot"></span>
 | 
				
			||||||
 | 
								<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 | 
				
			||||||
 | 
									<img src="{{.Poster.RelAvatarLink}}">
 | 
				
			||||||
 | 
								</a>
 | 
				
			||||||
 | 
								<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
 | 
				
			||||||
 | 
								{{$.i18n.Tr "repo.issues.change_title_at" (.OldTitle|Escape) (.NewTitle|Escape) $createdStr | Safe}}
 | 
				
			||||||
 | 
								</span>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 | 
					 | 
				
			||||||
			<img src="{{.Poster.RelAvatarLink}}">
 | 
					 | 
				
			||||||
		</a>
 | 
					 | 
				
			||||||
		<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
 | 
					 | 
				
			||||||
		{{$.i18n.Tr "repo.issues.change_title_at" .OldTitle .NewTitle $createdStr | Safe}}
 | 
					 | 
				
			||||||
		</span>
 | 
					 | 
				
			||||||
	{{else if eq .Type 11}}
 | 
						{{else if eq .Type 11}}
 | 
				
			||||||
		<div class="event">
 | 
							<div class="event">
 | 
				
			||||||
			<span class="octicon octicon-primitive-dot"></span>
 | 
								<span class="octicon octicon-primitive-dot"></span>
 | 
				
			||||||
 | 
								<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 | 
				
			||||||
 | 
									<img src="{{.Poster.RelAvatarLink}}">
 | 
				
			||||||
 | 
								</a>
 | 
				
			||||||
 | 
								<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
 | 
				
			||||||
 | 
								{{$.i18n.Tr "repo.issues.delete_branch_at" (.CommitSHA|Escape) $createdStr | Safe}}
 | 
				
			||||||
 | 
								</span>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 | 
					 | 
				
			||||||
			<img src="{{.Poster.RelAvatarLink}}">
 | 
					 | 
				
			||||||
		</a>
 | 
					 | 
				
			||||||
		<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
 | 
					 | 
				
			||||||
		{{$.i18n.Tr "repo.issues.delete_branch_at" .CommitSHA $createdStr | Safe}}
 | 
					 | 
				
			||||||
		</span>
 | 
					 | 
				
			||||||
    {{else if eq .Type 12}}
 | 
					    {{else if eq .Type 12}}
 | 
				
			||||||
		<div class="event">
 | 
							<div class="event">
 | 
				
			||||||
			<span class="octicon octicon-primitive-dot"></span>
 | 
								<span class="octicon octicon-primitive-dot"></span>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		{{if .Keyword}}
 | 
							{{if .Keyword}}
 | 
				
			||||||
			<h3>
 | 
								<h3>
 | 
				
			||||||
				{{.i18n.Tr "repo.search.results" .Keyword .RepoLink .RepoName | Str2html}}
 | 
									{{.i18n.Tr "repo.search.results" (.Keyword|Escape) .RepoLink .RepoName | Str2html }}
 | 
				
			||||||
			</h3>
 | 
								</h3>
 | 
				
			||||||
			<div class="repository search">
 | 
								<div class="repository search">
 | 
				
			||||||
				{{range $result := .SearchResults}}
 | 
									{{range $result := .SearchResults}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -302,7 +302,7 @@
 | 
				
			|||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div class="content">
 | 
							<div class="content">
 | 
				
			||||||
			<div class="ui warning message text left">
 | 
								<div class="ui warning message text left">
 | 
				
			||||||
				{{.i18n.Tr "repo.settings.convert_notices_1" | Safe}}
 | 
									{{.i18n.Tr "repo.settings.convert_notices_1"}}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<form class="ui form" action="{{.Link}}" method="post">
 | 
								<form class="ui form" action="{{.Link}}" method="post">
 | 
				
			||||||
				{{.CsrfTokenHtml}}
 | 
									{{.CsrfTokenHtml}}
 | 
				
			||||||
@@ -333,8 +333,8 @@
 | 
				
			|||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div class="content">
 | 
							<div class="content">
 | 
				
			||||||
			<div class="ui warning message text left">
 | 
								<div class="ui warning message text left">
 | 
				
			||||||
				{{.i18n.Tr "repo.settings.transfer_notices_1" | Safe}} <br>
 | 
									{{.i18n.Tr "repo.settings.transfer_notices_1"}} <br>
 | 
				
			||||||
				{{.i18n.Tr "repo.settings.transfer_notices_2" | Safe}}
 | 
									{{.i18n.Tr "repo.settings.transfer_notices_2"}}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<form class="ui form" action="{{.Link}}" method="post">
 | 
								<form class="ui form" action="{{.Link}}" method="post">
 | 
				
			||||||
				{{.CsrfTokenHtml}}
 | 
									{{.CsrfTokenHtml}}
 | 
				
			||||||
@@ -371,7 +371,7 @@
 | 
				
			|||||||
				{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br>
 | 
									{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br>
 | 
				
			||||||
				{{.i18n.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}}
 | 
									{{.i18n.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}}
 | 
				
			||||||
				{{if .Repository.NumForks}}<br>
 | 
									{{if .Repository.NumForks}}<br>
 | 
				
			||||||
				{{.i18n.Tr "repo.settings.delete_notices_fork_1" | Safe}}
 | 
									{{.i18n.Tr "repo.settings.delete_notices_fork_1"}}
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<form class="ui form" action="{{.Link}}" method="post">
 | 
								<form class="ui form" action="{{.Link}}" method="post">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -104,7 +104,7 @@
 | 
				
			|||||||
		{{.i18n.Tr "repo.wiki.delete_page_button"}}
 | 
							{{.i18n.Tr "repo.wiki.delete_page_button"}}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div class="content">
 | 
						<div class="content">
 | 
				
			||||||
		<p>{{.i18n.Tr "repo.wiki.delete_page_notice_1" $title | Safe}}</p>
 | 
							<p>{{.i18n.Tr "repo.wiki.delete_page_notice_1" ($title|Escape) | Safe}}</p>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	{{template "base/delete_modal_actions" .}}
 | 
						{{template "base/delete_modal_actions" .}}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,7 @@
 | 
				
			|||||||
					<div class="activity meta">
 | 
										<div class="activity meta">
 | 
				
			||||||
						<i>{{$.i18n.Tr "settings.add_on"}} <span>{{.AddedUnix.FormatShort}}</span></i>
 | 
											<i>{{$.i18n.Tr "settings.add_on"}} <span>{{.AddedUnix.FormatShort}}</span></i>
 | 
				
			||||||
						-
 | 
											-
 | 
				
			||||||
						<i>{{if .ExpiredUnix}}{{$.i18n.Tr "settings.valid_until"}} <span>{{.ExpiredUnix.FormatShort}}</span>{{else}}{{$.i18n.Tr "settings.valid_forever"}}{{end}}</i>
 | 
											<i>{{if not .ExpiredUnix.IsZero}}{{$.i18n.Tr "settings.valid_until"}} <span>{{.ExpiredUnix.FormatShort}}</span>{{else}}{{$.i18n.Tr "settings.valid_forever"}}{{end}}</i>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										97
									
								
								vendor/github.com/markbates/goth/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										97
									
								
								vendor/github.com/markbates/goth/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -8,6 +8,10 @@ protocol providers, as long as they implement the `Provider` and `Session` inter
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
This package was inspired by [https://github.com/intridea/omniauth](https://github.com/intridea/omniauth).
 | 
					This package was inspired by [https://github.com/intridea/omniauth](https://github.com/intridea/omniauth).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Goth Needs a New Maintainer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[https://blog.gobuffalo.io/goth-needs-a-new-maintainer-626cd47ca37b](https://blog.gobuffalo.io/goth-needs-a-new-maintainer-626cd47ca37b) - TL;DR: I, @markbates, won't be responding to any more issues, PRs, etc... for this package. A new maintainer needs to be found ASAP. Is this you?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Installation
 | 
					## Installation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```text
 | 
					```text
 | 
				
			||||||
@@ -18,6 +22,8 @@ $ go get github.com/markbates/goth
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
* Amazon
 | 
					* Amazon
 | 
				
			||||||
* Auth0
 | 
					* Auth0
 | 
				
			||||||
 | 
					* Azure AD
 | 
				
			||||||
 | 
					* Battle.net
 | 
				
			||||||
* Bitbucket
 | 
					* Bitbucket
 | 
				
			||||||
* Box
 | 
					* Box
 | 
				
			||||||
* Cloud Foundry
 | 
					* Cloud Foundry
 | 
				
			||||||
@@ -26,6 +32,7 @@ $ go get github.com/markbates/goth
 | 
				
			|||||||
* Digital Ocean
 | 
					* Digital Ocean
 | 
				
			||||||
* Discord
 | 
					* Discord
 | 
				
			||||||
* Dropbox
 | 
					* Dropbox
 | 
				
			||||||
 | 
					* Eve Online
 | 
				
			||||||
* Facebook
 | 
					* Facebook
 | 
				
			||||||
* Fitbit
 | 
					* Fitbit
 | 
				
			||||||
* GitHub
 | 
					* GitHub
 | 
				
			||||||
@@ -38,6 +45,7 @@ $ go get github.com/markbates/goth
 | 
				
			|||||||
* Lastfm
 | 
					* Lastfm
 | 
				
			||||||
* Linkedin
 | 
					* Linkedin
 | 
				
			||||||
* Meetup
 | 
					* Meetup
 | 
				
			||||||
 | 
					* MicrosoftOnline
 | 
				
			||||||
* OneDrive
 | 
					* OneDrive
 | 
				
			||||||
* OpenID Connect (auto discovery)
 | 
					* OpenID Connect (auto discovery)
 | 
				
			||||||
* Paypal
 | 
					* Paypal
 | 
				
			||||||
@@ -50,7 +58,9 @@ $ go get github.com/markbates/goth
 | 
				
			|||||||
* Twitch
 | 
					* Twitch
 | 
				
			||||||
* Twitter
 | 
					* Twitter
 | 
				
			||||||
* Uber
 | 
					* Uber
 | 
				
			||||||
 | 
					* VK
 | 
				
			||||||
* Wepay
 | 
					* Wepay
 | 
				
			||||||
 | 
					* Xero
 | 
				
			||||||
* Yahoo
 | 
					* Yahoo
 | 
				
			||||||
* Yammer
 | 
					* Yammer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,17 +81,51 @@ $ go get github.com/markbates/goth
 | 
				
			|||||||
```text
 | 
					```text
 | 
				
			||||||
$ cd goth/examples
 | 
					$ cd goth/examples
 | 
				
			||||||
$ go get -v
 | 
					$ go get -v
 | 
				
			||||||
$ go build 
 | 
					$ go build
 | 
				
			||||||
$ ./examples
 | 
					$ ./examples
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Now open up your browser and go to [http://localhost:3000](http://localhost:3000) to see the example.
 | 
					Now open up your browser and go to [http://localhost:3000](http://localhost:3000) to see the example.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To actually use the different providers, please make sure you configure them given the system environments as defined in the examples/main.go file
 | 
					To actually use the different providers, please make sure you set environment variables. Example given in the examples/main.go file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Security Notes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					By default, gothic uses a `CookieStore` from the `gorilla/sessions` package to store session data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					As configured, this default store (`gothic.Store`) will generate cookies with `Options`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					&Options{
 | 
				
			||||||
 | 
					   Path:   "/",
 | 
				
			||||||
 | 
					   Domain: "",
 | 
				
			||||||
 | 
					   MaxAge: 86400 * 30,
 | 
				
			||||||
 | 
					   HttpOnly: true,
 | 
				
			||||||
 | 
					   Secure: false,
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To tailor these fields for your application, you can override the `gothic.Store` variable at startup.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The follow snippet show one way to do this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					key := ""             // Replace with your SESSION_SECRET or similar
 | 
				
			||||||
 | 
					maxAge := 86400 * 30  // 30 days
 | 
				
			||||||
 | 
					isProd := false       // Set to true when serving over https
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					store := sessions.NewCookieStore([]byte(key))
 | 
				
			||||||
 | 
					store.MaxAge(maxAge)
 | 
				
			||||||
 | 
					store.Options.Path = "/"
 | 
				
			||||||
 | 
					store.Options.HttpOnly = true   // HttpOnly should always be enabled
 | 
				
			||||||
 | 
					store.Options.Secure = isProd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gothic.Store = store
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Issues
 | 
					## Issues
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Issues always stand a significantly better chance of getting fixed if the are accompanied by a
 | 
					Issues always stand a significantly better chance of getting fixed if they are accompanied by a
 | 
				
			||||||
pull request.
 | 
					pull request.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Contributing
 | 
					## Contributing
 | 
				
			||||||
@@ -94,50 +138,3 @@ Would I love to see more providers? Certainly! Would you love to contribute one?
 | 
				
			|||||||
4. Commit your changes (git commit -am 'Add some feature')
 | 
					4. Commit your changes (git commit -am 'Add some feature')
 | 
				
			||||||
5. Push to the branch (git push origin my-new-feature)
 | 
					5. Push to the branch (git push origin my-new-feature)
 | 
				
			||||||
6. Create new Pull Request
 | 
					6. Create new Pull Request
 | 
				
			||||||
 | 
					 | 
				
			||||||
## Contributors
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* Mark Bates
 | 
					 | 
				
			||||||
* Tyler Bunnell
 | 
					 | 
				
			||||||
* Corey McGrillis
 | 
					 | 
				
			||||||
* willemvd
 | 
					 | 
				
			||||||
* Rakesh Goyal
 | 
					 | 
				
			||||||
* Andy Grunwald
 | 
					 | 
				
			||||||
* Glenn Walker
 | 
					 | 
				
			||||||
* Kevin Fitzpatrick
 | 
					 | 
				
			||||||
* Ben Tranter
 | 
					 | 
				
			||||||
* Sharad Ganapathy
 | 
					 | 
				
			||||||
* Andrew Chilton
 | 
					 | 
				
			||||||
* sharadgana
 | 
					 | 
				
			||||||
* Aurorae
 | 
					 | 
				
			||||||
* Craig P Jolicoeur
 | 
					 | 
				
			||||||
* Zac Bergquist
 | 
					 | 
				
			||||||
* Geoff Franks
 | 
					 | 
				
			||||||
* Raphael Geronimi
 | 
					 | 
				
			||||||
* Noah Shibley
 | 
					 | 
				
			||||||
* lumost
 | 
					 | 
				
			||||||
* oov
 | 
					 | 
				
			||||||
* Felix Lamouroux
 | 
					 | 
				
			||||||
* Rafael Quintela
 | 
					 | 
				
			||||||
* Tyler
 | 
					 | 
				
			||||||
* DenSm
 | 
					 | 
				
			||||||
* Samy KACIMI
 | 
					 | 
				
			||||||
* dante gray
 | 
					 | 
				
			||||||
* Noah
 | 
					 | 
				
			||||||
* Jacob Walker
 | 
					 | 
				
			||||||
* Marin Martinic
 | 
					 | 
				
			||||||
* Roy
 | 
					 | 
				
			||||||
* Omni Adams
 | 
					 | 
				
			||||||
* Sasa Brankovic
 | 
					 | 
				
			||||||
* dkhamsing
 | 
					 | 
				
			||||||
* Dante Swift
 | 
					 | 
				
			||||||
* Attila Domokos
 | 
					 | 
				
			||||||
* Albin Gilles
 | 
					 | 
				
			||||||
* Syed Zubairuddin
 | 
					 | 
				
			||||||
* Johnny Boursiquot
 | 
					 | 
				
			||||||
* Jerome Touffe-Blin
 | 
					 | 
				
			||||||
* bryanl
 | 
					 | 
				
			||||||
* Masanobu YOSHIOKA
 | 
					 | 
				
			||||||
* Jonathan Hall
 | 
					 | 
				
			||||||
* HaiMing.Yin
 | 
					 | 
				
			||||||
* Sairam Kunala
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										181
									
								
								vendor/github.com/markbates/goth/gothic/gothic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										181
									
								
								vendor/github.com/markbates/goth/gothic/gothic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -8,10 +8,18 @@ See https://github.com/markbates/goth/examples/main.go to see this in action.
 | 
				
			|||||||
package gothic
 | 
					package gothic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"compress/gzip"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gorilla/mux"
 | 
						"github.com/gorilla/mux"
 | 
				
			||||||
	"github.com/gorilla/sessions"
 | 
						"github.com/gorilla/sessions"
 | 
				
			||||||
@@ -27,15 +35,21 @@ var defaultStore sessions.Store
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var keySet = false
 | 
					var keySet = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var gothicRand *rand.Rand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	key := []byte(os.Getenv("SESSION_SECRET"))
 | 
						key := []byte(os.Getenv("SESSION_SECRET"))
 | 
				
			||||||
	keySet = len(key) != 0
 | 
						keySet = len(key) != 0
 | 
				
			||||||
	Store = sessions.NewCookieStore([]byte(key))
 | 
					
 | 
				
			||||||
 | 
						cookieStore := sessions.NewCookieStore([]byte(key))
 | 
				
			||||||
 | 
						cookieStore.Options.HttpOnly = true
 | 
				
			||||||
 | 
						Store = cookieStore
 | 
				
			||||||
	defaultStore = Store
 | 
						defaultStore = Store
 | 
				
			||||||
 | 
						gothicRand = rand.New(rand.NewSource(time.Now().UnixNano()))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
BeginAuthHandler is a convienence handler for starting the authentication process.
 | 
					BeginAuthHandler is a convenience handler for starting the authentication process.
 | 
				
			||||||
It expects to be able to get the name of the provider from the query parameters
 | 
					It expects to be able to get the name of the provider from the query parameters
 | 
				
			||||||
as either "provider" or ":provider".
 | 
					as either "provider" or ":provider".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,8 +79,16 @@ var SetState = func(req *http.Request) string {
 | 
				
			|||||||
		return state
 | 
							return state
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return "state"
 | 
						// If a state query param is not passed in, generate a random
 | 
				
			||||||
 | 
						// base64-encoded nonce so that the state on the auth URL
 | 
				
			||||||
 | 
						// is unguessable, preventing CSRF attacks, as described in
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
 | 
				
			||||||
 | 
						nonceBytes := make([]byte, 64)
 | 
				
			||||||
 | 
						for i := 0; i < 64; i++ {
 | 
				
			||||||
 | 
							nonceBytes[i] = byte(gothicRand.Int63() % 256)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return base64.URLEncoding.EncodeToString(nonceBytes)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetState gets the state returned by the provider during the callback.
 | 
					// GetState gets the state returned by the provider during the callback.
 | 
				
			||||||
@@ -87,7 +109,6 @@ I would recommend using the BeginAuthHandler instead of doing all of these steps
 | 
				
			|||||||
yourself, but that's entirely up to you.
 | 
					yourself, but that's entirely up to you.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
 | 
					func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if !keySet && defaultStore == Store {
 | 
						if !keySet && defaultStore == Store {
 | 
				
			||||||
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
 | 
							fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -111,7 +132,7 @@ func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
 | 
				
			|||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = storeInSession(providerName, sess.Marshal(), req, res)
 | 
						err = StoreInSession(providerName, sess.Marshal(), req, res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
@@ -130,7 +151,7 @@ as either "provider" or ":provider".
 | 
				
			|||||||
See https://github.com/markbates/goth/examples/main.go to see this in action.
 | 
					See https://github.com/markbates/goth/examples/main.go to see this in action.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
 | 
					var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
 | 
				
			||||||
 | 
						defer Logout(res, req)
 | 
				
			||||||
	if !keySet && defaultStore == Store {
 | 
						if !keySet && defaultStore == Store {
 | 
				
			||||||
		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
 | 
							fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -145,7 +166,7 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
 | 
				
			|||||||
		return goth.User{}, err
 | 
							return goth.User{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	value, err := getFromSession(providerName, req)
 | 
						value, err := GetFromSession(providerName, req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return goth.User{}, err
 | 
							return goth.User{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -155,6 +176,11 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
 | 
				
			|||||||
		return goth.User{}, err
 | 
							return goth.User{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = validateState(req, sess)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return goth.User{}, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	user, err := provider.FetchUser(sess)
 | 
						user, err := provider.FetchUser(sess)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		// user can be found with existing session data
 | 
							// user can be found with existing session data
 | 
				
			||||||
@@ -167,13 +193,49 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
 | 
				
			|||||||
		return goth.User{}, err
 | 
							return goth.User{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = storeInSession(providerName, sess.Marshal(), req, res)
 | 
						err = StoreInSession(providerName, sess.Marshal(), req, res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return goth.User{}, err
 | 
							return goth.User{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return provider.FetchUser(sess)
 | 
						gu, err := provider.FetchUser(sess)
 | 
				
			||||||
 | 
						return gu, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// validateState ensures that the state token param from the original
 | 
				
			||||||
 | 
					// AuthURL matches the one included in the current (callback) request.
 | 
				
			||||||
 | 
					func validateState(req *http.Request, sess goth.Session) error {
 | 
				
			||||||
 | 
						rawAuthURL, err := sess.GetAuthURL()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						authURL, err := url.Parse(rawAuthURL)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						originalState := authURL.Query().Get("state")
 | 
				
			||||||
 | 
						if originalState != "" && (originalState != req.URL.Query().Get("state")) {
 | 
				
			||||||
 | 
							return errors.New("state token mismatch")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Logout invalidates a user session.
 | 
				
			||||||
 | 
					func Logout(res http.ResponseWriter, req *http.Request) error {
 | 
				
			||||||
 | 
						session, err := Store.Get(req, SessionName)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						session.Options.MaxAge = -1
 | 
				
			||||||
 | 
						session.Values = make(map[interface{}]interface{})
 | 
				
			||||||
 | 
						err = session.Save(req, res)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return errors.New("Could not delete user session ")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetProviderName is a function used to get the name of a provider
 | 
					// GetProviderName is a function used to get the name of a provider
 | 
				
			||||||
@@ -184,36 +246,99 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
 | 
				
			|||||||
var GetProviderName = getProviderName
 | 
					var GetProviderName = getProviderName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getProviderName(req *http.Request) (string, error) {
 | 
					func getProviderName(req *http.Request) (string, error) {
 | 
				
			||||||
	provider := req.URL.Query().Get("provider")
 | 
					
 | 
				
			||||||
	if provider == "" {
 | 
						// get all the used providers
 | 
				
			||||||
		if p, ok := mux.Vars(req)["provider"]; ok {
 | 
						providers := goth.GetProviders()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name
 | 
				
			||||||
 | 
						for _, provider := range providers {
 | 
				
			||||||
 | 
							p := provider.Name()
 | 
				
			||||||
 | 
							session, _ := Store.Get(req, p+SessionName)
 | 
				
			||||||
 | 
							value := session.Values[p]
 | 
				
			||||||
 | 
							if _, ok := value.(string); ok {
 | 
				
			||||||
			return p, nil
 | 
								return p, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if provider == "" {
 | 
					
 | 
				
			||||||
		provider = req.URL.Query().Get(":provider")
 | 
						// try to get it from the url param "provider"
 | 
				
			||||||
 | 
						if p := req.URL.Query().Get("provider"); p != "" {
 | 
				
			||||||
 | 
							return p, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if provider == "" {
 | 
					
 | 
				
			||||||
		return provider, errors.New("you must select a provider")
 | 
						// try to get it from the url param ":provider"
 | 
				
			||||||
 | 
						if p := req.URL.Query().Get(":provider"); p != "" {
 | 
				
			||||||
 | 
							return p, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return provider, nil
 | 
					
 | 
				
			||||||
 | 
						// try to get it from the context's value of "provider" key
 | 
				
			||||||
 | 
						if p, ok := mux.Vars(req)["provider"]; ok {
 | 
				
			||||||
 | 
							return p, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//  try to get it from the go-context's value of "provider" key
 | 
				
			||||||
 | 
						if p, ok := req.Context().Value("provider").(string); ok {
 | 
				
			||||||
 | 
							return p, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if not found then return an empty string with the corresponding error
 | 
				
			||||||
 | 
						return "", errors.New("you must select a provider")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func storeInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
 | 
					// StoreInSession stores a specified key/value pair in the session.
 | 
				
			||||||
	session, _ := Store.Get(req, key + SessionName)
 | 
					func StoreInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
 | 
				
			||||||
 | 
						session, _ := Store.New(req, SessionName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session.Values[key] = value
 | 
						if err := updateSessionValue(session, key, value); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return session.Save(req, res)
 | 
						return session.Save(req, res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getFromSession(key string, req *http.Request) (string, error) {
 | 
					// GetFromSession retrieves a previously-stored value from the session.
 | 
				
			||||||
	session, _ := Store.Get(req, key + SessionName)
 | 
					// If no value has previously been stored at the specified key, it will return an error.
 | 
				
			||||||
 | 
					func GetFromSession(key string, req *http.Request) (string, error) {
 | 
				
			||||||
	value := session.Values[key]
 | 
						session, _ := Store.Get(req, SessionName)
 | 
				
			||||||
	if value == nil {
 | 
						value, err := getSessionValue(session, key)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
		return "", errors.New("could not find a matching session for this request")
 | 
							return "", errors.New("could not find a matching session for this request")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return value.(string), nil
 | 
						return value, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getSessionValue(session *sessions.Session, key string) (string, error) {
 | 
				
			||||||
 | 
						value := session.Values[key]
 | 
				
			||||||
 | 
						if value == nil {
 | 
				
			||||||
 | 
							return "", fmt.Errorf("could not find a matching session for this request")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rdata := strings.NewReader(value.(string))
 | 
				
			||||||
 | 
						r, err := gzip.NewReader(rdata)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						s, err := ioutil.ReadAll(r)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return string(s), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func updateSessionValue(session *sessions.Session, key, value string) error {
 | 
				
			||||||
 | 
						var b bytes.Buffer
 | 
				
			||||||
 | 
						gz := gzip.NewWriter(&b)
 | 
				
			||||||
 | 
						if _, err := gz.Write([]byte(value)); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := gz.Flush(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := gz.Close(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session.Values[key] = b.String()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -9,9 +9,9 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"github.com/markbates/goth"
 | 
						"github.com/markbates/goth"
 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -26,10 +26,10 @@ const (
 | 
				
			|||||||
// one manually.
 | 
					// one manually.
 | 
				
			||||||
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 | 
					func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 | 
				
			||||||
	p := &Provider{
 | 
						p := &Provider{
 | 
				
			||||||
		ClientKey:           clientKey,
 | 
							ClientKey:    clientKey,
 | 
				
			||||||
		Secret:              secret,
 | 
							Secret:       secret,
 | 
				
			||||||
		CallbackURL:         callbackURL,
 | 
							CallbackURL:  callbackURL,
 | 
				
			||||||
		providerName:        "bitbucket",
 | 
							providerName: "bitbucket",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	p.config = newConfig(p, scopes)
 | 
						p.config = newConfig(p, scopes)
 | 
				
			||||||
	return p
 | 
						return p
 | 
				
			||||||
@@ -125,7 +125,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func userFromReader(reader io.Reader, user *goth.User) error {
 | 
					func userFromReader(reader io.Reader, user *goth.User) error {
 | 
				
			||||||
	u := struct {
 | 
						u := struct {
 | 
				
			||||||
		ID string `json:"uuid"`
 | 
							ID    string `json:"uuid"`
 | 
				
			||||||
		Links struct {
 | 
							Links struct {
 | 
				
			||||||
			Avatar struct {
 | 
								Avatar struct {
 | 
				
			||||||
				URL string `json:"href"`
 | 
									URL string `json:"href"`
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										62
									
								
								vendor/github.com/markbates/goth/providers/dropbox/dropbox.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/markbates/goth/providers/dropbox/dropbox.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -2,21 +2,24 @@
 | 
				
			|||||||
package dropbox
 | 
					package dropbox
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/markbates/goth"
 | 
						"github.com/markbates/goth"
 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	authURL    = "https://www.dropbox.com/1/oauth2/authorize"
 | 
						authURL    = "https://www.dropbox.com/oauth2/authorize"
 | 
				
			||||||
	tokenURL   = "https://api.dropbox.com/1/oauth2/token"
 | 
						tokenURL   = "https://api.dropbox.com/oauth2/token"
 | 
				
			||||||
	accountURL = "https://api.dropbox.com/1/account/info"
 | 
						accountURL = "https://api.dropbox.com/2/users/get_current_account"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Provider is the implementation of `goth.Provider` for accessing Dropbox.
 | 
					// Provider is the implementation of `goth.Provider` for accessing Dropbox.
 | 
				
			||||||
@@ -24,6 +27,7 @@ type Provider struct {
 | 
				
			|||||||
	ClientKey    string
 | 
						ClientKey    string
 | 
				
			||||||
	Secret       string
 | 
						Secret       string
 | 
				
			||||||
	CallbackURL  string
 | 
						CallbackURL  string
 | 
				
			||||||
 | 
						AccountURL   string
 | 
				
			||||||
	HTTPClient   *http.Client
 | 
						HTTPClient   *http.Client
 | 
				
			||||||
	config       *oauth2.Config
 | 
						config       *oauth2.Config
 | 
				
			||||||
	providerName string
 | 
						providerName string
 | 
				
			||||||
@@ -40,10 +44,11 @@ type Session struct {
 | 
				
			|||||||
// create one manually.
 | 
					// create one manually.
 | 
				
			||||||
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 | 
					func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 | 
				
			||||||
	p := &Provider{
 | 
						p := &Provider{
 | 
				
			||||||
		ClientKey:           clientKey,
 | 
							ClientKey:    clientKey,
 | 
				
			||||||
		Secret:              secret,
 | 
							Secret:       secret,
 | 
				
			||||||
		CallbackURL:         callbackURL,
 | 
							CallbackURL:  callbackURL,
 | 
				
			||||||
		providerName:        "dropbox",
 | 
							AccountURL:   accountURL,
 | 
				
			||||||
 | 
							providerName: "dropbox",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	p.config = newConfig(p, scopes)
 | 
						p.config = newConfig(p, scopes)
 | 
				
			||||||
	return p
 | 
						return p
 | 
				
			||||||
@@ -86,7 +91,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 | 
				
			|||||||
		return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
 | 
							return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req, err := http.NewRequest("GET", accountURL, nil)
 | 
						req, err := http.NewRequest("POST", p.AccountURL, nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return user, err
 | 
							return user, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -101,7 +106,17 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 | 
				
			|||||||
		return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, resp.StatusCode)
 | 
							return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, resp.StatusCode)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = userFromReader(resp.Body, &user)
 | 
						bits, err := ioutil.ReadAll(resp.Body)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return user, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return user, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = userFromReader(bytes.NewReader(bits), &user)
 | 
				
			||||||
	return user, err
 | 
						return user, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -161,22 +176,29 @@ func newConfig(p *Provider, scopes []string) *oauth2.Config {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func userFromReader(r io.Reader, user *goth.User) error {
 | 
					func userFromReader(r io.Reader, user *goth.User) error {
 | 
				
			||||||
	u := struct {
 | 
						u := struct {
 | 
				
			||||||
		Name string `json:"display_name"`
 | 
							AccountID string `json:"account_id"`
 | 
				
			||||||
		NameDetails struct {
 | 
							Name      struct {
 | 
				
			||||||
			NickName string `json:"familiar_name"`
 | 
								GivenName   string `json:"given_name"`
 | 
				
			||||||
		} `json:"name_details"`
 | 
								Surname     string `json:"surname"`
 | 
				
			||||||
		Location string `json:"country"`
 | 
								DisplayName string `json:"display_name"`
 | 
				
			||||||
		Email    string `json:"email"`
 | 
							} `json:"name"`
 | 
				
			||||||
 | 
							Country         string `json:"country"`
 | 
				
			||||||
 | 
							Email           string `json:"email"`
 | 
				
			||||||
 | 
							ProfilePhotoURL string `json:"profile_photo_url"`
 | 
				
			||||||
	}{}
 | 
						}{}
 | 
				
			||||||
	err := json.NewDecoder(r).Decode(&u)
 | 
						err := json.NewDecoder(r).Decode(&u)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						user.UserID = u.AccountID // The user's unique Dropbox ID.
 | 
				
			||||||
 | 
						user.FirstName = u.Name.GivenName
 | 
				
			||||||
 | 
						user.LastName = u.Name.Surname
 | 
				
			||||||
 | 
						user.Name = strings.TrimSpace(fmt.Sprintf("%s %s", u.Name.GivenName, u.Name.Surname))
 | 
				
			||||||
 | 
						user.Description = u.Name.DisplayName // Full name plus parenthetical team naem
 | 
				
			||||||
	user.Email = u.Email
 | 
						user.Email = u.Email
 | 
				
			||||||
	user.Name = u.Name
 | 
						user.NickName = u.Email // Email is the dropbox username
 | 
				
			||||||
	user.NickName = u.NameDetails.NickName
 | 
						user.Location = u.Country
 | 
				
			||||||
	user.UserID = u.Email // Dropbox doesn't provide a separate user ID
 | 
						user.AvatarURL = u.ProfilePhotoURL // May be blank
 | 
				
			||||||
	user.Location = u.Location
 | 
					 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								vendor/github.com/markbates/goth/providers/facebook/facebook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/markbates/goth/providers/facebook/facebook.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,12 +11,12 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/markbates/goth"
 | 
					 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"crypto/hmac"
 | 
						"crypto/hmac"
 | 
				
			||||||
	"crypto/sha256"
 | 
						"crypto/sha256"
 | 
				
			||||||
	"encoding/hex"
 | 
						"encoding/hex"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/markbates/goth"
 | 
				
			||||||
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -30,10 +30,10 @@ const (
 | 
				
			|||||||
// one manually.
 | 
					// one manually.
 | 
				
			||||||
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 | 
					func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 | 
				
			||||||
	p := &Provider{
 | 
						p := &Provider{
 | 
				
			||||||
		ClientKey:           clientKey,
 | 
							ClientKey:    clientKey,
 | 
				
			||||||
		Secret:              secret,
 | 
							Secret:       secret,
 | 
				
			||||||
		CallbackURL:         callbackURL,
 | 
							CallbackURL:  callbackURL,
 | 
				
			||||||
		providerName:        "facebook",
 | 
							providerName: "facebook",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	p.config = newConfig(p, scopes)
 | 
						p.config = newConfig(p, scopes)
 | 
				
			||||||
	return p
 | 
						return p
 | 
				
			||||||
@@ -129,7 +129,7 @@ func userFromReader(reader io.Reader, user *goth.User) error {
 | 
				
			|||||||
		FirstName string `json:"first_name"`
 | 
							FirstName string `json:"first_name"`
 | 
				
			||||||
		LastName  string `json:"last_name"`
 | 
							LastName  string `json:"last_name"`
 | 
				
			||||||
		Link      string `json:"link"`
 | 
							Link      string `json:"link"`
 | 
				
			||||||
		Picture struct {
 | 
							Picture   struct {
 | 
				
			||||||
			Data struct {
 | 
								Data struct {
 | 
				
			||||||
				URL string `json:"url"`
 | 
									URL string `json:"url"`
 | 
				
			||||||
			} `json:"data"`
 | 
								} `json:"data"`
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								vendor/github.com/markbates/goth/providers/gitlab/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/markbates/goth/providers/gitlab/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,9 +11,9 @@ import (
 | 
				
			|||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"github.com/markbates/goth"
 | 
						"github.com/markbates/goth"
 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// These vars define the Authentication, Token, and Profile URLS for Gitlab. If
 | 
					// These vars define the Authentication, Token, and Profile URLS for Gitlab. If
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								vendor/github.com/markbates/goth/providers/gplus/gplus.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/markbates/goth/providers/gplus/gplus.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -11,9 +11,9 @@ import (
 | 
				
			|||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"github.com/markbates/goth"
 | 
						"github.com/markbates/goth"
 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -27,10 +27,10 @@ const (
 | 
				
			|||||||
// one manually.
 | 
					// one manually.
 | 
				
			||||||
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 | 
					func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 | 
				
			||||||
	p := &Provider{
 | 
						p := &Provider{
 | 
				
			||||||
		ClientKey:           clientKey,
 | 
							ClientKey:    clientKey,
 | 
				
			||||||
		Secret:              secret,
 | 
							Secret:       secret,
 | 
				
			||||||
		CallbackURL:         callbackURL,
 | 
							CallbackURL:  callbackURL,
 | 
				
			||||||
		providerName:        "gplus",
 | 
							providerName: "gplus",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	p.config = newConfig(p, scopes)
 | 
						p.config = newConfig(p, scopes)
 | 
				
			||||||
	return p
 | 
						return p
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,17 +1,17 @@
 | 
				
			|||||||
package openidConnect
 | 
					package openidConnect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/markbates/goth"
 | 
				
			||||||
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"encoding/base64"
 | 
					 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
					 | 
				
			||||||
	"github.com/markbates/goth"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -89,14 +89,14 @@ func New(clientKey, secret, callbackURL, openIDAutoDiscoveryURL string, scopes .
 | 
				
			|||||||
		Secret:      secret,
 | 
							Secret:      secret,
 | 
				
			||||||
		CallbackURL: callbackURL,
 | 
							CallbackURL: callbackURL,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		UserIdClaims:   []string{subjectClaim},
 | 
							UserIdClaims:    []string{subjectClaim},
 | 
				
			||||||
		NameClaims:     []string{NameClaim},
 | 
							NameClaims:      []string{NameClaim},
 | 
				
			||||||
		NickNameClaims: []string{NicknameClaim, PreferredUsernameClaim},
 | 
							NickNameClaims:  []string{NicknameClaim, PreferredUsernameClaim},
 | 
				
			||||||
		EmailClaims:    []string{EmailClaim},
 | 
							EmailClaims:     []string{EmailClaim},
 | 
				
			||||||
		AvatarURLClaims:[]string{PictureClaim},
 | 
							AvatarURLClaims: []string{PictureClaim},
 | 
				
			||||||
		FirstNameClaims:[]string{GivenNameClaim},
 | 
							FirstNameClaims: []string{GivenNameClaim},
 | 
				
			||||||
		LastNameClaims: []string{FamilyNameClaim},
 | 
							LastNameClaims:  []string{FamilyNameClaim},
 | 
				
			||||||
		LocationClaims: []string{AddressClaim},
 | 
							LocationClaims:  []string{AddressClaim},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		providerName: "openid-connect",
 | 
							providerName: "openid-connect",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								vendor/github.com/markbates/goth/providers/openidConnect/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/markbates/goth/providers/openidConnect/session.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,12 +1,12 @@
 | 
				
			|||||||
package openidConnect
 | 
					package openidConnect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"github.com/markbates/goth"
 | 
						"github.com/markbates/goth"
 | 
				
			||||||
	"encoding/json"
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Session stores data during the auth process with the OpenID Connect provider.
 | 
					// Session stores data during the auth process with the OpenID Connect provider.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								vendor/github.com/markbates/goth/providers/twitter/twitter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/markbates/goth/providers/twitter/twitter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -9,10 +9,11 @@ import (
 | 
				
			|||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/markbates/goth"
 | 
						"github.com/markbates/goth"
 | 
				
			||||||
	"github.com/mrjones/oauth"
 | 
						"github.com/mrjones/oauth"
 | 
				
			||||||
	"golang.org/x/oauth2"
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -30,10 +31,10 @@ var (
 | 
				
			|||||||
// If you'd like to use authenticate instead of authorize, use NewAuthenticate instead.
 | 
					// If you'd like to use authenticate instead of authorize, use NewAuthenticate instead.
 | 
				
			||||||
func New(clientKey, secret, callbackURL string) *Provider {
 | 
					func New(clientKey, secret, callbackURL string) *Provider {
 | 
				
			||||||
	p := &Provider{
 | 
						p := &Provider{
 | 
				
			||||||
		ClientKey:           clientKey,
 | 
							ClientKey:    clientKey,
 | 
				
			||||||
		Secret:              secret,
 | 
							Secret:       secret,
 | 
				
			||||||
		CallbackURL:         callbackURL,
 | 
							CallbackURL:  callbackURL,
 | 
				
			||||||
		providerName:        "twitter",
 | 
							providerName: "twitter",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	p.consumer = newConsumer(p, authorizeURL)
 | 
						p.consumer = newConsumer(p, authorizeURL)
 | 
				
			||||||
	return p
 | 
						return p
 | 
				
			||||||
@@ -43,10 +44,10 @@ func New(clientKey, secret, callbackURL string) *Provider {
 | 
				
			|||||||
// NewAuthenticate uses the authenticate URL instead of the authorize URL.
 | 
					// NewAuthenticate uses the authenticate URL instead of the authorize URL.
 | 
				
			||||||
func NewAuthenticate(clientKey, secret, callbackURL string) *Provider {
 | 
					func NewAuthenticate(clientKey, secret, callbackURL string) *Provider {
 | 
				
			||||||
	p := &Provider{
 | 
						p := &Provider{
 | 
				
			||||||
		ClientKey:           clientKey,
 | 
							ClientKey:    clientKey,
 | 
				
			||||||
		Secret:              secret,
 | 
							Secret:       secret,
 | 
				
			||||||
		CallbackURL:         callbackURL,
 | 
							CallbackURL:  callbackURL,
 | 
				
			||||||
		providerName:        "twitter",
 | 
							providerName: "twitter",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	p.consumer = newConsumer(p, authenticateURL)
 | 
						p.consumer = newConsumer(p, authenticateURL)
 | 
				
			||||||
	return p
 | 
						return p
 | 
				
			||||||
@@ -107,7 +108,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	response, err := p.consumer.Get(
 | 
						response, err := p.consumer.Get(
 | 
				
			||||||
		endpointProfile,
 | 
							endpointProfile,
 | 
				
			||||||
		map[string]string{"include_entities": "false", "skip_status": "true"},
 | 
							map[string]string{"include_entities": "false", "skip_status": "true", "include_email": "true"},
 | 
				
			||||||
		sess.AccessToken)
 | 
							sess.AccessToken)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return user, err
 | 
							return user, err
 | 
				
			||||||
@@ -126,6 +127,9 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	user.Name = user.RawData["name"].(string)
 | 
						user.Name = user.RawData["name"].(string)
 | 
				
			||||||
	user.NickName = user.RawData["screen_name"].(string)
 | 
						user.NickName = user.RawData["screen_name"].(string)
 | 
				
			||||||
 | 
						if user.RawData["email"] != nil {
 | 
				
			||||||
 | 
							user.Email = user.RawData["email"].(string)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	user.Description = user.RawData["description"].(string)
 | 
						user.Description = user.RawData["description"].(string)
 | 
				
			||||||
	user.AvatarURL = user.RawData["profile_image_url"].(string)
 | 
						user.AvatarURL = user.RawData["profile_image_url"].(string)
 | 
				
			||||||
	user.UserID = user.RawData["id_str"].(string)
 | 
						user.UserID = user.RawData["id_str"].(string)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										68
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										68
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							@@ -654,64 +654,74 @@
 | 
				
			|||||||
			"revisionTime": "2017-10-25T03:15:54Z"
 | 
								"revisionTime": "2017-10-25T03:15:54Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "O3KUfEXQPfdQ+tCMpP2RAIRJJqY=",
 | 
								"checksumSHA1": "q9MD1ienC+kmKq5i51oAktQEV1E=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth",
 | 
				
			||||||
			"path": "github.com/markbates/goth",
 | 
								"path": "github.com/markbates/goth",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "MkFKwLV3icyUo4oP0BgEs+7+R1Y=",
 | 
								"checksumSHA1": "FISfgOkoMtn98wglLUvfBTZ6baE=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth/gothic",
 | 
				
			||||||
			"path": "github.com/markbates/goth/gothic",
 | 
								"path": "github.com/markbates/goth/gothic",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "crNSlQADjX6hcxykON2tFCqY4iw=",
 | 
								"checksumSHA1": "pJ+Cws/TU22K6tZ/ALFOvvH1K5U=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth/providers/bitbucket",
 | 
				
			||||||
			"path": "github.com/markbates/goth/providers/bitbucket",
 | 
								"path": "github.com/markbates/goth/providers/bitbucket",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "1Kp4DKkJNVn135Xg8H4a6CFBNy8=",
 | 
								"checksumSHA1": "XsF5HI4240QHbFXbtWWnGgTsoq8=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth/providers/dropbox",
 | 
				
			||||||
			"path": "github.com/markbates/goth/providers/dropbox",
 | 
								"path": "github.com/markbates/goth/providers/dropbox",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "cGs1da29iOBJh5EAH0icKDbN8CA=",
 | 
								"checksumSHA1": "VzbroIA9R00Ig3iGnOlZLU7d4ls=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth/providers/facebook",
 | 
				
			||||||
			"path": "github.com/markbates/goth/providers/facebook",
 | 
								"path": "github.com/markbates/goth/providers/facebook",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "P6nBZ850aaekpOcoXNdRhK86bH8=",
 | 
								"checksumSHA1": "P6nBZ850aaekpOcoXNdRhK86bH8=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth/providers/github",
 | 
				
			||||||
			"path": "github.com/markbates/goth/providers/github",
 | 
								"path": "github.com/markbates/goth/providers/github",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "o/109paSRy9HqV87gR4zUZMMSzs=",
 | 
								"checksumSHA1": "ld488t+yGoTwtmiCSSggEX4fxVk=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth/providers/gitlab",
 | 
				
			||||||
			"path": "github.com/markbates/goth/providers/gitlab",
 | 
								"path": "github.com/markbates/goth/providers/gitlab",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "cX6kR9y94BWFZvI/7UFrsFsP3FQ=",
 | 
								"checksumSHA1": "qXEulD7vnwY9hFrxh91Pm5YrvTM=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth/providers/gplus",
 | 
				
			||||||
			"path": "github.com/markbates/goth/providers/gplus",
 | 
								"path": "github.com/markbates/goth/providers/gplus",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "sMYKhqAUZXM1+T/TjlMhWh8Vveo=",
 | 
								"checksumSHA1": "wsOBzyp4LKDhfCPmX1LLP7T0S3U=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth/providers/openidConnect",
 | 
				
			||||||
			"path": "github.com/markbates/goth/providers/openidConnect",
 | 
								"path": "github.com/markbates/goth/providers/openidConnect",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "1w0V6jYXaGlEtZcMeYTOAAucvgw=",
 | 
								"checksumSHA1": "o6RqMbbE8QNZhNT9TsAIRMPI8tg=",
 | 
				
			||||||
 | 
								"origin": "github.com/go-gitea/goth/providers/twitter",
 | 
				
			||||||
			"path": "github.com/markbates/goth/providers/twitter",
 | 
								"path": "github.com/markbates/goth/providers/twitter",
 | 
				
			||||||
			"revision": "90362394a367f9d77730911973462a53d69662ba",
 | 
								"revision": "3b54d96084a5e11030f19556cf68a6ab5d93ba20",
 | 
				
			||||||
			"revisionTime": "2017-02-23T14:12:10Z"
 | 
								"revisionTime": "2018-03-12T06:32:04Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "61HNjGetaBoMp8HBOpuEZRSim8g=",
 | 
								"checksumSHA1": "61HNjGetaBoMp8HBOpuEZRSim8g=",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user