mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	Compare commits
	
		
			81 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					918d3d96ff | ||
| 
						 | 
					92c91d7d8b | ||
| 
						 | 
					9dc76b2036 | ||
| 
						 | 
					802a4314ef | ||
| 
						 | 
					edd4ab49c8 | ||
| 
						 | 
					55e6cde7c1 | ||
| 
						 | 
					729fa06468 | ||
| 
						 | 
					b228a0aa44 | ||
| 
						 | 
					9e7e11224f | ||
| 
						 | 
					85880b2a0b | ||
| 
						 | 
					211bb911e3 | ||
| 
						 | 
					0554d1dd01 | ||
| 
						 | 
					00e55dd223 | ||
| 
						 | 
					b28c3245cc | ||
| 
						 | 
					ddfb729168 | ||
| 
						 | 
					6ef62e3f8e | ||
| 
						 | 
					2c4f1ed13e | ||
| 
						 | 
					fa3fe1e28a | ||
| 
						 | 
					62f5cf4386 | ||
| 
						 | 
					779d1185e7 | ||
| 
						 | 
					f3d0c76afc | ||
| 
						 | 
					5a4729d5e2 | ||
| 
						 | 
					88a7349375 | ||
| 
						 | 
					c3398906a1 | ||
| 
						 | 
					330fa75945 | ||
| 
						 | 
					55e159ca5f | ||
| 
						 | 
					87074ec860 | ||
| 
						 | 
					1fe5fe419e | ||
| 
						 | 
					67a12b8fac | ||
| 
						 | 
					e861dcbbaf | ||
| 
						 | 
					53c2136a9a | ||
| 
						 | 
					24ebc7e517 | ||
| 
						 | 
					b072907987 | ||
| 
						 | 
					942b0360ad | ||
| 
						 | 
					1ec4913add | ||
| 
						 | 
					16e34025b4 | ||
| 
						 | 
					456d63b6cf | ||
| 
						 | 
					798ac3f85a | ||
| 
						 | 
					460093b952 | ||
| 
						 | 
					38d184d518 | ||
| 
						 | 
					80b55263d8 | ||
| 
						 | 
					32232db55f | ||
| 
						 | 
					cf9b6c281f | ||
| 
						 | 
					a8c6a4a70e | ||
| 
						 | 
					e6050e80f7 | ||
| 
						 | 
					3803b15d76 | ||
| 
						 | 
					af73e1ee35 | ||
| 
						 | 
					4bc8dfc6a3 | ||
| 
						 | 
					33c4e246fe | ||
| 
						 | 
					8ec7beb9f4 | ||
| 
						 | 
					c6eb9b30ae | ||
| 
						 | 
					f75a9b27b0 | ||
| 
						 | 
					2705696d4d | ||
| 
						 | 
					2b68f66e0e | ||
| 
						 | 
					5c7d30cf52 | ||
| 
						 | 
					e520dff4da | ||
| 
						 | 
					2bc759518e | ||
| 
						 | 
					92b2883058 | ||
| 
						 | 
					0ebfc1405c | ||
| 
						 | 
					fd5c67226e | ||
| 
						 | 
					61308825a6 | ||
| 
						 | 
					0cccad04f0 | ||
| 
						 | 
					a0e5c49ac3 | ||
| 
						 | 
					3558310c1f | ||
| 
						 | 
					e99534cfd2 | ||
| 
						 | 
					27acf6165e | ||
| 
						 | 
					f286a28568 | ||
| 
						 | 
					b5c4cb1bde | ||
| 
						 | 
					26b98417ad | ||
| 
						 | 
					8b0cf88c0c | ||
| 
						 | 
					23db3375df | ||
| 
						 | 
					14011d77c9 | ||
| 
						 | 
					5519e26c2f | ||
| 
						 | 
					6feb435867 | ||
| 
						 | 
					61444ed8ca | ||
| 
						 | 
					d770cc9886 | ||
| 
						 | 
					fbaa01998a | ||
| 
						 | 
					ac2ae66ae7 | ||
| 
						 | 
					ed60fe0986 | ||
| 
						 | 
					29e0d62790 | ||
