mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	Compare commits
	
		
			15 Commits
		
	
	
		
			v1.26.0-de
			...
			v1.15.0-rc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					0b06b2019f | ||
| 
						 | 
					057205a4b7 | ||
| 
						 | 
					1b6c0c6bdc | ||
| 
						 | 
					c4f3f5bdf2 | ||
| 
						 | 
					1f5011dff7 | ||
| 
						 | 
					cf9aeca508 | ||
| 
						 | 
					09a4364b21 | ||
| 
						 | 
					0c3467ffb7 | ||
| 
						 | 
					d268c9d6e1 | ||
| 
						 | 
					7f6019e492 | ||
| 
						 | 
					0f11c5f592 | ||
| 
						 | 
					bae0e1d773 | ||
| 
						 | 
					0877d497f3 | ||
| 
						 | 
					e5fde7ef00 | ||
| 
						 | 
					6243638c11 | 
@@ -404,7 +404,7 @@ steps:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  - name: update
 | 
					  - name: update
 | 
				
			||||||
    pull: default
 | 
					    pull: default
 | 
				
			||||||
    image: alpine:3.14
 | 
					    image: alpine:3.13
 | 
				
			||||||
    commands:
 | 
					    commands:
 | 
				
			||||||
      - ./build/update-locales.sh
 | 
					      - ./build/update-locales.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -4,6 +4,19 @@ 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.15.0-rc2](https://github.com/go-gitea/gitea/releases/tag/v1.15.0-rc2) - 2021-07-22
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Restore creation of git-daemon-export-ok files (#16508) (#16514)
 | 
				
			||||||
 | 
					  * Fix data race in bleve indexer (#16474) (#16509)
 | 
				
			||||||
 | 
					  * Restore CORS on git smart http protocol (#16496) (#16506)
 | 
				
			||||||
 | 
					  * Fix race in log (#16490) (#16505)
 | 
				
			||||||
 | 
					  * Fix prepareWikiFileName to respect existing unescaped files (#16487) (#16498)
 | 
				
			||||||
 | 
					  * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16480)
 | 
				
			||||||
 | 
					  * Update notification table with only latest data (#16445) (#16469)
 | 
				
			||||||
 | 
					  * Revert to use alpine 3.13 to fix multiple seccomp related issues with Docker <20 (#16451) (#16452)
 | 
				
			||||||
 | 
					  * Fix crash following ldap authentication update (#16447) (#16448)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [1.15.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.15.0-rc1) - 2021-07-15
 | 
					## [1.15.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.15.0-rc1) - 2021-07-15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* BREAKING
 | 
					* BREAKING
 | 
				
			||||||
@@ -216,6 +229,18 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Remove utf8 option from installation page (#16126)
 | 
					  * Remove utf8 option from installation page (#16126)
 | 
				
			||||||
  * Use Wants= over Requires= in systemd file (#15897)
 | 
					  * Use Wants= over Requires= in systemd file (#15897)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.14.5](https://github.com/go-gitea/gitea/releases/tag/v1.14.5) - 2021-07-16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* SECURITY
 | 
				
			||||||
 | 
					  * Hide mirror passwords on repo settings page (#16022) (#16355)
 | 
				
			||||||
 | 
					  * Update bluemonday to v1.0.15 (#16379) (#16380)
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Retry rename on lock induced failures (#16435) (#16439)
 | 
				
			||||||
 | 
					  * Validate issue index before querying DB (#16406) (#16410)
 | 
				
			||||||
 | 
					  * Fix crash following ldap authentication update (#16447) (#16449)
 | 
				
			||||||
 | 
					* ENHANCEMENTS
 | 
				
			||||||
 | 
					  * Redirect on bad CSRF instead of presenting bad page (#14937) (#16378)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [1.14.4](https://github.com/go-gitea/gitea/releases/tag/v1.14.4) - 2021-07-06
 | 
					## [1.14.4](https://github.com/go-gitea/gitea/releases/tag/v1.14.4) - 2021-07-06
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* BUGFIXES
 | 
					* BUGFIXES
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
###################################
 | 
					###################################
 | 
				
			||||||
#Build stage
 | 
					#Build stage
 | 
				
			||||||
FROM golang:1.16-alpine3.14 AS build-env
 | 
					FROM golang:1.16-alpine3.13 AS build-env
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ARG GOPROXY
 | 
					ARG GOPROXY
 | 
				
			||||||
ENV GOPROXY ${GOPROXY:-direct}
 | 
					ENV GOPROXY ${GOPROXY:-direct}
 | 
				
			||||||
@@ -25,7 +25,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
 | 
				
			|||||||
# Begin env-to-ini build
 | 
					# Begin env-to-ini build
 | 
				
			||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
 | 
					RUN go build contrib/environment-to-ini/environment-to-ini.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FROM alpine:3.14
 | 
					FROM alpine:3.13
 | 
				
			||||||
LABEL maintainer="maintainers@gitea.io"
 | 
					LABEL maintainer="maintainers@gitea.io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPOSE 22 3000
 | 
					EXPOSE 22 3000
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
###################################
 | 
					###################################
 | 
				
			||||||
#Build stage
 | 
					#Build stage
 | 
				
			||||||
FROM golang:1.16-alpine3.14 AS build-env
 | 
					FROM golang:1.16-alpine3.13 AS build-env
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ARG GOPROXY
 | 
					ARG GOPROXY
 | 
				
			||||||
ENV GOPROXY ${GOPROXY:-direct}
 | 
					ENV GOPROXY ${GOPROXY:-direct}
 | 
				
			||||||
@@ -25,7 +25,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
 | 
				
			|||||||
# Begin env-to-ini build
 | 
					# Begin env-to-ini build
 | 
				
			||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
 | 
					RUN go build contrib/environment-to-ini/environment-to-ini.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FROM alpine:3.14
 | 
					FROM alpine:3.13
 | 
				
			||||||
LABEL maintainer="maintainers@gitea.io"
 | 
					LABEL maintainer="maintainers@gitea.io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPOSE 2222 3000
 | 
					EXPOSE 2222 3000
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ You absolutely must not place a general ToS or privacy statement that implies th
 | 
				
			|||||||
Create or append to `/path/to/custom/templates/custom/extra_links_footer.tmpl`:
 | 
					Create or append to `/path/to/custom/templates/custom/extra_links_footer.tmpl`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```go
 | 
					```go
 | 
				
			||||||
<a class="item" href="{{AppSubUrl}}/privacy.html">Privacy Policy</a>
 | 
					<a class="item" href="{{AppSubUrl}}/assets/privacy.html">Privacy Policy</a>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Restart Gitea to see the changes.
 | 
					Restart Gitea to see the changes.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,7 +102,7 @@ For instance, let's say you are in Germany and must add the famously legally-req
 | 
				
			|||||||
just place it under your "$GITEA_CUSTOM/public/" directory (for instance `$GITEA_CUSTOM/public/impressum.html`) and put a link to it in either `$GITEA_CUSTOM/templates/custom/extra_links.tmpl` or `$GITEA_CUSTOM/templates/custom/extra_links_footer.tmpl`.
 | 
					just place it under your "$GITEA_CUSTOM/public/" directory (for instance `$GITEA_CUSTOM/public/impressum.html`) and put a link to it in either `$GITEA_CUSTOM/templates/custom/extra_links.tmpl` or `$GITEA_CUSTOM/templates/custom/extra_links_footer.tmpl`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To match the current style, the link should have the class name "item", and you can use `{{AppSubUrl}}` to get the base URL:
 | 
					To match the current style, the link should have the class name "item", and you can use `{{AppSubUrl}}` to get the base URL:
 | 
				
			||||||
`<a class="item" href="{{AppSubUrl}}/impressum.html">Impressum</a>`
 | 
					`<a class="item" href="{{AppSubUrl}}/assets/impressum.html">Impressum</a>`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
For more information, see [Adding Legal Pages](https://docs.gitea.io/en-us/adding-legal-pages).
 | 
					For more information, see [Adding Legal Pages](https://docs.gitea.io/en-us/adding-legal-pages).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -174,13 +174,13 @@ You can display STL file directly in Gitea by adding:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if ($('.view-raw>a[href$=".stl" i]').length) {
 | 
					  if ($('.view-raw>a[href$=".stl" i]').length) {
 | 
				
			||||||
    $("body").append(
 | 
					    $("body").append(
 | 
				
			||||||
      '<link href="/Madeleine.js/src/css/Madeleine.css" rel="stylesheet">'
 | 
					      '<link href="/assets/Madeleine.js/src/css/Madeleine.css" rel="stylesheet">'
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    Promise.all([
 | 
					    Promise.all([
 | 
				
			||||||
      lS("/Madeleine.js/src/lib/stats.js"),
 | 
					      lS("/assets/Madeleine.js/src/lib/stats.js"),
 | 
				
			||||||
      lS("/Madeleine.js/src/lib/detector.js"),
 | 
					      lS("/assets/Madeleine.js/src/lib/detector.js"),
 | 
				
			||||||
      lS("/Madeleine.js/src/lib/three.min.js"),
 | 
					      lS("/assets/Madeleine.js/src/lib/three.min.js"),
 | 
				
			||||||
      lS("/Madeleine.js/src/Madeleine.js"),
 | 
					      lS("/assets/Madeleine.js/src/Madeleine.js"),
 | 
				
			||||||
    ]).then(function () {
 | 
					    ]).then(function () {
 | 
				
			||||||
      $(".view-raw")
 | 
					      $(".view-raw")
 | 
				
			||||||
        .attr("id", "view-raw")
 | 
					        .attr("id", "view-raw")
 | 
				
			||||||
@@ -188,7 +188,7 @@ You can display STL file directly in Gitea by adding:
 | 
				
			|||||||
      new Madeleine({
 | 
					      new Madeleine({
 | 
				
			||||||
        target: "view-raw",
 | 
					        target: "view-raw",
 | 
				
			||||||
        data: $('.view-raw>a[href$=".stl" i]').attr("href"),
 | 
					        data: $('.view-raw>a[href$=".stl" i]').attr("href"),
 | 
				
			||||||
        path: "/Madeleine.js/src",
 | 
					        path: "/assets/Madeleine.js/src",
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      $('.view-raw>a[href$=".stl"]').remove();
 | 
					      $('.view-raw>a[href$=".stl"]').remove();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -61,7 +61,7 @@ Gitea 引用 `custom` 目录中的自定义配置文件来覆盖配置、模板
 | 
				
			|||||||
"custom/public/"目录下(比如 `custom/public/impressum.html`)并且将它与 `custom/templates/custom/extra_links.tmpl` 链接起来即可。
 | 
					"custom/public/"目录下(比如 `custom/public/impressum.html`)并且将它与 `custom/templates/custom/extra_links.tmpl` 链接起来即可。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
这个链接应当使用一个名为“item”的 class 来匹配当前样式,您可以使用 `{{AppSubUrl}}` 来获取 base URL:
 | 
					这个链接应当使用一个名为“item”的 class 来匹配当前样式,您可以使用 `{{AppSubUrl}}` 来获取 base URL:
 | 
				
			||||||
`<a class="item" href="{{AppSubUrl}}/impressum.html">Impressum</a>`
 | 
					`<a class="item" href="{{AppSubUrl}}/assets/impressum.html">Impressum</a>`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
同理,您可以将页签添加到 `extra_tabs.tmpl` 中,使用同样的方式来添加页签。它的具体样式需要与
 | 
					同理,您可以将页签添加到 `extra_tabs.tmpl` 中,使用同样的方式来添加页签。它的具体样式需要与
 | 
				
			||||||
`templates/repo/header.tmpl` 中已有的其他选项卡的样式匹配
 | 
					`templates/repo/header.tmpl` 中已有的其他选项卡的样式匹配
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -164,5 +164,5 @@ And so you could write some CSS:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Add your stylesheet to your custom directory e.g `custom/public/css/my-style-XXXXX.css` and import it using a custom header file `custom/templates/custom/header.tmpl`:
 | 
					Add your stylesheet to your custom directory e.g `custom/public/css/my-style-XXXXX.css` and import it using a custom header file `custom/templates/custom/header.tmpl`:
 | 
				
			||||||
```html
 | 
					```html
 | 
				
			||||||
<link type="text/css" href="{{AppSubUrl}}/css/my-style-XXXXX.css" />
 | 
					<link type="text/css" href="{{AppSubUrl}}/assets/css/my-style-XXXXX.css" />
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -144,6 +144,60 @@ func TestLDAPUserSignin(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, u.Email, htmlDoc.Find(`label[for="email"]`).Siblings().First().Text())
 | 
						assert.Equal(t, u.Email, htmlDoc.Find(`label[for="email"]`).Siblings().First().Text())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestLDAPAuthChange(t *testing.T) {
 | 
				
			||||||
 | 
						defer prepareTestEnv(t)()
 | 
				
			||||||
 | 
						addAuthSourceLDAP(t, "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session := loginUser(t, "user1")
 | 
				
			||||||
 | 
						req := NewRequest(t, "GET", "/admin/auths")
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						doc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
						href, exists := doc.Find("table.table td a").Attr("href")
 | 
				
			||||||
 | 
						if !exists {
 | 
				
			||||||
 | 
							assert.True(t, exists, "No authentication source found")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", href)
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						doc = NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
						csrf := doc.GetCSRF()
 | 
				
			||||||
 | 
						host, _ := doc.Find(`input[name="host"]`).Attr("value")
 | 
				
			||||||
 | 
						assert.Equal(t, host, getLDAPServerHost())
 | 
				
			||||||
 | 
						binddn, _ := doc.Find(`input[name="bind_dn"]`).Attr("value")
 | 
				
			||||||
 | 
						assert.Equal(t, binddn, "uid=gitea,ou=service,dc=planetexpress,dc=com")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequestWithValues(t, "POST", href, map[string]string{
 | 
				
			||||||
 | 
							"_csrf":                    csrf,
 | 
				
			||||||
 | 
							"type":                     "2",
 | 
				
			||||||
 | 
							"name":                     "ldap",
 | 
				
			||||||
 | 
							"host":                     getLDAPServerHost(),
 | 
				
			||||||
 | 
							"port":                     "389",
 | 
				
			||||||
 | 
							"bind_dn":                  "uid=gitea,ou=service,dc=planetexpress,dc=com",
 | 
				
			||||||
 | 
							"bind_password":            "password",
 | 
				
			||||||
 | 
							"user_base":                "ou=people,dc=planetexpress,dc=com",
 | 
				
			||||||
 | 
							"filter":                   "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))",
 | 
				
			||||||
 | 
							"admin_filter":             "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)",
 | 
				
			||||||
 | 
							"restricted_filter":        "(uid=leela)",
 | 
				
			||||||
 | 
							"attribute_username":       "uid",
 | 
				
			||||||
 | 
							"attribute_name":           "givenName",
 | 
				
			||||||
 | 
							"attribute_surname":        "sn",
 | 
				
			||||||
 | 
							"attribute_mail":           "mail",
 | 
				
			||||||
 | 
							"attribute_ssh_public_key": "",
 | 
				
			||||||
 | 
							"is_sync_enabled":          "on",
 | 
				
			||||||
 | 
							"is_active":                "on",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						session.MakeRequest(t, req, http.StatusFound)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", href)
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						doc = NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
						host, _ = doc.Find(`input[name="host"]`).Attr("value")
 | 
				
			||||||
 | 
						assert.Equal(t, host, getLDAPServerHost())
 | 
				
			||||||
 | 
						binddn, _ = doc.Find(`input[name="bind_dn"]`).Attr("value")
 | 
				
			||||||
 | 
						assert.Equal(t, binddn, "uid=gitea,ou=service,dc=planetexpress,dc=com")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestLDAPUserSync(t *testing.T) {
 | 
					func TestLDAPUserSync(t *testing.T) {
 | 
				
			||||||
	if skipLDAPTests() {
 | 
						if skipLDAPTests() {
 | 
				
			||||||
		t.Skip()
 | 
							t.Skip()
 | 
				
			||||||
 
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1 +1 @@
 | 
				
			|||||||
423313fbd38093bb10d0c8387db9105409c6f196
 | 
					0dca5bd9b5d7ef937710e056f575e86c0184ba85
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ package models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"encoding/binary"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/smtp"
 | 
						"net/smtp"
 | 
				
			||||||
@@ -70,13 +71,32 @@ var (
 | 
				
			|||||||
	_ convert.Conversion = &SSPIConfig{}
 | 
						_ convert.Conversion = &SSPIConfig{}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// jsonUnmarshalIgnoreErroneousBOM - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
 | 
					// jsonUnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
 | 
				
			||||||
// possible that a Blob may gain an unwanted prefix of 0xff 0xfe.
 | 
					// possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe.
 | 
				
			||||||
func jsonUnmarshalIgnoreErroneousBOM(bs []byte, v interface{}) error {
 | 
					func jsonUnmarshalHandleDoubleEncode(bs []byte, v interface{}) error {
 | 
				
			||||||
	json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
						json := jsoniter.ConfigCompatibleWithStandardLibrary
 | 
				
			||||||
	err := json.Unmarshal(bs, &v)
 | 
						err := json.Unmarshal(bs, v)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ok := true
 | 
				
			||||||
 | 
							rs := []byte{}
 | 
				
			||||||
 | 
							temp := make([]byte, 2)
 | 
				
			||||||
 | 
							for _, rn := range string(bs) {
 | 
				
			||||||
 | 
								if rn > 0xffff {
 | 
				
			||||||
 | 
									ok = false
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								binary.LittleEndian.PutUint16(temp, uint16(rn))
 | 
				
			||||||
 | 
								rs = append(rs, temp...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								if rs[0] == 0xff && rs[1] == 0xfe {
 | 
				
			||||||
 | 
									rs = rs[2:]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								err = json.Unmarshal(rs, v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if err != nil && len(bs) > 2 && bs[0] == 0xff && bs[1] == 0xfe {
 | 
						if err != nil && len(bs) > 2 && bs[0] == 0xff && bs[1] == 0xfe {
 | 
				
			||||||
		err = json.Unmarshal(bs[2:], &v)
 | 
							err = json.Unmarshal(bs[2:], v)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -88,7 +108,7 @@ type LDAPConfig struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up a LDAPConfig from serialized format.
 | 
					// FromDB fills up a LDAPConfig from serialized format.
 | 
				
			||||||
func (cfg *LDAPConfig) FromDB(bs []byte) error {
 | 
					func (cfg *LDAPConfig) FromDB(bs []byte) error {
 | 
				
			||||||
	err := jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
 | 
						err := jsonUnmarshalHandleDoubleEncode(bs, &cfg)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -129,7 +149,7 @@ type SMTPConfig struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up an SMTPConfig from serialized format.
 | 
					// FromDB fills up an SMTPConfig from serialized format.
 | 
				
			||||||
func (cfg *SMTPConfig) FromDB(bs []byte) error {
 | 
					func (cfg *SMTPConfig) FromDB(bs []byte) error {
 | 
				
			||||||
	return jsonUnmarshalIgnoreErroneousBOM(bs, cfg)
 | 
						return jsonUnmarshalHandleDoubleEncode(bs, cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToDB exports an SMTPConfig to a serialized format.
 | 
					// ToDB exports an SMTPConfig to a serialized format.
 | 
				
			||||||
@@ -146,7 +166,7 @@ type PAMConfig struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up a PAMConfig from serialized format.
 | 
					// FromDB fills up a PAMConfig from serialized format.
 | 
				
			||||||
func (cfg *PAMConfig) FromDB(bs []byte) error {
 | 
					func (cfg *PAMConfig) FromDB(bs []byte) error {
 | 
				
			||||||
	return jsonUnmarshalIgnoreErroneousBOM(bs, cfg)
 | 
						return jsonUnmarshalHandleDoubleEncode(bs, cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToDB exports a PAMConfig to a serialized format.
 | 
					// ToDB exports a PAMConfig to a serialized format.
 | 
				
			||||||
@@ -167,7 +187,7 @@ type OAuth2Config struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up an OAuth2Config from serialized format.
 | 
					// FromDB fills up an OAuth2Config from serialized format.
 | 
				
			||||||
func (cfg *OAuth2Config) FromDB(bs []byte) error {
 | 
					func (cfg *OAuth2Config) FromDB(bs []byte) error {
 | 
				
			||||||
	return jsonUnmarshalIgnoreErroneousBOM(bs, cfg)
 | 
						return jsonUnmarshalHandleDoubleEncode(bs, cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToDB exports an SMTPConfig to a serialized format.
 | 
					// ToDB exports an SMTPConfig to a serialized format.
 | 
				
			||||||
@@ -187,7 +207,7 @@ type SSPIConfig struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up an SSPIConfig from serialized format.
 | 
					// FromDB fills up an SSPIConfig from serialized format.
 | 
				
			||||||
func (cfg *SSPIConfig) FromDB(bs []byte) error {
 | 
					func (cfg *SSPIConfig) FromDB(bs []byte) error {
 | 
				
			||||||
	return jsonUnmarshalIgnoreErroneousBOM(bs, cfg)
 | 
						return jsonUnmarshalHandleDoubleEncode(bs, cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToDB exports an SSPIConfig to a serialized format.
 | 
					// ToDB exports an SSPIConfig to a serialized format.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1152,6 +1152,16 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, overwriteO
 | 
				
			|||||||
		return fmt.Errorf("recalculateAccesses: %v", err)
 | 
							return fmt.Errorf("recalculateAccesses: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if u.Visibility == api.VisibleTypePublic && !repo.IsPrivate {
 | 
				
			||||||
 | 
							// Create/Remove git-daemon-export-ok for git-daemon...
 | 
				
			||||||
 | 
							daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
 | 
				
			||||||
 | 
							if f, err := os.Create(daemonExportFile); err != nil {
 | 
				
			||||||
 | 
								log.Error("Failed to create %s: %v", daemonExportFile, err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								f.Close()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if setting.Service.AutoWatchNewRepos {
 | 
						if setting.Service.AutoWatchNewRepos {
 | 
				
			||||||
		if err = watchRepo(ctx.e, doer.ID, repo.ID, true); err != nil {
 | 
							if err = watchRepo(ctx.e, doer.ID, repo.ID, true); err != nil {
 | 
				
			||||||
			return fmt.Errorf("watchRepo: %v", err)
 | 
								return fmt.Errorf("watchRepo: %v", err)
 | 
				
			||||||
@@ -1310,15 +1320,16 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
 | 
				
			|||||||
		// Create/Remove git-daemon-export-ok for git-daemon...
 | 
							// Create/Remove git-daemon-export-ok for git-daemon...
 | 
				
			||||||
		daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
 | 
							daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
 | 
				
			||||||
		isExist, err := util.IsExist(daemonExportFile)
 | 
							isExist, err := util.IsExist(daemonExportFile)
 | 
				
			||||||
 | 
							isPublic := !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePublic
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
 | 
								log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if repo.IsPrivate && isExist {
 | 
							if !isPublic && isExist {
 | 
				
			||||||
			if err = util.Remove(daemonExportFile); err != nil {
 | 
								if err = util.Remove(daemonExportFile); err != nil {
 | 
				
			||||||
				log.Error("Failed to remove %s: %v", daemonExportFile, err)
 | 
									log.Error("Failed to remove %s: %v", daemonExportFile, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if !repo.IsPrivate && !isExist {
 | 
							} else if isPublic && !isExist {
 | 
				
			||||||
			if f, err := os.Create(daemonExportFile); err != nil {
 | 
								if f, err := os.Create(daemonExportFile); err != nil {
 | 
				
			||||||
				log.Error("Failed to create %s: %v", daemonExportFile, err)
 | 
									log.Error("Failed to create %s: %v", daemonExportFile, err)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ type UnitConfig struct{}
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up a UnitConfig from serialized format.
 | 
					// FromDB fills up a UnitConfig from serialized format.
 | 
				
			||||||
func (cfg *UnitConfig) FromDB(bs []byte) error {
 | 
					func (cfg *UnitConfig) FromDB(bs []byte) error {
 | 
				
			||||||
	return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
 | 
						return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToDB exports a UnitConfig to a serialized format.
 | 
					// ToDB exports a UnitConfig to a serialized format.
 | 
				
			||||||
@@ -44,7 +44,7 @@ type ExternalWikiConfig struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up a ExternalWikiConfig from serialized format.
 | 
					// FromDB fills up a ExternalWikiConfig from serialized format.
 | 
				
			||||||
func (cfg *ExternalWikiConfig) FromDB(bs []byte) error {
 | 
					func (cfg *ExternalWikiConfig) FromDB(bs []byte) error {
 | 
				
			||||||
	return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
 | 
						return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToDB exports a ExternalWikiConfig to a serialized format.
 | 
					// ToDB exports a ExternalWikiConfig to a serialized format.
 | 
				
			||||||
@@ -62,7 +62,7 @@ type ExternalTrackerConfig struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up a ExternalTrackerConfig from serialized format.
 | 
					// FromDB fills up a ExternalTrackerConfig from serialized format.
 | 
				
			||||||
func (cfg *ExternalTrackerConfig) FromDB(bs []byte) error {
 | 
					func (cfg *ExternalTrackerConfig) FromDB(bs []byte) error {
 | 
				
			||||||
	return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
 | 
						return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToDB exports a ExternalTrackerConfig to a serialized format.
 | 
					// ToDB exports a ExternalTrackerConfig to a serialized format.
 | 
				
			||||||
@@ -80,7 +80,7 @@ type IssuesConfig struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up a IssuesConfig from serialized format.
 | 
					// FromDB fills up a IssuesConfig from serialized format.
 | 
				
			||||||
func (cfg *IssuesConfig) FromDB(bs []byte) error {
 | 
					func (cfg *IssuesConfig) FromDB(bs []byte) error {
 | 
				
			||||||
	return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
 | 
						return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToDB exports a IssuesConfig to a serialized format.
 | 
					// ToDB exports a IssuesConfig to a serialized format.
 | 
				
			||||||
@@ -104,7 +104,7 @@ type PullRequestsConfig struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FromDB fills up a PullRequestsConfig from serialized format.
 | 
					// FromDB fills up a PullRequestsConfig from serialized format.
 | 
				
			||||||
func (cfg *PullRequestsConfig) FromDB(bs []byte) error {
 | 
					func (cfg *PullRequestsConfig) FromDB(bs []byte) error {
 | 
				
			||||||
	return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
 | 
						return jsonUnmarshalHandleDoubleEncode(bs, &cfg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToDB exports a PullRequestsConfig to a serialized format.
 | 
					// ToDB exports a PullRequestsConfig to a serialized format.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,9 @@ package doctor
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
@@ -14,6 +16,9 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/repository"
 | 
						"code.gitea.io/gitea/modules/repository"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
						lru "github.com/hashicorp/golang-lru"
 | 
				
			||||||
	"xorm.io/builder"
 | 
						"xorm.io/builder"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,6 +80,7 @@ func checkUserStarNum(logger log.Logger, autofix bool) error {
 | 
				
			|||||||
func checkEnablePushOptions(logger log.Logger, autofix bool) error {
 | 
					func checkEnablePushOptions(logger log.Logger, autofix bool) error {
 | 
				
			||||||
	numRepos := 0
 | 
						numRepos := 0
 | 
				
			||||||
	numNeedUpdate := 0
 | 
						numNeedUpdate := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := iterateRepositories(func(repo *models.Repository) error {
 | 
						if err := iterateRepositories(func(repo *models.Repository) error {
 | 
				
			||||||
		numRepos++
 | 
							numRepos++
 | 
				
			||||||
		r, err := git.OpenRepository(repo.RepoPath())
 | 
							r, err := git.OpenRepository(repo.RepoPath())
 | 
				
			||||||
@@ -114,6 +120,66 @@ func checkEnablePushOptions(logger log.Logger, autofix bool) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkDaemonExport(logger log.Logger, autofix bool) error {
 | 
				
			||||||
 | 
						numRepos := 0
 | 
				
			||||||
 | 
						numNeedUpdate := 0
 | 
				
			||||||
 | 
						cache, err := lru.New(512)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Critical("Unable to create cache: %v", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := iterateRepositories(func(repo *models.Repository) error {
 | 
				
			||||||
 | 
							numRepos++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if owner, has := cache.Get(repo.OwnerID); has {
 | 
				
			||||||
 | 
								repo.Owner = owner.(*models.User)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if err := repo.GetOwner(); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								cache.Add(repo.OwnerID, repo.Owner)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Create/Remove git-daemon-export-ok for git-daemon...
 | 
				
			||||||
 | 
							daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
 | 
				
			||||||
 | 
							isExist, err := util.IsExist(daemonExportFile)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							isPublic := !repo.IsPrivate && repo.Owner.Visibility == structs.VisibleTypePublic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if isPublic != isExist {
 | 
				
			||||||
 | 
								numNeedUpdate++
 | 
				
			||||||
 | 
								if autofix {
 | 
				
			||||||
 | 
									if !isPublic && isExist {
 | 
				
			||||||
 | 
										if err = util.Remove(daemonExportFile); err != nil {
 | 
				
			||||||
 | 
											log.Error("Failed to remove %s: %v", daemonExportFile, err)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else if isPublic && !isExist {
 | 
				
			||||||
 | 
										if f, err := os.Create(daemonExportFile); err != nil {
 | 
				
			||||||
 | 
											log.Error("Failed to create %s: %v", daemonExportFile, err)
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											f.Close()
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							logger.Critical("Unable to checkDaemonExport: %v", err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if autofix {
 | 
				
			||||||
 | 
							logger.Info("Updated git-daemon-export-ok files for %d of %d repositories.", numNeedUpdate, numRepos)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							logger.Info("Checked %d repositories, %d need updates.", numRepos, numNeedUpdate)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	Register(&Check{
 | 
						Register(&Check{
 | 
				
			||||||
		Title:     "Check if SCRIPT_TYPE is available",
 | 
							Title:     "Check if SCRIPT_TYPE is available",
 | 
				
			||||||
@@ -143,4 +209,11 @@ func init() {
 | 
				
			|||||||
		Run:       checkEnablePushOptions,
 | 
							Run:       checkEnablePushOptions,
 | 
				
			||||||
		Priority:  7,
 | 
							Priority:  7,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						Register(&Check{
 | 
				
			||||||
 | 
							Title:     "Check git-daemon-export-ok files",
 | 
				
			||||||
 | 
							Name:      "check-git-daemon-export-ok",
 | 
				
			||||||
 | 
							IsDefault: false,
 | 
				
			||||||
 | 
							Run:       checkDaemonExport,
 | 
				
			||||||
 | 
							Priority:  8,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ package git
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"math"
 | 
						"math"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
@@ -28,16 +29,20 @@ type WriteCloserError interface {
 | 
				
			|||||||
func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
 | 
					func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
 | 
				
			||||||
	batchStdinReader, batchStdinWriter := io.Pipe()
 | 
						batchStdinReader, batchStdinWriter := io.Pipe()
 | 
				
			||||||
	batchStdoutReader, batchStdoutWriter := io.Pipe()
 | 
						batchStdoutReader, batchStdoutWriter := io.Pipe()
 | 
				
			||||||
 | 
						ctx, ctxCancel := context.WithCancel(DefaultContext)
 | 
				
			||||||
 | 
						closed := make(chan struct{})
 | 
				
			||||||
	cancel := func() {
 | 
						cancel := func() {
 | 
				
			||||||
		_ = batchStdinReader.Close()
 | 
							_ = batchStdinReader.Close()
 | 
				
			||||||
		_ = batchStdinWriter.Close()
 | 
							_ = batchStdinWriter.Close()
 | 
				
			||||||
		_ = batchStdoutReader.Close()
 | 
							_ = batchStdoutReader.Close()
 | 
				
			||||||
		_ = batchStdoutWriter.Close()
 | 
							_ = batchStdoutWriter.Close()
 | 
				
			||||||
 | 
							ctxCancel()
 | 
				
			||||||
 | 
							<-closed
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		stderr := strings.Builder{}
 | 
							stderr := strings.Builder{}
 | 
				
			||||||
		err := NewCommand("cat-file", "--batch-check").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
 | 
							err := NewCommandContext(ctx, "cat-file", "--batch-check").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
 | 
								_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
 | 
				
			||||||
			_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
 | 
								_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
 | 
				
			||||||
@@ -45,6 +50,7 @@ func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()
 | 
				
			|||||||
			_ = batchStdoutWriter.Close()
 | 
								_ = batchStdoutWriter.Close()
 | 
				
			||||||
			_ = batchStdinReader.Close()
 | 
								_ = batchStdinReader.Close()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							close(closed)
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// For simplicities sake we'll use a buffered reader to read from the cat-file --batch-check
 | 
						// For simplicities sake we'll use a buffered reader to read from the cat-file --batch-check
 | 
				
			||||||
@@ -59,16 +65,20 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
 | 
				
			|||||||
	// so let's create a batch stdin and stdout
 | 
						// so let's create a batch stdin and stdout
 | 
				
			||||||
	batchStdinReader, batchStdinWriter := io.Pipe()
 | 
						batchStdinReader, batchStdinWriter := io.Pipe()
 | 
				
			||||||
	batchStdoutReader, batchStdoutWriter := nio.Pipe(buffer.New(32 * 1024))
 | 
						batchStdoutReader, batchStdoutWriter := nio.Pipe(buffer.New(32 * 1024))
 | 
				
			||||||
 | 
						ctx, ctxCancel := context.WithCancel(DefaultContext)
 | 
				
			||||||
 | 
						closed := make(chan struct{})
 | 
				
			||||||
	cancel := func() {
 | 
						cancel := func() {
 | 
				
			||||||
		_ = batchStdinReader.Close()
 | 
							_ = batchStdinReader.Close()
 | 
				
			||||||
		_ = batchStdinWriter.Close()
 | 
							_ = batchStdinWriter.Close()
 | 
				
			||||||
		_ = batchStdoutReader.Close()
 | 
							_ = batchStdoutReader.Close()
 | 
				
			||||||
		_ = batchStdoutWriter.Close()
 | 
							_ = batchStdoutWriter.Close()
 | 
				
			||||||
 | 
							ctxCancel()
 | 
				
			||||||
 | 
							<-closed
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
		stderr := strings.Builder{}
 | 
							stderr := strings.Builder{}
 | 
				
			||||||
		err := NewCommand("cat-file", "--batch").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
 | 
							err := NewCommandContext(ctx, "cat-file", "--batch").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
 | 
								_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
 | 
				
			||||||
			_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
 | 
								_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
 | 
				
			||||||
@@ -76,6 +86,7 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
 | 
				
			|||||||
			_ = batchStdoutWriter.Close()
 | 
								_ = batchStdoutWriter.Close()
 | 
				
			||||||
			_ = batchStdinReader.Close()
 | 
								_ = batchStdinReader.Close()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							close(closed)
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// For simplicities sake we'll us a buffered reader to read from the cat-file --batch
 | 
						// For simplicities sake we'll us a buffered reader to read from the cat-file --batch
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
package git
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,3 +46,23 @@ func (t *Tree) SubTree(rpath string) (*Tree, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return g, nil
 | 
						return g, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LsTree checks if the given filenames are in the tree
 | 
				
			||||||
 | 
					func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) {
 | 
				
			||||||
 | 
						cmd := NewCommand("ls-tree", "-z", "--name-only", "--", ref)
 | 
				
			||||||
 | 
						for _, arg := range filenames {
 | 
				
			||||||
 | 
							if arg != "" {
 | 
				
			||||||
 | 
								cmd.AddArguments(arg)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res, err := cmd.RunInDirBytes(repo.Path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						filelist := make([]string, 0, len(filenames))
 | 
				
			||||||
 | 
						for _, line := range bytes.Split(res, []byte{'\000'}) {
 | 
				
			||||||
 | 
							filelist = append(filelist, string(line))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return filelist, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										59
									
								
								modules/indexer/bleve/batch.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								modules/indexer/bleve/batch.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					// 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 bleve
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/blevesearch/bleve/v2"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FlushingBatch is a batch of operations that automatically flushes to the
 | 
				
			||||||
 | 
					// underlying index once it reaches a certain size.
 | 
				
			||||||
 | 
					type FlushingBatch struct {
 | 
				
			||||||
 | 
						maxBatchSize int
 | 
				
			||||||
 | 
						batch        *bleve.Batch
 | 
				
			||||||
 | 
						index        bleve.Index
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewFlushingBatch creates a new flushing batch for the specified index. Once
 | 
				
			||||||
 | 
					// the number of operations in the batch reaches the specified limit, the batch
 | 
				
			||||||
 | 
					// automatically flushes its operations to the index.
 | 
				
			||||||
 | 
					func NewFlushingBatch(index bleve.Index, maxBatchSize int) *FlushingBatch {
 | 
				
			||||||
 | 
						return &FlushingBatch{
 | 
				
			||||||
 | 
							maxBatchSize: maxBatchSize,
 | 
				
			||||||
 | 
							batch:        index.NewBatch(),
 | 
				
			||||||
 | 
							index:        index,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Index add a new index to batch
 | 
				
			||||||
 | 
					func (b *FlushingBatch) Index(id string, data interface{}) error {
 | 
				
			||||||
 | 
						if err := b.batch.Index(id, data); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.flushIfFull()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Delete add a delete index to batch
 | 
				
			||||||
 | 
					func (b *FlushingBatch) Delete(id string) error {
 | 
				
			||||||
 | 
						b.batch.Delete(id)
 | 
				
			||||||
 | 
						return b.flushIfFull()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *FlushingBatch) flushIfFull() error {
 | 
				
			||||||
 | 
						if b.batch.Size() < b.maxBatchSize {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.Flush()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Flush submit the batch and create a new one
 | 
				
			||||||
 | 
					func (b *FlushingBatch) Flush() error {
 | 
				
			||||||
 | 
						err := b.index.Batch(b.batch)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.batch = b.index.NewBatch()
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -18,6 +18,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/analyze"
 | 
						"code.gitea.io/gitea/modules/analyze"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/charset"
 | 
						"code.gitea.io/gitea/modules/charset"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
 | 
						gitea_bleve "code.gitea.io/gitea/modules/indexer/bleve"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
@@ -176,7 +177,8 @@ func NewBleveIndexer(indexDir string) (*BleveIndexer, bool, error) {
 | 
				
			|||||||
	return indexer, created, err
 | 
						return indexer, created, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader *bufio.Reader, commitSha string, update fileUpdate, repo *models.Repository, batch rupture.FlushingBatch) error {
 | 
					func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader *bufio.Reader, commitSha string,
 | 
				
			||||||
 | 
						update fileUpdate, repo *models.Repository, batch *gitea_bleve.FlushingBatch) error {
 | 
				
			||||||
	// Ignore vendored files in code search
 | 
						// Ignore vendored files in code search
 | 
				
			||||||
	if setting.Indexer.ExcludeVendored && analyze.IsVendor(update.Filename) {
 | 
						if setting.Indexer.ExcludeVendored && analyze.IsVendor(update.Filename) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -229,7 +231,7 @@ func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader *
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *BleveIndexer) addDelete(filename string, repo *models.Repository, batch rupture.FlushingBatch) error {
 | 
					func (b *BleveIndexer) addDelete(filename string, repo *models.Repository, batch *gitea_bleve.FlushingBatch) error {
 | 
				
			||||||
	id := filenameIndexerID(repo.ID, filename)
 | 
						id := filenameIndexerID(repo.ID, filename)
 | 
				
			||||||
	return batch.Delete(id)
 | 
						return batch.Delete(id)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -267,7 +269,7 @@ func (b *BleveIndexer) Close() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Index indexes the data
 | 
					// Index indexes the data
 | 
				
			||||||
func (b *BleveIndexer) Index(repo *models.Repository, sha string, changes *repoChanges) error {
 | 
					func (b *BleveIndexer) Index(repo *models.Repository, sha string, changes *repoChanges) error {
 | 
				
			||||||
	batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize)
 | 
						batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
 | 
				
			||||||
	if len(changes.Updates) > 0 {
 | 
						if len(changes.Updates) > 0 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		batchWriter, batchReader, cancel := git.CatFileBatch(repo.RepoPath())
 | 
							batchWriter, batchReader, cancel := git.CatFileBatch(repo.RepoPath())
 | 
				
			||||||
@@ -296,7 +298,7 @@ func (b *BleveIndexer) Delete(repoID int64) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize)
 | 
						batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
 | 
				
			||||||
	for _, hit := range result.Hits {
 | 
						for _, hit := range result.Hits {
 | 
				
			||||||
		if err = batch.Delete(hit.ID); err != nil {
 | 
							if err = batch.Delete(hit.ID); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,10 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gitea_bleve "code.gitea.io/gitea/modules/indexer/bleve"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/blevesearch/bleve/v2"
 | 
						"github.com/blevesearch/bleve/v2"
 | 
				
			||||||
	"github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
 | 
						"github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
 | 
				
			||||||
	"github.com/blevesearch/bleve/v2/analysis/token/lowercase"
 | 
						"github.com/blevesearch/bleve/v2/analysis/token/lowercase"
 | 
				
			||||||
@@ -197,7 +199,7 @@ func (b *BleveIndexer) Close() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Index will save the index data
 | 
					// Index will save the index data
 | 
				
			||||||
func (b *BleveIndexer) Index(issues []*IndexerData) error {
 | 
					func (b *BleveIndexer) Index(issues []*IndexerData) error {
 | 
				
			||||||
	batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize)
 | 
						batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
 | 
				
			||||||
	for _, issue := range issues {
 | 
						for _, issue := range issues {
 | 
				
			||||||
		if err := batch.Index(indexerID(issue.ID), struct {
 | 
							if err := batch.Index(indexerID(issue.ID), struct {
 | 
				
			||||||
			RepoID   int64
 | 
								RepoID   int64
 | 
				
			||||||
@@ -218,7 +220,7 @@ func (b *BleveIndexer) Index(issues []*IndexerData) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Delete deletes indexes by ids
 | 
					// Delete deletes indexes by ids
 | 
				
			||||||
func (b *BleveIndexer) Delete(ids ...int64) error {
 | 
					func (b *BleveIndexer) Delete(ids ...int64) error {
 | 
				
			||||||
	batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize)
 | 
						batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
 | 
				
			||||||
	for _, id := range ids {
 | 
						for _, id := range ids {
 | 
				
			||||||
		if err := batch.Delete(indexerID(id)); err != nil {
 | 
							if err := batch.Delete(indexerID(id)); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,7 +143,7 @@ type MultiChannelledLog struct {
 | 
				
			|||||||
	name            string
 | 
						name            string
 | 
				
			||||||
	bufferLength    int64
 | 
						bufferLength    int64
 | 
				
			||||||
	queue           chan *Event
 | 
						queue           chan *Event
 | 
				
			||||||
	mutex           sync.Mutex
 | 
						rwmutex         sync.RWMutex
 | 
				
			||||||
	loggers         map[string]EventLogger
 | 
						loggers         map[string]EventLogger
 | 
				
			||||||
	flush           chan bool
 | 
						flush           chan bool
 | 
				
			||||||
	close           chan bool
 | 
						close           chan bool
 | 
				
			||||||
@@ -173,10 +173,10 @@ func NewMultiChannelledLog(name string, bufferLength int64) *MultiChannelledLog
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// AddLogger adds a logger to this MultiChannelledLog
 | 
					// AddLogger adds a logger to this MultiChannelledLog
 | 
				
			||||||
func (m *MultiChannelledLog) AddLogger(logger EventLogger) error {
 | 
					func (m *MultiChannelledLog) AddLogger(logger EventLogger) error {
 | 
				
			||||||
	m.mutex.Lock()
 | 
						m.rwmutex.Lock()
 | 
				
			||||||
	name := logger.GetName()
 | 
						name := logger.GetName()
 | 
				
			||||||
	if _, has := m.loggers[name]; has {
 | 
						if _, has := m.loggers[name]; has {
 | 
				
			||||||
		m.mutex.Unlock()
 | 
							m.rwmutex.Unlock()
 | 
				
			||||||
		return ErrDuplicateName{name}
 | 
							return ErrDuplicateName{name}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	m.loggers[name] = logger
 | 
						m.loggers[name] = logger
 | 
				
			||||||
@@ -186,7 +186,7 @@ func (m *MultiChannelledLog) AddLogger(logger EventLogger) error {
 | 
				
			|||||||
	if logger.GetStacktraceLevel() < m.stacktraceLevel {
 | 
						if logger.GetStacktraceLevel() < m.stacktraceLevel {
 | 
				
			||||||
		m.stacktraceLevel = logger.GetStacktraceLevel()
 | 
							m.stacktraceLevel = logger.GetStacktraceLevel()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	m.mutex.Unlock()
 | 
						m.rwmutex.Unlock()
 | 
				
			||||||
	go m.Start()
 | 
						go m.Start()
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -195,15 +195,15 @@ func (m *MultiChannelledLog) AddLogger(logger EventLogger) error {
 | 
				
			|||||||
// NB: If you delete the last sublogger this logger will simply drop
 | 
					// NB: If you delete the last sublogger this logger will simply drop
 | 
				
			||||||
// log events
 | 
					// log events
 | 
				
			||||||
func (m *MultiChannelledLog) DelLogger(name string) bool {
 | 
					func (m *MultiChannelledLog) DelLogger(name string) bool {
 | 
				
			||||||
	m.mutex.Lock()
 | 
						m.rwmutex.Lock()
 | 
				
			||||||
	logger, has := m.loggers[name]
 | 
						logger, has := m.loggers[name]
 | 
				
			||||||
	if !has {
 | 
						if !has {
 | 
				
			||||||
		m.mutex.Unlock()
 | 
							m.rwmutex.Unlock()
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	delete(m.loggers, name)
 | 
						delete(m.loggers, name)
 | 
				
			||||||
	m.internalResetLevel()
 | 
						m.internalResetLevel()
 | 
				
			||||||
	m.mutex.Unlock()
 | 
						m.rwmutex.Unlock()
 | 
				
			||||||
	logger.Flush()
 | 
						logger.Flush()
 | 
				
			||||||
	logger.Close()
 | 
						logger.Close()
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
@@ -211,15 +211,15 @@ func (m *MultiChannelledLog) DelLogger(name string) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetEventLogger returns a sub logger from this MultiChannelledLog
 | 
					// GetEventLogger returns a sub logger from this MultiChannelledLog
 | 
				
			||||||
func (m *MultiChannelledLog) GetEventLogger(name string) EventLogger {
 | 
					func (m *MultiChannelledLog) GetEventLogger(name string) EventLogger {
 | 
				
			||||||
	m.mutex.Lock()
 | 
						m.rwmutex.RLock()
 | 
				
			||||||
	defer m.mutex.Unlock()
 | 
						defer m.rwmutex.RUnlock()
 | 
				
			||||||
	return m.loggers[name]
 | 
						return m.loggers[name]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetEventLoggerNames returns a list of names
 | 
					// GetEventLoggerNames returns a list of names
 | 
				
			||||||
func (m *MultiChannelledLog) GetEventLoggerNames() []string {
 | 
					func (m *MultiChannelledLog) GetEventLoggerNames() []string {
 | 
				
			||||||
	m.mutex.Lock()
 | 
						m.rwmutex.RLock()
 | 
				
			||||||
	defer m.mutex.Unlock()
 | 
						defer m.rwmutex.RUnlock()
 | 
				
			||||||
	var keys []string
 | 
						var keys []string
 | 
				
			||||||
	for k := range m.loggers {
 | 
						for k := range m.loggers {
 | 
				
			||||||
		keys = append(keys, k)
 | 
							keys = append(keys, k)
 | 
				
			||||||
@@ -228,12 +228,12 @@ func (m *MultiChannelledLog) GetEventLoggerNames() []string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *MultiChannelledLog) closeLoggers() {
 | 
					func (m *MultiChannelledLog) closeLoggers() {
 | 
				
			||||||
	m.mutex.Lock()
 | 
						m.rwmutex.Lock()
 | 
				
			||||||
	for _, logger := range m.loggers {
 | 
						for _, logger := range m.loggers {
 | 
				
			||||||
		logger.Flush()
 | 
							logger.Flush()
 | 
				
			||||||
		logger.Close()
 | 
							logger.Close()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	m.mutex.Unlock()
 | 
						m.rwmutex.Unlock()
 | 
				
			||||||
	m.closed <- true
 | 
						m.closed <- true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -249,8 +249,8 @@ func (m *MultiChannelledLog) Resume() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ReleaseReopen causes this logger to tell its subloggers to release and reopen
 | 
					// ReleaseReopen causes this logger to tell its subloggers to release and reopen
 | 
				
			||||||
func (m *MultiChannelledLog) ReleaseReopen() error {
 | 
					func (m *MultiChannelledLog) ReleaseReopen() error {
 | 
				
			||||||
	m.mutex.Lock()
 | 
						m.rwmutex.Lock()
 | 
				
			||||||
	defer m.mutex.Unlock()
 | 
						defer m.rwmutex.Unlock()
 | 
				
			||||||
	var accumulatedErr error
 | 
						var accumulatedErr error
 | 
				
			||||||
	for _, logger := range m.loggers {
 | 
						for _, logger := range m.loggers {
 | 
				
			||||||
		if err := logger.ReleaseReopen(); err != nil {
 | 
							if err := logger.ReleaseReopen(); err != nil {
 | 
				
			||||||
@@ -266,13 +266,13 @@ func (m *MultiChannelledLog) ReleaseReopen() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Start processing the MultiChannelledLog
 | 
					// Start processing the MultiChannelledLog
 | 
				
			||||||
func (m *MultiChannelledLog) Start() {
 | 
					func (m *MultiChannelledLog) Start() {
 | 
				
			||||||
	m.mutex.Lock()
 | 
						m.rwmutex.Lock()
 | 
				
			||||||
	if m.started {
 | 
						if m.started {
 | 
				
			||||||
		m.mutex.Unlock()
 | 
							m.rwmutex.Unlock()
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	m.started = true
 | 
						m.started = true
 | 
				
			||||||
	m.mutex.Unlock()
 | 
						m.rwmutex.Unlock()
 | 
				
			||||||
	paused := false
 | 
						paused := false
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		if paused {
 | 
							if paused {
 | 
				
			||||||
@@ -286,11 +286,11 @@ func (m *MultiChannelledLog) Start() {
 | 
				
			|||||||
					m.closeLoggers()
 | 
										m.closeLoggers()
 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				m.mutex.Lock()
 | 
									m.rwmutex.RLock()
 | 
				
			||||||
				for _, logger := range m.loggers {
 | 
									for _, logger := range m.loggers {
 | 
				
			||||||
					logger.Flush()
 | 
										logger.Flush()
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				m.mutex.Unlock()
 | 
									m.rwmutex.RUnlock()
 | 
				
			||||||
			case <-m.close:
 | 
								case <-m.close:
 | 
				
			||||||
				m.closeLoggers()
 | 
									m.closeLoggers()
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
@@ -307,24 +307,24 @@ func (m *MultiChannelledLog) Start() {
 | 
				
			|||||||
				m.closeLoggers()
 | 
									m.closeLoggers()
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			m.mutex.Lock()
 | 
								m.rwmutex.RLock()
 | 
				
			||||||
			for _, logger := range m.loggers {
 | 
								for _, logger := range m.loggers {
 | 
				
			||||||
				err := logger.LogEvent(event)
 | 
									err := logger.LogEvent(event)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					fmt.Println(err)
 | 
										fmt.Println(err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			m.mutex.Unlock()
 | 
								m.rwmutex.RUnlock()
 | 
				
			||||||
		case _, ok := <-m.flush:
 | 
							case _, ok := <-m.flush:
 | 
				
			||||||
			if !ok {
 | 
								if !ok {
 | 
				
			||||||
				m.closeLoggers()
 | 
									m.closeLoggers()
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			m.mutex.Lock()
 | 
								m.rwmutex.RLock()
 | 
				
			||||||
			for _, logger := range m.loggers {
 | 
								for _, logger := range m.loggers {
 | 
				
			||||||
				logger.Flush()
 | 
									logger.Flush()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			m.mutex.Unlock()
 | 
								m.rwmutex.RUnlock()
 | 
				
			||||||
		case <-m.close:
 | 
							case <-m.close:
 | 
				
			||||||
			m.closeLoggers()
 | 
								m.closeLoggers()
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -359,11 +359,15 @@ func (m *MultiChannelledLog) Flush() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetLevel gets the level of this MultiChannelledLog
 | 
					// GetLevel gets the level of this MultiChannelledLog
 | 
				
			||||||
func (m *MultiChannelledLog) GetLevel() Level {
 | 
					func (m *MultiChannelledLog) GetLevel() Level {
 | 
				
			||||||
 | 
						m.rwmutex.RLock()
 | 
				
			||||||
 | 
						defer m.rwmutex.RUnlock()
 | 
				
			||||||
	return m.level
 | 
						return m.level
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetStacktraceLevel gets the level of this MultiChannelledLog
 | 
					// GetStacktraceLevel gets the level of this MultiChannelledLog
 | 
				
			||||||
func (m *MultiChannelledLog) GetStacktraceLevel() Level {
 | 
					func (m *MultiChannelledLog) GetStacktraceLevel() Level {
 | 
				
			||||||
 | 
						m.rwmutex.RLock()
 | 
				
			||||||
 | 
						defer m.rwmutex.RUnlock()
 | 
				
			||||||
	return m.stacktraceLevel
 | 
						return m.stacktraceLevel
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -384,8 +388,8 @@ func (m *MultiChannelledLog) internalResetLevel() Level {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ResetLevel will reset the level of this MultiChannelledLog
 | 
					// ResetLevel will reset the level of this MultiChannelledLog
 | 
				
			||||||
func (m *MultiChannelledLog) ResetLevel() Level {
 | 
					func (m *MultiChannelledLog) ResetLevel() Level {
 | 
				
			||||||
	m.mutex.Lock()
 | 
						m.rwmutex.Lock()
 | 
				
			||||||
	defer m.mutex.Unlock()
 | 
						defer m.rwmutex.Unlock()
 | 
				
			||||||
	return m.internalResetLevel()
 | 
						return m.internalResetLevel()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,6 @@ import (
 | 
				
			|||||||
func TestPersistableChannelQueue(t *testing.T) {
 | 
					func TestPersistableChannelQueue(t *testing.T) {
 | 
				
			||||||
	handleChan := make(chan *testData)
 | 
						handleChan := make(chan *testData)
 | 
				
			||||||
	handle := func(data ...Data) {
 | 
						handle := func(data ...Data) {
 | 
				
			||||||
		assert.True(t, len(data) == 2)
 | 
					 | 
				
			||||||
		for _, datum := range data {
 | 
							for _, datum := range data {
 | 
				
			||||||
			testDatum := datum.(*testData)
 | 
								testDatum := datum.(*testData)
 | 
				
			||||||
			handleChan <- testDatum
 | 
								handleChan <- testDatum
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,10 +6,13 @@ package util
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const windowsSharingViolationError syscall.Errno = 32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Remove removes the named file or (empty) directory with at most 5 attempts.
 | 
					// Remove removes the named file or (empty) directory with at most 5 attempts.
 | 
				
			||||||
func Remove(name string) error {
 | 
					func Remove(name string) error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
@@ -25,6 +28,12 @@ func Remove(name string) error {
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" {
 | 
				
			||||||
 | 
								// try again
 | 
				
			||||||
 | 
								<-time.After(100 * time.Millisecond)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if unwrapped == syscall.ENOENT {
 | 
							if unwrapped == syscall.ENOENT {
 | 
				
			||||||
			// it's already gone
 | 
								// it's already gone
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
@@ -48,6 +57,12 @@ func RemoveAll(name string) error {
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" {
 | 
				
			||||||
 | 
								// try again
 | 
				
			||||||
 | 
								<-time.After(100 * time.Millisecond)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if unwrapped == syscall.ENOENT {
 | 
							if unwrapped == syscall.ENOENT {
 | 
				
			||||||
			// it's already gone
 | 
								// it's already gone
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
@@ -64,13 +79,19 @@ func Rename(oldpath, newpath string) error {
 | 
				
			|||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		unwrapped := err.(*os.PathError).Err
 | 
							unwrapped := err.(*os.LinkError).Err
 | 
				
			||||||
		if unwrapped == syscall.EBUSY || unwrapped == syscall.ENOTEMPTY || unwrapped == syscall.EPERM || unwrapped == syscall.EMFILE || unwrapped == syscall.ENFILE {
 | 
							if unwrapped == syscall.EBUSY || unwrapped == syscall.ENOTEMPTY || unwrapped == syscall.EPERM || unwrapped == syscall.EMFILE || unwrapped == syscall.ENFILE {
 | 
				
			||||||
			// try again
 | 
								// try again
 | 
				
			||||||
			<-time.After(100 * time.Millisecond)
 | 
								<-time.After(100 * time.Millisecond)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" {
 | 
				
			||||||
 | 
								// try again
 | 
				
			||||||
 | 
								<-time.After(100 * time.Millisecond)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if i == 0 && os.IsNotExist(err) {
 | 
							if i == 0 && os.IsNotExist(err) {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -269,6 +269,26 @@ func (r *Route) Get(pattern string, h ...interface{}) {
 | 
				
			|||||||
	r.R.Get(r.getPattern(pattern), Wrap(middlewares...))
 | 
						r.R.Get(r.getPattern(pattern), Wrap(middlewares...))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Options delegate options method
 | 
				
			||||||
 | 
					func (r *Route) Options(pattern string, h ...interface{}) {
 | 
				
			||||||
 | 
						var middlewares = r.getMiddlewares(h)
 | 
				
			||||||
 | 
						r.R.Options(r.getPattern(pattern), Wrap(middlewares...))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetOptions delegate get and options method
 | 
				
			||||||
 | 
					func (r *Route) GetOptions(pattern string, h ...interface{}) {
 | 
				
			||||||
 | 
						var middlewares = r.getMiddlewares(h)
 | 
				
			||||||
 | 
						r.R.Get(r.getPattern(pattern), Wrap(middlewares...))
 | 
				
			||||||
 | 
						r.R.Options(r.getPattern(pattern), Wrap(middlewares...))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PostOptions delegate post and options method
 | 
				
			||||||
 | 
					func (r *Route) PostOptions(pattern string, h ...interface{}) {
 | 
				
			||||||
 | 
						var middlewares = r.getMiddlewares(h)
 | 
				
			||||||
 | 
						r.R.Post(r.getPattern(pattern), Wrap(middlewares...))
 | 
				
			||||||
 | 
						r.R.Options(r.getPattern(pattern), Wrap(middlewares...))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Head delegate head method
 | 
					// Head delegate head method
 | 
				
			||||||
func (r *Route) Head(pattern string, h ...interface{}) {
 | 
					func (r *Route) Head(pattern string, h ...interface{}) {
 | 
				
			||||||
	var middlewares = r.getMiddlewares(h)
 | 
						var middlewares = r.getMiddlewares(h)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,7 @@ func TestWiki(t *testing.T) {
 | 
				
			|||||||
	Wiki(ctx)
 | 
						Wiki(ctx)
 | 
				
			||||||
	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
						assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
				
			||||||
	assert.EqualValues(t, "Home", ctx.Data["Title"])
 | 
						assert.EqualValues(t, "Home", ctx.Data["Title"])
 | 
				
			||||||
	assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name"}, ctx.Data["Pages"])
 | 
						assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestWikiPages(t *testing.T) {
 | 
					func TestWikiPages(t *testing.T) {
 | 
				
			||||||
@@ -91,7 +91,7 @@ func TestWikiPages(t *testing.T) {
 | 
				
			|||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	WikiPages(ctx)
 | 
						WikiPages(ctx)
 | 
				
			||||||
	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
						assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
				
			||||||
	assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name"}, ctx.Data["Pages"])
 | 
						assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestNewWiki(t *testing.T) {
 | 
					func TestNewWiki(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,6 +50,7 @@ func Notifications(c *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if c.QueryBool("div-only") {
 | 
						if c.QueryBool("div-only") {
 | 
				
			||||||
 | 
							c.Data["SequenceNumber"] = c.Query("sequence-number")
 | 
				
			||||||
		c.HTML(http.StatusOK, tplNotificationDiv)
 | 
							c.HTML(http.StatusOK, tplNotificationDiv)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -175,6 +176,7 @@ func NotificationStatusPost(c *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	c.Data["Link"] = setting.AppURL + "notifications"
 | 
						c.Data["Link"] = setting.AppURL + "notifications"
 | 
				
			||||||
 | 
						c.Data["SequenceNumber"] = c.Req.PostFormValue("sequence-number")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.HTML(http.StatusOK, tplNotificationDiv)
 | 
						c.HTML(http.StatusOK, tplNotificationDiv)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1006,17 +1006,17 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
			}, ignSignInAndCsrf, lfsServerEnabled)
 | 
								}, ignSignInAndCsrf, lfsServerEnabled)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			m.Group("", func() {
 | 
								m.Group("", func() {
 | 
				
			||||||
				m.Post("/git-upload-pack", repo.ServiceUploadPack)
 | 
									m.PostOptions("/git-upload-pack", repo.ServiceUploadPack)
 | 
				
			||||||
				m.Post("/git-receive-pack", repo.ServiceReceivePack)
 | 
									m.PostOptions("/git-receive-pack", repo.ServiceReceivePack)
 | 
				
			||||||
				m.Get("/info/refs", repo.GetInfoRefs)
 | 
									m.GetOptions("/info/refs", repo.GetInfoRefs)
 | 
				
			||||||
				m.Get("/HEAD", repo.GetTextFile("HEAD"))
 | 
									m.GetOptions("/HEAD", repo.GetTextFile("HEAD"))
 | 
				
			||||||
				m.Get("/objects/info/alternates", repo.GetTextFile("objects/info/alternates"))
 | 
									m.GetOptions("/objects/info/alternates", repo.GetTextFile("objects/info/alternates"))
 | 
				
			||||||
				m.Get("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates"))
 | 
									m.GetOptions("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates"))
 | 
				
			||||||
				m.Get("/objects/info/packs", repo.GetInfoPacks)
 | 
									m.GetOptions("/objects/info/packs", repo.GetInfoPacks)
 | 
				
			||||||
				m.Get("/objects/info/{file:[^/]*}", repo.GetTextFile(""))
 | 
									m.GetOptions("/objects/info/{file:[^/]*}", repo.GetTextFile(""))
 | 
				
			||||||
				m.Get("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
 | 
									m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
 | 
				
			||||||
				m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
 | 
									m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
 | 
				
			||||||
				m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
 | 
									m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
 | 
				
			||||||
			}, ignSignInAndCsrf)
 | 
								}, ignSignInAndCsrf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			m.Head("/tasks/trigger", repo.TriggerTask)
 | 
								m.Head("/tasks/trigger", repo.TriggerTask)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,7 +88,7 @@ func prepareWikiFileName(gitRepo *git.Repository, wikiName string) (bool, string
 | 
				
			|||||||
	escaped := NameToFilename(wikiName)
 | 
						escaped := NameToFilename(wikiName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Look for both files
 | 
						// Look for both files
 | 
				
			||||||
	filesInIndex, err := gitRepo.LsFiles(unescaped, escaped)
 | 
						filesInIndex, err := gitRepo.LsTree("master", unescaped, escaped)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("%v", err)
 | 
							log.Error("%v", err)
 | 
				
			||||||
		return false, escaped, err
 | 
							return false, escaped, err
 | 
				
			||||||
@@ -308,14 +308,9 @@ func DeleteWikiPage(doer *models.User, repo *models.Repository, wikiName string)
 | 
				
			|||||||
		return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err)
 | 
							return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wikiPath := NameToFilename(wikiName)
 | 
						found, wikiPath, err := prepareWikiFileName(gitRepo, wikiName)
 | 
				
			||||||
	filesInIndex, err := gitRepo.LsFiles(wikiPath)
 | 
						if err != nil {
 | 
				
			||||||
	found := false
 | 
							return err
 | 
				
			||||||
	for _, file := range filesInIndex {
 | 
					 | 
				
			||||||
		if file == wikiPath {
 | 
					 | 
				
			||||||
			found = true
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if found {
 | 
						if found {
 | 
				
			||||||
		err := gitRepo.RemoveFilesFromIndex(wikiPath)
 | 
							err := gitRepo.RemoveFilesFromIndex(wikiPath)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -210,3 +210,54 @@ func TestRepository_DeleteWikiPage(t *testing.T) {
 | 
				
			|||||||
	_, err = masterTree.GetTreeEntryByPath(wikiPath)
 | 
						_, err = masterTree.GetTreeEntryByPath(wikiPath)
 | 
				
			||||||
	assert.Error(t, err)
 | 
						assert.Error(t, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPrepareWikiFileName(t *testing.T) {
 | 
				
			||||||
 | 
						models.PrepareTestEnv(t)
 | 
				
			||||||
 | 
						repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
				
			||||||
 | 
						gitRepo, err := git.OpenRepository(repo.WikiPath())
 | 
				
			||||||
 | 
						defer gitRepo.Close()
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name      string
 | 
				
			||||||
 | 
							arg       string
 | 
				
			||||||
 | 
							existence bool
 | 
				
			||||||
 | 
							wikiPath  string
 | 
				
			||||||
 | 
							wantErr   bool
 | 
				
			||||||
 | 
						}{{
 | 
				
			||||||
 | 
							name:      "add suffix",
 | 
				
			||||||
 | 
							arg:       "Home",
 | 
				
			||||||
 | 
							existence: true,
 | 
				
			||||||
 | 
							wikiPath:  "Home.md",
 | 
				
			||||||
 | 
							wantErr:   false,
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							name:      "test special chars",
 | 
				
			||||||
 | 
							arg:       "home of and & or wiki page!",
 | 
				
			||||||
 | 
							existence: false,
 | 
				
			||||||
 | 
							wikiPath:  "home-of-and-%26-or-wiki-page%21.md",
 | 
				
			||||||
 | 
							wantErr:   false,
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							name:      "fount unescaped cases",
 | 
				
			||||||
 | 
							arg:       "Unescaped File",
 | 
				
			||||||
 | 
							existence: true,
 | 
				
			||||||
 | 
							wikiPath:  "Unescaped File.md",
 | 
				
			||||||
 | 
							wantErr:   false,
 | 
				
			||||||
 | 
						}}
 | 
				
			||||||
 | 
						for _, tt := range tests {
 | 
				
			||||||
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								existence, newWikiPath, err := prepareWikiFileName(gitRepo, tt.arg)
 | 
				
			||||||
 | 
								if (err != nil) != tt.wantErr {
 | 
				
			||||||
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if existence != tt.existence {
 | 
				
			||||||
 | 
									if existence {
 | 
				
			||||||
 | 
										t.Errorf("expect to find no escaped file but we detect one")
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										t.Errorf("expect to find an escaped file but we could not detect one")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								assert.Equal(t, tt.wikiPath, newWikiPath)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
<div class="page-content user notification" id="notification_div" data-params="{{.Page.GetParams}}">
 | 
					<div class="page-content user notification" id="notification_div" data-params="{{.Page.GetParams}}" data-sequence-number="{{.SequenceNumber}}">
 | 
				
			||||||
	<div class="ui container">
 | 
						<div class="ui container">
 | 
				
			||||||
		<h1 class="ui dividing header">{{.i18n.Tr "notification.notifications"}}</h1>
 | 
							<h1 class="ui dividing header">{{.i18n.Tr "notification.notifications"}}</h1>
 | 
				
			||||||
		<div class="ui top attached tabular menu">
 | 
							<div class="ui top attached tabular menu">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
const {AppSubUrl, csrf, NotificationSettings} = window.config;
 | 
					const {AppSubUrl, csrf, NotificationSettings} = window.config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let notificationSequenceNumber = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function initNotificationsTable() {
 | 
					export function initNotificationsTable() {
 | 
				
			||||||
  $('#notification_table .button').on('click', async function () {
 | 
					  $('#notification_table .button').on('click', async function () {
 | 
				
			||||||
    const data = await updateNotification(
 | 
					    const data = await updateNotification(
 | 
				
			||||||
@@ -10,8 +12,10 @@ export function initNotificationsTable() {
 | 
				
			|||||||
      $(this).data('notification-id'),
 | 
					      $(this).data('notification-id'),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $('#notification_div').replaceWith(data);
 | 
					    if ($(data).data('sequence-number') === notificationSequenceNumber) {
 | 
				
			||||||
    initNotificationsTable();
 | 
					      $('#notification_div').replaceWith(data);
 | 
				
			||||||
 | 
					      initNotificationsTable();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    await updateNotificationCount();
 | 
					    await updateNotificationCount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
@@ -139,10 +143,13 @@ async function updateNotificationTable() {
 | 
				
			|||||||
      url: `${AppSubUrl}/notifications?${notificationDiv.data('params')}`,
 | 
					      url: `${AppSubUrl}/notifications?${notificationDiv.data('params')}`,
 | 
				
			||||||
      data: {
 | 
					      data: {
 | 
				
			||||||
        'div-only': true,
 | 
					        'div-only': true,
 | 
				
			||||||
 | 
					        'sequence-number': ++notificationSequenceNumber,
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    notificationDiv.replaceWith(data);
 | 
					    if ($(data).data('sequence-number') === notificationSequenceNumber) {
 | 
				
			||||||
    initNotificationsTable();
 | 
					      notificationDiv.replaceWith(data);
 | 
				
			||||||
 | 
					      initNotificationsTable();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -182,6 +189,7 @@ async function updateNotification(url, status, page, q, notificationID) {
 | 
				
			|||||||
      page,
 | 
					      page,
 | 
				
			||||||
      q,
 | 
					      q,
 | 
				
			||||||
      noredirect: true,
 | 
					      noredirect: true,
 | 
				
			||||||
 | 
					      'sequence-number': ++notificationSequenceNumber,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user