| 
						 | 
					7b464fa67b | 
							
								
								
									
										100
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -4,14 +4,46 @@ 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.14.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.14.0) - 2021-03-19
 | 
					## [1.14.1](https://github.com/go-gitea/gitea/releases/tag/v1.14.1) - 2021-04-15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Fix bug clone wiki (#15499) (#15502)
 | 
				
			||||||
 | 
					  * Github Migration ignore rate limit, if not enabled (#15490) (#15495)
 | 
				
			||||||
 | 
					  * Use subdir for URL (#15446) (#15493)
 | 
				
			||||||
 | 
					  * Query the DB for the hash before inserting in to email_hash (#15457) (#15491)
 | 
				
			||||||
 | 
					  * Ensure review dismissal only dismisses the correct review (#15477) (#15489)
 | 
				
			||||||
 | 
					  * Use index of the supported tags to choose user lang (#15452) (#15488)
 | 
				
			||||||
 | 
					  * Fix wrong file link in code search page (#15466) (#15486)
 | 
				
			||||||
 | 
					  * Quick template fix for built-in SSH server in admin config (#15464) (#15481)
 | 
				
			||||||
 | 
					  * Prevent superfluous response.WriteHeader (#15456) (#15476)
 | 
				
			||||||
 | 
					  * Fix ambiguous argument error on tags (#15432) (#15474)
 | 
				
			||||||
 | 
					  * Add created_unix instead of expiry to migration (#15458) (#15463)
 | 
				
			||||||
 | 
					  * Fix repository search (#15428) (#15442)
 | 
				
			||||||
 | 
					  * Prevent NPE on avatar direct rendering if federated avatars disabled (#15434) (#15439)
 | 
				
			||||||
 | 
					  * Fix wiki clone urls (#15430) (#15431)
 | 
				
			||||||
 | 
					  * Fix dingtalk icon url at webhook (#15417) (#15426)
 | 
				
			||||||
 | 
					  * Standardise icon on projects PR page (#15387) (#15408)
 | 
				
			||||||
 | 
					* ENHANCEMENTS
 | 
				
			||||||
 | 
					  * Add option to skip LFS/attachment files for `dump` (#15407) (#15492)
 | 
				
			||||||
 | 
					  * Clone panel fixes (#15436)
 | 
				
			||||||
 | 
					  * Use semantic dropdown for code search query type (#15276) (#15364)
 | 
				
			||||||
 | 
					* BUILD
 | 
				
			||||||
 | 
					  * Build go-git variants for windows (#15482) (#15487)
 | 
				
			||||||
 | 
					  * Lock down build-images dependencies (Partial #15479) (#15480)
 | 
				
			||||||
 | 
					* MISC
 | 
				
			||||||
 | 
					  * Performance improvement for list pull requests (#15447) (#15500)
 | 
				
			||||||
 | 
					  * Fix potential copy lfs records failure when fork a repository (#15441) (#15485)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.14.0](https://github.com/go-gitea/gitea/releases/tag/v1.14.0) - 2021-04-11
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* SECURITY
 | 
					* SECURITY
 | 
				
			||||||
  * Respect approved email domain list for externally validated user registration (#15014)
 | 
					  * Respect approved email domain list for externally validated user registration (#15014)
 | 
				
			||||||
  * Add reverse proxy configuration support for remote IP address detection (#14959)
 | 
					  * Add reverse proxy configuration support for remote IP address detection (#14959)
 | 
				
			||||||
  * Ensure validation occurs on clone addresses too (#14994)
 | 
					  * Ensure validation occurs on clone addresses too (#14994)
 | 
				
			||||||
  * Fix several render issues highlighted during fuzzing (#14986)
 | 
					 | 
				
			||||||
* BREAKING
 | 
					* BREAKING
 | 
				
			||||||
 | 
					  * Fix double 'push tag' action feed (#15078) (#15083)
 | 
				
			||||||
 | 
					  * Remove possible resource leak (#15067) (#15082)
 | 
				
			||||||
 | 
					  * Handle unauthorized user events gracefully (#15071) (#15074)
 | 
				
			||||||
  * Restore Access.log following migration to Chi framework (Stops access logging of /api/internal routes) (#14475)
 | 
					  * Restore Access.log following migration to Chi framework (Stops access logging of /api/internal routes) (#14475)
 | 
				
			||||||
  * Migrate from Macaron to Chi framework (#14293)
 | 
					  * Migrate from Macaron to Chi framework (#14293)
 | 
				
			||||||
  * Deprecate building for mips (#14174)
 | 
					  * Deprecate building for mips (#14174)
 | 
				
			||||||
@@ -42,6 +74,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Dump github/gitlab/gitea repository data to a local directory and restore to gitea (#12244)
 | 
					  * Dump github/gitlab/gitea repository data to a local directory and restore to gitea (#12244)
 | 
				
			||||||
  * Create Rootless Docker image (#10154)
 | 
					  * Create Rootless Docker image (#10154)
 | 
				
			||||||
* API
 | 
					* API
 | 
				
			||||||
 | 
					  * Speedup issue search (#15179) (#15192)
 | 
				
			||||||
  * Get pull, return head branch sha, even if deleted (#14931)
 | 
					  * Get pull, return head branch sha, even if deleted (#14931)
 | 
				
			||||||
  * Export LFS & TimeTracking function status (#14753)
 | 
					  * Export LFS & TimeTracking function status (#14753)
 | 
				
			||||||
  * Show Gitea version in swagger (#14654)
 | 
					  * Show Gitea version in swagger (#14654)
 | 
				
			||||||
@@ -66,6 +99,20 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Add more filters to issues search (#13514)
 | 
					  * Add more filters to issues search (#13514)
 | 
				
			||||||
  * Add review request api (#11355)
 | 
					  * Add review request api (#11355)
 | 
				
			||||||
* BUGFIXES
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Fix delete nonexist oauth application 500 and prevent deadlock (#15384) (#15396)
 | 
				
			||||||
 | 
					  * Always set the merge base used to merge the commit (#15352) (#15385)
 | 
				
			||||||
 | 
					  * Upgrade to bluemonday 1.0.7 (#15379) (#15380)
 | 
				
			||||||
 | 
					  * Turn RepoRef and RepoAssignment back into func(*Context) (#15372) (#15377)
 | 
				
			||||||
 | 
					  * Move FCGI req.URL.Path fix-up to the FCGI listener (#15292) (#15361)
 | 
				
			||||||
 | 
					  * Show diff on rename with diff changes (#15338) (#15339)
 | 
				
			||||||
 | 
					  * Fix handling of logout event (#15323) (#15337)
 | 
				
			||||||
 | 
					  * Fix CanCreateRepo check (#15311) (#15321)
 | 
				
			||||||
 | 
					  * Fix xorm log stack level (#15285) (#15316)
 | 
				
			||||||
 | 
					  * Fix bug in Wrap (#15302) (#15309)
 | 
				
			||||||
 | 
					  * Drop the event source if we are unauthorized (#15275) (#15280)
 | 
				
			||||||
 | 
					  * Backport Fix graph pagination (#15225)  (#15249)
 | 
				
			||||||
 | 
					  * Prevent NPE in CommentMustAsDiff if no hunk header (#15199) (#15200)
 | 
				
			||||||
 | 
					  * should run RetrieveRepoMetas() for empty pr (#15187) (#15190)
 | 
				
			||||||
  * Move setting to enable closing issue via commit in non default branch to repo settings (#14965)
 | 
					  * Move setting to enable closing issue via commit in non default branch to repo settings (#14965)
 | 
				
			||||||
  * Show correct issues for team dashboard (#14952)
 | 
					  * Show correct issues for team dashboard (#14952)
 | 
				
			||||||
  * Ensure that new pull request button works on forked forks owned by owner of the root and reduce ambiguity (#14932)
 | 
					  * Ensure that new pull request button works on forked forks owned by owner of the root and reduce ambiguity (#14932)
 | 
				
			||||||
@@ -122,6 +169,9 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Use GO variable in go-check target (#13146) (#13147)
 | 
					  * Use GO variable in go-check target (#13146) (#13147)
 | 
				
			||||||
* ENHANCEMENTS
 | 
					* ENHANCEMENTS
 | 
				
			||||||
  * UI style improvements
 | 
					  * UI style improvements
 | 
				
			||||||
 | 
					  * Dropzone styling improvements (#15291) (#15374)
 | 
				
			||||||
 | 
					  * Add size to Save function (#15264) (#15270)
 | 
				
			||||||
 | 
					  * Monaco improvements (#15333) (#15345)
 | 
				
			||||||
  * Support .mailmap in code activity stats (#15009)
 | 
					  * Support .mailmap in code activity stats (#15009)
 | 
				
			||||||
  * Sort release attachments by name (#15008)  
 | 
					  * Sort release attachments by name (#15008)  
 | 
				
			||||||
  * Add ui.explore settings to control view of explore pages (#14094)
 | 
					  * Add ui.explore settings to control view of explore pages (#14094)
 | 
				
			||||||
@@ -267,6 +317,52 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Reduce make verbosity (#13803)
 | 
					  * Reduce make verbosity (#13803)
 | 
				
			||||||
  * Add git command error directory on log (#13194)
 | 
					  * Add git command error directory on log (#13194)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.13.7](https://github.com/go-gitea/gitea/releases/tag/v1.13.7) - 2021-04-07
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* SECURITY
 | 
				
			||||||
 | 
					  * Update to bluemonday-1.0.6 (#15294) (#15298)
 | 
				
			||||||
 | 
					  * Clusterfuzz found another way (#15160) (#15169)
 | 
				
			||||||
 | 
					* API
 | 
				
			||||||
 | 
					  * Fix wrong user returned in API (#15139) (#15150)
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Add 'fonts' into 'KnownPublicEntries' (#15188) (#15317)
 | 
				
			||||||
 | 
					  * Speed up `enry.IsVendor` (#15213) (#15246)
 | 
				
			||||||
 | 
					  * Response 404 for diff/patch of a commit that not exist (#15221) (#15238)
 | 
				
			||||||
 | 
					  * Prevent NPE in CommentMustAsDiff if no hunk header (#15199) (#15201)
 | 
				
			||||||
 | 
					* MISC
 | 
				
			||||||
 | 
					  * Add size to Save function (#15264) (#15271)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.13.6](https://github.com/go-gitea/gitea/releases/tag/v1.13.6) - 2021-03-23
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* SECURITY
 | 
				
			||||||
 | 
					  * Fix bug on avatar middleware (#15124) (#15125)
 | 
				
			||||||
 | 
					  * Fix another clusterfuzz identified issue (#15096) (#15114)
 | 
				
			||||||
 | 
					* API
 | 
				
			||||||
 | 
					  * Fix nil pointer exception in get pull reviews API (#15106)
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Fix markdown rendering in milestone content (#15056) (#15092)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.13.5](https://github.com/go-gitea/gitea/releases/tag/v1.13.5) - 2021-03-21
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* SECURITY
 | 
				
			||||||
 | 
					  * Update to goldmark 1.3.3 (#15059) (#15061)
 | 
				
			||||||
 | 
					  * Another clusterfuzz spotted issue (#15032) (#15034)
 | 
				
			||||||
 | 
					* API
 | 
				
			||||||
 | 
					  * Fix set milestone on PR creation (#14981) (#15001)
 | 
				
			||||||
 | 
					  * Prevent panic when editing forked repos by API (#14960) (#14963)
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Fix bug when upload on web (#15042) (#15055)
 | 
				
			||||||
 | 
					  * Delete Labels & IssueLabels on Repo Delete too (#15039) (#15051)
 | 
				
			||||||
 | 
					  * Fix postgres ID sequences broken by recreate-table (#15015) (#15029)
 | 
				
			||||||
 | 
					  * Fix several render issues (#14986) (#15013)
 | 
				
			||||||
 | 
					  * Make sure sibling images get a link too (#14979) (#14995)
 | 
				
			||||||
 | 
					  * Fix Anchor jumping with escaped query components (#14969) (#14977)
 | 
				
			||||||
 | 
					  * Fix release mail html template (#14976)
 | 
				
			||||||
 | 
					  * Fix excluding more than two labels on issues list (#14962) (#14973)
 | 
				
			||||||
 | 
					  * Don't mark each comment poster as OP (#14971) (#14972)
 | 
				
			||||||
 | 
					  * Add "captcha" to list of reserved usernames (#14930)
 | 
				
			||||||
 | 
					  * Re-enable import local paths after reversion from #13610 (#14925) (#14927)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [1.13.4](https://github.com/go-gitea/gitea/releases/tag/v1.13.4) - 2021-03-07
 | 
					## [1.13.4](https://github.com/go-gitea/gitea/releases/tag/v1.13.4) - 2021-03-07
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* SECURITY
 | 
					* SECURITY
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								Makefile
									
									
									
									
									
								
							@@ -577,6 +577,9 @@ release-windows: | $(DIST_DIRS)
 | 
				
			|||||||
		$(GO) install src.techknowlogick.com/xgo@latest; \
 | 
							$(GO) install src.techknowlogick.com/xgo@latest; \
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
	CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
 | 
						CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
 | 
				
			||||||
 | 
					ifeq (,$(findstring gogit,$(TAGS)))
 | 
				
			||||||
 | 
						CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
ifeq ($(CI),drone)
 | 
					ifeq ($(CI),drone)
 | 
				
			||||||
	cp /build/* $(DIST)/binaries
 | 
						cp /build/* $(DIST)/binaries
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
@@ -699,8 +702,8 @@ generate-gitignore:
 | 
				
			|||||||
	GO111MODULE=on $(GO) run build/generate-gitignores.go
 | 
						GO111MODULE=on $(GO) run build/generate-gitignores.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: generate-images
 | 
					.PHONY: generate-images
 | 
				
			||||||
generate-images:
 | 
					generate-images: | node_modules
 | 
				
			||||||
	npm install --no-save --no-package-lock fabric imagemin-zopfli
 | 
						npm install --no-save --no-package-lock fabric@4 imagemin-zopfli@7
 | 
				
			||||||
	node build/generate-images.js $(TAGS)
 | 
						node build/generate-images.js $(TAGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: generate-manpage
 | 
					.PHONY: generate-manpage
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										16
									
								
								cmd/dump.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								cmd/dump.go
									
									
									
									
									
								
							@@ -129,6 +129,14 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
 | 
				
			|||||||
			Name:  "skip-custom-dir",
 | 
								Name:  "skip-custom-dir",
 | 
				
			||||||
			Usage: "Skip custom directory",
 | 
								Usage: "Skip custom directory",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							cli.BoolFlag{
 | 
				
			||||||
 | 
								Name:  "skip-lfs-data",
 | 
				
			||||||
 | 
								Usage: "Skip LFS data",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							cli.BoolFlag{
 | 
				
			||||||
 | 
								Name:  "skip-attachment-data",
 | 
				
			||||||
 | 
								Usage: "Skip attachment data",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		cli.GenericFlag{
 | 
							cli.GenericFlag{
 | 
				
			||||||
			Name:  "type",
 | 
								Name:  "type",
 | 
				
			||||||
			Value: outputTypeEnum,
 | 
								Value: outputTypeEnum,
 | 
				
			||||||
@@ -214,7 +222,9 @@ func runDump(ctx *cli.Context) error {
 | 
				
			|||||||
			fatal("Failed to include repositories: %v", err)
 | 
								fatal("Failed to include repositories: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := storage.LFS.IterateObjects(func(objPath string, object storage.Object) error {
 | 
							if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") {
 | 
				
			||||||
 | 
								log.Info("Skip dumping LFS data")
 | 
				
			||||||
 | 
							} else if err := storage.LFS.IterateObjects(func(objPath string, object storage.Object) error {
 | 
				
			||||||
			info, err := object.Stat()
 | 
								info, err := object.Stat()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
@@ -313,7 +323,9 @@ func runDump(ctx *cli.Context) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := storage.Attachments.IterateObjects(func(objPath string, object storage.Object) error {
 | 
						if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
 | 
				
			||||||
 | 
							log.Info("Skip dumping attachment data")
 | 
				
			||||||
 | 
						} else if err := storage.Attachments.IterateObjects(func(objPath string, object storage.Object) error {
 | 
				
			||||||
		info, err := object.Stat()
 | 
							info, err := object.Stat()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,9 +9,11 @@ import (
 | 
				
			|||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/http/fcgi"
 | 
						"net/http/fcgi"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/graceful"
 | 
						"code.gitea.io/gitea/modules/graceful"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func runHTTP(network, listenAddr, name string, m http.Handler) error {
 | 
					func runHTTP(network, listenAddr, name string, m http.Handler) error {
 | 
				
			||||||
@@ -48,7 +50,12 @@ func runFCGI(network, listenAddr, name string, m http.Handler) error {
 | 
				
			|||||||
	fcgiServer := graceful.NewServer(network, listenAddr, name)
 | 
						fcgiServer := graceful.NewServer(network, listenAddr, name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
 | 
						err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
 | 
				
			||||||
		return fcgi.Serve(listener, m)
 | 
							return fcgi.Serve(listener, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
								if setting.AppSubURL != "" {
 | 
				
			||||||
 | 
									req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								m.ServeHTTP(resp, req)
 | 
				
			||||||
 | 
							}))
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal("Failed to start FCGI main server: %v", err)
 | 
							log.Fatal("Failed to start FCGI main server: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								go.mod
									
									
									
									
									
								
							@@ -5,7 +5,7 @@ go 1.14
 | 
				
			|||||||
require (
 | 
					require (
 | 
				
			||||||
	cloud.google.com/go v0.78.0 // indirect
 | 
						cloud.google.com/go v0.78.0 // indirect
 | 
				
			||||||
	code.gitea.io/gitea-vet v0.2.1
 | 
						code.gitea.io/gitea-vet v0.2.1
 | 
				
			||||||
	code.gitea.io/sdk/gitea v0.13.2
 | 
						code.gitea.io/sdk/gitea v0.14.0
 | 
				
			||||||
	gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7
 | 
						gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7
 | 
				
			||||||
	gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e
 | 
						gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e
 | 
				
			||||||
	gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e
 | 
						gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e
 | 
				
			||||||
@@ -86,7 +86,7 @@ require (
 | 
				
			|||||||
	github.com/mgechev/revive v1.0.3
 | 
						github.com/mgechev/revive v1.0.3
 | 
				
			||||||
	github.com/mholt/acmez v0.1.3 // indirect
 | 
						github.com/mholt/acmez v0.1.3 // indirect
 | 
				
			||||||
	github.com/mholt/archiver/v3 v3.5.0
 | 
						github.com/mholt/archiver/v3 v3.5.0
 | 
				
			||||||
	github.com/microcosm-cc/bluemonday v1.0.4
 | 
						github.com/microcosm-cc/bluemonday v1.0.7
 | 
				
			||||||
	github.com/miekg/dns v1.1.40 // indirect
 | 
						github.com/miekg/dns v1.1.40 // indirect
 | 
				
			||||||
	github.com/minio/md5-simd v1.1.2 // indirect
 | 
						github.com/minio/md5-simd v1.1.2 // indirect
 | 
				
			||||||
	github.com/minio/minio-go/v7 v7.0.10
 | 
						github.com/minio/minio-go/v7 v7.0.10
 | 
				
			||||||
@@ -128,7 +128,7 @@ require (
 | 
				
			|||||||
	github.com/xanzy/go-gitlab v0.44.0
 | 
						github.com/xanzy/go-gitlab v0.44.0
 | 
				
			||||||
	github.com/xanzy/ssh-agent v0.3.0 // indirect
 | 
						github.com/xanzy/ssh-agent v0.3.0 // indirect
 | 
				
			||||||
	github.com/yohcop/openid-go v1.0.0
 | 
						github.com/yohcop/openid-go v1.0.0
 | 
				
			||||||
	github.com/yuin/goldmark v1.3.2
 | 
						github.com/yuin/goldmark v1.3.3
 | 
				
			||||||
	github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
 | 
						github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
 | 
				
			||||||
	github.com/yuin/goldmark-meta v1.0.0
 | 
						github.com/yuin/goldmark-meta v1.0.0
 | 
				
			||||||
	go.jolheiser.com/hcaptcha v0.0.4
 | 
						go.jolheiser.com/hcaptcha v0.0.4
 | 
				
			||||||
@@ -136,9 +136,9 @@ require (
 | 
				
			|||||||
	go.uber.org/multierr v1.6.0 // indirect
 | 
						go.uber.org/multierr v1.6.0 // indirect
 | 
				
			||||||
	go.uber.org/zap v1.16.0 // indirect
 | 
						go.uber.org/zap v1.16.0 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
 | 
						golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
 | 
				
			||||||
	golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
 | 
						golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
 | 
				
			||||||
	golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93
 | 
						golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93
 | 
				
			||||||
	golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46
 | 
						golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44
 | 
				
			||||||
	golang.org/x/text v0.3.5
 | 
						golang.org/x/text v0.3.5
 | 
				
			||||||
	golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
 | 
						golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
 | 
				
			||||||
	golang.org/x/tools v0.1.0
 | 
						golang.org/x/tools v0.1.0
 | 
				
			||||||
@@ -153,5 +153,3 @@ require (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.2.4
 | 
					replace github.com/hashicorp/go-version => github.com/6543/go-version v1.2.4
 | 
				
			||||||
 | 
					 | 
				
			||||||
replace github.com/microcosm-cc/bluemonday => github.com/lunny/bluemonday v1.0.5-0.20201227154428-ca34796141e8
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								go.sum
									
									
									
									
									
								
							@@ -38,8 +38,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
 | 
				
			|||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 | 
					cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 | 
				
			||||||
code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
 | 
					code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
 | 
				
			||||||
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
 | 
					code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
 | 
				
			||||||
code.gitea.io/sdk/gitea v0.13.2 h1:wAnT/J7Z62q3fJXbgnecoaOBh8CM1Qq0/DakWxiv4yA=
 | 
					code.gitea.io/sdk/gitea v0.14.0 h1:m4J352I3p9+bmJUfS+g0odeQzBY/5OXP91Gv6D4fnJ0=
 | 
				
			||||||
code.gitea.io/sdk/gitea v0.13.2/go.mod h1:lee2y8LeV3kQb2iK+hHlMqoadL4bp27QOkOV/hawLKg=
 | 
					code.gitea.io/sdk/gitea v0.14.0/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs=
 | 
				
			||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
					dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
				
			||||||
gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7 h1:xCVJPY823C8RWpgMabTw2kOglDrg0iS3GcQU6wdwHkU=
 | 
					gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7 h1:xCVJPY823C8RWpgMabTw2kOglDrg0iS3GcQU6wdwHkU=
 | 
				
			||||||
gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7/go.mod h1:AyfTrwtfYN54R/HmVvMYPnSTenH5bVoyh8x6tBluxEA=
 | 
					gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7/go.mod h1:AyfTrwtfYN54R/HmVvMYPnSTenH5bVoyh8x6tBluxEA=
 | 
				
			||||||
@@ -196,8 +196,6 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
 | 
				
			|||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
					github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
				
			||||||
github.com/chi-middleware/proxy v1.1.1 h1:4HaXUp8o2+bhHr1OhVy+VjN0+L7/07JDcn6v7YrTjrQ=
 | 
					github.com/chi-middleware/proxy v1.1.1 h1:4HaXUp8o2+bhHr1OhVy+VjN0+L7/07JDcn6v7YrTjrQ=
 | 
				
			||||||
github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdikvbVJVHv/M+0=
 | 
					github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdikvbVJVHv/M+0=
 | 
				
			||||||
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
 | 
					 | 
				
			||||||
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
 | 
					 | 
				
			||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 | 
					github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 | 
				
			||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 | 
					github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 | 
				
			||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 | 
					github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 | 
				
			||||||
@@ -776,8 +774,6 @@ github.com/libdns/libdns v0.2.0 h1:ewg3ByWrdUrxrje8ChPVMBNcotg7H9LQYg+u5De2RzI=
 | 
				
			|||||||
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
 | 
					github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
 | 
				
			||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 | 
					github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
 | 
				
			||||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
 | 
					github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
 | 
				
			||||||
github.com/lunny/bluemonday v1.0.5-0.20201227154428-ca34796141e8 h1:1omo92DLtxQu6VwVPSZAmduHaK5zssed6cvkHyl1XOg=
 | 
					 | 
				
			||||||
github.com/lunny/bluemonday v1.0.5-0.20201227154428-ca34796141e8/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
 | 
					 | 
				
			||||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY=
 | 
					github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY=
 | 
				
			||||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ=
 | 
					github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ=
 | 
				
			||||||
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
 | 
					github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
 | 
				
			||||||
@@ -834,6 +830,8 @@ github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk=
 | 
				
			|||||||
github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
 | 
					github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
 | 
				
			||||||
github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
 | 
					github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
 | 
				
			||||||
github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=
 | 
					github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=
 | 
				
			||||||
 | 
					github.com/microcosm-cc/bluemonday v1.0.7 h1:6yAQfk4XT+PI/dk1ZeBp1gr3Q2Hd1DR0O3aEyPUJVTE=
 | 
				
			||||||
 | 
					github.com/microcosm-cc/bluemonday v1.0.7/go.mod h1:HOT/6NaBlR0f9XlxD3zolN6Z3N8Lp4pvhp+jLS5ihnI=
 | 
				
			||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 | 
					github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 | 
				
			||||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
 | 
					github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
 | 
				
			||||||
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
 | 
					github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
 | 
				
			||||||
@@ -1145,8 +1143,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 | 
				
			|||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
github.com/yuin/goldmark v1.3.2 h1:YjHC5TgyMmHpicTgEqDN0Q96Xo8K6tLXPnmNOHXCgs0=
 | 
					github.com/yuin/goldmark v1.3.3 h1:37BdQwPx8VOSic8eDSWee6QL9mRpZRm9VJp/QugNrW0=
 | 
				
			||||||
github.com/yuin/goldmark v1.3.2/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 | 
					github.com/yuin/goldmark v1.3.3/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 | 
				
			||||||
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 h1:VWSxtAiQNh3zgHJpdpkpVYjTPqRE3P6UZCOPa1nRDio=
 | 
					github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 h1:VWSxtAiQNh3zgHJpdpkpVYjTPqRE3P6UZCOPa1nRDio=
 | 
				
			||||||
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691/go.mod h1:YLF3kDffRfUH/bTxOxHhV6lxwIB3Vfj91rEwNMS9MXo=
 | 
					github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691/go.mod h1:YLF3kDffRfUH/bTxOxHhV6lxwIB3Vfj91rEwNMS9MXo=
 | 
				
			||||||
github.com/yuin/goldmark-meta v1.0.0 h1:ScsatUIT2gFS6azqzLGUjgOnELsBOxMXerM3ogdJhAM=
 | 
					github.com/yuin/goldmark-meta v1.0.0 h1:ScsatUIT2gFS6azqzLGUjgOnELsBOxMXerM3ogdJhAM=
 | 
				
			||||||
@@ -1321,8 +1319,9 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
 | 
				
			|||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
					golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
					golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
					golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
 | 
					golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
					golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
 | 
				
			||||||
 | 
					golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
@@ -1418,8 +1417,8 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
				
			|||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46 h1:V066+OYJ66oTjnhm4Yrn7SXIwSCiDQJxpBxmvqb1N1c=
 | 
					golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 | 
					golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 | 
				
			||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 | 
				
			||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -239,6 +239,26 @@ func doAPICreatePullRequest(ctx APITestContext, owner, repo, baseBranch, headBra
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func doAPIGetPullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) (api.PullRequest, error) {
 | 
				
			||||||
 | 
						return func(t *testing.T) (api.PullRequest, error) {
 | 
				
			||||||
 | 
							urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d?token=%s",
 | 
				
			||||||
 | 
								owner, repo, index, ctx.Token)
 | 
				
			||||||
 | 
							req := NewRequest(t, http.MethodGet, urlStr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							expected := 200
 | 
				
			||||||
 | 
							if ctx.ExpectedCode != 0 {
 | 
				
			||||||
 | 
								expected = ctx.ExpectedCode
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							resp := ctx.Session.MakeRequest(t, req, expected)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
 | 
							decoder := json.NewDecoder(resp.Body)
 | 
				
			||||||
 | 
							pr := api.PullRequest{}
 | 
				
			||||||
 | 
							err := decoder.Decode(&pr)
 | 
				
			||||||
 | 
							return pr, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
 | 
					func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) {
 | 
				
			||||||
	return func(t *testing.T) {
 | 
						return func(t *testing.T) {
 | 
				
			||||||
		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s",
 | 
							urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d/merge?token=%s",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -92,6 +92,10 @@ func testAPIDeleteOAuth2Application(t *testing.T) {
 | 
				
			|||||||
	session.MakeRequest(t, req, http.StatusNoContent)
 | 
						session.MakeRequest(t, req, http.StatusNoContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	models.AssertNotExistsBean(t, &models.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name})
 | 
						models.AssertNotExistsBean(t, &models.OAuth2Application{UID: oldApp.UID, Name: oldApp.Name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete again will return not found
 | 
				
			||||||
 | 
						req = NewRequest(t, "DELETE", urlStr)
 | 
				
			||||||
 | 
						session.MakeRequest(t, req, http.StatusNotFound)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func testAPIGetOAuth2Application(t *testing.T) {
 | 
					func testAPIGetOAuth2Application(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -122,7 +122,7 @@ func TestGetAttachment(t *testing.T) {
 | 
				
			|||||||
		t.Run(tc.name, func(t *testing.T) {
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
			//Write empty file to be available for response
 | 
								//Write empty file to be available for response
 | 
				
			||||||
			if tc.createFile {
 | 
								if tc.createFile {
 | 
				
			||||||
				_, err := storage.Attachments.Save(models.AttachmentRelativePath(tc.uuid), strings.NewReader("hello world"))
 | 
									_, err := storage.Attachments.Save(models.AttachmentRelativePath(tc.uuid), strings.NewReader("hello world"), -1)
 | 
				
			||||||
				assert.NoError(t, err)
 | 
									assert.NoError(t, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			//Actual test
 | 
								//Actual test
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
package integrations
 | 
					package integrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/hex"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"math/rand"
 | 
						"math/rand"
 | 
				
			||||||
@@ -208,13 +209,13 @@ func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS s
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Request raw paths
 | 
							// Request raw paths
 | 
				
			||||||
		req := NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", little))
 | 
							req := NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", little))
 | 
				
			||||||
		resp := session.MakeRequest(t, req, http.StatusOK)
 | 
							resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 | 
				
			||||||
		assert.Equal(t, littleSize, resp.Body.Len())
 | 
							assert.Equal(t, littleSize, resp.Length)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		setting.CheckLFSVersion()
 | 
							setting.CheckLFSVersion()
 | 
				
			||||||
		if setting.LFS.StartServer {
 | 
							if setting.LFS.StartServer {
 | 
				
			||||||
			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
 | 
								req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
 | 
				
			||||||
			resp = session.MakeRequest(t, req, http.StatusOK)
 | 
								resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
			assert.NotEqual(t, littleSize, resp.Body.Len())
 | 
								assert.NotEqual(t, littleSize, resp.Body.Len())
 | 
				
			||||||
			assert.LessOrEqual(t, resp.Body.Len(), 1024)
 | 
								assert.LessOrEqual(t, resp.Body.Len(), 1024)
 | 
				
			||||||
			if resp.Body.Len() != littleSize && resp.Body.Len() <= 1024 {
 | 
								if resp.Body.Len() != littleSize && resp.Body.Len() <= 1024 {
 | 
				
			||||||
@@ -224,12 +225,12 @@ func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS s
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if !testing.Short() {
 | 
							if !testing.Short() {
 | 
				
			||||||
			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
 | 
								req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
 | 
				
			||||||
			resp = session.MakeRequest(t, req, http.StatusOK)
 | 
								resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 | 
				
			||||||
			assert.Equal(t, bigSize, resp.Body.Len())
 | 
								assert.Equal(t, bigSize, resp.Length)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if setting.LFS.StartServer {
 | 
								if setting.LFS.StartServer {
 | 
				
			||||||
				req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
 | 
									req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
 | 
				
			||||||
				resp = session.MakeRequest(t, req, http.StatusOK)
 | 
									resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
				assert.NotEqual(t, bigSize, resp.Body.Len())
 | 
									assert.NotEqual(t, bigSize, resp.Body.Len())
 | 
				
			||||||
				if resp.Body.Len() != bigSize && resp.Body.Len() <= 1024 {
 | 
									if resp.Body.Len() != bigSize && resp.Body.Len() <= 1024 {
 | 
				
			||||||
					assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
 | 
										assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
 | 
				
			||||||
@@ -450,27 +451,35 @@ func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) fun
 | 
				
			|||||||
		t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr))
 | 
							t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Then get the diff string
 | 
							// Then get the diff string
 | 
				
			||||||
		var diffStr string
 | 
							var diffHash string
 | 
				
			||||||
 | 
							var diffLength int
 | 
				
			||||||
		t.Run("GetDiff", func(t *testing.T) {
 | 
							t.Run("GetDiff", func(t *testing.T) {
 | 
				
			||||||
			req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/pulls/%d.diff", url.PathEscape(baseCtx.Username), url.PathEscape(baseCtx.Reponame), pr.Index))
 | 
								req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/pulls/%d.diff", url.PathEscape(baseCtx.Username), url.PathEscape(baseCtx.Reponame), pr.Index))
 | 
				
			||||||
			resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
 | 
								resp := ctx.Session.MakeRequestNilResponseHashSumRecorder(t, req, http.StatusOK)
 | 
				
			||||||
			diffStr = resp.Body.String()
 | 
								diffHash = string(resp.Hash.Sum(nil))
 | 
				
			||||||
 | 
								diffLength = resp.Length
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Now: Merge the PR & make sure that doesn't break the PR page or change its diff
 | 
							// Now: Merge the PR & make sure that doesn't break the PR page or change its diff
 | 
				
			||||||
		t.Run("MergePR", doAPIMergePullRequest(baseCtx, baseCtx.Username, baseCtx.Reponame, pr.Index))
 | 
							t.Run("MergePR", doAPIMergePullRequest(baseCtx, baseCtx.Username, baseCtx.Reponame, pr.Index))
 | 
				
			||||||
		t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr))
 | 
							t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr))
 | 
				
			||||||
		t.Run("EnsureDiffNoChange", doEnsureDiffNoChange(baseCtx, pr, diffStr))
 | 
							t.Run("CheckPR", func(t *testing.T) {
 | 
				
			||||||
 | 
								oldMergeBase := pr.MergeBase
 | 
				
			||||||
 | 
								pr2, err := doAPIGetPullRequest(baseCtx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t)
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
								assert.Equal(t, oldMergeBase, pr2.MergeBase)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							t.Run("EnsurDiffNoChange", doEnsureDiffNoChange(baseCtx, pr, diffHash, diffLength))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Then: Delete the head branch & make sure that doesn't break the PR page or change its diff
 | 
							// Then: Delete the head branch & make sure that doesn't break the PR page or change its diff
 | 
				
			||||||
		t.Run("DeleteHeadBranch", doBranchDelete(baseCtx, baseCtx.Username, baseCtx.Reponame, headBranch))
 | 
							t.Run("DeleteHeadBranch", doBranchDelete(baseCtx, baseCtx.Username, baseCtx.Reponame, headBranch))
 | 
				
			||||||
		t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr))
 | 
							t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr))
 | 
				
			||||||
		t.Run("EnsureDiffNoChange", doEnsureDiffNoChange(baseCtx, pr, diffStr))
 | 
							t.Run("EnsureDiffNoChange", doEnsureDiffNoChange(baseCtx, pr, diffHash, diffLength))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Delete the head repository & make sure that doesn't break the PR page or change its diff
 | 
							// Delete the head repository & make sure that doesn't break the PR page or change its diff
 | 
				
			||||||
		t.Run("DeleteHeadRepository", doAPIDeleteRepository(ctx))
 | 
							t.Run("DeleteHeadRepository", doAPIDeleteRepository(ctx))
 | 
				
			||||||
		t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr))
 | 
							t.Run("EnsureCanSeePull", doEnsureCanSeePull(baseCtx, pr))
 | 
				
			||||||
		t.Run("EnsureDiffNoChange", doEnsureDiffNoChange(baseCtx, pr, diffStr))
 | 
							t.Run("EnsureDiffNoChange", doEnsureDiffNoChange(baseCtx, pr, diffHash, diffLength))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -514,20 +523,15 @@ func doEnsureCanSeePull(ctx APITestContext, pr api.PullRequest) func(t *testing.
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func doEnsureDiffNoChange(ctx APITestContext, pr api.PullRequest, diffStr string) func(t *testing.T) {
 | 
					func doEnsureDiffNoChange(ctx APITestContext, pr api.PullRequest, diffHash string, diffLength int) func(t *testing.T) {
 | 
				
			||||||
	return func(t *testing.T) {
 | 
						return func(t *testing.T) {
 | 
				
			||||||
		req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/pulls/%d.diff", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), pr.Index))
 | 
							req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/pulls/%d.diff", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), pr.Index))
 | 
				
			||||||
		resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
 | 
							resp := ctx.Session.MakeRequestNilResponseHashSumRecorder(t, req, http.StatusOK)
 | 
				
			||||||
		expectedMaxLen := len(diffStr)
 | 
							actual := string(resp.Hash.Sum(nil))
 | 
				
			||||||
		if expectedMaxLen > 800 {
 | 
							actualLength := resp.Length
 | 
				
			||||||
			expectedMaxLen = 800
 | 
					
 | 
				
			||||||
		}
 | 
							equal := diffHash == actual
 | 
				
			||||||
		actual := resp.Body.String()
 | 
							assert.True(t, equal, "Unexpected change in the diff string: expected hash: %s size: %d but was actually: %s size: %d", hex.EncodeToString([]byte(diffHash)), diffLength, hex.EncodeToString([]byte(actual)), actualLength)
 | 
				
			||||||
		actualMaxLen := len(actual)
 | 
					 | 
				
			||||||
		if actualMaxLen > 800 {
 | 
					 | 
				
			||||||
			actualMaxLen = 800
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		assert.Equal(t, diffStr, actual, "Unexpected change in the diff string: expected: %s but was actually: %s", diffStr[:expectedMaxLen], actual[:actualMaxLen])
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,8 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"database/sql"
 | 
						"database/sql"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"hash"
 | 
				
			||||||
 | 
						"hash/fnv"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/http/cookiejar"
 | 
						"net/http/cookiejar"
 | 
				
			||||||
@@ -58,6 +60,26 @@ func NewNilResponseRecorder() *NilResponseRecorder {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type NilResponseHashSumRecorder struct {
 | 
				
			||||||
 | 
						httptest.ResponseRecorder
 | 
				
			||||||
 | 
						Hash   hash.Hash
 | 
				
			||||||
 | 
						Length int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *NilResponseHashSumRecorder) Write(b []byte) (int, error) {
 | 
				
			||||||
 | 
						_, _ = n.Hash.Write(b)
 | 
				
			||||||
 | 
						n.Length += len(b)
 | 
				
			||||||
 | 
						return len(b), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewRecorder returns an initialized ResponseRecorder.
 | 
				
			||||||
 | 
					func NewNilResponseHashSumRecorder() *NilResponseHashSumRecorder {
 | 
				
			||||||
 | 
						return &NilResponseHashSumRecorder{
 | 
				
			||||||
 | 
							Hash:             fnv.New32(),
 | 
				
			||||||
 | 
							ResponseRecorder: *httptest.NewRecorder(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestMain(m *testing.M) {
 | 
					func TestMain(m *testing.M) {
 | 
				
			||||||
	defer log.Close()
 | 
						defer log.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -284,6 +306,23 @@ func (s *TestSession) MakeRequestNilResponseRecorder(t testing.TB, req *http.Req
 | 
				
			|||||||
	return resp
 | 
						return resp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *TestSession) MakeRequestNilResponseHashSumRecorder(t testing.TB, req *http.Request, expectedStatus int) *NilResponseHashSumRecorder {
 | 
				
			||||||
 | 
						t.Helper()
 | 
				
			||||||
 | 
						baseURL, err := url.Parse(setting.AppURL)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						for _, c := range s.jar.Cookies(baseURL) {
 | 
				
			||||||
 | 
							req.AddCookie(c)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp := MakeRequestNilResponseHashSumRecorder(t, req, expectedStatus)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ch := http.Header{}
 | 
				
			||||||
 | 
						ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
 | 
				
			||||||
 | 
						cr := http.Request{Header: ch}
 | 
				
			||||||
 | 
						s.jar.SetCookies(baseURL, cr.Cookies())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return resp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const userPassword = "password"
 | 
					const userPassword = "password"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var loginSessionCache = make(map[string]*TestSession, 10)
 | 
					var loginSessionCache = make(map[string]*TestSession, 10)
 | 
				
			||||||
@@ -429,6 +468,19 @@ func MakeRequestNilResponseRecorder(t testing.TB, req *http.Request, expectedSta
 | 
				
			|||||||
	return recorder
 | 
						return recorder
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func MakeRequestNilResponseHashSumRecorder(t testing.TB, req *http.Request, expectedStatus int) *NilResponseHashSumRecorder {
 | 
				
			||||||
 | 
						t.Helper()
 | 
				
			||||||
 | 
						recorder := NewNilResponseHashSumRecorder()
 | 
				
			||||||
 | 
						c.ServeHTTP(recorder, req)
 | 
				
			||||||
 | 
						if expectedStatus != NoExpectedStatus {
 | 
				
			||||||
 | 
							if !assert.EqualValues(t, expectedStatus, recorder.Code,
 | 
				
			||||||
 | 
								"Request: %s %s", req.Method, req.URL.String()) {
 | 
				
			||||||
 | 
								logUnexpectedResponse(t, &recorder.ResponseRecorder)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return recorder
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// logUnexpectedResponse logs the contents of an unexpected response.
 | 
					// logUnexpectedResponse logs the contents of an unexpected response.
 | 
				
			||||||
func logUnexpectedResponse(t testing.TB, recorder *httptest.ResponseRecorder) {
 | 
					func logUnexpectedResponse(t testing.TB, recorder *httptest.ResponseRecorder) {
 | 
				
			||||||
	t.Helper()
 | 
						t.Helper()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -382,7 +382,7 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if opts.Date != "" {
 | 
						if opts.Date != "" {
 | 
				
			||||||
		dateLow, err := time.Parse("2006-01-02", opts.Date)
 | 
							dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
 | 
								log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,7 +85,7 @@ func (a *Attachment) LinkedRepository() (*Repository, UnitType, error) {
 | 
				
			|||||||
func NewAttachment(attach *Attachment, buf []byte, file io.Reader) (_ *Attachment, err error) {
 | 
					func NewAttachment(attach *Attachment, buf []byte, file io.Reader) (_ *Attachment, err error) {
 | 
				
			||||||
	attach.UUID = gouuid.New().String()
 | 
						attach.UUID = gouuid.New().String()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size, err := storage.Attachments.Save(attach.RelativePath(), io.MultiReader(bytes.NewReader(buf), file))
 | 
						size, err := storage.Attachments.Save(attach.RelativePath(), io.MultiReader(bytes.NewReader(buf), file), -1)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("Create: %v", err)
 | 
							return nil, fmt.Errorf("Create: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -96,6 +96,11 @@ func HashedAvatarLink(email string) string {
 | 
				
			|||||||
			// we don't care about any DB problem just return the lowerEmail
 | 
								// we don't care about any DB problem just return the lowerEmail
 | 
				
			||||||
			return lowerEmail, nil
 | 
								return lowerEmail, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							has, err := sess.Where("email = ? AND hash = ?", emailHash.Email, emailHash.Hash).Get(new(EmailHash))
 | 
				
			||||||
 | 
							if has || err != nil {
 | 
				
			||||||
 | 
								// Seriously we don't care about any DB problems just return the lowerEmail - we expect the transaction to fail most of the time
 | 
				
			||||||
 | 
								return lowerEmail, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		_, _ = sess.Insert(emailHash)
 | 
							_, _ = sess.Insert(emailHash)
 | 
				
			||||||
		if err := sess.Commit(); err != nil {
 | 
							if err := sess.Commit(); err != nil {
 | 
				
			||||||
			// Seriously we don't care about any DB problems just return the lowerEmail - we expect the transaction to fail most of the time
 | 
								// Seriously we don't care about any DB problems just return the lowerEmail - we expect the transaction to fail most of the time
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -338,7 +338,7 @@ func FixCommentTypeLabelWithEmptyLabel() (int64, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// CountCommentTypeLabelWithOutsideLabels count label comments with outside label
 | 
					// CountCommentTypeLabelWithOutsideLabels count label comments with outside label
 | 
				
			||||||
func CountCommentTypeLabelWithOutsideLabels() (int64, error) {
 | 
					func CountCommentTypeLabelWithOutsideLabels() (int64, error) {
 | 
				
			||||||
	return x.Where("comment.type = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND repository.owner_id != label.org_id))", CommentTypeLabel).
 | 
						return x.Where("comment.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))", CommentTypeLabel).
 | 
				
			||||||
		Table("comment").
 | 
							Table("comment").
 | 
				
			||||||
		Join("inner", "label", "label.id = comment.label_id").
 | 
							Join("inner", "label", "label.id = comment.label_id").
 | 
				
			||||||
		Join("inner", "issue", "issue.id = comment.issue_id ").
 | 
							Join("inner", "issue", "issue.id = comment.issue_id ").
 | 
				
			||||||
@@ -354,8 +354,9 @@ func FixCommentTypeLabelWithOutsideLabels() (int64, error) {
 | 
				
			|||||||
				FROM comment AS com
 | 
									FROM comment AS com
 | 
				
			||||||
					INNER JOIN label ON com.label_id = label.id
 | 
										INNER JOIN label ON com.label_id = label.id
 | 
				
			||||||
					INNER JOIN issue on issue.id = com.issue_id
 | 
										INNER JOIN issue on issue.id = com.issue_id
 | 
				
			||||||
 | 
										INNER JOIN repository ON issue.repo_id = repository.id
 | 
				
			||||||
				WHERE
 | 
									WHERE
 | 
				
			||||||
					com.type = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != repo.owner_id))
 | 
										com.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))
 | 
				
			||||||
	) AS il_too)`, CommentTypeLabel)
 | 
						) AS il_too)`, CommentTypeLabel)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
@@ -366,9 +367,9 @@ func FixCommentTypeLabelWithOutsideLabels() (int64, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// CountIssueLabelWithOutsideLabels count label comments with outside label
 | 
					// CountIssueLabelWithOutsideLabels count label comments with outside label
 | 
				
			||||||
func CountIssueLabelWithOutsideLabels() (int64, error) {
 | 
					func CountIssueLabelWithOutsideLabels() (int64, error) {
 | 
				
			||||||
	return x.Where(builder.Expr("issue.repo_id != label.repo_id OR (label.repo_id = 0 AND repository.owner_id != label.org_id)")).
 | 
						return x.Where(builder.Expr("(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)")).
 | 
				
			||||||
		Table("issue_label").
 | 
							Table("issue_label").
 | 
				
			||||||
		Join("inner", "label", "issue_label.id = label.id ").
 | 
							Join("inner", "label", "issue_label.label_id = label.id ").
 | 
				
			||||||
		Join("inner", "issue", "issue.id = issue_label.issue_id ").
 | 
							Join("inner", "issue", "issue.id = issue_label.issue_id ").
 | 
				
			||||||
		Join("inner", "repository", "issue.repo_id = repository.id").
 | 
							Join("inner", "repository", "issue.repo_id = repository.id").
 | 
				
			||||||
		Count(new(IssueLabel))
 | 
							Count(new(IssueLabel))
 | 
				
			||||||
@@ -380,11 +381,11 @@ func FixIssueLabelWithOutsideLabels() (int64, error) {
 | 
				
			|||||||
		SELECT il_too.id FROM (
 | 
							SELECT il_too.id FROM (
 | 
				
			||||||
			SELECT il_too_too.id
 | 
								SELECT il_too_too.id
 | 
				
			||||||
				FROM issue_label AS il_too_too
 | 
									FROM issue_label AS il_too_too
 | 
				
			||||||
					INNER JOIN label ON il_too_too.id = label.id
 | 
										INNER JOIN label ON il_too_too.label_id = label.id
 | 
				
			||||||
					INNER JOIN issue on issue.id = il_too_too.issue_id
 | 
										INNER JOIN issue on issue.id = il_too_too.issue_id
 | 
				
			||||||
					INNER JOIN repository on repository.id = issue.repo_id
 | 
										INNER JOIN repository on repository.id = issue.repo_id
 | 
				
			||||||
				WHERE
 | 
									WHERE
 | 
				
			||||||
					issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
 | 
										(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
 | 
				
			||||||
	) AS il_too )`)
 | 
						) AS il_too )`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,6 +53,9 @@ func (issues IssueList) loadRepositories(e Engine) ([]*Repository, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, issue := range issues {
 | 
						for _, issue := range issues {
 | 
				
			||||||
		issue.Repo = repoMaps[issue.RepoID]
 | 
							issue.Repo = repoMaps[issue.RepoID]
 | 
				
			||||||
 | 
							if issue.PullRequest != nil {
 | 
				
			||||||
 | 
								issue.PullRequest.BaseRepo = issue.Repo
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return valuesRepository(repoMaps), nil
 | 
						return valuesRepository(repoMaps), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -516,6 +519,11 @@ func (issues IssueList) LoadDiscussComments() error {
 | 
				
			|||||||
	return issues.loadComments(x, builder.Eq{"comment.type": CommentTypeComment})
 | 
						return issues.loadComments(x, builder.Eq{"comment.type": CommentTypeComment})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LoadPullRequests loads pull requests
 | 
				
			||||||
 | 
					func (issues IssueList) LoadPullRequests() error {
 | 
				
			||||||
 | 
						return issues.loadPullRequests(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetApprovalCounts returns a map of issue ID to slice of approval counts
 | 
					// GetApprovalCounts returns a map of issue ID to slice of approval counts
 | 
				
			||||||
// FIXME: only returns official counts due to double counting of non-official approvals
 | 
					// FIXME: only returns official counts due to double counting of non-official approvals
 | 
				
			||||||
func (issues IssueList) GetApprovalCounts() (map[int64][]*ReviewCount, error) {
 | 
					func (issues IssueList) GetApprovalCounts() (map[int64][]*ReviewCount, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,8 @@ func NewXORMLogger(showSQL bool) xormlog.Logger {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const stackLevel = 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Log a message with defined skip and at logging level
 | 
					// Log a message with defined skip and at logging level
 | 
				
			||||||
func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...interface{}) error {
 | 
					func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...interface{}) error {
 | 
				
			||||||
	return l.logger.Log(skip+1, level, format, v...)
 | 
						return l.logger.Log(skip+1, level, format, v...)
 | 
				
			||||||
@@ -33,42 +35,42 @@ func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...inter
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Debug show debug log
 | 
					// Debug show debug log
 | 
				
			||||||
func (l *XORMLogBridge) Debug(v ...interface{}) {
 | 
					func (l *XORMLogBridge) Debug(v ...interface{}) {
 | 
				
			||||||
	_ = l.Log(2, log.DEBUG, fmt.Sprint(v...))
 | 
						_ = l.Log(stackLevel, log.DEBUG, fmt.Sprint(v...))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Debugf show debug log
 | 
					// Debugf show debug log
 | 
				
			||||||
func (l *XORMLogBridge) Debugf(format string, v ...interface{}) {
 | 
					func (l *XORMLogBridge) Debugf(format string, v ...interface{}) {
 | 
				
			||||||
	_ = l.Log(2, log.DEBUG, format, v...)
 | 
						_ = l.Log(stackLevel, log.DEBUG, format, v...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Error show error log
 | 
					// Error show error log
 | 
				
			||||||
func (l *XORMLogBridge) Error(v ...interface{}) {
 | 
					func (l *XORMLogBridge) Error(v ...interface{}) {
 | 
				
			||||||
	_ = l.Log(2, log.ERROR, fmt.Sprint(v...))
 | 
						_ = l.Log(stackLevel, log.ERROR, fmt.Sprint(v...))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Errorf show error log
 | 
					// Errorf show error log
 | 
				
			||||||
func (l *XORMLogBridge) Errorf(format string, v ...interface{}) {
 | 
					func (l *XORMLogBridge) Errorf(format string, v ...interface{}) {
 | 
				
			||||||
	_ = l.Log(2, log.ERROR, format, v...)
 | 
						_ = l.Log(stackLevel, log.ERROR, format, v...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Info show information level log
 | 
					// Info show information level log
 | 
				
			||||||
func (l *XORMLogBridge) Info(v ...interface{}) {
 | 
					func (l *XORMLogBridge) Info(v ...interface{}) {
 | 
				
			||||||
	_ = l.Log(2, log.INFO, fmt.Sprint(v...))
 | 
						_ = l.Log(stackLevel, log.INFO, fmt.Sprint(v...))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Infof show information level log
 | 
					// Infof show information level log
 | 
				
			||||||
func (l *XORMLogBridge) Infof(format string, v ...interface{}) {
 | 
					func (l *XORMLogBridge) Infof(format string, v ...interface{}) {
 | 
				
			||||||
	_ = l.Log(2, log.INFO, format, v...)
 | 
						_ = l.Log(stackLevel, log.INFO, format, v...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Warn show warning log
 | 
					// Warn show warning log
 | 
				
			||||||
func (l *XORMLogBridge) Warn(v ...interface{}) {
 | 
					func (l *XORMLogBridge) Warn(v ...interface{}) {
 | 
				
			||||||
	_ = l.Log(2, log.WARN, fmt.Sprint(v...))
 | 
						_ = l.Log(stackLevel, log.WARN, fmt.Sprint(v...))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Warnf show warnning log
 | 
					// Warnf show warnning log
 | 
				
			||||||
func (l *XORMLogBridge) Warnf(format string, v ...interface{}) {
 | 
					func (l *XORMLogBridge) Warnf(format string, v ...interface{}) {
 | 
				
			||||||
	_ = l.Log(2, log.WARN, format, v...)
 | 
						_ = l.Log(stackLevel, log.WARN, format, v...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Level get logger level
 | 
					// Level get logger level
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,7 @@ func InsertMilestones(ms ...*Milestone) (err error) {
 | 
				
			|||||||
// InsertIssues insert issues to database
 | 
					// InsertIssues insert issues to database
 | 
				
			||||||
func InsertIssues(issues ...*Issue) error {
 | 
					func InsertIssues(issues ...*Issue) error {
 | 
				
			||||||
	sess := x.NewSession()
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
	if err := sess.Begin(); err != nil {
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -194,6 +195,7 @@ func InsertPullRequests(prs ...*PullRequest) error {
 | 
				
			|||||||
// InsertReleases migrates release
 | 
					// InsertReleases migrates release
 | 
				
			||||||
func InsertReleases(rels ...*Release) error {
 | 
					func InsertReleases(rels ...*Release) error {
 | 
				
			||||||
	sess := x.NewSession()
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
	if err := sess.Begin(); err != nil {
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,9 +12,9 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func addSessionTable(x *xorm.Engine) error {
 | 
					func addSessionTable(x *xorm.Engine) error {
 | 
				
			||||||
	type Session struct {
 | 
						type Session struct {
 | 
				
			||||||
		Key         string `xorm:"pk CHAR(16)"`
 | 
							Key    string `xorm:"pk CHAR(16)"`
 | 
				
			||||||
		Data        []byte `xorm:"BLOB"`
 | 
							Data   []byte `xorm:"BLOB"`
 | 
				
			||||||
		CreatedUnix timeutil.TimeStamp
 | 
							Expiry timeutil.TimeStamp
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return x.Sync2(new(Session))
 | 
						return x.Sync2(new(Session))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,11 +48,11 @@ func removeInvalidLabels(x *xorm.Engine) error {
 | 
				
			|||||||
		SELECT il_too.id FROM (
 | 
							SELECT il_too.id FROM (
 | 
				
			||||||
			SELECT il_too_too.id
 | 
								SELECT il_too_too.id
 | 
				
			||||||
				FROM issue_label AS il_too_too
 | 
									FROM issue_label AS il_too_too
 | 
				
			||||||
					INNER JOIN label ON il_too_too.id = label.id
 | 
										INNER JOIN label ON il_too_too.label_id = label.id
 | 
				
			||||||
					INNER JOIN issue on issue.id = il_too_too.issue_id
 | 
										INNER JOIN issue on issue.id = il_too_too.issue_id
 | 
				
			||||||
					INNER JOIN repository on repository.id = issue.repo_id
 | 
										INNER JOIN repository on repository.id = issue.repo_id
 | 
				
			||||||
				WHERE
 | 
									WHERE
 | 
				
			||||||
					issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
 | 
										(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
 | 
				
			||||||
	) AS il_too )`); err != nil {
 | 
						) AS il_too )`); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -65,7 +65,7 @@ func removeInvalidLabels(x *xorm.Engine) error {
 | 
				
			|||||||
					INNER JOIN issue on issue.id = com.issue_id
 | 
										INNER JOIN issue on issue.id = com.issue_id
 | 
				
			||||||
					INNER JOIN repository on repository.id = issue.repo_id
 | 
										INNER JOIN repository on repository.id = issue.repo_id
 | 
				
			||||||
				WHERE
 | 
									WHERE
 | 
				
			||||||
					com.type = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != repository.owner_id))
 | 
										com.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))
 | 
				
			||||||
	) AS il_too)`, 7); err != nil {
 | 
						) AS il_too)`, 7); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -235,7 +235,7 @@ func deleteOAuth2Application(sess *xorm.Session, id, userid int64) error {
 | 
				
			|||||||
	if deleted, err := sess.Delete(&OAuth2Application{ID: id, UID: userid}); err != nil {
 | 
						if deleted, err := sess.Delete(&OAuth2Application{ID: id, UID: userid}); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	} else if deleted == 0 {
 | 
						} else if deleted == 0 {
 | 
				
			||||||
		return fmt.Errorf("cannot find oauth2 application")
 | 
							return ErrOAuthApplicationNotFound{ID: id}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	codes := make([]*OAuth2AuthorizationCode, 0)
 | 
						codes := make([]*OAuth2AuthorizationCode, 0)
 | 
				
			||||||
	// delete correlating auth codes
 | 
						// delete correlating auth codes
 | 
				
			||||||
@@ -261,6 +261,7 @@ func deleteOAuth2Application(sess *xorm.Session, id, userid int64) error {
 | 
				
			|||||||
// DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app.
 | 
					// DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app.
 | 
				
			||||||
func DeleteOAuth2Application(id, userid int64) error {
 | 
					func DeleteOAuth2Application(id, userid int64) error {
 | 
				
			||||||
	sess := x.NewSession()
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
	if err := sess.Begin(); err != nil {
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -406,7 +406,8 @@ func (pr *PullRequest) SetMerged() (bool, error) {
 | 
				
			|||||||
		return false, fmt.Errorf("Issue.changeStatus: %v", err)
 | 
							return false, fmt.Errorf("Issue.changeStatus: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
 | 
						// We need to save all of the data used to compute this merge as it may have already been changed by TestPatch. FIXME: need to set some state to prevent TestPatch from running whilst we are merging.
 | 
				
			||||||
 | 
						if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
 | 
				
			||||||
		return false, fmt.Errorf("Failed to update pr[%d]: %v", pr.ID, err)
 | 
							return false, fmt.Errorf("Failed to update pr[%d]: %v", pr.ID, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"xorm.io/builder"
 | 
						"xorm.io/builder"
 | 
				
			||||||
 | 
						"xorm.io/xorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RepositoryListDefaultPageSize is the default number of repositories
 | 
					// RepositoryListDefaultPageSize is the default number of repositories
 | 
				
			||||||
@@ -363,6 +364,35 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// SearchRepositoryByCondition search repositories by condition
 | 
					// SearchRepositoryByCondition search repositories by condition
 | 
				
			||||||
func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) {
 | 
					func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) {
 | 
				
			||||||
 | 
						sess, count, err := searchRepositoryByCondition(opts, cond)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defaultSize := 50
 | 
				
			||||||
 | 
						if opts.PageSize > 0 {
 | 
				
			||||||
 | 
							defaultSize = opts.PageSize
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repos := make(RepositoryList, 0, defaultSize)
 | 
				
			||||||
 | 
						if err := sess.Find(&repos); err != nil {
 | 
				
			||||||
 | 
							return nil, 0, fmt.Errorf("Repo: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PageSize <= 0 {
 | 
				
			||||||
 | 
							count = int64(len(repos))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if loadAttributes {
 | 
				
			||||||
 | 
							if err := repos.loadAttributes(sess); err != nil {
 | 
				
			||||||
 | 
								return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return repos, count, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func searchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond) (*xorm.Session, int64, error) {
 | 
				
			||||||
	if opts.Page <= 0 {
 | 
						if opts.Page <= 0 {
 | 
				
			||||||
		opts.Page = 1
 | 
							opts.Page = 1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -376,31 +406,24 @@ func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond, loa
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sess := x.NewSession()
 | 
						sess := x.NewSession()
 | 
				
			||||||
	defer sess.Close()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	count, err := sess.
 | 
						var count int64
 | 
				
			||||||
		Where(cond).
 | 
						if opts.PageSize > 0 {
 | 
				
			||||||
		Count(new(Repository))
 | 
							var err error
 | 
				
			||||||
	if err != nil {
 | 
							count, err = sess.
 | 
				
			||||||
		return nil, 0, fmt.Errorf("Count: %v", err)
 | 
								Where(cond).
 | 
				
			||||||
 | 
								Count(new(Repository))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								_ = sess.Close()
 | 
				
			||||||
 | 
								return nil, 0, fmt.Errorf("Count: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repos := make(RepositoryList, 0, opts.PageSize)
 | 
					 | 
				
			||||||
	sess.Where(cond).OrderBy(opts.OrderBy.String())
 | 
						sess.Where(cond).OrderBy(opts.OrderBy.String())
 | 
				
			||||||
	if opts.PageSize > 0 {
 | 
						if opts.PageSize > 0 {
 | 
				
			||||||
		sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
 | 
							sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err = sess.Find(&repos); err != nil {
 | 
						return sess, count, nil
 | 
				
			||||||
		return nil, 0, fmt.Errorf("Repo: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if loadAttributes {
 | 
					 | 
				
			||||||
		if err = repos.loadAttributes(sess); err != nil {
 | 
					 | 
				
			||||||
			return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return repos, count, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
 | 
					// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
 | 
				
			||||||
@@ -456,6 +479,33 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
 | 
				
			|||||||
	return SearchRepository(opts)
 | 
						return SearchRepository(opts)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchRepositoryIDs takes keyword and part of repository name to search,
 | 
				
			||||||
 | 
					// it returns results in given range and number of total results.
 | 
				
			||||||
 | 
					func SearchRepositoryIDs(opts *SearchRepoOptions) ([]int64, int64, error) {
 | 
				
			||||||
 | 
						opts.IncludeDescription = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cond := SearchRepositoryCondition(opts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sess, count, err := searchRepositoryByCondition(opts, cond)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defaultSize := 50
 | 
				
			||||||
 | 
						if opts.PageSize > 0 {
 | 
				
			||||||
 | 
							defaultSize = opts.PageSize
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ids := make([]int64, 0, defaultSize)
 | 
				
			||||||
 | 
						err = sess.Select("id").Table("repository").Find(&ids)
 | 
				
			||||||
 | 
						if opts.PageSize <= 0 {
 | 
				
			||||||
 | 
							count = int64(len(ids))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ids, count, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered.
 | 
					// AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered.
 | 
				
			||||||
func AccessibleRepoIDsQuery(user *User) *builder.Builder {
 | 
					func AccessibleRepoIDsQuery(user *User) *builder.Builder {
 | 
				
			||||||
	// NB: Please note this code needs to still work if user is nil
 | 
						// NB: Please note this code needs to still work if user is nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -330,10 +330,10 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
 | 
				
			|||||||
			SELECT il_too.id FROM (
 | 
								SELECT il_too.id FROM (
 | 
				
			||||||
				SELECT il_too_too.id
 | 
									SELECT il_too_too.id
 | 
				
			||||||
					FROM issue_label AS il_too_too
 | 
										FROM issue_label AS il_too_too
 | 
				
			||||||
						INNER JOIN label ON il_too_too.id = label.id
 | 
											INNER JOIN label ON il_too_too.label_id = label.id
 | 
				
			||||||
						INNER JOIN issue on issue.id = il_too_too.issue_id
 | 
											INNER JOIN issue on issue.id = il_too_too.issue_id
 | 
				
			||||||
					WHERE
 | 
										WHERE
 | 
				
			||||||
						issue.repo_id = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != ?))
 | 
											issue.repo_id = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != ?))
 | 
				
			||||||
		) AS il_too )`, repo.ID, newOwner.ID); err != nil {
 | 
							) AS il_too )`, repo.ID, newOwner.ID); err != nil {
 | 
				
			||||||
			return fmt.Errorf("Unable to remove old org labels: %v", err)
 | 
								return fmt.Errorf("Unable to remove old org labels: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -343,9 +343,9 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
 | 
				
			|||||||
				SELECT com.id
 | 
									SELECT com.id
 | 
				
			||||||
					FROM comment AS com
 | 
										FROM comment AS com
 | 
				
			||||||
						INNER JOIN label ON com.label_id = label.id
 | 
											INNER JOIN label ON com.label_id = label.id
 | 
				
			||||||
						INNER JOIN issue on issue.id = com.issue_id
 | 
											INNER JOIN issue ON issue.id = com.issue_id
 | 
				
			||||||
					WHERE
 | 
										WHERE
 | 
				
			||||||
						com.type = ? AND issue.repo_id = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != ?))
 | 
											com.type = ? AND issue.repo_id = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != ?))
 | 
				
			||||||
		) AS il_too)`, CommentTypeLabel, repo.ID, newOwner.ID); err != nil {
 | 
							) AS il_too)`, CommentTypeLabel, repo.ID, newOwner.ID); err != nil {
 | 
				
			||||||
			return fmt.Errorf("Unable to remove old org label comments: %v", err)
 | 
								return fmt.Errorf("Unable to remove old org label comments: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -566,7 +566,11 @@ func DismissReview(review *Review, isDismiss bool) (err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	review.Dismissed = isDismiss
 | 
						review.Dismissed = isDismiss
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err = x.Cols("dismissed").Update(review)
 | 
						if review.ID == 0 {
 | 
				
			||||||
 | 
							return ErrReviewNotExist{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = x.ID(review.ID).Cols("dismissed").Update(review)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,11 +143,57 @@ func TestGetReviewersByIssueID(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDismissReview(t *testing.T) {
 | 
					func TestDismissReview(t *testing.T) {
 | 
				
			||||||
	review1 := AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
 | 
						assert.NoError(t, PrepareTestDatabase())
 | 
				
			||||||
	review2 := AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
 | 
					
 | 
				
			||||||
	assert.NoError(t, DismissReview(review1, true))
 | 
						rejectReviewExample := AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
 | 
				
			||||||
	assert.NoError(t, DismissReview(review2, true))
 | 
						requestReviewExample := AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
 | 
				
			||||||
	assert.NoError(t, DismissReview(review2, true))
 | 
						approveReviewExample := AssertExistsAndLoadBean(t, &Review{ID: 8}).(*Review)
 | 
				
			||||||
	assert.NoError(t, DismissReview(review2, false))
 | 
						assert.False(t, rejectReviewExample.Dismissed)
 | 
				
			||||||
	assert.NoError(t, DismissReview(review2, false))
 | 
						assert.False(t, requestReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, approveReviewExample.Dismissed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, DismissReview(rejectReviewExample, true))
 | 
				
			||||||
 | 
						rejectReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
 | 
				
			||||||
 | 
						requestReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
 | 
				
			||||||
 | 
						assert.True(t, rejectReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, requestReviewExample.Dismissed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, DismissReview(requestReviewExample, true))
 | 
				
			||||||
 | 
						rejectReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
 | 
				
			||||||
 | 
						requestReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
 | 
				
			||||||
 | 
						assert.True(t, rejectReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, requestReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, approveReviewExample.Dismissed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, DismissReview(requestReviewExample, true))
 | 
				
			||||||
 | 
						rejectReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
 | 
				
			||||||
 | 
						requestReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
 | 
				
			||||||
 | 
						assert.True(t, rejectReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, requestReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, approveReviewExample.Dismissed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, DismissReview(requestReviewExample, false))
 | 
				
			||||||
 | 
						rejectReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
 | 
				
			||||||
 | 
						requestReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
 | 
				
			||||||
 | 
						assert.True(t, rejectReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, requestReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, approveReviewExample.Dismissed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, DismissReview(requestReviewExample, false))
 | 
				
			||||||
 | 
						rejectReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review)
 | 
				
			||||||
 | 
						requestReviewExample = AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review)
 | 
				
			||||||
 | 
						assert.True(t, rejectReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, requestReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, approveReviewExample.Dismissed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, DismissReview(rejectReviewExample, false))
 | 
				
			||||||
 | 
						assert.False(t, rejectReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, requestReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, approveReviewExample.Dismissed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, DismissReview(approveReviewExample, true))
 | 
				
			||||||
 | 
						assert.False(t, rejectReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.False(t, requestReviewExample.Dismissed)
 | 
				
			||||||
 | 
						assert.True(t, approveReviewExample.Dismissed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -239,10 +239,10 @@ func (u *User) GetEmail() string {
 | 
				
			|||||||
	return u.Email
 | 
						return u.Email
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetAllUsers returns a slice of all users found in DB.
 | 
					// GetAllUsers returns a slice of all individual users found in DB.
 | 
				
			||||||
func GetAllUsers() ([]*User, error) {
 | 
					func GetAllUsers() ([]*User, error) {
 | 
				
			||||||
	users := make([]*User, 0)
 | 
						users := make([]*User, 0)
 | 
				
			||||||
	return users, x.OrderBy("id").Find(&users)
 | 
						return users, x.OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsLocal returns true if user login type is LoginPlain.
 | 
					// IsLocal returns true if user login type is LoginPlain.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										70
									
								
								modules/analyze/vendor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								modules/analyze/vendor.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					// Copyright 2021 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 analyze
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-enry/go-enry/v2/data"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var isVendorRegExp *regexp.Regexp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						matchers := data.VendorMatchers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						caretStrings := make([]string, 0, 10)
 | 
				
			||||||
 | 
						caretShareStrings := make([]string, 0, 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						matcherStrings := make([]string, 0, len(matchers))
 | 
				
			||||||
 | 
						for _, matcher := range matchers {
 | 
				
			||||||
 | 
							str := matcher.String()
 | 
				
			||||||
 | 
							if str[0] == '^' {
 | 
				
			||||||
 | 
								caretStrings = append(caretStrings, str[1:])
 | 
				
			||||||
 | 
							} else if str[0:5] == "(^|/)" {
 | 
				
			||||||
 | 
								caretShareStrings = append(caretShareStrings, str[5:])
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								matcherStrings = append(matcherStrings, str)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sort.Strings(caretShareStrings)
 | 
				
			||||||
 | 
						sort.Strings(caretStrings)
 | 
				
			||||||
 | 
						sort.Strings(matcherStrings)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sb := &strings.Builder{}
 | 
				
			||||||
 | 
						sb.WriteString("(?:^(?:")
 | 
				
			||||||
 | 
						sb.WriteString(caretStrings[0])
 | 
				
			||||||
 | 
						for _, matcher := range caretStrings[1:] {
 | 
				
			||||||
 | 
							sb.WriteString(")|(?:")
 | 
				
			||||||
 | 
							sb.WriteString(matcher)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sb.WriteString("))")
 | 
				
			||||||
 | 
						sb.WriteString("|")
 | 
				
			||||||
 | 
						sb.WriteString("(?:(?:^|/)(?:")
 | 
				
			||||||
 | 
						sb.WriteString(caretShareStrings[0])
 | 
				
			||||||
 | 
						for _, matcher := range caretShareStrings[1:] {
 | 
				
			||||||
 | 
							sb.WriteString(")|(?:")
 | 
				
			||||||
 | 
							sb.WriteString(matcher)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sb.WriteString("))")
 | 
				
			||||||
 | 
						sb.WriteString("|")
 | 
				
			||||||
 | 
						sb.WriteString("(?:")
 | 
				
			||||||
 | 
						sb.WriteString(matcherStrings[0])
 | 
				
			||||||
 | 
						for _, matcher := range matcherStrings[1:] {
 | 
				
			||||||
 | 
							sb.WriteString(")|(?:")
 | 
				
			||||||
 | 
							sb.WriteString(matcher)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sb.WriteString(")")
 | 
				
			||||||
 | 
						combined := sb.String()
 | 
				
			||||||
 | 
						isVendorRegExp = regexp.MustCompile(combined)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsVendor returns whether or not path is a vendor path.
 | 
				
			||||||
 | 
					func IsVendor(path string) bool {
 | 
				
			||||||
 | 
						return isVendorRegExp.MatchString(path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								modules/analyze/vendor_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								modules/analyze/vendor_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					// Copyright 2021 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 analyze
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestIsVendor(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							path string
 | 
				
			||||||
 | 
							want bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{"cache/", true},
 | 
				
			||||||
 | 
							{"random/cache/", true},
 | 
				
			||||||
 | 
							{"cache", false},
 | 
				
			||||||
 | 
							{"dependencies/", true},
 | 
				
			||||||
 | 
							{"Dependencies/", true},
 | 
				
			||||||
 | 
							{"dependency/", false},
 | 
				
			||||||
 | 
							{"dist/", true},
 | 
				
			||||||
 | 
							{"dist", false},
 | 
				
			||||||
 | 
							{"random/dist/", true},
 | 
				
			||||||
 | 
							{"random/dist", false},
 | 
				
			||||||
 | 
							{"deps/", true},
 | 
				
			||||||
 | 
							{"configure", true},
 | 
				
			||||||
 | 
							{"a/configure", true},
 | 
				
			||||||
 | 
							{"config.guess", true},
 | 
				
			||||||
 | 
							{"config.guess/", false},
 | 
				
			||||||
 | 
							{".vscode/", true},
 | 
				
			||||||
 | 
							{"doc/_build/", true},
 | 
				
			||||||
 | 
							{"a/docs/_build/", true},
 | 
				
			||||||
 | 
							{"a/dasdocs/_build-vsdoc.js", true},
 | 
				
			||||||
 | 
							{"a/dasdocs/_build-vsdoc.j", false},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.path, func(t *testing.T) {
 | 
				
			||||||
 | 
								if got := IsVendor(tt.path); got != tt.want {
 | 
				
			||||||
 | 
									t.Errorf("IsVendor() = %v, want %v", got, tt.want)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -8,7 +8,6 @@ package context
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
@@ -394,238 +393,231 @@ func RepoIDAssignment() func(ctx *Context) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RepoAssignment returns a middleware to handle repository assignment
 | 
					// RepoAssignment returns a middleware to handle repository assignment
 | 
				
			||||||
func RepoAssignment() func(http.Handler) http.Handler {
 | 
					func RepoAssignment(ctx *Context) {
 | 
				
			||||||
	return func(next http.Handler) http.Handler {
 | 
						var (
 | 
				
			||||||
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
							owner *models.User
 | 
				
			||||||
			var (
 | 
							err   error
 | 
				
			||||||
				owner *models.User
 | 
						)
 | 
				
			||||||
				err   error
 | 
					 | 
				
			||||||
				ctx   = GetContext(req)
 | 
					 | 
				
			||||||
			)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			userName := ctx.Params(":username")
 | 
						userName := ctx.Params(":username")
 | 
				
			||||||
			repoName := ctx.Params(":reponame")
 | 
						repoName := ctx.Params(":reponame")
 | 
				
			||||||
			repoName = strings.TrimSuffix(repoName, ".git")
 | 
						repoName = strings.TrimSuffix(repoName, ".git")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Check if the user is the same as the repository owner
 | 
						// Check if the user is the same as the repository owner
 | 
				
			||||||
			if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
 | 
						if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
 | 
				
			||||||
				owner = ctx.User
 | 
							owner = ctx.User
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							owner, err = models.GetUserByName(userName)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if models.IsErrUserNotExist(err) {
 | 
				
			||||||
 | 
									if ctx.Query("go-get") == "1" {
 | 
				
			||||||
 | 
										EarlyResponseForGoGetMeta(ctx)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									ctx.NotFound("GetUserByName", nil)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				owner, err = models.GetUserByName(userName)
 | 
									ctx.ServerError("GetUserByName", err)
 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					if models.IsErrUserNotExist(err) {
 | 
					 | 
				
			||||||
						if ctx.Query("go-get") == "1" {
 | 
					 | 
				
			||||||
							EarlyResponseForGoGetMeta(ctx)
 | 
					 | 
				
			||||||
							return
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						ctx.NotFound("GetUserByName", nil)
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						ctx.ServerError("GetUserByName", err)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Repo.Owner = owner
 | 
								return
 | 
				
			||||||
			ctx.Data["Username"] = ctx.Repo.Owner.Name
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Repo.Owner = owner
 | 
				
			||||||
 | 
						ctx.Data["Username"] = ctx.Repo.Owner.Name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Get repository.
 | 
						// Get repository.
 | 
				
			||||||
			repo, err := models.GetRepositoryByName(owner.ID, repoName)
 | 
						repo, err := models.GetRepositoryByName(owner.ID, repoName)
 | 
				
			||||||
			if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
				if models.IsErrRepoNotExist(err) {
 | 
							if models.IsErrRepoNotExist(err) {
 | 
				
			||||||
					redirectRepoID, err := models.LookupRepoRedirect(owner.ID, repoName)
 | 
								redirectRepoID, err := models.LookupRepoRedirect(owner.ID, repoName)
 | 
				
			||||||
					if err == nil {
 | 
					 | 
				
			||||||
						RedirectToRepo(ctx, redirectRepoID)
 | 
					 | 
				
			||||||
					} else if models.IsErrRepoRedirectNotExist(err) {
 | 
					 | 
				
			||||||
						if ctx.Query("go-get") == "1" {
 | 
					 | 
				
			||||||
							EarlyResponseForGoGetMeta(ctx)
 | 
					 | 
				
			||||||
							return
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						ctx.NotFound("GetRepositoryByName", nil)
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						ctx.ServerError("LookupRepoRedirect", err)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					ctx.ServerError("GetRepositoryByName", err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			repo.Owner = owner
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			repoAssignment(ctx, repo)
 | 
					 | 
				
			||||||
			if ctx.Written() {
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			ctx.Repo.RepoLink = repo.Link()
 | 
					 | 
				
			||||||
			ctx.Data["RepoLink"] = ctx.Repo.RepoLink
 | 
					 | 
				
			||||||
			ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalTracker)
 | 
					 | 
				
			||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
				ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
 | 
									RedirectToRepo(ctx, redirectRepoID)
 | 
				
			||||||
			}
 | 
								} else if models.IsErrRepoRedirectNotExist(err) {
 | 
				
			||||||
 | 
									if ctx.Query("go-get") == "1" {
 | 
				
			||||||
			ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
 | 
										EarlyResponseForGoGetMeta(ctx)
 | 
				
			||||||
				IncludeTags: true,
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				ctx.ServerError("GetReleaseCountByRepoID", err)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			ctx.Data["NumReleases"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{})
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				ctx.ServerError("GetReleaseCountByRepoID", err)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			ctx.Data["Title"] = owner.Name + "/" + repo.Name
 | 
					 | 
				
			||||||
			ctx.Data["Repository"] = repo
 | 
					 | 
				
			||||||
			ctx.Data["Owner"] = ctx.Repo.Repository.Owner
 | 
					 | 
				
			||||||
			ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
 | 
					 | 
				
			||||||
			ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
 | 
					 | 
				
			||||||
			ctx.Data["RepoOwnerIsOrganization"] = repo.Owner.IsOrganization()
 | 
					 | 
				
			||||||
			ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(models.UnitTypeCode)
 | 
					 | 
				
			||||||
			ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(models.UnitTypeIssues)
 | 
					 | 
				
			||||||
			ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(models.UnitTypePullRequests)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if ctx.Data["CanSignedUserFork"], err = ctx.Repo.Repository.CanUserFork(ctx.User); err != nil {
 | 
					 | 
				
			||||||
				ctx.ServerError("CanUserFork", err)
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			ctx.Data["DisableSSH"] = setting.SSH.Disabled
 | 
					 | 
				
			||||||
			ctx.Data["ExposeAnonSSH"] = setting.SSH.ExposeAnonymous
 | 
					 | 
				
			||||||
			ctx.Data["DisableHTTP"] = setting.Repository.DisableHTTPGit
 | 
					 | 
				
			||||||
			ctx.Data["RepoSearchEnabled"] = setting.Indexer.RepoIndexerEnabled
 | 
					 | 
				
			||||||
			ctx.Data["CloneLink"] = repo.CloneLink()
 | 
					 | 
				
			||||||
			ctx.Data["WikiCloneLink"] = repo.WikiCloneLink()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if ctx.IsSigned {
 | 
					 | 
				
			||||||
				ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID)
 | 
					 | 
				
			||||||
				ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if repo.IsFork {
 | 
					 | 
				
			||||||
				RetrieveBaseRepo(ctx, repo)
 | 
					 | 
				
			||||||
				if ctx.Written() {
 | 
					 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
									ctx.NotFound("GetRepositoryByName", nil)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									ctx.ServerError("LookupRepoRedirect", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.ServerError("GetRepositoryByName", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						repo.Owner = owner
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if repo.IsGenerated() {
 | 
						repoAssignment(ctx, repo)
 | 
				
			||||||
				RetrieveTemplateRepo(ctx, repo)
 | 
						if ctx.Written() {
 | 
				
			||||||
				if ctx.Written() {
 | 
							return
 | 
				
			||||||
					return
 | 
						}
 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Disable everything when the repo is being created
 | 
						ctx.Repo.RepoLink = repo.Link()
 | 
				
			||||||
			if ctx.Repo.Repository.IsBeingCreated() {
 | 
						ctx.Data["RepoLink"] = ctx.Repo.RepoLink
 | 
				
			||||||
				ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
 | 
						ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
 | 
						unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalTracker)
 | 
				
			||||||
			if err != nil {
 | 
						if err == nil {
 | 
				
			||||||
				ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err)
 | 
							ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
 | 
				
			||||||
				return
 | 
						}
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			ctx.Repo.GitRepo = gitRepo
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// We opened it, we should close it
 | 
						ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
 | 
				
			||||||
			defer func() {
 | 
							IncludeTags: true,
 | 
				
			||||||
				// If it's been set to nil then assume someone else has closed it.
 | 
						})
 | 
				
			||||||
				if ctx.Repo.GitRepo != nil {
 | 
						if err != nil {
 | 
				
			||||||
					ctx.Repo.GitRepo.Close()
 | 
							ctx.ServerError("GetReleaseCountByRepoID", err)
 | 
				
			||||||
				}
 | 
							return
 | 
				
			||||||
			}()
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["NumReleases"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.ServerError("GetReleaseCountByRepoID", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Stop at this point when the repo is empty.
 | 
						ctx.Data["Title"] = owner.Name + "/" + repo.Name
 | 
				
			||||||
			if ctx.Repo.Repository.IsEmpty {
 | 
						ctx.Data["Repository"] = repo
 | 
				
			||||||
				ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
 | 
						ctx.Data["Owner"] = ctx.Repo.Repository.Owner
 | 
				
			||||||
				next.ServeHTTP(w, req)
 | 
						ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
 | 
				
			||||||
				return
 | 
						ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
 | 
				
			||||||
			}
 | 
						ctx.Data["RepoOwnerIsOrganization"] = repo.Owner.IsOrganization()
 | 
				
			||||||
 | 
						ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(models.UnitTypeCode)
 | 
				
			||||||
 | 
						ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(models.UnitTypeIssues)
 | 
				
			||||||
 | 
						ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(models.UnitTypePullRequests)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			tags, err := ctx.Repo.GitRepo.GetTags()
 | 
						if ctx.Data["CanSignedUserFork"], err = ctx.Repo.Repository.CanUserFork(ctx.User); err != nil {
 | 
				
			||||||
			if err != nil {
 | 
							ctx.ServerError("CanUserFork", err)
 | 
				
			||||||
				ctx.ServerError("GetTags", err)
 | 
							return
 | 
				
			||||||
				return
 | 
						}
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			ctx.Data["Tags"] = tags
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
 | 
						ctx.Data["DisableSSH"] = setting.SSH.Disabled
 | 
				
			||||||
			if err != nil {
 | 
						ctx.Data["ExposeAnonSSH"] = setting.SSH.ExposeAnonymous
 | 
				
			||||||
				ctx.ServerError("GetBranches", err)
 | 
						ctx.Data["DisableHTTP"] = setting.Repository.DisableHTTPGit
 | 
				
			||||||
				return
 | 
						ctx.Data["RepoSearchEnabled"] = setting.Indexer.RepoIndexerEnabled
 | 
				
			||||||
			}
 | 
						ctx.Data["CloneLink"] = repo.CloneLink()
 | 
				
			||||||
			ctx.Data["Branches"] = brs
 | 
						ctx.Data["WikiCloneLink"] = repo.WikiCloneLink()
 | 
				
			||||||
			ctx.Data["BranchesCount"] = len(brs)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Data["TagName"] = ctx.Repo.TagName
 | 
						if ctx.IsSigned {
 | 
				
			||||||
 | 
							ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID)
 | 
				
			||||||
 | 
							ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// If not branch selected, try default one.
 | 
						if repo.IsFork {
 | 
				
			||||||
			// If default branch doesn't exists, fall back to some other branch.
 | 
							RetrieveBaseRepo(ctx, repo)
 | 
				
			||||||
			if len(ctx.Repo.BranchName) == 0 {
 | 
							if ctx.Written() {
 | 
				
			||||||
				if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
 | 
								return
 | 
				
			||||||
					ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
 | 
							}
 | 
				
			||||||
				} else if len(brs) > 0 {
 | 
						}
 | 
				
			||||||
					ctx.Repo.BranchName = brs[0]
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			ctx.Data["BranchName"] = ctx.Repo.BranchName
 | 
					 | 
				
			||||||
			ctx.Data["CommitID"] = ctx.Repo.CommitID
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// People who have push access or have forked repository can propose a new pull request.
 | 
						if repo.IsGenerated() {
 | 
				
			||||||
			canPush := ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID))
 | 
							RetrieveTemplateRepo(ctx, repo)
 | 
				
			||||||
			canCompare := false
 | 
							if ctx.Written() {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Pull request is allowed if this is a fork repository
 | 
						// Disable everything when the repo is being created
 | 
				
			||||||
			// and base repository accepts pull requests.
 | 
						if ctx.Repo.Repository.IsBeingCreated() {
 | 
				
			||||||
			if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls() {
 | 
							ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
 | 
				
			||||||
				canCompare = true
 | 
							return
 | 
				
			||||||
				ctx.Data["BaseRepo"] = repo.BaseRepo
 | 
						}
 | 
				
			||||||
				ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
 | 
					 | 
				
			||||||
				ctx.Repo.PullRequest.Allowed = canPush
 | 
					 | 
				
			||||||
				ctx.Repo.PullRequest.HeadInfo = ctx.Repo.Owner.Name + ":" + ctx.Repo.BranchName
 | 
					 | 
				
			||||||
			} else if repo.AllowsPulls() {
 | 
					 | 
				
			||||||
				// Or, this is repository accepts pull requests between branches.
 | 
					 | 
				
			||||||
				canCompare = true
 | 
					 | 
				
			||||||
				ctx.Data["BaseRepo"] = repo
 | 
					 | 
				
			||||||
				ctx.Repo.PullRequest.BaseRepo = repo
 | 
					 | 
				
			||||||
				ctx.Repo.PullRequest.Allowed = canPush
 | 
					 | 
				
			||||||
				ctx.Repo.PullRequest.SameRepo = true
 | 
					 | 
				
			||||||
				ctx.Repo.PullRequest.HeadInfo = ctx.Repo.BranchName
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			ctx.Data["CanCompareOrPull"] = canCompare
 | 
					 | 
				
			||||||
			ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if ctx.Repo.Repository.Status == models.RepositoryPendingTransfer {
 | 
						gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
 | 
				
			||||||
				repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository)
 | 
						if err != nil {
 | 
				
			||||||
				if err != nil {
 | 
							ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err)
 | 
				
			||||||
					ctx.ServerError("GetPendingRepositoryTransfer", err)
 | 
							return
 | 
				
			||||||
					return
 | 
						}
 | 
				
			||||||
				}
 | 
						ctx.Repo.GitRepo = gitRepo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if err := repoTransfer.LoadAttributes(); err != nil {
 | 
						// We opened it, we should close it
 | 
				
			||||||
					ctx.ServerError("LoadRecipient", err)
 | 
						defer func() {
 | 
				
			||||||
					return
 | 
							// If it's been set to nil then assume someone else has closed it.
 | 
				
			||||||
				}
 | 
							if ctx.Repo.GitRepo != nil {
 | 
				
			||||||
 | 
								ctx.Repo.GitRepo.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				ctx.Data["RepoTransfer"] = repoTransfer
 | 
						// Stop at this point when the repo is empty.
 | 
				
			||||||
				if ctx.User != nil {
 | 
						if ctx.Repo.Repository.IsEmpty {
 | 
				
			||||||
					ctx.Data["CanUserAcceptTransfer"] = repoTransfer.CanUserAcceptTransfer(ctx.User)
 | 
							ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
 | 
				
			||||||
				}
 | 
							return
 | 
				
			||||||
			}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if ctx.Query("go-get") == "1" {
 | 
						tags, err := ctx.Repo.GitRepo.GetTags()
 | 
				
			||||||
				ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name)
 | 
						if err != nil {
 | 
				
			||||||
				prefix := setting.AppURL + path.Join(owner.Name, repo.Name, "src", "branch", ctx.Repo.BranchName)
 | 
							ctx.ServerError("GetTags", err)
 | 
				
			||||||
				ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
 | 
							return
 | 
				
			||||||
				ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
 | 
						}
 | 
				
			||||||
			}
 | 
						ctx.Data["Tags"] = tags
 | 
				
			||||||
			next.ServeHTTP(w, req)
 | 
					
 | 
				
			||||||
		})
 | 
						brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.ServerError("GetBranches", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["Branches"] = brs
 | 
				
			||||||
 | 
						ctx.Data["BranchesCount"] = len(brs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["TagName"] = ctx.Repo.TagName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If not branch selected, try default one.
 | 
				
			||||||
 | 
						// If default branch doesn't exists, fall back to some other branch.
 | 
				
			||||||
 | 
						if len(ctx.Repo.BranchName) == 0 {
 | 
				
			||||||
 | 
							if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
 | 
				
			||||||
 | 
								ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
 | 
				
			||||||
 | 
							} else if len(brs) > 0 {
 | 
				
			||||||
 | 
								ctx.Repo.BranchName = brs[0]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["BranchName"] = ctx.Repo.BranchName
 | 
				
			||||||
 | 
						ctx.Data["CommitID"] = ctx.Repo.CommitID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// People who have push access or have forked repository can propose a new pull request.
 | 
				
			||||||
 | 
						canPush := ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID))
 | 
				
			||||||
 | 
						canCompare := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Pull request is allowed if this is a fork repository
 | 
				
			||||||
 | 
						// and base repository accepts pull requests.
 | 
				
			||||||
 | 
						if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls() {
 | 
				
			||||||
 | 
							canCompare = true
 | 
				
			||||||
 | 
							ctx.Data["BaseRepo"] = repo.BaseRepo
 | 
				
			||||||
 | 
							ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
 | 
				
			||||||
 | 
							ctx.Repo.PullRequest.Allowed = canPush
 | 
				
			||||||
 | 
							ctx.Repo.PullRequest.HeadInfo = ctx.Repo.Owner.Name + ":" + ctx.Repo.BranchName
 | 
				
			||||||
 | 
						} else if repo.AllowsPulls() {
 | 
				
			||||||
 | 
							// Or, this is repository accepts pull requests between branches.
 | 
				
			||||||
 | 
							canCompare = true
 | 
				
			||||||
 | 
							ctx.Data["BaseRepo"] = repo
 | 
				
			||||||
 | 
							ctx.Repo.PullRequest.BaseRepo = repo
 | 
				
			||||||
 | 
							ctx.Repo.PullRequest.Allowed = canPush
 | 
				
			||||||
 | 
							ctx.Repo.PullRequest.SameRepo = true
 | 
				
			||||||
 | 
							ctx.Repo.PullRequest.HeadInfo = ctx.Repo.BranchName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Data["CanCompareOrPull"] = canCompare
 | 
				
			||||||
 | 
						ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.Repo.Repository.Status == models.RepositoryPendingTransfer {
 | 
				
			||||||
 | 
							repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.ServerError("GetPendingRepositoryTransfer", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err := repoTransfer.LoadAttributes(); err != nil {
 | 
				
			||||||
 | 
								ctx.ServerError("LoadRecipient", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ctx.Data["RepoTransfer"] = repoTransfer
 | 
				
			||||||
 | 
							if ctx.User != nil {
 | 
				
			||||||
 | 
								ctx.Data["CanUserAcceptTransfer"] = repoTransfer.CanUserAcceptTransfer(ctx.User)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.Query("go-get") == "1" {
 | 
				
			||||||
 | 
							ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name)
 | 
				
			||||||
 | 
							prefix := setting.AppURL + path.Join(owner.Name, repo.Name, "src", "branch", ctx.Repo.BranchName)
 | 
				
			||||||
 | 
							ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
 | 
				
			||||||
 | 
							ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -651,7 +643,7 @@ const (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// RepoRef handles repository reference names when the ref name is not
 | 
					// RepoRef handles repository reference names when the ref name is not
 | 
				
			||||||
// explicitly given
 | 
					// explicitly given
 | 
				
			||||||
func RepoRef() func(http.Handler) http.Handler {
 | 
					func RepoRef() func(*Context) {
 | 
				
			||||||
	// since no ref name is explicitly specified, ok to just use branch
 | 
						// since no ref name is explicitly specified, ok to just use branch
 | 
				
			||||||
	return RepoRefByType(RepoRefBranch)
 | 
						return RepoRefByType(RepoRefBranch)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -730,130 +722,125 @@ func getRefName(ctx *Context, pathType RepoRefType) string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// RepoRefByType handles repository reference name for a specific type
 | 
					// RepoRefByType handles repository reference name for a specific type
 | 
				
			||||||
// of repository reference
 | 
					// of repository reference
 | 
				
			||||||
func RepoRefByType(refType RepoRefType) func(http.Handler) http.Handler {
 | 
					func RepoRefByType(refType RepoRefType) func(*Context) {
 | 
				
			||||||
	return func(next http.Handler) http.Handler {
 | 
						return func(ctx *Context) {
 | 
				
			||||||
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
							// Empty repository does not have reference information.
 | 
				
			||||||
			ctx := GetContext(req)
 | 
							if ctx.Repo.Repository.IsEmpty {
 | 
				
			||||||
			// Empty repository does not have reference information.
 | 
								return
 | 
				
			||||||
			if ctx.Repo.Repository.IsEmpty {
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var (
 | 
				
			||||||
 | 
								refName string
 | 
				
			||||||
 | 
								err     error
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ctx.Repo.GitRepo == nil {
 | 
				
			||||||
 | 
								repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
				
			||||||
 | 
								ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									ctx.ServerError("RepoRef Invalid repo "+repoPath, err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								// We opened it, we should close it
 | 
				
			||||||
 | 
								defer func() {
 | 
				
			||||||
 | 
									// If it's been set to nil then assume someone else has closed it.
 | 
				
			||||||
 | 
									if ctx.Repo.GitRepo != nil {
 | 
				
			||||||
 | 
										ctx.Repo.GitRepo.Close()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			var (
 | 
							// Get default branch.
 | 
				
			||||||
				refName string
 | 
							if len(ctx.Params("*")) == 0 {
 | 
				
			||||||
				err     error
 | 
								refName = ctx.Repo.Repository.DefaultBranch
 | 
				
			||||||
			)
 | 
								ctx.Repo.BranchName = refName
 | 
				
			||||||
 | 
								if !ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
				
			||||||
			if ctx.Repo.GitRepo == nil {
 | 
									brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
 | 
				
			||||||
				repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 | 
					 | 
				
			||||||
				ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
 | 
					 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					ctx.ServerError("RepoRef Invalid repo "+repoPath, err)
 | 
										ctx.ServerError("GetBranches", err)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									} else if len(brs) == 0 {
 | 
				
			||||||
 | 
										err = fmt.Errorf("No branches in non-empty repository %s",
 | 
				
			||||||
 | 
											ctx.Repo.GitRepo.Path)
 | 
				
			||||||
 | 
										ctx.ServerError("GetBranches", err)
 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				// We opened it, we should close it
 | 
									refName = brs[0]
 | 
				
			||||||
				defer func() {
 | 
					 | 
				
			||||||
					// If it's been set to nil then assume someone else has closed it.
 | 
					 | 
				
			||||||
					if ctx.Repo.GitRepo != nil {
 | 
					 | 
				
			||||||
						ctx.Repo.GitRepo.Close()
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}()
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									ctx.ServerError("GetBranchCommit", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
 | 
				
			||||||
 | 
								ctx.Repo.IsViewBranch = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								refName = getRefName(ctx, refType)
 | 
				
			||||||
 | 
								ctx.Repo.BranchName = refName
 | 
				
			||||||
 | 
								if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
				
			||||||
 | 
									ctx.Repo.IsViewBranch = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Get default branch.
 | 
					 | 
				
			||||||
			if len(ctx.Params("*")) == 0 {
 | 
					 | 
				
			||||||
				refName = ctx.Repo.Repository.DefaultBranch
 | 
					 | 
				
			||||||
				ctx.Repo.BranchName = refName
 | 
					 | 
				
			||||||
				if !ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
					 | 
				
			||||||
					brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						ctx.ServerError("GetBranches", err)
 | 
					 | 
				
			||||||
						return
 | 
					 | 
				
			||||||
					} else if len(brs) == 0 {
 | 
					 | 
				
			||||||
						err = fmt.Errorf("No branches in non-empty repository %s",
 | 
					 | 
				
			||||||
							ctx.Repo.GitRepo.Path)
 | 
					 | 
				
			||||||
						ctx.ServerError("GetBranches", err)
 | 
					 | 
				
			||||||
						return
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					refName = brs[0]
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
 | 
									ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					ctx.ServerError("GetBranchCommit", err)
 | 
										ctx.ServerError("GetBranchCommit", err)
 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
 | 
									ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
 | 
				
			||||||
				ctx.Repo.IsViewBranch = true
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								} else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
 | 
				
			||||||
 | 
									ctx.Repo.IsViewTag = true
 | 
				
			||||||
 | 
									ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										ctx.ServerError("GetTagCommit", err)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
 | 
				
			||||||
 | 
								} else if len(refName) >= 7 && len(refName) <= 40 {
 | 
				
			||||||
 | 
									ctx.Repo.IsViewCommit = true
 | 
				
			||||||
 | 
									ctx.Repo.CommitID = refName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										ctx.NotFound("GetCommit", err)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									// If short commit ID add canonical link header
 | 
				
			||||||
 | 
									if len(refName) < 40 {
 | 
				
			||||||
 | 
										ctx.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
 | 
				
			||||||
 | 
											util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), refName, ctx.Repo.Commit.ID.String(), 1))))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				refName = getRefName(ctx, refType)
 | 
									ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
 | 
				
			||||||
				ctx.Repo.BranchName = refName
 | 
					 | 
				
			||||||
				if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
					 | 
				
			||||||
					ctx.Repo.IsViewBranch = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						ctx.ServerError("GetBranchCommit", err)
 | 
					 | 
				
			||||||
						return
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				} else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
 | 
					 | 
				
			||||||
					ctx.Repo.IsViewTag = true
 | 
					 | 
				
			||||||
					ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						ctx.ServerError("GetTagCommit", err)
 | 
					 | 
				
			||||||
						return
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
 | 
					 | 
				
			||||||
				} else if len(refName) >= 7 && len(refName) <= 40 {
 | 
					 | 
				
			||||||
					ctx.Repo.IsViewCommit = true
 | 
					 | 
				
			||||||
					ctx.Repo.CommitID = refName
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						ctx.NotFound("GetCommit", err)
 | 
					 | 
				
			||||||
						return
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					// If short commit ID add canonical link header
 | 
					 | 
				
			||||||
					if len(refName) < 40 {
 | 
					 | 
				
			||||||
						ctx.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
 | 
					 | 
				
			||||||
							util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), refName, ctx.Repo.Commit.ID.String(), 1))))
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
 | 
					 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if refType == RepoRefLegacy {
 | 
					 | 
				
			||||||
					// redirect from old URL scheme to new URL scheme
 | 
					 | 
				
			||||||
					ctx.Redirect(path.Join(
 | 
					 | 
				
			||||||
						setting.AppSubURL,
 | 
					 | 
				
			||||||
						strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")),
 | 
					 | 
				
			||||||
						ctx.Repo.BranchNameSubURL(),
 | 
					 | 
				
			||||||
						ctx.Repo.TreePath))
 | 
					 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			ctx.Data["BranchName"] = ctx.Repo.BranchName
 | 
					 | 
				
			||||||
			ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
 | 
					 | 
				
			||||||
			ctx.Data["CommitID"] = ctx.Repo.CommitID
 | 
					 | 
				
			||||||
			ctx.Data["TreePath"] = ctx.Repo.TreePath
 | 
					 | 
				
			||||||
			ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
 | 
					 | 
				
			||||||
			ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
 | 
					 | 
				
			||||||
			ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
 | 
					 | 
				
			||||||
			ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				ctx.ServerError("GetCommitsCount", err)
 | 
					 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			next.ServeHTTP(w, req)
 | 
								if refType == RepoRefLegacy {
 | 
				
			||||||
		})
 | 
									// redirect from old URL scheme to new URL scheme
 | 
				
			||||||
 | 
									ctx.Redirect(path.Join(
 | 
				
			||||||
 | 
										setting.AppSubURL,
 | 
				
			||||||
 | 
										strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")),
 | 
				
			||||||
 | 
										ctx.Repo.BranchNameSubURL(),
 | 
				
			||||||
 | 
										ctx.Repo.TreePath))
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ctx.Data["BranchName"] = ctx.Repo.BranchName
 | 
				
			||||||
 | 
							ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
 | 
				
			||||||
 | 
							ctx.Data["CommitID"] = ctx.Repo.CommitID
 | 
				
			||||||
 | 
							ctx.Data["TreePath"] = ctx.Repo.TreePath
 | 
				
			||||||
 | 
							ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
 | 
				
			||||||
 | 
							ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
 | 
				
			||||||
 | 
							ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
 | 
				
			||||||
 | 
							ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.ServerError("GetCommitsCount", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package context
 | 
					package context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "net/http"
 | 
					import (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ResponseWriter represents a response writer for HTTP
 | 
					// ResponseWriter represents a response writer for HTTP
 | 
				
			||||||
type ResponseWriter interface {
 | 
					type ResponseWriter interface {
 | 
				
			||||||
@@ -60,8 +62,10 @@ func (r *Response) WriteHeader(statusCode int) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		r.beforeExecuted = true
 | 
							r.beforeExecuted = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	r.status = statusCode
 | 
						if r.status == 0 {
 | 
				
			||||||
	r.ResponseWriter.WriteHeader(statusCode)
 | 
							r.status = statusCode
 | 
				
			||||||
 | 
							r.ResponseWriter.WriteHeader(statusCode)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Flush flush cached data
 | 
					// Flush flush cached data
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,18 +85,17 @@ func ToPullReviewCommentList(review *models.Review, doer *models.User) ([]*api.P
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	apiComments := make([]*api.PullReviewComment, 0, len(review.CodeComments))
 | 
						apiComments := make([]*api.PullReviewComment, 0, len(review.CodeComments))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auth := false
 | 
					 | 
				
			||||||
	if doer != nil {
 | 
					 | 
				
			||||||
		auth = doer.IsAdmin || doer.ID == review.ReviewerID
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, lines := range review.CodeComments {
 | 
						for _, lines := range review.CodeComments {
 | 
				
			||||||
		for _, comments := range lines {
 | 
							for _, comments := range lines {
 | 
				
			||||||
			for _, comment := range comments {
 | 
								for _, comment := range comments {
 | 
				
			||||||
 | 
									auth := false
 | 
				
			||||||
 | 
									if doer != nil {
 | 
				
			||||||
 | 
										auth = doer.IsAdmin || doer.ID == comment.Poster.ID
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				apiComment := &api.PullReviewComment{
 | 
									apiComment := &api.PullReviewComment{
 | 
				
			||||||
					ID:           comment.ID,
 | 
										ID:           comment.ID,
 | 
				
			||||||
					Body:         comment.Content,
 | 
										Body:         comment.Content,
 | 
				
			||||||
					Reviewer:     ToUser(review.Reviewer, doer != nil, auth),
 | 
										Reviewer:     ToUser(comment.Poster, doer != nil, auth),
 | 
				
			||||||
					ReviewID:     review.ID,
 | 
										ReviewID:     review.ID,
 | 
				
			||||||
					Created:      comment.CreatedUnix.AsTime(),
 | 
										Created:      comment.CreatedUnix.AsTime(),
 | 
				
			||||||
					Updated:      comment.UpdatedUnix.AsTime(),
 | 
										Updated:      comment.UpdatedUnix.AsTime(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ package git
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@@ -14,32 +15,15 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestBlob_Data(t *testing.T) {
 | 
					func TestBlob_Data(t *testing.T) {
 | 
				
			||||||
	output := `Copyright (c) 2016 The Gitea Authors
 | 
						output := "file2\n"
 | 
				
			||||||
Copyright (c) 2015 The Gogs Authors
 | 
						bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
				
			||||||
 | 
						repo, err := OpenRepository(bareRepo1Path)
 | 
				
			||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
						if !assert.NoError(t, err) {
 | 
				
			||||||
of this software and associated documentation files (the "Software"), to deal
 | 
							t.Fatal()
 | 
				
			||||||
in the Software without restriction, including without limitation the rights
 | 
						}
 | 
				
			||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
					 | 
				
			||||||
copies of the Software, and to permit persons to whom the Software is
 | 
					 | 
				
			||||||
furnished to do so, subject to the following conditions:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The above copyright notice and this permission notice shall be included in
 | 
					 | 
				
			||||||
all copies or substantial portions of the Software.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
					 | 
				
			||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
					 | 
				
			||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
					 | 
				
			||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
					 | 
				
			||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
					 | 
				
			||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
					 | 
				
			||||||
THE SOFTWARE.
 | 
					 | 
				
			||||||
`
 | 
					 | 
				
			||||||
	repo, err := OpenRepository("../../.git")
 | 
					 | 
				
			||||||
	assert.NoError(t, err)
 | 
					 | 
				
			||||||
	defer repo.Close()
 | 
						defer repo.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testBlob, err := repo.GetBlob("a8d4b49dd073a4a38a7e58385eeff7cc52568697")
 | 
						testBlob, err := repo.GetBlob("6c493ff740f9380390d5c9ddef4af18697ac9375")
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r, err := testBlob.DataAsync()
 | 
						r, err := testBlob.DataAsync()
 | 
				
			||||||
@@ -53,13 +37,14 @@ THE SOFTWARE.
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Benchmark_Blob_Data(b *testing.B) {
 | 
					func Benchmark_Blob_Data(b *testing.B) {
 | 
				
			||||||
	repo, err := OpenRepository("../../.git")
 | 
						bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
 | 
				
			||||||
 | 
						repo, err := OpenRepository(bareRepo1Path)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		b.Fatal(err)
 | 
							b.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer repo.Close()
 | 
						defer repo.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testBlob, err := repo.GetBlob("a8d4b49dd073a4a38a7e58385eeff7cc52568697")
 | 
						testBlob, err := repo.GetBlob("6c493ff740f9380390d5c9ddef4af18697ac9375")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		b.Fatal(err)
 | 
							b.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,7 +47,7 @@ func GetRawDiffForFile(repoPath, startCommit, endCommit string, diffType RawDiff
 | 
				
			|||||||
func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diffType RawDiffType, file string, writer io.Writer) error {
 | 
					func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diffType RawDiffType, file string, writer io.Writer) error {
 | 
				
			||||||
	commit, err := repo.GetCommit(endCommit)
 | 
						commit, err := repo.GetCommit(endCommit)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("GetCommit: %v", err)
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fileArgs := make([]string, 0)
 | 
						fileArgs := make([]string, 0)
 | 
				
			||||||
	if len(file) > 0 {
 | 
						if len(file) > 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,14 +21,7 @@ func (repo *Repository) GetBranchCommitID(name string) (string, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetTagCommitID returns last commit ID string of given tag.
 | 
					// GetTagCommitID returns last commit ID string of given tag.
 | 
				
			||||||
func (repo *Repository) GetTagCommitID(name string) (string, error) {
 | 
					func (repo *Repository) GetTagCommitID(name string) (string, error) {
 | 
				
			||||||
	stdout, err := NewCommand("rev-list", "-n", "1", TagPrefix+name).RunInDir(repo.Path)
 | 
						return repo.GetRefCommitID(TagPrefix + name)
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if strings.Contains(err.Error(), "unknown revision or path") {
 | 
					 | 
				
			||||||
			return "", ErrNotExist{name, ""}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return strings.TrimSpace(stdout), nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConvertToSHA1 returns a Hash object from a potential ID string
 | 
					// ConvertToSHA1 returns a Hash object from a potential ID string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	sizes := make(map[string]int64)
 | 
						sizes := make(map[string]int64)
 | 
				
			||||||
	err = tree.Files().ForEach(func(f *object.File) error {
 | 
						err = tree.Files().ForEach(func(f *object.File) error {
 | 
				
			||||||
		if f.Size == 0 || enry.IsVendor(f.Name) || enry.IsDotFile(f.Name) ||
 | 
							if f.Size == 0 || analyze.IsVendor(f.Name) || enry.IsDotFile(f.Name) ||
 | 
				
			||||||
			enry.IsDocumentation(f.Name) || enry.IsConfiguration(f.Name) {
 | 
								enry.IsDocumentation(f.Name) || enry.IsConfiguration(f.Name) {
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,7 +67,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
 | 
				
			|||||||
	for _, f := range entries {
 | 
						for _, f := range entries {
 | 
				
			||||||
		contentBuf.Reset()
 | 
							contentBuf.Reset()
 | 
				
			||||||
		content = contentBuf.Bytes()
 | 
							content = contentBuf.Bytes()
 | 
				
			||||||
		if f.Size() == 0 || enry.IsVendor(f.Name()) || enry.IsDotFile(f.Name()) ||
 | 
							if f.Size() == 0 || analyze.IsVendor(f.Name()) || enry.IsDotFile(f.Name()) ||
 | 
				
			||||||
			enry.IsDocumentation(f.Name()) || enry.IsConfiguration(f.Name()) {
 | 
								enry.IsDocumentation(f.Name()) || enry.IsConfiguration(f.Name()) {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,17 +68,19 @@ func (g *Manager) start() {
 | 
				
			|||||||
	// Set the running state
 | 
						// Set the running state
 | 
				
			||||||
	g.setState(stateRunning)
 | 
						g.setState(stateRunning)
 | 
				
			||||||
	if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip {
 | 
						if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip {
 | 
				
			||||||
 | 
							log.Trace("Skipping SVC check as SKIP_MINWINSVC is set")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Make SVC process
 | 
						// Make SVC process
 | 
				
			||||||
	run := svc.Run
 | 
						run := svc.Run
 | 
				
			||||||
	isInteractive, err := svc.IsWindowsService()
 | 
						isWindowsService, err := svc.IsWindowsService()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("Unable to ascertain if running as an Interactive Session: %v", err)
 | 
							log.Error("Unable to ascertain if running as an Windows Service: %v", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if isInteractive {
 | 
						if !isWindowsService {
 | 
				
			||||||
 | 
							log.Trace("Not running a service ... using the debug SVC manager")
 | 
				
			||||||
		run = debug.Run
 | 
							run = debug.Run
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
@@ -94,38 +96,49 @@ func (g *Manager) Execute(args []string, changes <-chan svc.ChangeRequest, statu
 | 
				
			|||||||
		status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout / time.Millisecond)}
 | 
							status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout / time.Millisecond)}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("Awaiting server start-up")
 | 
				
			||||||
	// Now need to wait for everything to start...
 | 
						// Now need to wait for everything to start...
 | 
				
			||||||
	if !g.awaitServer(setting.StartupTimeout) {
 | 
						if !g.awaitServer(setting.StartupTimeout) {
 | 
				
			||||||
 | 
							log.Trace("... start-up failed ... Stopped")
 | 
				
			||||||
		return false, 1
 | 
							return false, 1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("Sending Running state to SVC")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We need to implement some way of svc.AcceptParamChange/svc.ParamChange
 | 
						// We need to implement some way of svc.AcceptParamChange/svc.ParamChange
 | 
				
			||||||
	status <- svc.Status{
 | 
						status <- svc.Status{
 | 
				
			||||||
		State:   svc.Running,
 | 
							State:   svc.Running,
 | 
				
			||||||
		Accepts: svc.AcceptStop | svc.AcceptShutdown | acceptHammerCode,
 | 
							Accepts: svc.AcceptStop | svc.AcceptShutdown | acceptHammerCode,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("Started")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	waitTime := 30 * time.Second
 | 
						waitTime := 30 * time.Second
 | 
				
			||||||
 | 
					
 | 
				
			||||||
loop:
 | 
					loop:
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
		case <-g.ctx.Done():
 | 
							case <-g.ctx.Done():
 | 
				
			||||||
 | 
								log.Trace("Shutting down")
 | 
				
			||||||
			g.DoGracefulShutdown()
 | 
								g.DoGracefulShutdown()
 | 
				
			||||||
			waitTime += setting.GracefulHammerTime
 | 
								waitTime += setting.GracefulHammerTime
 | 
				
			||||||
			break loop
 | 
								break loop
 | 
				
			||||||
		case <-g.shutdownRequested:
 | 
							case <-g.shutdownRequested:
 | 
				
			||||||
 | 
								log.Trace("Shutting down")
 | 
				
			||||||
			waitTime += setting.GracefulHammerTime
 | 
								waitTime += setting.GracefulHammerTime
 | 
				
			||||||
			break loop
 | 
								break loop
 | 
				
			||||||
		case change := <-changes:
 | 
							case change := <-changes:
 | 
				
			||||||
			switch change.Cmd {
 | 
								switch change.Cmd {
 | 
				
			||||||
			case svc.Interrogate:
 | 
								case svc.Interrogate:
 | 
				
			||||||
 | 
									log.Trace("SVC sent interrogate")
 | 
				
			||||||
				status <- change.CurrentStatus
 | 
									status <- change.CurrentStatus
 | 
				
			||||||
			case svc.Stop, svc.Shutdown:
 | 
								case svc.Stop, svc.Shutdown:
 | 
				
			||||||
 | 
									log.Trace("SVC requested shutdown - shutting down")
 | 
				
			||||||
				g.DoGracefulShutdown()
 | 
									g.DoGracefulShutdown()
 | 
				
			||||||
				waitTime += setting.GracefulHammerTime
 | 
									waitTime += setting.GracefulHammerTime
 | 
				
			||||||
				break loop
 | 
									break loop
 | 
				
			||||||
			case hammerCode:
 | 
								case hammerCode:
 | 
				
			||||||
 | 
									log.Trace("SVC requested hammer - shutting down and hammering immediately")
 | 
				
			||||||
				g.DoGracefulShutdown()
 | 
									g.DoGracefulShutdown()
 | 
				
			||||||
				g.DoImmediateHammer()
 | 
									g.DoImmediateHammer()
 | 
				
			||||||
				break loop
 | 
									break loop
 | 
				
			||||||
@@ -134,6 +147,8 @@ loop:
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("Sending StopPending state to SVC")
 | 
				
			||||||
	status <- svc.Status{
 | 
						status <- svc.Status{
 | 
				
			||||||
		State:    svc.StopPending,
 | 
							State:    svc.StopPending,
 | 
				
			||||||
		WaitHint: uint32(waitTime / time.Millisecond),
 | 
							WaitHint: uint32(waitTime / time.Millisecond),
 | 
				
			||||||
@@ -145,8 +160,10 @@ hammerLoop:
 | 
				
			|||||||
		case change := <-changes:
 | 
							case change := <-changes:
 | 
				
			||||||
			switch change.Cmd {
 | 
								switch change.Cmd {
 | 
				
			||||||
			case svc.Interrogate:
 | 
								case svc.Interrogate:
 | 
				
			||||||
 | 
									log.Trace("SVC sent interrogate")
 | 
				
			||||||
				status <- change.CurrentStatus
 | 
									status <- change.CurrentStatus
 | 
				
			||||||
			case svc.Stop, svc.Shutdown, hammerCmd:
 | 
								case svc.Stop, svc.Shutdown, hammerCmd:
 | 
				
			||||||
 | 
									log.Trace("SVC requested hammer - hammering immediately")
 | 
				
			||||||
				g.DoImmediateHammer()
 | 
									g.DoImmediateHammer()
 | 
				
			||||||
				break hammerLoop
 | 
									break hammerLoop
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
@@ -156,6 +173,8 @@ hammerLoop:
 | 
				
			|||||||
			break hammerLoop
 | 
								break hammerLoop
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Trace("Stopped")
 | 
				
			||||||
	return false, 0
 | 
						return false, 0
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -178,7 +178,7 @@ func NewBleveIndexer(indexDir string) (*BleveIndexer, bool, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (b *BleveIndexer) addUpdate(batchWriter *io.PipeWriter, batchReader *bufio.Reader, commitSha string, update fileUpdate, repo *models.Repository, batch rupture.FlushingBatch) error {
 | 
					func (b *BleveIndexer) addUpdate(batchWriter *io.PipeWriter, batchReader *bufio.Reader, commitSha string, update fileUpdate, repo *models.Repository, batch rupture.FlushingBatch) error {
 | 
				
			||||||
	// Ignore vendored files in code search
 | 
						// Ignore vendored files in code search
 | 
				
			||||||
	if setting.Indexer.ExcludeVendored && enry.IsVendor(update.Filename) {
 | 
						if setting.Indexer.ExcludeVendored && analyze.IsVendor(update.Filename) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -177,7 +177,7 @@ func (b *ElasticSearchIndexer) init() (bool, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (b *ElasticSearchIndexer) addUpdate(batchWriter *io.PipeWriter, batchReader *bufio.Reader, sha string, update fileUpdate, repo *models.Repository) ([]elastic.BulkableRequest, error) {
 | 
					func (b *ElasticSearchIndexer) addUpdate(batchWriter *io.PipeWriter, batchReader *bufio.Reader, sha string, update fileUpdate, repo *models.Repository) ([]elastic.BulkableRequest, error) {
 | 
				
			||||||
	// Ignore vendored files in code search
 | 
						// Ignore vendored files in code search
 | 
				
			||||||
	if setting.Indexer.ExcludeVendored && enry.IsVendor(update.Filename) {
 | 
						if setting.Indexer.ExcludeVendored && analyze.IsVendor(update.Filename) {
 | 
				
			||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,24 +44,13 @@ type ContentStore struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Get takes a Meta object and retrieves the content from the store, returning
 | 
					// Get takes a Meta object and retrieves the content from the store, returning
 | 
				
			||||||
// it as an io.Reader. If fromByte > 0, the reader starts from that byte
 | 
					// it as an io.ReadSeekCloser.
 | 
				
			||||||
func (s *ContentStore) Get(meta *models.LFSMetaObject, fromByte int64) (io.ReadCloser, error) {
 | 
					func (s *ContentStore) Get(meta *models.LFSMetaObject) (storage.Object, error) {
 | 
				
			||||||
	f, err := s.Open(meta.RelativePath())
 | 
						f, err := s.Open(meta.RelativePath())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("Whilst trying to read LFS OID[%s]: Unable to open Error: %v", meta.Oid, err)
 | 
							log.Error("Whilst trying to read LFS OID[%s]: Unable to open Error: %v", meta.Oid, err)
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if fromByte > 0 {
 | 
					 | 
				
			||||||
		if fromByte >= meta.Size {
 | 
					 | 
				
			||||||
			return nil, ErrRangeNotSatisfiable{
 | 
					 | 
				
			||||||
				FromByte: fromByte,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		_, err = f.Seek(fromByte, io.SeekStart)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Error("Whilst trying to read LFS OID[%s]: Unable to seek to %d Error: %v", meta.Oid, fromByte, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return f, err
 | 
						return f, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -74,7 +63,7 @@ func (s *ContentStore) Put(meta *models.LFSMetaObject, r io.Reader) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// now pass the wrapped reader to Save - if there is a size mismatch or hash mismatch then
 | 
						// now pass the wrapped reader to Save - if there is a size mismatch or hash mismatch then
 | 
				
			||||||
	// the errors returned by the newHashingReader should percolate up to here
 | 
						// the errors returned by the newHashingReader should percolate up to here
 | 
				
			||||||
	written, err := s.Save(p, wrappedRd)
 | 
						written, err := s.Save(p, wrappedRd, meta.Size)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("Whilst putting LFS OID[%s]: Failed to copy to tmpPath: %s Error: %v", meta.Oid, p, err)
 | 
							log.Error("Whilst putting LFS OID[%s]: Failed to copy to tmpPath: %s Error: %v", meta.Oid, p, err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,5 +67,5 @@ func IsPointerFile(buf *[]byte) *models.LFSMetaObject {
 | 
				
			|||||||
// ReadMetaObject will read a models.LFSMetaObject and return a reader
 | 
					// ReadMetaObject will read a models.LFSMetaObject and return a reader
 | 
				
			||||||
func ReadMetaObject(meta *models.LFSMetaObject) (io.ReadCloser, error) {
 | 
					func ReadMetaObject(meta *models.LFSMetaObject) (io.ReadCloser, error) {
 | 
				
			||||||
	contentStore := &ContentStore{ObjectStorage: storage.LFS}
 | 
						contentStore := &ContentStore{ObjectStorage: storage.LFS}
 | 
				
			||||||
	return contentStore.Get(meta, 0)
 | 
						return contentStore.Get(meta)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -175,6 +175,11 @@ func getContentHandler(ctx *context.Context) {
 | 
				
			|||||||
			statusCode = 206
 | 
								statusCode = 206
 | 
				
			||||||
			fromByte, _ = strconv.ParseInt(match[1], 10, 32)
 | 
								fromByte, _ = strconv.ParseInt(match[1], 10, 32)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if fromByte >= meta.Size {
 | 
				
			||||||
 | 
									writeStatus(ctx, http.StatusRequestedRangeNotSatisfiable)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if match[2] != "" {
 | 
								if match[2] != "" {
 | 
				
			||||||
				_toByte, _ := strconv.ParseInt(match[2], 10, 32)
 | 
									_toByte, _ := strconv.ParseInt(match[2], 10, 32)
 | 
				
			||||||
				if _toByte >= fromByte && _toByte < toByte {
 | 
									if _toByte >= fromByte && _toByte < toByte {
 | 
				
			||||||
@@ -188,18 +193,24 @@ func getContentHandler(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	contentStore := &ContentStore{ObjectStorage: storage.LFS}
 | 
						contentStore := &ContentStore{ObjectStorage: storage.LFS}
 | 
				
			||||||
	content, err := contentStore.Get(meta, fromByte)
 | 
						content, err := contentStore.Get(meta)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if IsErrRangeNotSatisfiable(err) {
 | 
							// Errors are logged in contentStore.Get
 | 
				
			||||||
			writeStatus(ctx, http.StatusRequestedRangeNotSatisfiable)
 | 
							writeStatus(ctx, http.StatusNotFound)
 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			// Errors are logged in contentStore.Get
 | 
					 | 
				
			||||||
			writeStatus(ctx, 404)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer content.Close()
 | 
						defer content.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if fromByte > 0 {
 | 
				
			||||||
 | 
							_, err = content.Seek(fromByte, io.SeekStart)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Error("Whilst trying to read LFS OID[%s]: Unable to seek to %d Error: %v", meta.Oid, fromByte, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								writeStatus(ctx, http.StatusInternalServerError)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	contentLength := toByte + 1 - fromByte
 | 
						contentLength := toByte + 1 - fromByte
 | 
				
			||||||
	ctx.Resp.Header().Set("Content-Length", strconv.FormatInt(contentLength, 10))
 | 
						ctx.Resp.Header().Set("Content-Length", strconv.FormatInt(contentLength, 10))
 | 
				
			||||||
	ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
 | 
						ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -313,7 +313,7 @@ func RenderEmoji(
 | 
				
			|||||||
	return ctx.postProcess(rawHTML)
 | 
						return ctx.postProcess(rawHTML)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var tagCleaner = regexp.MustCompile(`<((?:/?\w+/\w+)|(?:/[\w ]+/)|(/?[hH][tT][mM][lL][ />])|(/?[hH][eE][aA][dD][ />]))`)
 | 
					var tagCleaner = regexp.MustCompile(`<((?:/?\w+/\w+)|(?:/[\w ]+/)|(/?[hH][tT][mM][lL]\b)|(/?[hH][eE][aA][dD]\b))`)
 | 
				
			||||||
var nulCleaner = strings.NewReplacer("\000", "")
 | 
					var nulCleaner = strings.NewReplacer("\000", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
 | 
					func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
 | 
				
			||||||
@@ -327,7 +327,7 @@ func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
 | 
				
			|||||||
	_, _ = res.WriteString("<html><body>")
 | 
						_, _ = res.WriteString("<html><body>")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Strip out nuls - they're always invalid
 | 
						// Strip out nuls - they're always invalid
 | 
				
			||||||
	_, _ = nulCleaner.WriteString(res, string(tagCleaner.ReplaceAll(rawHTML, []byte("<$1"))))
 | 
						_, _ = res.Write(tagCleaner.ReplaceAll([]byte(nulCleaner.Replace(string(rawHTML))), []byte("<$1")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// close the tags
 | 
						// close the tags
 | 
				
			||||||
	_, _ = res.WriteString("</body></html>")
 | 
						_, _ = res.WriteString("</body></html>")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -124,7 +124,7 @@ func TestRender_links(t *testing.T) {
 | 
				
			|||||||
		`<p><a href="http://www.example.com/wpstyle/?p=364" rel="nofollow">http://www.example.com/wpstyle/?p=364</a></p>`)
 | 
							`<p><a href="http://www.example.com/wpstyle/?p=364" rel="nofollow">http://www.example.com/wpstyle/?p=364</a></p>`)
 | 
				
			||||||
	test(
 | 
						test(
 | 
				
			||||||
		"https://www.example.com/foo/?bar=baz&inga=42&quux",
 | 
							"https://www.example.com/foo/?bar=baz&inga=42&quux",
 | 
				
			||||||
		`<p><a href="https://www.example.com/foo/?bar=baz&inga=42&quux=" rel="nofollow">https://www.example.com/foo/?bar=baz&inga=42&quux</a></p>`)
 | 
							`<p><a href="https://www.example.com/foo/?bar=baz&inga=42&quux" rel="nofollow">https://www.example.com/foo/?bar=baz&inga=42&quux</a></p>`)
 | 
				
			||||||
	test(
 | 
						test(
 | 
				
			||||||
		"http://142.42.1.1/",
 | 
							"http://142.42.1.1/",
 | 
				
			||||||
		`<p><a href="http://142.42.1.1/" rel="nofollow">http://142.42.1.1/</a></p>`)
 | 
							`<p><a href="http://142.42.1.1/" rel="nofollow">http://142.42.1.1/</a></p>`)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,7 +46,9 @@ func ReplaceSanitizer() {
 | 
				
			|||||||
	sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
 | 
						sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Custom URL-Schemes
 | 
						// Custom URL-Schemes
 | 
				
			||||||
	sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
 | 
						if len(setting.Markdown.CustomURLSchemes) > 0 {
 | 
				
			||||||
 | 
							sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Allow keyword markup
 | 
						// Allow keyword markup
 | 
				
			||||||
	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^` + keywordClass + `$`)).OnElements("span")
 | 
						sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^` + keywordClass + `$`)).OnElements("span")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,8 @@
 | 
				
			|||||||
package markup
 | 
					package markup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"html/template"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@@ -50,3 +52,13 @@ func Test_Sanitizer(t *testing.T) {
 | 
				
			|||||||
		assert.Equal(t, testCases[i+1], string(SanitizeBytes([]byte(testCases[i]))))
 | 
							assert.Equal(t, testCases[i+1], string(SanitizeBytes([]byte(testCases[i]))))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSanitizeNonEscape(t *testing.T) {
 | 
				
			||||||
 | 
						descStr := "<scrİpt><script>alert(document.domain)</script></scrİpt>"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						output := template.HTML(Sanitize(string(descStr)))
 | 
				
			||||||
 | 
						if strings.Contains(string(output), "<script>") {
 | 
				
			||||||
 | 
							t.Errorf("un-escaped <script> in output: %q", output)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -525,9 +525,6 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques
 | 
				
			|||||||
				headRepoName = pr.Head.Repository.Name
 | 
									headRepoName = pr.Head.Repository.Name
 | 
				
			||||||
				headCloneURL = pr.Head.Repository.CloneURL
 | 
									headCloneURL = pr.Head.Repository.CloneURL
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if err := fixPullHeadSha(g.client, pr); err != nil {
 | 
					 | 
				
			||||||
				return nil, false, fmt.Errorf("error while resolving head git ref: %s for pull #%d. Error: %v", pr.Head.Ref, pr.Index, err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			headSHA = pr.Head.Sha
 | 
								headSHA = pr.Head.Sha
 | 
				
			||||||
			headRef = pr.Head.Ref
 | 
								headRef = pr.Head.Ref
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -679,22 +676,3 @@ func (g *GiteaDownloader) GetReviews(index int64) ([]*base.Review, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return allReviews, nil
 | 
						return allReviews, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// fixPullHeadSha is a workaround for https://github.com/go-gitea/gitea/issues/12675
 | 
					 | 
				
			||||||
// When no head sha is available, this is because the branch got deleted in the base repo.
 | 
					 | 
				
			||||||
// pr.Head.Ref points in this case not to the head repo branch name, but the base repo ref,
 | 
					 | 
				
			||||||
// which stays available to resolve the commit sha.
 | 
					 | 
				
			||||||
func fixPullHeadSha(client *gitea_sdk.Client, pr *gitea_sdk.PullRequest) error {
 | 
					 | 
				
			||||||
	owner := pr.Base.Repository.Owner.UserName
 | 
					 | 
				
			||||||
	repo := pr.Base.Repository.Name
 | 
					 | 
				
			||||||
	if pr.Head != nil && pr.Head.Sha == "" {
 | 
					 | 
				
			||||||
		refs, _, err := client.GetRepoRefs(owner, repo, pr.Head.Ref)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		} else if len(refs) == 0 {
 | 
					 | 
				
			||||||
			return fmt.Errorf("unable to resolve PR ref '%s'", pr.Head.Ref)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		pr.Head.Sha = refs[0].Object.SHA
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -283,7 +283,7 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
 | 
				
			|||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				defer rc.Close()
 | 
									defer rc.Close()
 | 
				
			||||||
				_, err = storage.Attachments.Save(attach.RelativePath(), rc)
 | 
									_, err = storage.Attachments.Save(attach.RelativePath(), rc, int64(*asset.Size))
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}()
 | 
								}()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,6 +132,11 @@ func (g *GithubDownloaderV3) sleep() {
 | 
				
			|||||||
func (g *GithubDownloaderV3) RefreshRate() error {
 | 
					func (g *GithubDownloaderV3) RefreshRate() error {
 | 
				
			||||||
	rates, _, err := g.client.RateLimits(g.ctx)
 | 
						rates, _, err := g.client.RateLimits(g.ctx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// if rate limit is not enabled, ignore it
 | 
				
			||||||
 | 
							if strings.Contains(err.Error(), "404") {
 | 
				
			||||||
 | 
								g.rate = nil
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -332,7 +332,8 @@ func (a *actionNotifier) NotifyPushCommits(pusher *models.User, repo *models.Rep
 | 
				
			|||||||
func (a *actionNotifier) NotifyCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
 | 
					func (a *actionNotifier) NotifyCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
 | 
				
			||||||
	opType := models.ActionCommitRepo
 | 
						opType := models.ActionCommitRepo
 | 
				
			||||||
	if refType == "tag" {
 | 
						if refType == "tag" {
 | 
				
			||||||
		opType = models.ActionPushTag
 | 
							// has sent same action in `NotifyPushCommits`, so skip it.
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := models.NotifyWatchers(&models.Action{
 | 
						if err := models.NotifyWatchers(&models.Action{
 | 
				
			||||||
		ActUserID: doer.ID,
 | 
							ActUserID: doer.ID,
 | 
				
			||||||
@@ -350,7 +351,8 @@ func (a *actionNotifier) NotifyCreateRef(doer *models.User, repo *models.Reposit
 | 
				
			|||||||
func (a *actionNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
 | 
					func (a *actionNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
 | 
				
			||||||
	opType := models.ActionDeleteBranch
 | 
						opType := models.ActionDeleteBranch
 | 
				
			||||||
	if refType == "tag" {
 | 
						if refType == "tag" {
 | 
				
			||||||
		opType = models.ActionDeleteTag
 | 
							// has sent same action in `NotifyPushCommits`, so skip it.
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := models.NotifyWatchers(&models.Action{
 | 
						if err := models.NotifyWatchers(&models.Action{
 | 
				
			||||||
		ActUserID: doer.ID,
 | 
							ActUserID: doer.ID,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ type Options struct {
 | 
				
			|||||||
// KnownPublicEntries list all direct children in the `public` directory
 | 
					// KnownPublicEntries list all direct children in the `public` directory
 | 
				
			||||||
var KnownPublicEntries = []string{
 | 
					var KnownPublicEntries = []string{
 | 
				
			||||||
	"css",
 | 
						"css",
 | 
				
			||||||
 | 
						"fonts",
 | 
				
			||||||
	"img",
 | 
						"img",
 | 
				
			||||||
	"js",
 | 
						"js",
 | 
				
			||||||
	"serviceworker.js",
 | 
						"serviceworker.js",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -174,6 +174,7 @@ func (m *Manager) FlushAll(baseCtx context.Context, timeout time.Duration) error
 | 
				
			|||||||
		default:
 | 
							default:
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		mqs := m.ManagedQueues()
 | 
							mqs := m.ManagedQueues()
 | 
				
			||||||
 | 
							log.Debug("Found %d Managed Queues", len(mqs))
 | 
				
			||||||
		wg := sync.WaitGroup{}
 | 
							wg := sync.WaitGroup{}
 | 
				
			||||||
		wg.Add(len(mqs))
 | 
							wg.Add(len(mqs))
 | 
				
			||||||
		allEmpty := true
 | 
							allEmpty := true
 | 
				
			||||||
@@ -184,6 +185,7 @@ func (m *Manager) FlushAll(baseCtx context.Context, timeout time.Duration) error
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			allEmpty = false
 | 
								allEmpty = false
 | 
				
			||||||
			if flushable, ok := mq.Managed.(Flushable); ok {
 | 
								if flushable, ok := mq.Managed.(Flushable); ok {
 | 
				
			||||||
 | 
									log.Debug("Flushing (flushable) queue: %s", mq.Name)
 | 
				
			||||||
				go func(q *ManagedQueue) {
 | 
									go func(q *ManagedQueue) {
 | 
				
			||||||
					localCtx, localCancel := context.WithCancel(ctx)
 | 
										localCtx, localCancel := context.WithCancel(ctx)
 | 
				
			||||||
					pid := q.RegisterWorkers(1, start, hasTimeout, end, localCancel, true)
 | 
										pid := q.RegisterWorkers(1, start, hasTimeout, end, localCancel, true)
 | 
				
			||||||
@@ -196,7 +198,11 @@ func (m *Manager) FlushAll(baseCtx context.Context, timeout time.Duration) error
 | 
				
			|||||||
					wg.Done()
 | 
										wg.Done()
 | 
				
			||||||
				}(mq)
 | 
									}(mq)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				wg.Done()
 | 
									log.Debug("Queue: %s is non-empty but is not flushable - adding 100 millisecond wait", mq.Name)
 | 
				
			||||||
 | 
									go func() {
 | 
				
			||||||
 | 
										<-time.After(100 * time.Millisecond)
 | 
				
			||||||
 | 
										wg.Done()
 | 
				
			||||||
 | 
									}()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,42 +99,10 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Copy uploaded files into repository.
 | 
						// Copy uploaded files into repository.
 | 
				
			||||||
	for i, uploadInfo := range infos {
 | 
						for i := range infos {
 | 
				
			||||||
		file, err := os.Open(uploadInfo.upload.LocalPath())
 | 
							if err := copyUploadedLFSFileIntoRepository(&infos[i], filename2attribute2info, t, opts.TreePath); err != nil {
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		defer file.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		var objectHash string
 | 
					 | 
				
			||||||
		if setting.LFS.StartServer && filename2attribute2info[uploadInfo.upload.Name] != nil && filename2attribute2info[uploadInfo.upload.Name]["filter"] == "lfs" {
 | 
					 | 
				
			||||||
			// Handle LFS
 | 
					 | 
				
			||||||
			// FIXME: Inefficient! this should probably happen in models.Upload
 | 
					 | 
				
			||||||
			oid, err := models.GenerateLFSOid(file)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			fileInfo, err := file.Stat()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			uploadInfo.lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: fileInfo.Size(), RepositoryID: t.repo.ID}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if objectHash, err = t.HashObject(strings.NewReader(uploadInfo.lfsMetaObject.Pointer())); err != nil {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			infos[i] = uploadInfo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		} else if objectHash, err = t.HashObject(file); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Add the object to the index
 | 
					 | 
				
			||||||
		if err := t.AddObjectToIndex("100644", objectHash, path.Join(opts.TreePath, uploadInfo.upload.Name)); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Now write the tree
 | 
						// Now write the tree
 | 
				
			||||||
@@ -154,11 +122,11 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Now deal with LFS objects
 | 
						// Now deal with LFS objects
 | 
				
			||||||
	for _, uploadInfo := range infos {
 | 
						for i := range infos {
 | 
				
			||||||
		if uploadInfo.lfsMetaObject == nil {
 | 
							if infos[i].lfsMetaObject == nil {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		uploadInfo.lfsMetaObject, err = models.NewLFSMetaObject(uploadInfo.lfsMetaObject)
 | 
							infos[i].lfsMetaObject, err = models.NewLFSMetaObject(infos[i].lfsMetaObject)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			// OK Now we need to cleanup
 | 
								// OK Now we need to cleanup
 | 
				
			||||||
			return cleanUpAfterFailure(&infos, t, err)
 | 
								return cleanUpAfterFailure(&infos, t, err)
 | 
				
			||||||
@@ -170,28 +138,10 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
 | 
				
			|||||||
	// OK now we can insert the data into the store - there's no way to clean up the store
 | 
						// OK now we can insert the data into the store - there's no way to clean up the store
 | 
				
			||||||
	// once it's in there, it's in there.
 | 
						// once it's in there, it's in there.
 | 
				
			||||||
	contentStore := &lfs.ContentStore{ObjectStorage: storage.LFS}
 | 
						contentStore := &lfs.ContentStore{ObjectStorage: storage.LFS}
 | 
				
			||||||
	for _, uploadInfo := range infos {
 | 
						for _, info := range infos {
 | 
				
			||||||
		if uploadInfo.lfsMetaObject == nil {
 | 
							if err := uploadToLFSContentStore(info, contentStore); err != nil {
 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		exist, err := contentStore.Exists(uploadInfo.lfsMetaObject)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return cleanUpAfterFailure(&infos, t, err)
 | 
								return cleanUpAfterFailure(&infos, t, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !exist {
 | 
					 | 
				
			||||||
			file, err := os.Open(uploadInfo.upload.LocalPath())
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				return cleanUpAfterFailure(&infos, t, err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			defer file.Close()
 | 
					 | 
				
			||||||
			// FIXME: Put regenerates the hash and copies the file over.
 | 
					 | 
				
			||||||
			// I guess this strictly ensures the soundness of the store but this is inefficient.
 | 
					 | 
				
			||||||
			if err := contentStore.Put(uploadInfo.lfsMetaObject, file); err != nil {
 | 
					 | 
				
			||||||
				// OK Now we need to cleanup
 | 
					 | 
				
			||||||
				// Can't clean up the store, once uploaded there they're there.
 | 
					 | 
				
			||||||
				return cleanUpAfterFailure(&infos, t, err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Then push this tree to NewBranch
 | 
						// Then push this tree to NewBranch
 | 
				
			||||||
@@ -201,3 +151,62 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return models.DeleteUploads(uploads...)
 | 
						return models.DeleteUploads(uploads...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func copyUploadedLFSFileIntoRepository(info *uploadInfo, filename2attribute2info map[string]map[string]string, t *TemporaryUploadRepository, treePath string) error {
 | 
				
			||||||
 | 
						file, err := os.Open(info.upload.LocalPath())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var objectHash string
 | 
				
			||||||
 | 
						if setting.LFS.StartServer && filename2attribute2info[info.upload.Name] != nil && filename2attribute2info[info.upload.Name]["filter"] == "lfs" {
 | 
				
			||||||
 | 
							// Handle LFS
 | 
				
			||||||
 | 
							// FIXME: Inefficient! this should probably happen in models.Upload
 | 
				
			||||||
 | 
							oid, err := models.GenerateLFSOid(file)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fileInfo, err := file.Stat()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							info.lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: fileInfo.Size(), RepositoryID: t.repo.ID}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if objectHash, err = t.HashObject(strings.NewReader(info.lfsMetaObject.Pointer())); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if objectHash, err = t.HashObject(file); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add the object to the index
 | 
				
			||||||
 | 
						return t.AddObjectToIndex("100644", objectHash, path.Join(treePath, info.upload.Name))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func uploadToLFSContentStore(info uploadInfo, contentStore *lfs.ContentStore) error {
 | 
				
			||||||
 | 
						if info.lfsMetaObject == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						exist, err := contentStore.Exists(info.lfsMetaObject)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !exist {
 | 
				
			||||||
 | 
							file, err := os.Open(info.upload.LocalPath())
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							defer file.Close()
 | 
				
			||||||
 | 
							// FIXME: Put regenerates the hash and copies the file over.
 | 
				
			||||||
 | 
							// I guess this strictly ensures the soundness of the store but this is inefficient.
 | 
				
			||||||
 | 
							if err := contentStore.Put(info.lfsMetaObject, file); err != nil {
 | 
				
			||||||
 | 
								// OK Now we need to cleanup
 | 
				
			||||||
 | 
								// Can't clean up the store, once uploaded there they're there.
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,6 +64,12 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name,
 | 
				
			|||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// copy lfs files failure should not be ignored
 | 
				
			||||||
 | 
							if err := models.CopyLFS(ctx, repo, oldRepo); err != nil {
 | 
				
			||||||
 | 
								rollbackRemoveFn()
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		repoPath := models.RepoPath(owner.Name, repo.Name)
 | 
							repoPath := models.RepoPath(owner.Name, repo.Name)
 | 
				
			||||||
		if stdout, err := git.NewCommand(
 | 
							if stdout, err := git.NewCommand(
 | 
				
			||||||
			"clone", "--bare", oldRepoPath, repoPath).
 | 
								"clone", "--bare", oldRepoPath, repoPath).
 | 
				
			||||||
@@ -92,6 +98,7 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name,
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// even if below operations failed, it could be ignored. And they will be retried
 | 
				
			||||||
	ctx := models.DefaultDBContext()
 | 
						ctx := models.DefaultDBContext()
 | 
				
			||||||
	if err = repo.UpdateSize(ctx); err != nil {
 | 
						if err = repo.UpdateSize(ctx); err != nil {
 | 
				
			||||||
		log.Error("Failed to update size for repository: %v", err)
 | 
							log.Error("Failed to update size for repository: %v", err)
 | 
				
			||||||
@@ -100,11 +107,5 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name,
 | 
				
			|||||||
		log.Error("Copy language stat from oldRepo failed")
 | 
							log.Error("Copy language stat from oldRepo failed")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := models.CopyLFS(ctx, repo, oldRepo); err != nil {
 | 
					 | 
				
			||||||
		if errDelete := models.DeleteRepository(doer, owner.ID, repo.ID); errDelete != nil {
 | 
					 | 
				
			||||||
			log.Error("Rollback deleteRepository: %v", errDelete)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return repo, nil
 | 
						return repo, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,7 +66,7 @@ func (l *LocalStorage) Open(path string) (Object, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save a file
 | 
					// Save a file
 | 
				
			||||||
func (l *LocalStorage) Save(path string, r io.Reader) (int64, error) {
 | 
					func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) {
 | 
				
			||||||
	p := filepath.Join(l.dir, path)
 | 
						p := filepath.Join(l.dir, path)
 | 
				
			||||||
	if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil {
 | 
						if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil {
 | 
				
			||||||
		return 0, err
 | 
							return 0, err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -131,13 +131,13 @@ func (m *MinioStorage) Open(path string) (Object, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Save save a file to minio
 | 
					// Save save a file to minio
 | 
				
			||||||
func (m *MinioStorage) Save(path string, r io.Reader) (int64, error) {
 | 
					func (m *MinioStorage) Save(path string, r io.Reader, size int64) (int64, error) {
 | 
				
			||||||
	uploadInfo, err := m.client.PutObject(
 | 
						uploadInfo, err := m.client.PutObject(
 | 
				
			||||||
		m.ctx,
 | 
							m.ctx,
 | 
				
			||||||
		m.bucket,
 | 
							m.bucket,
 | 
				
			||||||
		m.buildMinioPath(path),
 | 
							m.buildMinioPath(path),
 | 
				
			||||||
		r,
 | 
							r,
 | 
				
			||||||
		-1,
 | 
							size,
 | 
				
			||||||
		minio.PutObjectOptions{ContentType: "application/octet-stream"},
 | 
							minio.PutObjectOptions{ContentType: "application/octet-stream"},
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,7 +65,8 @@ type Object interface {
 | 
				
			|||||||
// ObjectStorage represents an object storage to handle a bucket and files
 | 
					// ObjectStorage represents an object storage to handle a bucket and files
 | 
				
			||||||
type ObjectStorage interface {
 | 
					type ObjectStorage interface {
 | 
				
			||||||
	Open(path string) (Object, error)
 | 
						Open(path string) (Object, error)
 | 
				
			||||||
	Save(path string, r io.Reader) (int64, error)
 | 
						// Save store a object, if size is unknown set -1
 | 
				
			||||||
 | 
						Save(path string, r io.Reader, size int64) (int64, error)
 | 
				
			||||||
	Stat(path string) (os.FileInfo, error)
 | 
						Stat(path string) (os.FileInfo, error)
 | 
				
			||||||
	Delete(path string) error
 | 
						Delete(path string) error
 | 
				
			||||||
	URL(path, name string) (*url.URL, error)
 | 
						URL(path, name string) (*url.URL, error)
 | 
				
			||||||
@@ -80,7 +81,13 @@ func Copy(dstStorage ObjectStorage, dstPath string, srcStorage ObjectStorage, sr
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	defer f.Close()
 | 
						defer f.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return dstStorage.Save(dstPath, f)
 | 
						size := int64(-1)
 | 
				
			||||||
 | 
						fsinfo, err := f.Stat()
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							size = fsinfo.Size()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return dstStorage.Save(dstPath, f, size)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SaveFrom saves data to the ObjectStorage with path p from the callback
 | 
					// SaveFrom saves data to the ObjectStorage with path p from the callback
 | 
				
			||||||
@@ -94,7 +101,7 @@ func SaveFrom(objStorage ObjectStorage, p string, callback func(w io.Writer) err
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := objStorage.Save(p, pr)
 | 
						_, err := objStorage.Save(p, pr, -1)
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,8 +27,9 @@ type LangType struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	matcher  language.Matcher
 | 
						matcher       language.Matcher
 | 
				
			||||||
	allLangs []LangType
 | 
						allLangs      []LangType
 | 
				
			||||||
 | 
						supportedTags []language.Tag
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AllLangs returns all supported langauages
 | 
					// AllLangs returns all supported langauages
 | 
				
			||||||
@@ -51,12 +52,12 @@ func InitLocales() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tags := make([]language.Tag, len(setting.Langs))
 | 
						supportedTags = make([]language.Tag, len(setting.Langs))
 | 
				
			||||||
	for i, lang := range setting.Langs {
 | 
						for i, lang := range setting.Langs {
 | 
				
			||||||
		tags[i] = language.Raw.Make(lang)
 | 
							supportedTags[i] = language.Raw.Make(lang)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	matcher = language.NewMatcher(tags)
 | 
						matcher = language.NewMatcher(supportedTags)
 | 
				
			||||||
	for i := range setting.Names {
 | 
						for i := range setting.Names {
 | 
				
			||||||
		key := "locale_" + setting.Langs[i] + ".ini"
 | 
							key := "locale_" + setting.Langs[i] + ".ini"
 | 
				
			||||||
		if err = i18n.SetMessageWithDesc(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil {
 | 
							if err = i18n.SetMessageWithDesc(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil {
 | 
				
			||||||
@@ -79,8 +80,9 @@ func InitLocales() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Match matches accept languages
 | 
					// Match matches accept languages
 | 
				
			||||||
func Match(tags ...language.Tag) (tag language.Tag, index int, c language.Confidence) {
 | 
					func Match(tags ...language.Tag) language.Tag {
 | 
				
			||||||
	return matcher.Match(tags...)
 | 
						_, i, _ := matcher.Match(tags...)
 | 
				
			||||||
 | 
						return supportedTags[i]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// locale represents the information of localization.
 | 
					// locale represents the information of localization.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ func Locale(resp http.ResponseWriter, req *http.Request) translation.Locale {
 | 
				
			|||||||
	// The first element in the list is chosen to be the default language automatically.
 | 
						// The first element in the list is chosen to be the default language automatically.
 | 
				
			||||||
	if len(lang) == 0 {
 | 
						if len(lang) == 0 {
 | 
				
			||||||
		tags, _, _ := language.ParseAcceptLanguage(req.Header.Get("Accept-Language"))
 | 
							tags, _, _ := language.ParseAcceptLanguage(req.Header.Get("Accept-Language"))
 | 
				
			||||||
		tag, _, _ := translation.Match(tags...)
 | 
							tag := translation.Match(tags...)
 | 
				
			||||||
		lang = tag.String()
 | 
							lang = tag.String()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,10 +68,11 @@ func Wrap(handlers ...interface{}) http.HandlerFunc {
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			case func(http.Handler) http.Handler:
 | 
								case func(http.Handler) http.Handler:
 | 
				
			||||||
				var next = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
 | 
									var next = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
 | 
				
			||||||
				t(next).ServeHTTP(resp, req)
 | 
									if len(handlers) > i+1 {
 | 
				
			||||||
				if r, ok := resp.(context.ResponseWriter); ok && r.Status() > 0 {
 | 
										next = Wrap(handlers[i+1:]...)
 | 
				
			||||||
					return
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
									t(next).ServeHTTP(resp, req)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				panic(fmt.Sprintf("Unsupported handler type: %#v", t))
 | 
									panic(fmt.Sprintf("Unsupported handler type: %#v", t))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,6 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/convert"
 | 
						"code.gitea.io/gitea/modules/convert"
 | 
				
			||||||
	issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
 | 
						issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/notification"
 | 
						"code.gitea.io/gitea/modules/notification"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
@@ -113,11 +112,7 @@ func SearchIssues(ctx *context.APIContext) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// find repos user can access (for issue search)
 | 
						// find repos user can access (for issue search)
 | 
				
			||||||
	repoIDs := make([]int64, 0)
 | 
					 | 
				
			||||||
	opts := &models.SearchRepoOptions{
 | 
						opts := &models.SearchRepoOptions{
 | 
				
			||||||
		ListOptions: models.ListOptions{
 | 
					 | 
				
			||||||
			PageSize: 15,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Private:     false,
 | 
							Private:     false,
 | 
				
			||||||
		AllPublic:   true,
 | 
							AllPublic:   true,
 | 
				
			||||||
		TopicOnly:   false,
 | 
							TopicOnly:   false,
 | 
				
			||||||
@@ -132,21 +127,10 @@ func SearchIssues(ctx *context.APIContext) {
 | 
				
			|||||||
		opts.AllLimited = true
 | 
							opts.AllLimited = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for page := 1; ; page++ {
 | 
						repoIDs, _, err := models.SearchRepositoryIDs(opts)
 | 
				
			||||||
		opts.Page = page
 | 
						if err != nil {
 | 
				
			||||||
		repos, count, err := models.SearchRepositoryByName(opts)
 | 
							ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
 | 
				
			||||||
		if err != nil {
 | 
							return
 | 
				
			||||||
			ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if len(repos) == 0 {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		log.Trace("Processing next %d repos of %d", len(repos), count)
 | 
					 | 
				
			||||||
		for _, repo := range repos {
 | 
					 | 
				
			||||||
			repoIDs = append(repoIDs, repo.ID)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var issues []*models.Issue
 | 
						var issues []*models.Issue
 | 
				
			||||||
@@ -157,7 +141,6 @@ func SearchIssues(ctx *context.APIContext) {
 | 
				
			|||||||
		keyword = ""
 | 
							keyword = ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var issueIDs []int64
 | 
						var issueIDs []int64
 | 
				
			||||||
	var labelIDs []int64
 | 
					 | 
				
			||||||
	if len(keyword) > 0 && len(repoIDs) > 0 {
 | 
						if len(keyword) > 0 && len(repoIDs) > 0 {
 | 
				
			||||||
		if issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword); err != nil {
 | 
							if issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword); err != nil {
 | 
				
			||||||
			ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err)
 | 
								ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err)
 | 
				
			||||||
@@ -192,7 +175,7 @@ func SearchIssues(ctx *context.APIContext) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Only fetch the issues if we either don't have a keyword or the search returned issues
 | 
						// Only fetch the issues if we either don't have a keyword or the search returned issues
 | 
				
			||||||
	// This would otherwise return all issues if no issues were found by the search.
 | 
						// This would otherwise return all issues if no issues were found by the search.
 | 
				
			||||||
	if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
 | 
						if len(keyword) == 0 || len(issueIDs) > 0 || len(includedLabelNames) > 0 {
 | 
				
			||||||
		issuesOpt := &models.IssuesOptions{
 | 
							issuesOpt := &models.IssuesOptions{
 | 
				
			||||||
			ListOptions: models.ListOptions{
 | 
								ListOptions: models.ListOptions{
 | 
				
			||||||
				Page:     ctx.QueryInt("page"),
 | 
									Page:     ctx.QueryInt("page"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -274,7 +274,11 @@ func DeleteOauth2Application(ctx *context.APIContext) {
 | 
				
			|||||||
	//     "$ref": "#/responses/empty"
 | 
						//     "$ref": "#/responses/empty"
 | 
				
			||||||
	appID := ctx.ParamsInt64(":id")
 | 
						appID := ctx.ParamsInt64(":id")
 | 
				
			||||||
	if err := models.DeleteOAuth2Application(appID, ctx.User.ID); err != nil {
 | 
						if err := models.DeleteOAuth2Application(appID, ctx.User.ID); err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, "DeleteOauth2ApplicationByID", err)
 | 
							if models.IsErrOAuthApplicationNotFound(err) {
 | 
				
			||||||
 | 
								ctx.NotFound()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Error(http.StatusInternalServerError, "DeleteOauth2ApplicationByID", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,17 @@ func Events(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Resp.Header().Set("X-Accel-Buffering", "no")
 | 
						ctx.Resp.Header().Set("X-Accel-Buffering", "no")
 | 
				
			||||||
	ctx.Resp.WriteHeader(http.StatusOK)
 | 
						ctx.Resp.WriteHeader(http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !ctx.IsSigned {
 | 
				
			||||||
 | 
							// Return unauthorized status event
 | 
				
			||||||
 | 
							event := (&eventsource.Event{
 | 
				
			||||||
 | 
								Name: "close",
 | 
				
			||||||
 | 
								Data: "unauthorized",
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							_, _ = event.WriteTo(ctx)
 | 
				
			||||||
 | 
							ctx.Resp.Flush()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Listen to connection close and un-register messageChan
 | 
						// Listen to connection close and un-register messageChan
 | 
				
			||||||
	notify := ctx.Req.Context().Done()
 | 
						notify := ctx.Req.Context().Done()
 | 
				
			||||||
	ctx.Resp.Flush()
 | 
						ctx.Resp.Flush()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
package repo
 | 
					package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -389,6 +390,11 @@ func RawDiff(ctx *context.Context) {
 | 
				
			|||||||
		git.RawDiffType(ctx.Params(":ext")),
 | 
							git.RawDiffType(ctx.Params(":ext")),
 | 
				
			||||||
		ctx.Resp,
 | 
							ctx.Resp,
 | 
				
			||||||
	); err != nil {
 | 
						); err != nil {
 | 
				
			||||||
 | 
							if git.IsErrNotExist(err) {
 | 
				
			||||||
 | 
								ctx.NotFound("GetRawDiff",
 | 
				
			||||||
 | 
									errors.New("commit "+ctx.Params(":sha")+" does not exist."))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		ctx.ServerError("GetRawDiff", err)
 | 
							ctx.ServerError("GetRawDiff", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -431,11 +431,15 @@ func PrepareCompareDiff(
 | 
				
			|||||||
		ctx.Data["IsNothingToCompare"] = true
 | 
							ctx.Data["IsNothingToCompare"] = true
 | 
				
			||||||
		if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil {
 | 
							if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil {
 | 
				
			||||||
			config := unit.PullRequestsConfig()
 | 
								config := unit.PullRequestsConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !config.AutodetectManualMerge {
 | 
								if !config.AutodetectManualMerge {
 | 
				
			||||||
				ctx.Data["AllowEmptyPr"] = !(baseBranch == headBranch && ctx.Repo.Repository.Name == headRepo.Name)
 | 
									allowEmptyPr := !(baseBranch == headBranch && ctx.Repo.Repository.Name == headRepo.Name)
 | 
				
			||||||
			} else {
 | 
									ctx.Data["AllowEmptyPr"] = allowEmptyPr
 | 
				
			||||||
				ctx.Data["AllowEmptyPr"] = false
 | 
					
 | 
				
			||||||
 | 
									return !allowEmptyPr
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ctx.Data["AllowEmptyPr"] = false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,9 +103,11 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	isWiki := false
 | 
						isWiki := false
 | 
				
			||||||
	var unitType = models.UnitTypeCode
 | 
						var unitType = models.UnitTypeCode
 | 
				
			||||||
 | 
						var wikiRepoName string
 | 
				
			||||||
	if strings.HasSuffix(reponame, ".wiki") {
 | 
						if strings.HasSuffix(reponame, ".wiki") {
 | 
				
			||||||
		isWiki = true
 | 
							isWiki = true
 | 
				
			||||||
		unitType = models.UnitTypeWiki
 | 
							unitType = models.UnitTypeWiki
 | 
				
			||||||
 | 
							wikiRepoName = reponame
 | 
				
			||||||
		reponame = reponame[:len(reponame)-5]
 | 
							reponame = reponame[:len(reponame)-5]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -314,6 +316,11 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if isWiki { // you cannot send wiki operation before create the repository
 | 
				
			||||||
 | 
								ctx.HandleText(http.StatusNotFound, "Repository not found")
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if owner.IsOrganization() && !setting.Repository.EnablePushCreateOrg {
 | 
							if owner.IsOrganization() && !setting.Repository.EnablePushCreateOrg {
 | 
				
			||||||
			ctx.HandleText(http.StatusForbidden, "Push to create is not enabled for organizations.")
 | 
								ctx.HandleText(http.StatusForbidden, "Push to create is not enabled for organizations.")
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -363,6 +370,9 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
 | 
				
			|||||||
	r.URL.Path = strings.ToLower(r.URL.Path) // blue: In case some repo name has upper case name
 | 
						r.URL.Path = strings.ToLower(r.URL.Path) // blue: In case some repo name has upper case name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dir := models.RepoPath(username, reponame)
 | 
						dir := models.RepoPath(username, reponame)
 | 
				
			||||||
 | 
						if isWiki {
 | 
				
			||||||
 | 
							dir = models.RepoPath(username, wikiRepoName)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &serviceHandler{cfg, w, r, dir, cfg.Env}
 | 
						return &serviceHandler{cfg, w, r, dir, cfg.Env}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -241,14 +241,13 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	approvalCounts, err := models.IssueList(issues).GetApprovalCounts()
 | 
						var issueList = models.IssueList(issues)
 | 
				
			||||||
 | 
						approvalCounts, err := issueList.GetApprovalCounts()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("ApprovalCounts", err)
 | 
							ctx.ServerError("ApprovalCounts", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var commitStatus = make(map[int64]*models.CommitStatus, len(issues))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Get posters.
 | 
						// Get posters.
 | 
				
			||||||
	for i := range issues {
 | 
						for i := range issues {
 | 
				
			||||||
		// Check read status
 | 
							// Check read status
 | 
				
			||||||
@@ -258,16 +257,12 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
 | 
				
			|||||||
			ctx.ServerError("GetIsRead", err)
 | 
								ctx.ServerError("GetIsRead", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if issues[i].IsPull {
 | 
						commitStatus, err := pull_service.GetIssuesLastCommitStatus(issues)
 | 
				
			||||||
			if err := issues[i].LoadPullRequest(); err != nil {
 | 
						if err != nil {
 | 
				
			||||||
				ctx.ServerError("LoadPullRequest", err)
 | 
							ctx.ServerError("GetIssuesLastCommitStatus", err)
 | 
				
			||||||
				return
 | 
							return
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var statuses, _ = pull_service.GetLastCommitStatus(issues[i].PullRequest)
 | 
					 | 
				
			||||||
			commitStatus[issues[i].PullRequest.ID] = models.CalcCommitStatus(statuses)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["Issues"] = issues
 | 
						ctx.Data["Issues"] = issues
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -192,6 +192,9 @@ func CreatePost(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["Licenses"] = models.Licenses
 | 
						ctx.Data["Licenses"] = models.Licenses
 | 
				
			||||||
	ctx.Data["Readmes"] = models.Readmes
 | 
						ctx.Data["Readmes"] = models.Readmes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Data["CanCreateRepo"] = ctx.User.CanCreateRepo()
 | 
				
			||||||
 | 
						ctx.Data["MaxCreationLimit"] = ctx.User.MaxCreationLimit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctxUser := checkContextUser(ctx, form.UID)
 | 
						ctxUser := checkContextUser(ctx, form.UID)
 | 
				
			||||||
	if ctx.Written() {
 | 
						if ctx.Written() {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@
 | 
				
			|||||||
package repo
 | 
					package repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"path"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
@@ -40,7 +39,7 @@ func Search(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["Keyword"] = keyword
 | 
						ctx.Data["Keyword"] = keyword
 | 
				
			||||||
	ctx.Data["Language"] = language
 | 
						ctx.Data["Language"] = language
 | 
				
			||||||
	ctx.Data["queryType"] = queryType
 | 
						ctx.Data["queryType"] = queryType
 | 
				
			||||||
	ctx.Data["SourcePath"] = path.Join(setting.AppSubURL, ctx.Repo.Repository.Owner.Name, ctx.Repo.Repository.Name)
 | 
						ctx.Data["SourcePath"] = ctx.Repo.Repository.HTMLURL()
 | 
				
			||||||
	ctx.Data["SearchResults"] = searchResults
 | 
						ctx.Data["SearchResults"] = searchResults
 | 
				
			||||||
	ctx.Data["SearchResultLanguages"] = searchResultLanguages
 | 
						ctx.Data["SearchResultLanguages"] = searchResultLanguages
 | 
				
			||||||
	ctx.Data["RequireHighlightJS"] = true
 | 
						ctx.Data["RequireHighlightJS"] = true
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -87,13 +88,21 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor
 | 
				
			|||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !strings.HasPrefix(req.URL.RequestURI(), "/"+prefix) {
 | 
								prefix := strings.Trim(prefix, "/")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !strings.HasPrefix(req.URL.EscapedPath(), "/"+prefix+"/") {
 | 
				
			||||||
				next.ServeHTTP(w, req)
 | 
									next.ServeHTTP(w, req)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			rPath := strings.TrimPrefix(req.URL.RequestURI(), "/"+prefix)
 | 
								rPath := strings.TrimPrefix(req.URL.EscapedPath(), "/"+prefix+"/")
 | 
				
			||||||
			rPath = strings.TrimPrefix(rPath, "/")
 | 
								rPath = strings.TrimPrefix(rPath, "/")
 | 
				
			||||||
 | 
								if rPath == "" {
 | 
				
			||||||
 | 
									http.Error(w, "file not found", 404)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								rPath = path.Clean("/" + filepath.ToSlash(rPath))
 | 
				
			||||||
 | 
								rPath = rPath[1:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			fi, err := objStore.Stat(rPath)
 | 
								fi, err := objStore.Stat(rPath)
 | 
				
			||||||
			if err == nil && httpcache.HandleTimeCache(req, w, fi) {
 | 
								if err == nil && httpcache.HandleTimeCache(req, w, fi) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -168,15 +168,6 @@ func WebRoutes() *web.Route {
 | 
				
			|||||||
		r.Use(h)
 | 
							r.Use(h)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (setting.Protocol == setting.FCGI || setting.Protocol == setting.FCGIUnix) && setting.AppSubURL != "" {
 | 
					 | 
				
			||||||
		r.Use(func(next http.Handler) http.Handler {
 | 
					 | 
				
			||||||
			return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
					 | 
				
			||||||
				req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL)
 | 
					 | 
				
			||||||
				next.ServeHTTP(resp, req)
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mailer.InitMailRender(templates.Mailer())
 | 
						mailer.InitMailRender(templates.Mailer())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if setting.Service.EnableCaptcha {
 | 
						if setting.Service.EnableCaptcha {
 | 
				
			||||||
@@ -400,7 +391,7 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
	}, reqSignOut)
 | 
						}, reqSignOut)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m.Any("/user/events", reqSignIn, events.Events)
 | 
						m.Any("/user/events", events.Events)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m.Group("/login/oauth", func() {
 | 
						m.Group("/login/oauth", func() {
 | 
				
			||||||
		m.Get("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth)
 | 
							m.Get("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth)
 | 
				
			||||||
@@ -700,7 +691,7 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
	}, reqSignIn)
 | 
						}, reqSignIn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// ***** Release Attachment Download without Signin
 | 
						// ***** Release Attachment Download without Signin
 | 
				
			||||||
	m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment(), repo.MustBeNotEmpty, repo.RedirectDownload)
 | 
						m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment, repo.MustBeNotEmpty, repo.RedirectDownload)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m.Group("/{username}/{reponame}", func() {
 | 
						m.Group("/{username}/{reponame}", func() {
 | 
				
			||||||
		m.Group("/settings", func() {
 | 
							m.Group("/settings", func() {
 | 
				
			||||||
@@ -780,9 +771,9 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
			ctx.Data["PageIsSettings"] = true
 | 
								ctx.Data["PageIsSettings"] = true
 | 
				
			||||||
			ctx.Data["LFSStartServer"] = setting.LFS.StartServer
 | 
								ctx.Data["LFSStartServer"] = setting.LFS.StartServer
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef())
 | 
						}, reqSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoAdmin, context.RepoRef())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m.Post("/{username}/{reponame}/action/{action}", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action)
 | 
						m.Post("/{username}/{reponame}/action/{action}", reqSignIn, context.RepoAssignment, context.UnitTypes(), repo.Action)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Grouping for those endpoints not requiring authentication
 | 
						// Grouping for those endpoints not requiring authentication
 | 
				
			||||||
	m.Group("/{username}/{reponame}", func() {
 | 
						m.Group("/{username}/{reponame}", func() {
 | 
				
			||||||
@@ -792,7 +783,7 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
		m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists).
 | 
							m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists).
 | 
				
			||||||
			Get(ignSignIn, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
 | 
								Get(ignSignIn, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
 | 
				
			||||||
			Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(auth.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
 | 
								Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(auth.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
 | 
				
			||||||
	}, context.RepoAssignment(), context.UnitTypes())
 | 
						}, context.RepoAssignment, context.UnitTypes())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Grouping for those endpoints that do require authentication
 | 
						// Grouping for those endpoints that do require authentication
 | 
				
			||||||
	m.Group("/{username}/{reponame}", func() {
 | 
						m.Group("/{username}/{reponame}", func() {
 | 
				
			||||||
@@ -899,7 +890,7 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
			m.Post("/restore", repo.RestoreBranchPost)
 | 
								m.Post("/restore", repo.RestoreBranchPost)
 | 
				
			||||||
		}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
 | 
							}, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}, reqSignIn, context.RepoAssignment(), context.UnitTypes())
 | 
						}, reqSignIn, context.RepoAssignment, context.UnitTypes())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Releases
 | 
						// Releases
 | 
				
			||||||
	m.Group("/{username}/{reponame}", func() {
 | 
						m.Group("/{username}/{reponame}", func() {
 | 
				
			||||||
@@ -937,11 +928,11 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
 | 
								ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}, ignSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoReleaseReader)
 | 
						}, ignSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoReleaseReader)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m.Group("/{username}/{reponame}", func() {
 | 
						m.Group("/{username}/{reponame}", func() {
 | 
				
			||||||
		m.Post("/topics", repo.TopicsPost)
 | 
							m.Post("/topics", repo.TopicsPost)
 | 
				
			||||||
	}, context.RepoAssignment(), context.RepoMustNotBeArchived(), reqRepoAdmin)
 | 
						}, context.RepoAssignment, context.RepoMustNotBeArchived(), reqRepoAdmin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m.Group("/{username}/{reponame}", func() {
 | 
						m.Group("/{username}/{reponame}", func() {
 | 
				
			||||||
		m.Group("", func() {
 | 
							m.Group("", func() {
 | 
				
			||||||
@@ -1089,17 +1080,17 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
		}, context.RepoRef(), reqRepoCodeReader)
 | 
							}, context.RepoRef(), reqRepoCodeReader)
 | 
				
			||||||
		m.Get("/commit/{sha:([a-f0-9]{7,40})}.{ext:patch|diff}",
 | 
							m.Get("/commit/{sha:([a-f0-9]{7,40})}.{ext:patch|diff}",
 | 
				
			||||||
			repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
 | 
								repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
 | 
				
			||||||
	}, ignSignIn, context.RepoAssignment(), context.UnitTypes())
 | 
						}, ignSignIn, context.RepoAssignment, context.UnitTypes())
 | 
				
			||||||
	m.Group("/{username}/{reponame}", func() {
 | 
						m.Group("/{username}/{reponame}", func() {
 | 
				
			||||||
		m.Get("/stars", repo.Stars)
 | 
							m.Get("/stars", repo.Stars)
 | 
				
			||||||
		m.Get("/watchers", repo.Watchers)
 | 
							m.Get("/watchers", repo.Watchers)
 | 
				
			||||||
		m.Get("/search", reqRepoCodeReader, repo.Search)
 | 
							m.Get("/search", reqRepoCodeReader, repo.Search)
 | 
				
			||||||
	}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
 | 
						}, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m.Group("/{username}", func() {
 | 
						m.Group("/{username}", func() {
 | 
				
			||||||
		m.Group("/{reponame}", func() {
 | 
							m.Group("/{reponame}", func() {
 | 
				
			||||||
			m.Get("", repo.SetEditorconfigIfExists, repo.Home)
 | 
								m.Get("", repo.SetEditorconfigIfExists, repo.Home)
 | 
				
			||||||
		}, goGet, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
 | 
							}, goGet, ignSignIn, context.RepoAssignment, context.RepoRef(), context.UnitTypes())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m.Group("/{reponame}", func() {
 | 
							m.Group("/{reponame}", func() {
 | 
				
			||||||
			m.Group("/info/lfs", func() {
 | 
								m.Group("/info/lfs", func() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,12 +7,14 @@ package user
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Avatar redirect browser to user avatar of requested size
 | 
					// Avatar redirect browser to user avatar of requested size
 | 
				
			||||||
@@ -70,8 +72,21 @@ func AvatarByEmailHash(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var avatarURL *url.URL
 | 
						var avatarURL *url.URL
 | 
				
			||||||
	avatarURL, err = models.LibravatarURL(email)
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
 | 
				
			||||||
 | 
							avatarURL, err = models.LibravatarURL(email)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								avatarURL, err = url.Parse(models.DefaultAvatarLink())
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									ctx.ServerError("invalid default avatar url", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if !setting.DisableGravatar {
 | 
				
			||||||
 | 
							copyOfGravatarSourceURL := *setting.GravatarSourceURL
 | 
				
			||||||
 | 
							avatarURL = ©OfGravatarSourceURL
 | 
				
			||||||
 | 
							avatarURL.Path = path.Join(avatarURL.Path, hash)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		avatarURL, err = url.Parse(models.DefaultAvatarLink())
 | 
							avatarURL, err = url.Parse(models.DefaultAvatarLink())
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("invalid default avatar url", err)
 | 
								ctx.ServerError("invalid default avatar url", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -546,14 +546,14 @@ func buildIssueOverview(ctx *context.Context, unitType models.UnitType) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// maps pull request IDs to their CommitStatus. Will be posted to ctx.Data.
 | 
						// maps pull request IDs to their CommitStatus. Will be posted to ctx.Data.
 | 
				
			||||||
	var commitStatus = make(map[int64]*models.CommitStatus, len(issues))
 | 
					 | 
				
			||||||
	for _, issue := range issues {
 | 
						for _, issue := range issues {
 | 
				
			||||||
		issue.Repo = showReposMap[issue.RepoID]
 | 
							issue.Repo = showReposMap[issue.RepoID]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if isPullList {
 | 
						commitStatus, err := pull_service.GetIssuesLastCommitStatus(issues)
 | 
				
			||||||
			var statuses, _ = pull_service.GetLastCommitStatus(issue.PullRequest)
 | 
						if err != nil {
 | 
				
			||||||
			commitStatus[issue.PullRequest.ID] = models.CalcCommitStatus(statuses)
 | 
							ctx.ServerError("GetIssuesLastCommitStatus", err)
 | 
				
			||||||
		}
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// -------------------------------
 | 
						// -------------------------------
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1014,6 +1014,11 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			diffLine := &DiffLine{Type: DiffLineAdd, RightIdx: rightLine}
 | 
								diffLine := &DiffLine{Type: DiffLineAdd, RightIdx: rightLine}
 | 
				
			||||||
			rightLine++
 | 
								rightLine++
 | 
				
			||||||
 | 
								if curSection == nil {
 | 
				
			||||||
 | 
									// Create a new section to represent this hunk
 | 
				
			||||||
 | 
									curSection = &DiffSection{}
 | 
				
			||||||
 | 
									curFile.Sections = append(curFile.Sections, curSection)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			curSection.Lines = append(curSection.Lines, diffLine)
 | 
								curSection.Lines = append(curSection.Lines, diffLine)
 | 
				
			||||||
		case '-':
 | 
							case '-':
 | 
				
			||||||
			curFileLinesCount++
 | 
								curFileLinesCount++
 | 
				
			||||||
@@ -1026,6 +1031,11 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
 | 
				
			|||||||
			if leftLine > 0 {
 | 
								if leftLine > 0 {
 | 
				
			||||||
				leftLine++
 | 
									leftLine++
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if curSection == nil {
 | 
				
			||||||
 | 
									// Create a new section to represent this hunk
 | 
				
			||||||
 | 
									curSection = &DiffSection{}
 | 
				
			||||||
 | 
									curFile.Sections = append(curFile.Sections, curSection)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			curSection.Lines = append(curSection.Lines, diffLine)
 | 
								curSection.Lines = append(curSection.Lines, diffLine)
 | 
				
			||||||
		case ' ':
 | 
							case ' ':
 | 
				
			||||||
			curFileLinesCount++
 | 
								curFileLinesCount++
 | 
				
			||||||
@@ -1036,6 +1046,11 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
 | 
				
			|||||||
			diffLine := &DiffLine{Type: DiffLinePlain, LeftIdx: leftLine, RightIdx: rightLine}
 | 
								diffLine := &DiffLine{Type: DiffLinePlain, LeftIdx: leftLine, RightIdx: rightLine}
 | 
				
			||||||
			leftLine++
 | 
								leftLine++
 | 
				
			||||||
			rightLine++
 | 
								rightLine++
 | 
				
			||||||
 | 
								if curSection == nil {
 | 
				
			||||||
 | 
									// Create a new section to represent this hunk
 | 
				
			||||||
 | 
									curSection = &DiffSection{}
 | 
				
			||||||
 | 
									curFile.Sections = append(curFile.Sections, curSection)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			curSection.Lines = append(curSection.Lines, diffLine)
 | 
								curSection.Lines = append(curSection.Lines, diffLine)
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			// This is unexpected
 | 
								// This is unexpected
 | 
				
			||||||
@@ -1288,6 +1303,14 @@ func CommentAsDiff(c *models.Comment) (*Diff, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// CommentMustAsDiff executes AsDiff and logs the error instead of returning
 | 
					// CommentMustAsDiff executes AsDiff and logs the error instead of returning
 | 
				
			||||||
func CommentMustAsDiff(c *models.Comment) *Diff {
 | 
					func CommentMustAsDiff(c *models.Comment) *Diff {
 | 
				
			||||||
 | 
						if c == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if err := recover(); err != nil {
 | 
				
			||||||
 | 
								log.Error("PANIC whilst retrieving diff for comment[%d] Error: %v\nStack: %s", c.ID, err, log.Stack(2))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
	diff, err := CommentAsDiff(c)
 | 
						diff, err := CommentAsDiff(c)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Warn("CommentMustAsDiff: %v", err)
 | 
							log.Warn("CommentMustAsDiff: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,7 +64,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 | 
				
			|||||||
	pr.Merger = doer
 | 
						pr.Merger = doer
 | 
				
			||||||
	pr.MergerID = doer.ID
 | 
						pr.MergerID = doer.ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err = pr.SetMerged(); err != nil {
 | 
						if _, err := pr.SetMerged(); err != nil {
 | 
				
			||||||
		log.Error("setMerged [%d]: %v", pr.ID, err)
 | 
							log.Error("setMerged [%d]: %v", pr.ID, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,6 @@ import (
 | 
				
			|||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@@ -643,33 +642,74 @@ func GetSquashMergeCommitMessages(pr *models.PullRequest) string {
 | 
				
			|||||||
	return stringBuilder.String()
 | 
						return stringBuilder.String()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetLastCommitStatus returns list of commit statuses for latest commit on this pull request.
 | 
					// GetIssuesLastCommitStatus returns a map
 | 
				
			||||||
func GetLastCommitStatus(pr *models.PullRequest) (status []*models.CommitStatus, err error) {
 | 
					func GetIssuesLastCommitStatus(issues models.IssueList) (map[int64]*models.CommitStatus, error) {
 | 
				
			||||||
	if err = pr.LoadBaseRepo(); err != nil {
 | 
						if err := issues.LoadPullRequests(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, err := issues.LoadRepositories(); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							gitRepos = make(map[int64]*git.Repository)
 | 
				
			||||||
 | 
							res      = make(map[int64]*models.CommitStatus)
 | 
				
			||||||
 | 
							err      error
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							for _, gitRepo := range gitRepos {
 | 
				
			||||||
 | 
								gitRepo.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, issue := range issues {
 | 
				
			||||||
 | 
							if !issue.IsPull {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							gitRepo, ok := gitRepos[issue.RepoID]
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								gitRepo, err = git.OpenRepository(issue.Repo.RepoPath())
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								gitRepos[issue.RepoID] = gitRepo
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							status, err := getLastCommitStatus(gitRepo, issue.PullRequest)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							res[issue.PullRequest.ID] = status
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetLastCommitStatus returns list of commit statuses for latest commit on this pull request.
 | 
				
			||||||
 | 
					func GetLastCommitStatus(pr *models.PullRequest) (status *models.CommitStatus, err error) {
 | 
				
			||||||
 | 
						if err = pr.LoadBaseRepo(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath())
 | 
						gitRepo, err := git.OpenRepository(pr.BaseRepo.RepoPath())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer gitRepo.Close()
 | 
						defer gitRepo.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	compareInfo, err := gitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName())
 | 
						return getLastCommitStatus(gitRepo, pr)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getLastCommitStatus get pr's last commit status. PR's last commit status is the head commit id's last commit status
 | 
				
			||||||
 | 
					func getLastCommitStatus(gitRepo *git.Repository, pr *models.PullRequest) (status *models.CommitStatus, err error) {
 | 
				
			||||||
 | 
						sha, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if compareInfo.Commits.Len() == 0 {
 | 
					 | 
				
			||||||
		return nil, errors.New("pull request has no commits")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sha := compareInfo.Commits.Front().Value.(*git.Commit).ID.String()
 | 
					 | 
				
			||||||
	statusList, err := models.GetLatestCommitStatus(pr.BaseRepo.ID, sha, models.ListOptions{})
 | 
						statusList, err := models.GetLatestCommitStatus(pr.BaseRepo.ID, sha, models.ListOptions{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return statusList, nil
 | 
						return models.CalcCommitStatus(statusList), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head
 | 
					// IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,7 +70,7 @@
 | 
				
			|||||||
				<dd>{{if not .SSH.Disabled}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 | 
									<dd>{{if not .SSH.Disabled}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 | 
				
			||||||
				{{if not .SSH.Disabled}}
 | 
									{{if not .SSH.Disabled}}
 | 
				
			||||||
					<dt>{{.i18n.Tr "admin.config.ssh_start_builtin_server"}}</dt>
 | 
										<dt>{{.i18n.Tr "admin.config.ssh_start_builtin_server"}}</dt>
 | 
				
			||||||
					<dd>{{if not .SSH.StartBuiltinServer}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 | 
										<dd>{{if .SSH.StartBuiltinServer}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd>
 | 
				
			||||||
					<dt>{{.i18n.Tr "admin.config.ssh_domain"}}</dt>
 | 
										<dt>{{.i18n.Tr "admin.config.ssh_domain"}}</dt>
 | 
				
			||||||
					<dd>{{.SSH.Domain}}</dd>
 | 
										<dd>{{.SSH.Domain}}</dd>
 | 
				
			||||||
					<dt>{{.i18n.Tr "admin.config.ssh_port"}}</dt>
 | 
										<dt>{{.i18n.Tr "admin.config.ssh_port"}}</dt>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,29 +3,27 @@
 | 
				
			|||||||
	{{template "explore/navbar" .}}
 | 
						{{template "explore/navbar" .}}
 | 
				
			||||||
	<div class="ui container">
 | 
						<div class="ui container">
 | 
				
			||||||
		<form class="ui form ignore-dirty" style="max-width: 100%">
 | 
							<form class="ui form ignore-dirty" style="max-width: 100%">
 | 
				
			||||||
            <input type="hidden" name="tab" value="{{$.TabName}}">
 | 
								<input type="hidden" name="tab" value="{{$.TabName}}">
 | 
				
			||||||
            <div class="ui fluid action input">
 | 
								<div class="ui fluid action input">
 | 
				
			||||||
            <div class="twelve wide field">
 | 
									<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
 | 
				
			||||||
                <input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
 | 
									<div class="ui dropdown selection">
 | 
				
			||||||
            </div>
 | 
										<input name="t" type="hidden" value="{{.queryType}}">{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 | 
				
			||||||
            <div class="two wide field">
 | 
										<div class="text">{{.i18n.Tr (printf "explore.search.%s" (or .queryType "fuzzy"))}}</div>
 | 
				
			||||||
                <select name="t">
 | 
										<div class="menu transition hidden" tabindex="-1" style="display: block !important;">
 | 
				
			||||||
                    <option value="">{{.i18n.Tr "explore.search.fuzzy"}}</option>
 | 
											<div class="item" data-value="">{{.i18n.Tr "explore.search.fuzzy"}}</div>
 | 
				
			||||||
                    <option value="match" {{if eq .queryType "match"}}selected{{end}}>{{.i18n.Tr "explore.search.match"}}</option>
 | 
											<div class="item" data-value="match">{{.i18n.Tr "explore.search.match"}}</div>
 | 
				
			||||||
                </select>
 | 
										</div>
 | 
				
			||||||
            </div>
 | 
									</div>
 | 
				
			||||||
            <div class="three field">
 | 
									<button class="ui blue button">{{.i18n.Tr "explore.search"}}</button>
 | 
				
			||||||
                <button class="ui blue button">{{.i18n.Tr "explore.search"}}</button>
 | 
								</div>
 | 
				
			||||||
            </div>
 | 
							</form>
 | 
				
			||||||
            </div>
 | 
							<div class="ui divider"></div>
 | 
				
			||||||
        </form>
 | 
					 | 
				
			||||||
        <div class="ui divider"></div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<div class="ui user list">
 | 
							<div class="ui user list">
 | 
				
			||||||
			{{if .SearchResults}}
 | 
								{{if .SearchResults}}
 | 
				
			||||||
                <h3>
 | 
									<h3>
 | 
				
			||||||
                    {{.i18n.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html }}
 | 
										{{.i18n.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html }}
 | 
				
			||||||
                </h3>
 | 
									</h3>
 | 
				
			||||||
				<div class="df ac fw">
 | 
									<div class="df ac fw">
 | 
				
			||||||
					{{range $term := .SearchResultLanguages}}
 | 
										{{range $term := .SearchResultLanguages}}
 | 
				
			||||||
					<a class="ui text-label df ac mr-1 my-1 {{if eq $.Language $term.Language}}primary {{end}}basic label" href="{{AppSubUrl}}/explore/code?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}{{if ne $.queryType ""}}&t={{$.queryType}}{{end}}">
 | 
										<a class="ui text-label df ac mr-1 my-1 {{if eq $.Language $term.Language}}primary {{end}}basic label" href="{{AppSubUrl}}/explore/code?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}{{if ne $.queryType ""}}&t={{$.queryType}}{{end}}">
 | 
				
			||||||
@@ -35,34 +33,34 @@
 | 
				
			|||||||
					</a>
 | 
										</a>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
                <div class="repository search">
 | 
									<div class="repository search">
 | 
				
			||||||
                    {{range $result := .SearchResults}}
 | 
										{{range $result := .SearchResults}}
 | 
				
			||||||
                        {{$repo := (index $.RepoMaps .RepoID)}}
 | 
											{{$repo := (index $.RepoMaps .RepoID)}}
 | 
				
			||||||
                        <div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
 | 
											<div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
 | 
				
			||||||
                            <h4 class="ui top attached normal header">
 | 
												<h4 class="ui top attached normal header">
 | 
				
			||||||
                                <span class="file"><a rel="nofollow" href="{{EscapePound $repo.HTMLURL}}">{{$repo.FullName}}</a> - {{.Filename}}</span>
 | 
													<span class="file"><a rel="nofollow" href="{{EscapePound $repo.HTMLURL}}">{{$repo.FullName}}</a> - {{.Filename}}</span>
 | 
				
			||||||
                                <a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $repo.HTMLURL}}/src/commit/{{$result.CommitID}}/{{EscapePound .Filename}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
 | 
													<a class="ui basic tiny button" rel="nofollow" href="{{EscapePound $repo.HTMLURL}}/src/commit/{{$result.CommitID}}/{{EscapePound .Filename}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
 | 
				
			||||||
                            </h4>
 | 
												</h4>
 | 
				
			||||||
                            <div class="ui attached table segment">
 | 
												<div class="ui attached table segment">
 | 
				
			||||||
                                <div class="file-body file-code code-view">
 | 
													<div class="file-body file-code code-view">
 | 
				
			||||||
                                    <table>
 | 
														<table>
 | 
				
			||||||
                                        <tbody>
 | 
															<tbody>
 | 
				
			||||||
                                            <tr>
 | 
																<tr>
 | 
				
			||||||
                                                <td class="lines-num">
 | 
																	<td class="lines-num">
 | 
				
			||||||
                                                    {{range .LineNumbers}}
 | 
																		{{range .LineNumbers}}
 | 
				
			||||||
                                                        <a href="{{EscapePound $repo.HTMLURL}}/src/commit/{{$result.CommitID}}/{{EscapePound $result.Filename}}#L{{.}}"><span>{{.}}</span></a>
 | 
																			<a href="{{EscapePound $repo.HTMLURL}}/src/commit/{{$result.CommitID}}/{{EscapePound $result.Filename}}#L{{.}}"><span>{{.}}</span></a>
 | 
				
			||||||
                                                    {{end}}
 | 
																		{{end}}
 | 
				
			||||||
                                                </td>
 | 
																	</td>
 | 
				
			||||||
                                                <td class="lines-code"><pre><code class="chroma"><ol class="linenums">{{.FormattedLines | Safe}}</ol></code></pre></td>
 | 
																	<td class="lines-code"><pre><code class="chroma"><ol class="linenums">{{.FormattedLines | Safe}}</ol></code></pre></td>
 | 
				
			||||||
                                            </tr>
 | 
																</tr>
 | 
				
			||||||
                                        </tbody>
 | 
															</tbody>
 | 
				
			||||||
                                    </table>
 | 
														</table>
 | 
				
			||||||
                                </div>
 | 
													</div>
 | 
				
			||||||
                            </div>
 | 
												</div>
 | 
				
			||||||
                            {{template "shared/searchbottom" dict "root" $ "result" .}}
 | 
												{{template "shared/searchbottom" dict "root" $ "result" .}}
 | 
				
			||||||
                        </div>
 | 
											</div>
 | 
				
			||||||
                    {{end}}
 | 
										{{end}}
 | 
				
			||||||
                </div>
 | 
									</div>
 | 
				
			||||||
			{{else}}
 | 
								{{else}}
 | 
				
			||||||
				<div>{{$.i18n.Tr "explore.code_no_results"}}</div>
 | 
									<div>{{$.i18n.Tr "explore.code_no_results"}}</div>
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@
 | 
				
			|||||||
						{{else if eq .HookType "discord"}}
 | 
											{{else if eq .HookType "discord"}}
 | 
				
			||||||
							<img width="26" height="26" src="{{StaticUrlPrefix}}/img/discord.png">
 | 
												<img width="26" height="26" src="{{StaticUrlPrefix}}/img/discord.png">
 | 
				
			||||||
						{{else if eq .HookType "dingtalk"}}
 | 
											{{else if eq .HookType "dingtalk"}}
 | 
				
			||||||
							<img width="26" height="26" src="{{StaticUrlPrefix}}/img/dingtalk.png">
 | 
												<img width="26" height="26" src="{{StaticUrlPrefix}}/img/dingtalk.ico">
 | 
				
			||||||
						{{else if eq .HookType "telegram"}}
 | 
											{{else if eq .HookType "telegram"}}
 | 
				
			||||||
							<img width="26" height="26" src="{{StaticUrlPrefix}}/img/telegram.png">
 | 
												<img width="26" height="26" src="{{StaticUrlPrefix}}/img/telegram.png">
 | 
				
			||||||
						{{else if eq .HookType "msteams"}}
 | 
											{{else if eq .HookType "msteams"}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,7 @@
 | 
				
			|||||||
						</td>
 | 
											</td>
 | 
				
			||||||
						<td class="right aligned overflow-visible">
 | 
											<td class="right aligned overflow-visible">
 | 
				
			||||||
							<div class="ui basic jump dropdown icon button poping up" data-content="{{$.i18n.Tr "repo.branch.download" ($.DefaultBranch)}}" data-variation="tiny inverted" data-position="top right">
 | 
												<div class="ui basic jump dropdown icon button poping up" data-content="{{$.i18n.Tr "repo.branch.download" ($.DefaultBranch)}}" data-variation="tiny inverted" data-position="top right">
 | 
				
			||||||
							  <i class="download icon"></i>
 | 
												  {{svg "octicon-download"}}
 | 
				
			||||||
							  <div class="menu">
 | 
												  <div class="menu">
 | 
				
			||||||
							    <a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.DefaultBranch}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
												    <a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.DefaultBranch}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
				
			||||||
							    <a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.DefaultBranch}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
												    <a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.DefaultBranch}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
				
			||||||
@@ -91,20 +91,20 @@
 | 
				
			|||||||
											</a>
 | 
																</a>
 | 
				
			||||||
											{{end}}
 | 
																{{end}}
 | 
				
			||||||
										{{else}}
 | 
															{{else}}
 | 
				
			||||||
											<a href="{{.LatestPullRequest.Issue.HTMLURL}}">{{if not .LatestPullRequest.IsSameRepo}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
 | 
																<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="vm">{{if not .LatestPullRequest.IsSameRepo}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
 | 
				
			||||||
											{{if .LatestPullRequest.HasMerged}}
 | 
																{{if .LatestPullRequest.HasMerged}}
 | 
				
			||||||
												<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui text-label purple mini label">{{svg "octicon-git-merge"}} {{$.i18n.Tr "repo.pulls.merged"}}</a>
 | 
																	<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui text-label purple large label vm">{{svg "octicon-git-merge" 16 "mr-2"}}{{$.i18n.Tr "repo.pulls.merged"}}</a>
 | 
				
			||||||
											{{else if .LatestPullRequest.Issue.IsClosed}}
 | 
																{{else if .LatestPullRequest.Issue.IsClosed}}
 | 
				
			||||||
												<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui text-label red mini label">{{svg "octicon-git-pull-request"}} {{$.i18n.Tr "repo.issues.closed_title"}}</a>
 | 
																	<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui text-label red large label vm">{{svg "octicon-git-pull-request" 16 "mr-2"}}{{$.i18n.Tr "repo.issues.closed_title"}}</a>
 | 
				
			||||||
											{{else}}
 | 
																{{else}}
 | 
				
			||||||
												<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui text-label green mini label">{{svg "octicon-git-pull-request"}} {{$.i18n.Tr "repo.issues.open_title"}}</a>
 | 
																	<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui text-label green large label vm">{{svg "octicon-git-pull-request" 16 "mr-2"}}{{$.i18n.Tr "repo.issues.open_title"}}</a>
 | 
				
			||||||
											{{end}}
 | 
																{{end}}
 | 
				
			||||||
										{{end}}
 | 
															{{end}}
 | 
				
			||||||
									</td>
 | 
														</td>
 | 
				
			||||||
									<td class="two wide right aligned overflow-visible">
 | 
														<td class="two wide right aligned overflow-visible">
 | 
				
			||||||
										{{if (not .IsDeleted)}}
 | 
															{{if (not .IsDeleted)}}
 | 
				
			||||||
											<div class="ui basic jump dropdown icon button poping up" data-content="{{$.i18n.Tr "repo.branch.download" (.Name)}}" data-variation="tiny inverted" data-position="top right">
 | 
																<div class="ui basic jump dropdown icon button poping up" data-content="{{$.i18n.Tr "repo.branch.download" (.Name)}}" data-variation="tiny inverted" data-position="top right">
 | 
				
			||||||
												<i class="download icon"></i>
 | 
																	{{svg "octicon-download"}}
 | 
				
			||||||
												<div class="menu">
 | 
																	<div class="menu">
 | 
				
			||||||
													<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound .Name}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
																		<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound .Name}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
				
			||||||
													<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound .Name}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
																		<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound .Name}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,17 @@
 | 
				
			|||||||
{{if not $.DisableHTTP}}
 | 
					{{if not $.DisableHTTP}}
 | 
				
			||||||
	<button class="ui basic clone button no-transition" id="repo-clone-https" data-link="{{.CloneLink.HTTPS}}">
 | 
						<button class="ui basic clone button no-transition" id="repo-clone-https" data-link="{{if $.PageIsWiki}}{{$.WikiCloneLink.HTTPS}}{{else}}{{$.CloneLink.HTTPS}}{{end}}">
 | 
				
			||||||
		{{if UseHTTPS}}HTTPS{{else}}HTTP{{end}}
 | 
							{{if UseHTTPS}}HTTPS{{else}}HTTP{{end}}
 | 
				
			||||||
	</button>
 | 
						</button>
 | 
				
			||||||
{{end}}
 | 
					{{end}}
 | 
				
			||||||
{{if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}}
 | 
					{{if and (not $.DisableSSH) (or $.IsSigned $.ExposeAnonSSH)}}
 | 
				
			||||||
	<button class="ui basic clone button no-transition" id="repo-clone-ssh" data-link="{{.CloneLink.SSH}}">
 | 
						<button class="ui basic clone button no-transition" id="repo-clone-ssh" data-link="{{if $.PageIsWiki}}{{$.WikiCloneLink.SSH}}{{else}}{{$.CloneLink.SSH}}{{end}}">
 | 
				
			||||||
		SSH
 | 
							SSH
 | 
				
			||||||
	</button>
 | 
						</button>
 | 
				
			||||||
{{end}}
 | 
					{{end}}
 | 
				
			||||||
{{if not $.DisableHTTP}}
 | 
					{{if not $.DisableHTTP}}
 | 
				
			||||||
	<input id="repo-clone-url" value="{{$.CloneLink.HTTPS}}" readonly>
 | 
						<input id="repo-clone-url" value="{{if $.PageIsWiki}}{{$.WikiCloneLink.HTTPS}}{{else}}{{$.CloneLink.HTTPS}}{{end}}" readonly>
 | 
				
			||||||
{{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="{{if $.PageIsWiki}}{{$.WikiCloneLink.SSH}}{{else}}{{$.CloneLink.SSH}}{{end}}" 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">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@
 | 
				
			|||||||
				<div class="ui attached segment">
 | 
									<div class="ui attached segment">
 | 
				
			||||||
					{{template "base/alert" .}}
 | 
										{{template "base/alert" .}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					<p class="ui center">{{.i18n.Tr "repo.new_repo_helper" "/repo/migrate" | Safe}}</p>
 | 
										<p class="ui center">{{.i18n.Tr "repo.new_repo_helper" (printf "%s%s" AppSubUrl "/repo/migrate") | Safe}}</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					{{if not .CanCreateRepo}}
 | 
										{{if not .CanCreateRepo}}
 | 
				
			||||||
						<div class="ui negative message">
 | 
											<div class="ui negative message">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,15 +43,13 @@
 | 
				
			|||||||
		</ol>
 | 
							</ol>
 | 
				
			||||||
		{{range $i, $file := .Diff.Files}}
 | 
							{{range $i, $file := .Diff.Files}}
 | 
				
			||||||
			{{if $file.IsIncomplete}}
 | 
								{{if $file.IsIncomplete}}
 | 
				
			||||||
				<div class="diff-file-box diff-box file-content">
 | 
									<div class="diff-file-box diff-box file-content mt-3">
 | 
				
			||||||
					<h4 class="ui top attached normal header rounded">
 | 
										<h4 class="ui top attached normal header rounded">
 | 
				
			||||||
						<a role="button" class="fold-file muted mr-2">
 | 
											<a role="button" class="fold-file muted mr-2">
 | 
				
			||||||
							{{svg "octicon-chevron-down" 18}}
 | 
												{{svg "octicon-chevron-down" 18}}
 | 
				
			||||||
						</a>
 | 
											</a>
 | 
				
			||||||
						<div class="bold ui left df ac">
 | 
											<div class="bold ui left df ac">
 | 
				
			||||||
							{{if not $file.IsRenamed}}
 | 
												{{template "repo/diff/stats" dict "file" . "root" $}}
 | 
				
			||||||
								{{template "repo/diff/stats" dict "file" . "root" $}}
 | 
					 | 
				
			||||||
							{{end}}
 | 
					 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
						<span class="file mono">{{$file.Name}}</span>
 | 
											<span class="file mono">{{$file.Name}}</span>
 | 
				
			||||||
						<div class="diff-file-header-actions df ac">
 | 
											<div class="diff-file-header-actions df ac">
 | 
				
			||||||
@@ -70,7 +68,7 @@
 | 
				
			|||||||
					</h4>
 | 
										</h4>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			{{else}}
 | 
								{{else}}
 | 
				
			||||||
				<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}" id="diff-{{.Index}}">
 | 
									<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}} mt-3" id="diff-{{.Index}}">
 | 
				
			||||||
					<h4 class="diff-file-header sticky-2nd-row ui top attached normal header df ac sb">
 | 
										<h4 class="diff-file-header sticky-2nd-row ui top attached normal header df ac sb">
 | 
				
			||||||
						<div class="df ac">
 | 
											<div class="df ac">
 | 
				
			||||||
							{{$isImage := false}}
 | 
												{{$isImage := false}}
 | 
				
			||||||
@@ -85,7 +83,7 @@
 | 
				
			|||||||
							<div class="bold df ac">
 | 
												<div class="bold df ac">
 | 
				
			||||||
								{{if $file.IsBin}}
 | 
													{{if $file.IsBin}}
 | 
				
			||||||
									{{$.i18n.Tr "repo.diff.bin"}}
 | 
														{{$.i18n.Tr "repo.diff.bin"}}
 | 
				
			||||||
								{{else if not $file.IsRenamed}}
 | 
													{{else}}
 | 
				
			||||||
									{{template "repo/diff/stats" dict "file" . "root" $}}
 | 
														{{template "repo/diff/stats" dict "file" . "root" $}}
 | 
				
			||||||
								{{end}}
 | 
													{{end}}
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
@@ -105,30 +103,28 @@
 | 
				
			|||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</h4>
 | 
										</h4>
 | 
				
			||||||
					<div class="diff-file-body ui attached unstackable table segment">
 | 
										<div class="diff-file-body ui attached unstackable table segment">
 | 
				
			||||||
						{{if ne $file.Type 4}}
 | 
											<div class="file-body file-code has-context-menu{{if not $isImage}} code-diff{{end}}{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $isImage}} py-4{{end}}">
 | 
				
			||||||
							<div class="file-body file-code has-context-menu{{if not $isImage}} code-diff{{end}}{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $isImage}} py-4{{end}}">
 | 
												<table class="chroma{{if $isImage}} w-100{{end}}">
 | 
				
			||||||
								<table class="chroma{{if $isImage}} w-100{{end}}">
 | 
													<tbody>
 | 
				
			||||||
									<tbody>
 | 
														{{if $isImage}}
 | 
				
			||||||
										{{if $isImage}}
 | 
															{{template "repo/diff/image_diff" dict "file" . "root" $}}
 | 
				
			||||||
											{{template "repo/diff/image_diff" dict "file" . "root" $}}
 | 
														{{else}}
 | 
				
			||||||
 | 
															{{if $.IsSplitStyle}}
 | 
				
			||||||
 | 
																{{template "repo/diff/section_split" dict "file" . "root" $}}
 | 
				
			||||||
										{{else}}
 | 
															{{else}}
 | 
				
			||||||
											{{if $.IsSplitStyle}}
 | 
																{{template "repo/diff/section_unified" dict "file" . "root" $}}
 | 
				
			||||||
												{{template "repo/diff/section_split" dict "file" . "root" $}}
 | 
					 | 
				
			||||||
											{{else}}
 | 
					 | 
				
			||||||
												{{template "repo/diff/section_unified" dict "file" . "root" $}}
 | 
					 | 
				
			||||||
											{{end}}
 | 
					 | 
				
			||||||
										{{end}}
 | 
															{{end}}
 | 
				
			||||||
									</tbody>
 | 
														{{end}}
 | 
				
			||||||
								</table>
 | 
													</tbody>
 | 
				
			||||||
							</div>
 | 
												</table>
 | 
				
			||||||
						{{end}}
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{{if .Diff.IsIncomplete}}
 | 
							{{if .Diff.IsIncomplete}}
 | 
				
			||||||
			<div class="diff-file-box diff-box file-content">
 | 
								<div class="diff-file-box diff-box file-content mt-3">
 | 
				
			||||||
				<h4 class="ui top attached normal header">
 | 
									<h4 class="ui top attached normal header">
 | 
				
			||||||
					{{$.i18n.Tr "repo.diff.too_many_files"}}
 | 
										{{$.i18n.Tr "repo.diff.too_many_files"}}
 | 
				
			||||||
				</h4>
 | 
									</h4>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@
 | 
				
			|||||||
{{ $createdStr:= TimeSinceUnix .CreatedUnix $.root.Lang }}
 | 
					{{ $createdStr:= TimeSinceUnix .CreatedUnix $.root.Lang }}
 | 
				
			||||||
<div class="comment" id="{{.HashTag}}">
 | 
					<div class="comment" id="{{.HashTag}}">
 | 
				
			||||||
	{{if .OriginalAuthor }}
 | 
						{{if .OriginalAuthor }}
 | 
				
			||||||
		<span class="avatar"><img src="/img/avatar_default.png"></span>
 | 
							<span class="avatar"><img src="{{AppSubUrl}}/img/avatar_default.png"></span>
 | 
				
			||||||
	{{else}}
 | 
						{{else}}
 | 
				
			||||||
		<a class="avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
 | 
							<a class="avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
 | 
				
			||||||
			{{avatar .Poster}}
 | 
								{{avatar .Poster}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,16 +3,20 @@
 | 
				
			|||||||
{{$isNotPending := (not (eq (index .comments 0).Review.Type 0))}}
 | 
					{{$isNotPending := (not (eq (index .comments 0).Review.Type 0))}}
 | 
				
			||||||
<div class="conversation-holder" data-path="{{(index .comments 0).TreePath}}" data-side="{{if lt (index .comments 0).Line 0}}left{{else}}right{{end}}" data-idx="{{(index .comments 0).UnsignedLine}}">
 | 
					<div class="conversation-holder" data-path="{{(index .comments 0).TreePath}}" data-side="{{if lt (index .comments 0).Line 0}}left{{else}}right{{end}}" data-idx="{{(index .comments 0).UnsignedLine}}">
 | 
				
			||||||
	{{if $resolved}}
 | 
						{{if $resolved}}
 | 
				
			||||||
		<div class="ui attached header resolved-placeholder">
 | 
							<div class="ui attached header resolved-placeholder df ac sb">
 | 
				
			||||||
			<span class="ui grey text left"><b>{{$resolveDoer.Name}}</b> {{$.i18n.Tr "repo.issues.review.resolved_by"}}</span>
 | 
								<div class="ui grey text">
 | 
				
			||||||
			<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny right labeled button show-outdated">
 | 
									<b>{{$resolveDoer.Name}}</b> {{$.i18n.Tr "repo.issues.review.resolved_by"}}
 | 
				
			||||||
				{{svg "octicon-unfold"}}
 | 
								</div>
 | 
				
			||||||
				{{$.i18n.Tr "repo.issues.review.show_resolved"}}
 | 
								<div>
 | 
				
			||||||
			</button>
 | 
									<button id="show-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="ui tiny right labeled button show-outdated df ac">
 | 
				
			||||||
			<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="hide ui tiny right labeled button hide-outdated">
 | 
										{{svg "octicon-unfold" 16 "mr-3"}}
 | 
				
			||||||
				{{svg "octicon-fold"}}
 | 
										{{$.i18n.Tr "repo.issues.review.show_resolved"}}
 | 
				
			||||||
				{{$.i18n.Tr "repo.issues.review.hide_resolved"}}
 | 
									</button>
 | 
				
			||||||
			</button>
 | 
									<button id="hide-outdated-{{(index .comments 0).ID}}" data-comment="{{(index .comments 0).ID}}" class="hide ui tiny right labeled button hide-outdated df ac">
 | 
				
			||||||
 | 
										{{svg "octicon-fold" 16 "mr-3"}}
 | 
				
			||||||
 | 
										{{$.i18n.Tr "repo.issues.review.hide_resolved"}}
 | 
				
			||||||
 | 
									</button>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	{{end}}
 | 
						{{end}}
 | 
				
			||||||
	<div id="code-comments-{{(index  .comments 0).ID}}" class="field comment-code-cloud {{if $resolved}}hide{{end}}">
 | 
						<div id="code-comments-{{(index  .comments 0).ID}}" class="field comment-code-cloud {{if $resolved}}hide{{end}}">
 | 
				
			||||||
@@ -32,4 +36,4 @@
 | 
				
			|||||||
			</button>
 | 
								</button>
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
<div id="rev-container">
 | 
					<div id="rev-container">
 | 
				
			||||||
	<ul id="rev-list">
 | 
						<ul id="rev-list">
 | 
				
			||||||
		{{ range $commitI, $commit := .Graph.Commits }}
 | 
							{{ range $commitI, $commit := .Graph.Commits }}
 | 
				
			||||||
			<li id="commit-{{$commit.Rev}}" data-flow="{{$commit.Flow}}">
 | 
								<li {{if $commit.Rev}}id="commit-{{$commit.Rev}}"{{end}} data-flow="{{$commit.Flow}}">
 | 
				
			||||||
				{{ if $commit.OnlyRelation }}
 | 
									{{ if $commit.OnlyRelation }}
 | 
				
			||||||
					<span />
 | 
										<span></span>
 | 
				
			||||||
				{{ else }}
 | 
									{{ else }}
 | 
				
			||||||
					<span class="sha" id="{{$commit.ShortRev}}">
 | 
										<span class="sha" id="{{$commit.ShortRev}}">
 | 
				
			||||||
						{{$class := "ui sha label"}}
 | 
											{{$class := "ui sha label"}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -110,13 +110,13 @@
 | 
				
			|||||||
				{{if eq $n 0}}
 | 
									{{if eq $n 0}}
 | 
				
			||||||
					<div class="ui action tiny input" id="clone-panel">
 | 
										<div class="ui action tiny input" id="clone-panel">
 | 
				
			||||||
						{{template "repo/clone_buttons" .}}
 | 
											{{template "repo/clone_buttons" .}}
 | 
				
			||||||
						<div class="ui basic jump dropdown icon button poping up" data-content="{{.i18n.Tr "repo.download_archive"}}" data-variation="tiny inverted" data-position="top right">
 | 
											<button id="download-btn" class="ui basic jump dropdown icon button poping up" data-content="{{.i18n.Tr "repo.download_archive"}}" data-variation="tiny inverted" data-position="top right">
 | 
				
			||||||
							{{svg "octicon-download"}}
 | 
												{{svg "octicon-download"}}
 | 
				
			||||||
							<div class="menu">
 | 
												<div class="menu">
 | 
				
			||||||
								<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
													<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.zip">{{svg "octicon-file-zip"}} ZIP</a>
 | 
				
			||||||
								<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
													<a class="item archive-link" data-url="{{$.RepoLink}}/archive/{{EscapePound $.BranchName}}.tar.gz">{{svg "octicon-file-zip"}} TAR.GZ</a>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
											</button>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@
 | 
				
			|||||||
				</div>
 | 
									</div>
 | 
				
			||||||
				<div class="three wide column">
 | 
									<div class="three wide column">
 | 
				
			||||||
					{{if $.PageIsOrgSettingsLabels}}
 | 
										{{if $.PageIsOrgSettingsLabels}}
 | 
				
			||||||
						<a class="ui right open-issues" href="/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
 | 
											<a class="ui right open-issues" href="{{AppSubUrl}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
 | 
				
			||||||
					{{else}}
 | 
										{{else}}
 | 
				
			||||||
						<a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
 | 
											<a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}">{{svg "octicon-issue-opened"}} {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,8 +4,8 @@
 | 
				
			|||||||
	<div class="ui container">
 | 
						<div class="ui container">
 | 
				
			||||||
		<div class="ui three column stackable grid">
 | 
							<div class="ui three column stackable grid">
 | 
				
			||||||
			<div class="column">
 | 
								<div class="column">
 | 
				
			||||||
				<h3>{{.Milestone.Name}}</h3>
 | 
									<h1>{{.Milestone.Name}}</h1>
 | 
				
			||||||
				<div class="content">
 | 
									<div class="markdown content">
 | 
				
			||||||
					{{.Milestone.RenderedContent|Str2html}}
 | 
										{{.Milestone.RenderedContent|Str2html}}
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,7 @@
 | 
				
			|||||||
		<div class="milestone list">
 | 
							<div class="milestone list">
 | 
				
			||||||
			{{range .Milestones}}
 | 
								{{range .Milestones}}
 | 
				
			||||||
				<li class="item">
 | 
									<li class="item">
 | 
				
			||||||
					{{svg "octicon-milestone"}} <a href="{{$.RepoLink}}/milestone/{{.ID}}">{{.Name}}</a>
 | 
										{{svg "octicon-milestone" 16 "mr-2"}} <a href="{{$.RepoLink}}/milestone/{{.ID}}">{{.Name}}</a>
 | 
				
			||||||
					<div class="ui right green progress" data-percent="{{.Completeness}}">
 | 
										<div class="ui right green progress" data-percent="{{.Completeness}}">
 | 
				
			||||||
						<div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}>
 | 
											<div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}>
 | 
				
			||||||
							<div class="progress"></div>
 | 
												<div class="progress"></div>
 | 
				
			||||||
@@ -80,7 +80,7 @@
 | 
				
			|||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
					{{if .Content}}
 | 
										{{if .Content}}
 | 
				
			||||||
						<div class="content">
 | 
											<div class="markdown content">
 | 
				
			||||||
							{{.RenderedContent|Str2html}}
 | 
												{{.RenderedContent|Str2html}}
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@
 | 
				
			|||||||
		<ui class="ui timeline">
 | 
							<ui class="ui timeline">
 | 
				
			||||||
			<div id="{{.Issue.HashTag}}" class="timeline-item comment first">
 | 
								<div id="{{.Issue.HashTag}}" class="timeline-item comment first">
 | 
				
			||||||
			{{if .Issue.OriginalAuthor }}
 | 
								{{if .Issue.OriginalAuthor }}
 | 
				
			||||||
				<span class="timeline-avatar"><img src="/img/avatar_default.png"></span>
 | 
									<span class="timeline-avatar"><img src="{{AppSubUrl}}/img/avatar_default.png"></span>
 | 
				
			||||||
			{{else}}
 | 
								{{else}}
 | 
				
			||||||
				<a class="timeline-avatar" {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>
 | 
									<a class="timeline-avatar" {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>
 | 
				
			||||||
					{{avatar .Issue.Poster}}
 | 
										{{avatar .Issue.Poster}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,12 +8,12 @@
 | 
				
			|||||||
	 18 = REMOVED_DEADLINE, 19 = ADD_DEPENDENCY, 20 = REMOVE_DEPENDENCY, 21 = CODE,
 | 
						 18 = REMOVED_DEADLINE, 19 = ADD_DEPENDENCY, 20 = REMOVE_DEPENDENCY, 21 = CODE,
 | 
				
			||||||
	 22 = REVIEW, 23 = ISSUE_LOCKED, 24 = ISSUE_UNLOCKED, 25 = TARGET_BRANCH_CHANGED,
 | 
						 22 = REVIEW, 23 = ISSUE_LOCKED, 24 = ISSUE_UNLOCKED, 25 = TARGET_BRANCH_CHANGED,
 | 
				
			||||||
	 26 = DELETE_TIME_MANUAL, 27 = REVIEW_REQUEST, 28 = MERGE_PULL_REQUEST,
 | 
						 26 = DELETE_TIME_MANUAL, 27 = REVIEW_REQUEST, 28 = MERGE_PULL_REQUEST,
 | 
				
			||||||
	 29 = PULL_PUSH_EVENT, 30 = PROJECT_CHANGED, 31 = PROJECT_BOARD_CHANGED 
 | 
						 29 = PULL_PUSH_EVENT, 30 = PROJECT_CHANGED, 31 = PROJECT_BOARD_CHANGED
 | 
				
			||||||
	 32 = DISMISSED_REVIEW -->
 | 
						 32 = DISMISSED_REVIEW -->
 | 
				
			||||||
	{{if eq .Type 0}}
 | 
						{{if eq .Type 0}}
 | 
				
			||||||
		<div class="timeline-item comment" id="{{.HashTag}}">
 | 
							<div class="timeline-item comment" id="{{.HashTag}}">
 | 
				
			||||||
		{{if .OriginalAuthor }}
 | 
							{{if .OriginalAuthor }}
 | 
				
			||||||
			<span class="timeline-avatar"><img src="/img/avatar_default.png"></span>
 | 
								<span class="timeline-avatar"><img src="{{AppSubUrl}}/img/avatar_default.png"></span>
 | 
				
			||||||
		{{else}}
 | 
							{{else}}
 | 
				
			||||||
			<a class="timeline-avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
 | 
								<a class="timeline-avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
 | 
				
			||||||
				{{avatar .Poster}}
 | 
									{{avatar .Poster}}
 | 
				
			||||||
@@ -459,35 +459,39 @@
 | 
				
			|||||||
				{{ range $filename, $lines := .Review.CodeComments}}
 | 
									{{ range $filename, $lines := .Review.CodeComments}}
 | 
				
			||||||
					{{range $line, $comms := $lines}}
 | 
										{{range $line, $comms := $lines}}
 | 
				
			||||||
							<div class="ui segments">
 | 
												<div class="ui segments">
 | 
				
			||||||
								<div class="ui segment py-3">
 | 
													<div class="ui segment py-3 df ac sb">
 | 
				
			||||||
									{{$invalid := (index $comms 0).Invalidated}}
 | 
														{{$invalid := (index $comms 0).Invalidated}}
 | 
				
			||||||
									{{$resolved := (index $comms 0).IsResolved}}
 | 
														{{$resolved := (index $comms 0).IsResolved}}
 | 
				
			||||||
									{{$resolveDoer := (index $comms 0).ResolveDoer}}
 | 
														{{$resolveDoer := (index $comms 0).ResolveDoer}}
 | 
				
			||||||
									{{$isNotPending := (not (eq (index $comms 0).Review.Type 0))}}
 | 
														{{$isNotPending := (not (eq (index $comms 0).Review.Type 0))}}
 | 
				
			||||||
								{{if or $invalid $resolved}}
 | 
														<div class="df ac">
 | 
				
			||||||
									<button id="show-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="{{if not $resolved}}hide {{end}}ui compact right labeled button show-outdated">
 | 
															<a href="{{(index $comms 0).CodeCommentURL}}" class="file-comment ml-3">{{$filename}}</a>
 | 
				
			||||||
										{{svg "octicon-unfold"}}
 | 
															{{if $invalid }}
 | 
				
			||||||
										{{if $resolved}}
 | 
																<span class="ui label basic small ml-3">
 | 
				
			||||||
											{{$.i18n.Tr "repo.issues.review.show_resolved"}}
 | 
																	{{$.i18n.Tr "repo.issues.review.outdated"}}
 | 
				
			||||||
										{{else}}
 | 
																</span>
 | 
				
			||||||
											{{$.i18n.Tr "repo.issues.review.show_outdated"}}
 | 
					 | 
				
			||||||
										{{end}}
 | 
															{{end}}
 | 
				
			||||||
									</button>
 | 
														</div>
 | 
				
			||||||
									<button id="hide-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="{{if $resolved}}hide {{end}}ui compact right labeled button hide-outdated">
 | 
														<div>
 | 
				
			||||||
										{{svg "octicon-fold"}}
 | 
															{{if or $invalid $resolved}}
 | 
				
			||||||
										{{if $resolved}}
 | 
																<button id="show-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="{{if not $resolved}}hide {{end}}ui compact right labeled button show-outdated df ac">
 | 
				
			||||||
											{{$.i18n.Tr "repo.issues.review.hide_resolved"}}
 | 
																	{{svg "octicon-unfold" 16 "mr-3"}}
 | 
				
			||||||
										{{else}}
 | 
																	{{if $resolved}}
 | 
				
			||||||
											{{$.i18n.Tr "repo.issues.review.hide_outdated"}}
 | 
																		{{$.i18n.Tr "repo.issues.review.show_resolved"}}
 | 
				
			||||||
 | 
																	{{else}}
 | 
				
			||||||
 | 
																		{{$.i18n.Tr "repo.issues.review.show_outdated"}}
 | 
				
			||||||
 | 
																	{{end}}
 | 
				
			||||||
 | 
																</button>
 | 
				
			||||||
 | 
																<button id="hide-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="{{if $resolved}}hide {{end}}ui compact right labeled button hide-outdated df ac">
 | 
				
			||||||
 | 
																	{{svg "octicon-fold" 16 "mr-3"}}
 | 
				
			||||||
 | 
																	{{if $resolved}}
 | 
				
			||||||
 | 
																		{{$.i18n.Tr "repo.issues.review.hide_resolved"}}
 | 
				
			||||||
 | 
																	{{else}}
 | 
				
			||||||
 | 
																		{{$.i18n.Tr "repo.issues.review.hide_outdated"}}
 | 
				
			||||||
 | 
																	{{end}}
 | 
				
			||||||
 | 
																</button>
 | 
				
			||||||
										{{end}}
 | 
															{{end}}
 | 
				
			||||||
									</button>
 | 
														</div>
 | 
				
			||||||
								{{end}}
 | 
					 | 
				
			||||||
								<a href="{{(index $comms 0).CodeCommentURL}}" class="file-comment">{{$filename}}</a>
 | 
					 | 
				
			||||||
								{{if $invalid }}
 | 
					 | 
				
			||||||
									<span class="ui label basic small yellow">
 | 
					 | 
				
			||||||
										{{$.i18n.Tr "repo.issues.review.outdated"}}
 | 
					 | 
				
			||||||
									</span>
 | 
					 | 
				
			||||||
								{{end}}
 | 
					 | 
				
			||||||
								</div>
 | 
													</div>
 | 
				
			||||||
								{{$diff := (CommentMustAsDiff (index $comms 0))}}
 | 
													{{$diff := (CommentMustAsDiff (index $comms 0))}}
 | 
				
			||||||
								{{if $diff}}
 | 
													{{if $diff}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -578,8 +578,6 @@
 | 
				
			|||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
				</button>
 | 
									</button>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<div class="ui tiny modal" id="lock">
 | 
								<div class="ui tiny modal" id="lock">
 | 
				
			||||||
				<div class="header">
 | 
									<div class="header">
 | 
				
			||||||
					{{ if .Issue.IsLocked }}
 | 
										{{ if .Issue.IsLocked }}
 | 
				
			||||||
@@ -588,62 +586,61 @@
 | 
				
			|||||||
						{{.i18n.Tr "repo.issues.lock.title"}}
 | 
											{{.i18n.Tr "repo.issues.lock.title"}}
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
									<div class="content">
 | 
				
			||||||
			<div class="content">
 | 
										<div class="ui warning message text left">
 | 
				
			||||||
				<div class="ui warning message text left">
 | 
											{{ if .Issue.IsLocked }}
 | 
				
			||||||
					{{ if .Issue.IsLocked }}
 | 
												{{.i18n.Tr "repo.issues.unlock.notice_1"}}<br>
 | 
				
			||||||
						{{.i18n.Tr "repo.issues.unlock.notice_1"}}<br>
 | 
												{{.i18n.Tr "repo.issues.unlock.notice_2"}}<br>
 | 
				
			||||||
						{{.i18n.Tr "repo.issues.unlock.notice_2"}}<br>
 | 
											{{else}}
 | 
				
			||||||
					{{else}}
 | 
												{{.i18n.Tr "repo.issues.lock.notice_1"}}<br>
 | 
				
			||||||
						{{.i18n.Tr "repo.issues.lock.notice_1"}}<br>
 | 
												{{.i18n.Tr "repo.issues.lock.notice_2"}}<br>
 | 
				
			||||||
						{{.i18n.Tr "repo.issues.lock.notice_2"}}<br>
 | 
												{{.i18n.Tr "repo.issues.lock.notice_3"}}<br>
 | 
				
			||||||
						{{.i18n.Tr "repo.issues.lock.notice_3"}}<br>
 | 
											{{end}}
 | 
				
			||||||
					{{end}}
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<form class="ui form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}{{ if .Issue.IsLocked }}/unlock{{else}}/lock{{end}}"
 | 
										<form class="ui form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}{{ if .Issue.IsLocked }}/unlock{{else}}/lock{{end}}"
 | 
				
			||||||
					method="post">
 | 
											method="post">
 | 
				
			||||||
					{{.CsrfTokenHtml}}
 | 
											{{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					{{ if not .Issue.IsLocked }}
 | 
											{{ if not .Issue.IsLocked }}
 | 
				
			||||||
						<div class="field">
 | 
												<div class="field">
 | 
				
			||||||
							<strong> {{ .i18n.Tr "repo.issues.lock.reason" }} </strong>
 | 
													<strong> {{ .i18n.Tr "repo.issues.lock.reason" }} </strong>
 | 
				
			||||||
						</div>
 | 
												</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						<div class="field">
 | 
												<div class="field">
 | 
				
			||||||
							<div class="ui fluid dropdown selection" tabindex="0">
 | 
													<div class="ui fluid dropdown selection" tabindex="0">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
								<select name="reason">
 | 
														<select name="reason">
 | 
				
			||||||
									<option value=""> </option>
 | 
															<option value=""> </option>
 | 
				
			||||||
									{{range .LockReasons}}
 | 
															{{range .LockReasons}}
 | 
				
			||||||
										<option value="{{.}}">{{.}}</option>
 | 
																<option value="{{.}}">{{.}}</option>
 | 
				
			||||||
									{{end}}
 | 
															{{end}}
 | 
				
			||||||
								</select>
 | 
														</select>
 | 
				
			||||||
								{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 | 
														{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
								<div class="default text"> </div>
 | 
														<div class="default text"> </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
								<div class="menu transition hidden" tabindex="-1" style="display: block !important;">
 | 
														<div class="menu transition hidden" tabindex="-1" style="display: block !important;">
 | 
				
			||||||
									{{range .LockReasons}}
 | 
															{{range .LockReasons}}
 | 
				
			||||||
										<div class="item" data-value="{{.}}">{{.}}</div>
 | 
																<div class="item" data-value="{{.}}">{{.}}</div>
 | 
				
			||||||
									{{end}}
 | 
															{{end}}
 | 
				
			||||||
 | 
														</div>
 | 
				
			||||||
								</div>
 | 
													</div>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
											{{end}}
 | 
				
			||||||
					{{end}}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
					<div class="text right actions">
 | 
											<div class="text right actions">
 | 
				
			||||||
						<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
 | 
												<div class="ui cancel button">{{.i18n.Tr "settings.cancel"}}</div>
 | 
				
			||||||
						<button class="ui red button">
 | 
												<button class="ui red button">
 | 
				
			||||||
							{{ if .Issue.IsLocked }}
 | 
													{{ if .Issue.IsLocked }}
 | 
				
			||||||
								{{.i18n.Tr "repo.issues.unlock_confirm"}}
 | 
														{{.i18n.Tr "repo.issues.unlock_confirm"}}
 | 
				
			||||||
							{{else}}
 | 
													{{else}}
 | 
				
			||||||
								{{.i18n.Tr "repo.issues.lock_confirm"}}
 | 
														{{.i18n.Tr "repo.issues.lock_confirm"}}
 | 
				
			||||||
							{{end}}
 | 
													{{end}}
 | 
				
			||||||
						</button>
 | 
												</button>
 | 
				
			||||||
					</div>
 | 
											</div>
 | 
				
			||||||
				</form>
 | 
										</form>
 | 
				
			||||||
			</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -158,10 +158,23 @@
 | 
				
			|||||||
					<div class="card board-card" data-issue="{{.ID}}">
 | 
										<div class="card board-card" data-issue="{{.ID}}">
 | 
				
			||||||
						<div class="content">
 | 
											<div class="content">
 | 
				
			||||||
							<div class="header">
 | 
												<div class="header">
 | 
				
			||||||
								<span class="{{if .IsClosed}}red{{else}}green{{end}}">
 | 
													<span>
 | 
				
			||||||
									{{if .IsPull}}{{svg "octicon-git-merge"}}
 | 
														{{if .IsPull}}
 | 
				
			||||||
									{{else if .IsClosed}}{{svg "octicon-issue-closed"}}
 | 
															{{if .PullRequest.HasMerged}}
 | 
				
			||||||
									{{else}}{{svg "octicon-issue-opened"}}
 | 
																{{svg "octicon-git-merge" 16 "text purple"}}
 | 
				
			||||||
 | 
															{{else}}
 | 
				
			||||||
 | 
																{{if .IsClosed}}
 | 
				
			||||||
 | 
																	{{svg "octicon-git-pull-request" 16 "text red"}}
 | 
				
			||||||
 | 
																{{else}}
 | 
				
			||||||
 | 
																	{{svg "octicon-git-pull-request" 16 "text green"}}
 | 
				
			||||||
 | 
																{{end}}
 | 
				
			||||||
 | 
															{{end}}
 | 
				
			||||||
 | 
														{{else}}
 | 
				
			||||||
 | 
															{{if .IsClosed}}
 | 
				
			||||||
 | 
																{{svg "octicon-issue-closed" 16 "text red"}}
 | 
				
			||||||
 | 
															{{else}}
 | 
				
			||||||
 | 
																{{svg "octicon-issue-opened" 16 "text green"}}
 | 
				
			||||||
 | 
															{{end}}
 | 
				
			||||||
									{{end}}
 | 
														{{end}}
 | 
				
			||||||
								</span>
 | 
													</span>
 | 
				
			||||||
								<a class="project-board-title" href="{{$.RepoLink}}/issues/{{.Index}}">#{{.Index}} {{.Title}}</a>
 | 
													<a class="project-board-title" href="{{$.RepoLink}}/issues/{{.Index}}">#{{.Index}} {{.Title}}</a>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,20 +5,16 @@
 | 
				
			|||||||
		<div class="ui repo-search">
 | 
							<div class="ui repo-search">
 | 
				
			||||||
			<form class="ui form ignore-dirty" method="get">
 | 
								<form class="ui form ignore-dirty" method="get">
 | 
				
			||||||
				<div class="ui fluid action input">
 | 
									<div class="ui fluid action input">
 | 
				
			||||||
					<div class="twelve wide field">
 | 
										<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "repo.search.search_repo"}}">
 | 
				
			||||||
						<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "repo.search.search_repo"}}">
 | 
										<div class="ui dropdown selection">
 | 
				
			||||||
					</div>
 | 
											<input name="t" type="hidden" value="{{.queryType}}">{{svg "octicon-triangle-down" 14 "dropdown icon"}}
 | 
				
			||||||
					<div class="two wide field">
 | 
											<div class="text">{{.i18n.Tr (printf "repo.search.%s" (or .queryType "fuzzy"))}}</div>
 | 
				
			||||||
						<select name="t">
 | 
											<div class="menu transition hidden" tabindex="-1" style="display: block !important;">
 | 
				
			||||||
							<option value="">{{.i18n.Tr "repo.search.fuzzy"}}</option>
 | 
												<div class="item" data-value="">{{.i18n.Tr "repo.search.fuzzy"}}</div>
 | 
				
			||||||
							<option value="match" {{if eq .queryType "match"}}selected{{end}}>{{.i18n.Tr "repo.search.match"}}</option>
 | 
												<div class="item" data-value="match">{{.i18n.Tr "repo.search.match"}}</div>
 | 
				
			||||||
						</select>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
					<div class="three field">
 | 
					 | 
				
			||||||
					  <button class="ui button" type="submit">
 | 
					 | 
				
			||||||
						  <i class="icon df ac jc">{{svg "octicon-search" 16}}</i>
 | 
					 | 
				
			||||||
					  </button>
 | 
					 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 | 
										<button class="ui icon button" type="submit">{{svg "octicon-search" 16}}</button>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</form>
 | 
								</form>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,12 +62,12 @@
 | 
				
			|||||||
						{{$.i18n.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}}
 | 
											{{$.i18n.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}}
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
					{{if and .Milestone (ne $.listType "milestone")}}
 | 
										{{if and .Milestone (ne $.listType "milestone")}}
 | 
				
			||||||
						<a class="milestone" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/milestone/{{.Milestone.ID}}"{{end}}>
 | 
											<a class="milestone" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/milestone/{{.Milestone.ID}}"{{end}}>
 | 
				
			||||||
							{{svg "octicon-milestone" 14 "mr-2"}}{{.Milestone.Name}}
 | 
												{{svg "octicon-milestone" 14 "mr-2"}}{{.Milestone.Name}}
 | 
				
			||||||
						</a>
 | 
											</a>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
					{{if .Ref}}
 | 
										{{if .Ref}}
 | 
				
			||||||
						<a class="ref" {{if $.RepoLink}}href="{{$.RepoLink}}{{index $.IssueRefURLs .ID}}"{{else}}href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}{{index $.IssueRefURLs .ID}}"{{end}}>
 | 
											<a class="ref" {{if $.RepoLink}}href="{{$.RepoLink}}{{index $.IssueRefURLs .ID}}"{{else}}href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{index $.IssueRefURLs .ID}}"{{end}}>
 | 
				
			||||||
							{{svg "octicon-git-branch" 14 "mr-2"}}{{index $.IssueRefEndNames .ID}}
 | 
												{{svg "octicon-git-branch" 14 "mr-2"}}{{index $.IssueRefEndNames .ID}}
 | 
				
			||||||
						</a>
 | 
											</a>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@
 | 
				
			|||||||
						{{end}}
 | 
											{{end}}
 | 
				
			||||||
					{{else}}
 | 
										{{else}}
 | 
				
			||||||
						{{if .NeedsPassword}}
 | 
											{{if .NeedsPassword}}
 | 
				
			||||||
							<form class="ui form" action="/user/activate" method="post">
 | 
												<form class="ui form" action="{{AppSubUrl}}/user/activate" method="post">
 | 
				
			||||||
								<div class="required inline field">
 | 
													<div class="required inline field">
 | 
				
			||||||
									<label for="password">{{.i18n.Tr "password"}}</label>
 | 
														<label for="password">{{.i18n.Tr "password"}}</label>
 | 
				
			||||||
									<input id="password" name="password" type="password" autocomplete="off" required>
 | 
														<input id="password" name="password" type="password" autocomplete="off" required>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -71,8 +71,8 @@
 | 
				
			|||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	{{if .ContextUser.IsOrganization}}
 | 
						{{if .ContextUser.IsOrganization}}
 | 
				
			||||||
		<div class="right stackable menu">
 | 
							<div class="right stackable menu">
 | 
				
			||||||
@@ -101,5 +101,6 @@
 | 
				
			|||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	{{end}}
 | 
						{{end}}
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<div class="ui divider"></div>
 | 
					<div class="ui divider"></div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@
 | 
				
			|||||||
			</h4>
 | 
								</h4>
 | 
				
			||||||
			<div class="ui attached segment repos-search">
 | 
								<div class="ui attached segment repos-search">
 | 
				
			||||||
				<div class="ui fluid right action left icon input" :class="{loading: isLoading}">
 | 
									<div class="ui fluid right action left icon input" :class="{loading: isLoading}">
 | 
				
			||||||
					<input @input="searchRepos(reposFilter)" v-model="searchQuery" ref="search" placeholder="{{.i18n.Tr "home.search_repos"}}">
 | 
										<input @input="changeReposFilter(reposFilter)" v-model="searchQuery" ref="search" placeholder="{{.i18n.Tr "home.search_repos"}}">
 | 
				
			||||||
					<i class="icon df ac jc">{{svg "octicon-search" 16}}</i>
 | 
										<i class="icon df ac jc">{{svg "octicon-search" 16}}</i>
 | 
				
			||||||
					<div class="ui dropdown icon button" title="{{.i18n.Tr "home.filter"}}">
 | 
										<div class="ui dropdown icon button" title="{{.i18n.Tr "home.filter"}}">
 | 
				
			||||||
						<i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i>
 | 
											<i class="icon df ac jc m-0">{{svg "octicon-filter" 16}}</i>
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user