Compare commits
81 Commits
options-bi
...
v1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2c8d6fcb2 | ||
|
|
1a5df9e822 | ||
|
|
21dc5996a5 | ||
|
|
023a6604e2 | ||
|
|
9a5009a2cc | ||
|
|
3121a7a037 | ||
|
|
61cdc32496 | ||
|
|
5fcf218ed9 | ||
|
|
91836614cd | ||
|
|
ab4eb0daf9 | ||
|
|
155fb93b9b | ||
|
|
3a8c792303 | ||
|
|
807c64800c | ||
|
|
6ddb2dcd57 | ||
|
|
729ab80065 | ||
|
|
a5ea9b4f30 | ||
|
|
8f08ccdb9f | ||
|
|
10d73d38e0 | ||
|
|
fbb424c61d | ||
|
|
e8bac94d1f | ||
|
|
99c6556ff3 | ||
|
|
6aacf4d2f0 | ||
|
|
7b67347104 | ||
|
|
b0c6217c4d | ||
|
|
eaa7b3c3f5 | ||
|
|
770e8310bd | ||
|
|
1e6dd98d51 | ||
|
|
a822bba3e1 | ||
|
|
ec1fe1183d | ||
|
|
dfb547099d | ||
|
|
a12da66dfb | ||
|
|
b33078fa33 | ||
|
|
c21e2c4151 | ||
|
|
0e1392501d | ||
|
|
e0ecd9fd93 | ||
|
|
65b1875d2b | ||
|
|
11df7ebfc5 | ||
|
|
47a7529d96 | ||
|
|
0c5c34d7dd | ||
|
|
4c89a9c33c | ||
|
|
df7fa4e995 | ||
|
|
d5d21b67d2 | ||
|
|
c46eb3f5b3 | ||
|
|
f8d94cb440 | ||
|
|
2197d298cb | ||
|
|
ce21ed6c34 | ||
|
|
618407c018 | ||
|
|
111c95ecaf | ||
|
|
235eb4c3d2 | ||
|
|
380e32e129 | ||
|
|
952587dbae | ||
|
|
1d30457a94 | ||
|
|
6ade13e86e | ||
|
|
608a60fb94 | ||
|
|
8559d6f267 | ||
|
|
7c46667e71 | ||
|
|
44428fdc38 | ||
|
|
0d6e88baef | ||
|
|
b11843b8dc | ||
|
|
578a8e258e | ||
|
|
15c3d14d55 | ||
|
|
8aeeed0a23 | ||
|
|
c6b6a61bf1 | ||
|
|
1831ee2d1d | ||
|
|
b4c794058a | ||
|
|
d771e978a1 | ||
|
|
73710c00a8 | ||
|
|
1d1c01875d | ||
|
|
f0a989c1d0 | ||
|
|
abcd39f7d5 | ||
|
|
cbcb4361d5 | ||
|
|
7b5b5178e1 | ||
|
|
bab737bf02 | ||
|
|
401a8db0ed | ||
|
|
39d0db52de | ||
|
|
04b9a7e7a2 | ||
|
|
08b9af9ad8 | ||
|
|
026ad4aee7 | ||
|
|
83ed234472 | ||
|
|
1b5b297c39 | ||
|
|
2b63f32b8a |
47
.drone.yml
47
.drone.yml
@@ -3,16 +3,21 @@ workspace:
|
||||
path: src/code.gitea.io/gitea
|
||||
|
||||
pipeline:
|
||||
clone:
|
||||
image: plugins/git
|
||||
tags: true
|
||||
|
||||
test:
|
||||
image: webhippie/golang:edge
|
||||
pull: true
|
||||
environment:
|
||||
CGO_ENABLED: 1
|
||||
TAGS: sqlite
|
||||
TAGS: sqlite bindata
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- apk -U add openssh-client
|
||||
- make clean
|
||||
- make generate
|
||||
- make vet
|
||||
- make lint
|
||||
- make test
|
||||
@@ -25,7 +30,7 @@ pipeline:
|
||||
pull: true
|
||||
environment:
|
||||
CGO_ENABLED: 1
|
||||
TAGS: sqlite
|
||||
TAGS: sqlite bindata
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make test-mysql
|
||||
@@ -37,7 +42,7 @@ pipeline:
|
||||
pull: true
|
||||
environment:
|
||||
CGO_ENABLED: 1
|
||||
TAGS: sqlite
|
||||
TAGS: sqlite bindata
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make test-pgsql
|
||||
@@ -49,13 +54,13 @@ pipeline:
|
||||
pull: true
|
||||
environment:
|
||||
CGO_ENABLED: 1
|
||||
TAGS: sqlite
|
||||
TAGS: sqlite bindata
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make release
|
||||
when:
|
||||
event: [ push, tag ]
|
||||
branch: [ master, refs/tags/* ]
|
||||
branch: [ master, release/*, refs/tags/* ]
|
||||
|
||||
coverage:
|
||||
image: plugins/coverage
|
||||
@@ -66,11 +71,19 @@ pipeline:
|
||||
docker:
|
||||
image: plugins/docker
|
||||
repo: gitea/gitea
|
||||
tags: [ '${TAG}' ]
|
||||
tags: [ '${DRONE_TAG##v}' ]
|
||||
when:
|
||||
event: [ tag ]
|
||||
branch: [ refs/tags/* ]
|
||||
|
||||
docker:
|
||||
image: plugins/docker
|
||||
repo: gitea/gitea
|
||||
tags: [ '${DRONE_BRANCH##release/v}' ]
|
||||
when:
|
||||
event: [ push ]
|
||||
branch: [ release/* ]
|
||||
|
||||
docker:
|
||||
image: plugins/docker
|
||||
repo: gitea/gitea
|
||||
@@ -84,20 +97,30 @@ pipeline:
|
||||
path_style: true
|
||||
strip_prefix: dist/release/
|
||||
source: dist/release/*
|
||||
target: /gitea/master
|
||||
target: /gitea/${DRONE_TAG##v}
|
||||
when:
|
||||
event: [ push ]
|
||||
branch: [ master ]
|
||||
event: [ tag ]
|
||||
branch: [ refs/tags/* ]
|
||||
|
||||
release:
|
||||
image: plugins/s3
|
||||
path_style: true
|
||||
strip_prefix: dist/release/
|
||||
source: dist/release/*
|
||||
target: /gitea/$$TAG
|
||||
target: /gitea/${DRONE_BRANCH##release/v}
|
||||
when:
|
||||
event: [ tag ]
|
||||
branch: [ refs/tags/* ]
|
||||
event: [ push ]
|
||||
branch: [ release/* ]
|
||||
|
||||
release:
|
||||
image: plugins/s3
|
||||
path_style: true
|
||||
strip_prefix: dist/release/
|
||||
source: dist/release/*
|
||||
target: /gitea/master
|
||||
when:
|
||||
event: [ push ]
|
||||
branch: [ master ]
|
||||
|
||||
github:
|
||||
image: plugins/github-release
|
||||
|
||||
@@ -1 +1 @@
|
||||
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9zcnYvYXBwCiAgcGF0aDogc3JjL2NvZGUuZ2l0ZWEuaW8vZ2l0ZWEKCnBpcGVsaW5lOgogIHRlc3Q6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIGFwayAtVSBhZGQgb3BlbnNzaC1jbGllbnQKICAgICAgLSBtYWtlIGNsZWFuCiAgICAgIC0gbWFrZSB2ZXQKICAgICAgLSBtYWtlIGxpbnQKICAgICAgLSBtYWtlIHRlc3QKICAgICAgLSBtYWtlIGJ1aWxkCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIHRlc3QtbXlzcWw6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgdGVzdC1teXNxbAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCgogIHRlc3QtcGdzcWw6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgdGVzdC1wZ3NxbAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCgogIHVwZGF0ZXI6CiAgICBpbWFnZToga2FyYWxhYmUveGdvLWxhdGVzdDpsYXRlc3QKICAgIHB1bGw6IHRydWUKICAgIGVudmlyb25tZW50OgogICAgICBDR09fRU5BQkxFRDogMQogICAgICBUQUdTOiBzcWxpdGUKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSByZWxlYXNlCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcgXQogICAgICBicmFuY2g6IFsgbWFzdGVyLCByZWZzL3RhZ3MvKiBdCgogIGNvdmVyYWdlOgogICAgaW1hZ2U6IHBsdWdpbnMvY292ZXJhZ2UKICAgIHNlcnZlcjogaHR0cHM6Ly9jb3ZlcmFnZS5naXRlYS5pbwogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICBkb2NrZXI6CiAgICBpbWFnZTogcGx1Z2lucy9kb2NrZXIKICAgIHJlcG86IGdpdGVhL2dpdGVhCiAgICB0YWdzOiBbICcke1RBR30nIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogZ2l0ZWEvZ2l0ZWEKICAgIHRhZ3M6IFsgJ2xhdGVzdCcgXQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCiAgICAgIGJyYW5jaDogWyBtYXN0ZXIgXQoKICByZWxlYXNlOgogICAgaW1hZ2U6IHBsdWdpbnMvczMKICAgIHBhdGhfc3R5bGU6IHRydWUKICAgIHN0cmlwX3ByZWZpeDogZGlzdC9yZWxlYXNlLwogICAgc291cmNlOiBkaXN0L3JlbGVhc2UvKgogICAgdGFyZ2V0OiAvZ2l0ZWEvbWFzdGVyCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc3RyaXBfcHJlZml4OiBkaXN0L3JlbGVhc2UvCiAgICBzb3VyY2U6IGRpc3QvcmVsZWFzZS8qCiAgICB0YXJnZXQ6IC9naXRlYS8kJFRBRwogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KCiAgZ2l0aHViOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0aHViLXJlbGVhc2UKICAgIGZpbGVzOgogICAgICAtIGRpc3QvcmVsZWFzZS8qCiAgICB3aGVuOgogICAgICBldmVudDogWyB0YWcgXQogICAgICBicmFuY2g6IFsgcmVmcy90YWdzLyogXQoKICBnaXR0ZXI6CiAgICBpbWFnZTogcGx1Z2lucy9naXR0ZXIKCnNlcnZpY2VzOgogIG15c3FsOgogICAgaW1hZ2U6IG15c3FsOjUuNwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfREFUQUJBU0U9dGVzdAogICAgICAtIE1ZU1FMX0FMTE9XX0VNUFRZX1BBU1NXT1JEPXllcwogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCgogIHBnc3FsOgogICAgaW1hZ2U6IHBvc3RncmVzOjkuNQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfREI9dGVzdAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCg.LjU9u_DQ4fD2CytUu9RwwSimdcS3-KzR6nO4ff4edNU
|
||||
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9zcnYvYXBwCiAgcGF0aDogc3JjL2NvZGUuZ2l0ZWEuaW8vZ2l0ZWEKCnBpcGVsaW5lOgogIGNsb25lOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0CiAgICB0YWdzOiB0cnVlCgogIHRlc3Q6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlIGJpbmRhdGEKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gYXBrIC1VIGFkZCBvcGVuc3NoLWNsaWVudAogICAgICAtIG1ha2UgY2xlYW4KICAgICAgLSBtYWtlIGdlbmVyYXRlCiAgICAgIC0gbWFrZSB2ZXQKICAgICAgLSBtYWtlIGxpbnQKICAgICAgLSBtYWtlIHRlc3QKICAgICAgLSBtYWtlIGJ1aWxkCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIHRlc3QtbXlzcWw6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlIGJpbmRhdGEKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSB0ZXN0LW15c3FsCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KCiAgdGVzdC1wZ3NxbDoKICAgIGltYWdlOiB3ZWJoaXBwaWUvZ29sYW5nOmVkZ2UKICAgIHB1bGw6IHRydWUKICAgIGVudmlyb25tZW50OgogICAgICBDR09fRU5BQkxFRDogMQogICAgICBUQUdTOiBzcWxpdGUgYmluZGF0YQogICAgICBHT1BBVEg6IC9zcnYvYXBwCiAgICBjb21tYW5kczoKICAgICAgLSBtYWtlIHRlc3QtcGdzcWwKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQoKICB1cGRhdGVyOgogICAgaW1hZ2U6IGthcmFsYWJlL3hnby1sYXRlc3Q6bGF0ZXN0CiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgQ0dPX0VOQUJMRUQ6IDEKICAgICAgVEFHUzogc3FsaXRlIGJpbmRhdGEKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gbWFrZSByZWxlYXNlCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcgXQogICAgICBicmFuY2g6IFsgbWFzdGVyLCByZWxlYXNlLyosIHJlZnMvdGFncy8qIF0KCiAgY292ZXJhZ2U6CiAgICBpbWFnZTogcGx1Z2lucy9jb3ZlcmFnZQogICAgc2VydmVyOiBodHRwczovL2NvdmVyYWdlLmdpdGVhLmlvCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogZ2l0ZWEvZ2l0ZWEKICAgIHRhZ3M6IFsgJyR7RFJPTkVfVEFHIyN2fScgXQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KCiAgZG9ja2VyOgogICAgaW1hZ2U6IHBsdWdpbnMvZG9ja2VyCiAgICByZXBvOiBnaXRlYS9naXRlYQogICAgdGFnczogWyAnJHtEUk9ORV9CUkFOQ0gjI3JlbGVhc2Uvdn0nIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQogICAgICBicmFuY2g6IFsgcmVsZWFzZS8qIF0KCiAgZG9ja2VyOgogICAgaW1hZ2U6IHBsdWdpbnMvZG9ja2VyCiAgICByZXBvOiBnaXRlYS9naXRlYQogICAgdGFnczogWyAnbGF0ZXN0JyBdCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc3RyaXBfcHJlZml4OiBkaXN0L3JlbGVhc2UvCiAgICBzb3VyY2U6IGRpc3QvcmVsZWFzZS8qCiAgICB0YXJnZXQ6IC9naXRlYS8ke0RST05FX1RBRyMjdn0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc3RyaXBfcHJlZml4OiBkaXN0L3JlbGVhc2UvCiAgICBzb3VyY2U6IGRpc3QvcmVsZWFzZS8qCiAgICB0YXJnZXQ6IC9naXRlYS8ke0RST05FX0JSQU5DSCMjcmVsZWFzZS92fQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCiAgICAgIGJyYW5jaDogWyByZWxlYXNlLyogXQoKICByZWxlYXNlOgogICAgaW1hZ2U6IHBsdWdpbnMvczMKICAgIHBhdGhfc3R5bGU6IHRydWUKICAgIHN0cmlwX3ByZWZpeDogZGlzdC9yZWxlYXNlLwogICAgc291cmNlOiBkaXN0L3JlbGVhc2UvKgogICAgdGFyZ2V0OiAvZ2l0ZWEvbWFzdGVyCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIGdpdGh1YjoKICAgIGltYWdlOiBwbHVnaW5zL2dpdGh1Yi1yZWxlYXNlCiAgICBmaWxlczoKICAgICAgLSBkaXN0L3JlbGVhc2UvKgogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KCiAgZ2l0dGVyOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0dGVyCgpzZXJ2aWNlczoKICBteXNxbDoKICAgIGltYWdlOiBteXNxbDo1LjcKICAgIGVudmlyb25tZW50OgogICAgICAtIE1ZU1FMX0RBVEFCQVNFPXRlc3QKICAgICAgLSBNWVNRTF9BTExPV19FTVBUWV9QQVNTV09SRD15ZXMKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQoKICBwZ3NxbDoKICAgIGltYWdlOiBwb3N0Z3Jlczo5LjUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX0RCPXRlc3QKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQo.NGE3UiNBappXiPimJXv1DzgjT3k2hofGPsCPhw7KsSM
|
||||
@@ -25,3 +25,6 @@ indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
4
.github/issue_template.md
vendored
4
.github/issue_template.md
vendored
@@ -12,6 +12,10 @@
|
||||
- [ ] PostgreSQL
|
||||
- [ ] MySQL
|
||||
- [ ] SQLite
|
||||
- Can you reproduce the bug at https://try.gitea.io:
|
||||
- [ ] Yes (provide example URL)
|
||||
- [ ] No
|
||||
- [ ] Not relevant
|
||||
- Log gist:
|
||||
|
||||
## Description
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -28,7 +28,9 @@ _testmain.go
|
||||
|
||||
coverage.out
|
||||
|
||||
/modules/options/bindata.go
|
||||
/modules/public/bindata.go
|
||||
/modules/templates/bindata.go
|
||||
|
||||
*.db
|
||||
*.log
|
||||
|
||||
69
CHANGELOG.md
Normal file
69
CHANGELOG.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Changelog
|
||||
|
||||
## [1.0.2](https://github.com/go-gitea/gitea/releases/tag/v1.0.2) - 2017-02-21
|
||||
|
||||
* BUGFIXES
|
||||
* Fixed issue counter [#882](https://github.com/go-gitea/gitea/pull/882)
|
||||
* Fixed XSS vulnerability on wiki page [#955](https://github.com/go-gitea/gitea/pull/955)
|
||||
* Add data dir without session to dump [#587](https://github.com/go-gitea/gitea/pull/587)
|
||||
* Fixed wiki page renaming [#958](https://github.com/go-gitea/gitea/pull/958)
|
||||
* Drop default console logger if not required [#960](https://github.com/go-gitea/gitea/pull/960)
|
||||
* Fixed docker docs link on install page [#972](https://github.com/go-gitea/gitea/pull/972)
|
||||
* Handle SetModel errors [#957](https://github.com/go-gitea/gitea/pull/957)
|
||||
* Fixed XSS vulnerability on milestones [#977](https://github.com/go-gitea/gitea/pull/977)
|
||||
* Fixed XSS vulnerability on alerts [#981](https://github.com/go-gitea/gitea/pull/981)
|
||||
|
||||
## [1.0.1](https://github.com/go-gitea/gitea/releases/tag/v1.0.1) - 2017-01-05
|
||||
|
||||
* BUGFIXES
|
||||
* Fixed localized MIN_PASSWORD_LENGTH [#501](https://github.com/go-gitea/gitea/pull/501)
|
||||
* Fixed 500 error on organization delete [#507](https://github.com/go-gitea/gitea/pull/507)
|
||||
* Ignore empty wiki repo on migrate [#544](https://github.com/go-gitea/gitea/pull/544)
|
||||
* Proper check access for forking [#563](https://github.com/go-gitea/gitea/pull/563)
|
||||
* Fix SSH domain on installer [#506](https://github.com/go-gitea/gitea/pull/506)
|
||||
* Fix missing data rows on admin UI [#580](https://github.com/go-gitea/gitea/pull/580)
|
||||
* Do not delete tags with releases by default [#579](https://github.com/go-gitea/gitea/pull/579)
|
||||
* Fix missing session config data on admin UI [#578](https://github.com/go-gitea/gitea/pull/578)
|
||||
* Properly show the version within footer on the UI [#593](https://github.com/go-gitea/gitea/pull/593)
|
||||
|
||||
## [1.0.0](https://github.com/go-gitea/gitea/releases/tag/v1.0.0) - 2016-12-23
|
||||
|
||||
* BREAKING
|
||||
* We have various changes on the API, scripting against API must be updated
|
||||
* FEATURE
|
||||
* Show last login for admins [#121](https://github.com/go-gitea/gitea/pull/121)
|
||||
* BUGFIXES
|
||||
* Fixed sender of notifications [#2](https://github.com/go-gitea/gitea/pull/2)
|
||||
* Fixed keyword hijacking vulnerability [#20](https://github.com/go-gitea/gitea/pull/20)
|
||||
* Fixed non-markdown readme rendering [#95](https://github.com/go-gitea/gitea/pull/95)
|
||||
* Allow updating draft releases [#169](https://github.com/go-gitea/gitea/pull/169)
|
||||
* GitHub API compliance [#227](https://github.com/go-gitea/gitea/pull/227)
|
||||
* Added commit SHA to tag webhook [#286](https://github.com/go-gitea/gitea/issues/286)
|
||||
* Secured links via noopener [#315](https://github.com/go-gitea/gitea/issues/315)
|
||||
* Replace tabs with spaces on wiki title [#371](https://github.com/go-gitea/gitea/pull/371)
|
||||
* Fixed vulnerability on labels and releases [#409](https://github.com/go-gitea/gitea/pull/409)
|
||||
* Fixed issue comment API [#449](https://github.com/go-gitea/gitea/pull/449)
|
||||
* ENHANCEMENT
|
||||
* Use proper import path for libravatar [#3](https://github.com/go-gitea/gitea/pull/3)
|
||||
* Integrated DroneCI for tests and builds [#24](https://github.com/go-gitea/gitea/issues/24)
|
||||
* Integrated dependency manager [#29](https://github.com/go-gitea/gitea/issues/29)
|
||||
* Embedded bindata optionally [#30](https://github.com/go-gitea/gitea/issues/30)
|
||||
* Integrated pagination for releases [#73](https://github.com/go-gitea/gitea/pull/73)
|
||||
* Autogenerate version on every build [#91](https://github.com/go-gitea/gitea/issues/91)
|
||||
* Refactored Docker container [#104](https://github.com/go-gitea/gitea/issues/104)
|
||||
* Added short-hash support for downloads [#211](https://github.com/go-gitea/gitea/issues/211)
|
||||
* Display tooltip for downloads [#221](https://github.com/go-gitea/gitea/issues/221)
|
||||
* Improved HTTP headers for issue attachments [#270](https://github.com/go-gitea/gitea/pull/270)
|
||||
* Integrate public as bindata optionally [#293](https://github.com/go-gitea/gitea/pull/293)
|
||||
* Integrate templates as bindata optionally [#314](https://github.com/go-gitea/gitea/pull/314)
|
||||
* Inject more ENV variables into custom hooks [#316](https://github.com/go-gitea/gitea/issues/316)
|
||||
* Correct LDAP login validation [#342](https://github.com/go-gitea/gitea/pull/342)
|
||||
* Integrate conf as bindata optionally [#354](https://github.com/go-gitea/gitea/pull/354)
|
||||
* Serve video files in browser [#418](https://github.com/go-gitea/gitea/pull/418)
|
||||
* Configurable SSH host binding [#431](https://github.com/go-gitea/gitea/issues/431)
|
||||
* MISC
|
||||
* Forked from Gogs and renamed to Gitea
|
||||
* Catching more errors with logs
|
||||
* Fixed all linting errors
|
||||
* Made the go linter entirely happy
|
||||
* Really integrated vendoring
|
||||
@@ -26,7 +26,8 @@ RUN apk update && \
|
||||
-s /bin/bash \
|
||||
-u 1000 \
|
||||
-G git \
|
||||
git
|
||||
git && \
|
||||
echo "git:$(date +%s | sha256sum | base64 | head -c 32)" | chpasswd
|
||||
|
||||
ENV USER git
|
||||
ENV GITEA_CUSTOM /data/gitea
|
||||
@@ -38,7 +39,4 @@ ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||
|
||||
COPY docker /
|
||||
|
||||
COPY public /app/gitea/public
|
||||
COPY templates /app/gitea/templates
|
||||
COPY gitea /app/gitea/gitea
|
||||
|
||||
38
Makefile
38
Makefile
@@ -2,26 +2,23 @@ DIST := dist
|
||||
EXECUTABLE := gitea
|
||||
IMPORT := code.gitea.io/gitea
|
||||
|
||||
SHA := $(shell git rev-parse --short HEAD)
|
||||
DATE := $(shell date -u '+%Y-%m-%d %I:%M:%S %Z')
|
||||
|
||||
BINDATA := $(shell find conf | sed 's/ /\\ /g')
|
||||
BINDATA := modules/{options,public,templates}/bindata.go
|
||||
STYLESHEETS := $(wildcard public/less/index.less public/less/_*.less)
|
||||
JAVASCRIPTS :=
|
||||
|
||||
LDFLAGS += -X "code.gitea.io/gitea/modules/setting.BuildTime=$(DATE)"
|
||||
LDFLAGS += -X "code.gitea.io/gitea/modules/setting.BuildGitHash=$(SHA)"
|
||||
LDFLAGS += -X "main.Version=$(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')"
|
||||
|
||||
TARGETS ?= linux/*,darwin/*,windows/*
|
||||
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
|
||||
SOURCES ?= $(shell find . -name "*.go" -type f)
|
||||
|
||||
TAGS ?=
|
||||
|
||||
ifneq ($(DRONE_TAG),)
|
||||
VERSION ?= $(DRONE_TAG)
|
||||
VERSION ?= $(subst v,,$(DRONE_TAG))
|
||||
else
|
||||
ifneq ($(DRONE_BRANCH),)
|
||||
VERSION ?= $(DRONE_BRANCH)
|
||||
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
||||
else
|
||||
VERSION ?= master
|
||||
endif
|
||||
@@ -33,7 +30,7 @@ all: build
|
||||
.PHONY: clean
|
||||
clean:
|
||||
go clean -i ./...
|
||||
rm -rf $(EXECUTABLE) $(DIST)
|
||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA)
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@@ -86,10 +83,14 @@ install: $(wildcard *.go)
|
||||
.PHONY: build
|
||||
build: $(EXECUTABLE)
|
||||
|
||||
.PHONY: $(EXECUTABLE)
|
||||
$(EXECUTABLE): $(wildcard *.go)
|
||||
$(EXECUTABLE): $(SOURCES)
|
||||
go build -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker run -ti --rm -v $(CURDIR):/srv/app/src/code.gitea.io/gitea -w /srv/app/src/code.gitea.io/gitea -e TAGS="$(TAGS)" webhippie/golang:edge make clean generate build
|
||||
docker build -t gitea/gitea:latest .
|
||||
|
||||
.PHONY: release
|
||||
release: release-dirs release-build release-copy release-check
|
||||
|
||||
@@ -115,19 +116,6 @@ release-copy:
|
||||
release-check:
|
||||
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
|
||||
|
||||
.PHONY: bindata
|
||||
bindata: modules/bindata/bindata.go
|
||||
|
||||
.IGNORE: modules/bindata/bindata.go
|
||||
modules/bindata/bindata.go: $(BINDATA)
|
||||
@which go-bindata > /dev/null; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/jteeuwen/go-bindata/...; \
|
||||
fi
|
||||
go-bindata -o=$@ -ignore="\\.go|README.md|TRANSLATORS" -pkg=bindata conf/...
|
||||
go fmt $@
|
||||
sed -i.bak 's/confLocaleLocale_/confLocaleLocale/' $@
|
||||
rm $@.bak
|
||||
|
||||
.PHONY: javascripts
|
||||
javascripts: public/js/index.js
|
||||
|
||||
@@ -143,4 +131,4 @@ public/css/index.css: $(STYLESHEETS)
|
||||
lessc $< $@
|
||||
|
||||
.PHONY: assets
|
||||
assets: bindata javascripts stylesheets
|
||||
assets: javascripts stylesheets
|
||||
|
||||
124
README.md
124
README.md
@@ -1,3 +1,5 @@
|
||||
[简体中文](https://github.com/go-gitea/gitea/blob/master/README_ZH.md)
|
||||
|
||||
# Gitea - Git with a cup of tea
|
||||
|
||||
[](http://drone.gitea.io/go-gitea/gitea)
|
||||
@@ -6,122 +8,28 @@
|
||||
[](https://coverage.gitea.io/go-gitea/gitea)
|
||||
[](https://goreportcard.com/report/code.gitea.io/gitea)
|
||||
[](https://godoc.org/code.gitea.io/gitea)
|
||||
[](https://github.com/go-gitea/gitea/releases/latest)
|
||||
|
||||
[](https://github.com/go-gitea/gitea)
|
||||
|
||||
##### Status
|
||||
|
||||
**Current version**: (see [Releases](https://github.com/go-gitea/gitea/releases))
|
||||
|
||||
| Web | UI | Preview |
|
||||
||||
|
||||
|:-------------:|:-------:|:-------:|
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
|
||||
### Important Notes
|
||||
|
||||
1. **YOU MUST READ THE [Contributors Guide](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST**.
|
||||
2. If you think there are vulnerabilities in the project, please talk privately to **security@gitea.io**. Thanks!
|
||||
3. If you're interested in using APIs, we have experimental support with [documentation](https://godoc.org/github.com/go-gitea/go-sdk).
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
|
||||
## Purpose
|
||||
|
||||
The goal of this project is to make the easiest, fastest, and most painless way of setting up a self-hosted Git service. With Go, this can be done with an independent binary distribution across **ALL platforms** that Go supports, including Linux, Mac OS X, Windows and ARM.
|
||||
The goal of this project is to make the easiest, fastest, and most painless way of setting up a self-hosted Git service. With Go, this can be done with an independent binary distribution across **all platforms** that Go supports, including Linux, macOS, and Windows on x86, amd64, ARM and PowerPC architectures. Want to try it before doing anything else? Do it [online](https://try.gitea.io/)!
|
||||
|
||||
## Features
|
||||
## Notes
|
||||
|
||||
- Activity timeline
|
||||
- SSH and HTTP/HTTPS protocols
|
||||
- SMTP/LDAP/Reverse proxy authentication
|
||||
- Reverse proxy with sub-path
|
||||
- Account/Organization/Repository management
|
||||
- Add/Remove repository collaborators
|
||||
- Repository/Organization webhooks (including Slack)
|
||||
- Repository Git hooks/deploy keys
|
||||
- Repository issues, pull requests and wiki
|
||||
- Migrate and mirror repository and its wiki
|
||||
- Web editor for repository files and wiki
|
||||
- Gravatar and Federated avatar with custom source
|
||||
- Mail service
|
||||
- Administration panel
|
||||
- Supports MySQL, PostgreSQL, SQLite3 and [TiDB](https://github.com/pingcap/tidb) (experimental)
|
||||
- Multi-language support ([20 languages](https://crowdin.com/project/gogs))
|
||||
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
|
||||
2. If you found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
|
||||
3. If you're interested in using our APIs, we have experimental support with [documentation](https://godoc.org/code.gitea.io/sdk/gitea).
|
||||
|
||||
## System Requirements
|
||||
|
||||
- A cheap Raspberry Pi is powerful enough for basic functionality.
|
||||
- 2 CPU cores and 1GB RAM would be the baseline for teamwork.
|
||||
|
||||
## Browser Support
|
||||
|
||||
- Please see [Semantic UI](https://github.com/Semantic-Org/Semantic-UI#browser-support) for specific versions of supported browsers.
|
||||
- The official support minimal size is **1024*768**, UI may still looks right in smaller size but no promises and fixes.
|
||||
|
||||
## Installation
|
||||
|
||||
**Note: As Gitea is a [Gogs](https://github.com/gogits/gogs) fork, tutorials and documentation related to gogs applies to Gitea too**
|
||||
|
||||
How to install Gitea:
|
||||
|
||||
- go get code.gitea.io/gitea
|
||||
- [Ship with Docker](https://github.com/go-gitea/gitea/tree/master/docker)
|
||||
- [Install with Vagrant](https://github.com/go-gitea/examples/tree/master/vagrant)
|
||||
|
||||
**Note: binary release will be available soon**
|
||||
|
||||
### Tutorials
|
||||
|
||||
- [How To Set Up Gogs on Ubuntu 14.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-gogs-on-ubuntu-14-04)
|
||||
- [Run your own GitHub-like service with the help of Docker](http://blog.hypriot.com/post/run-your-own-github-like-service-with-docker/)
|
||||
- [Dockerized Gogs git server and alpine postgres in 20 minutes or less](http://garthwaite.org/docker-gogs.html)
|
||||
- [Host Your Own Private GitHub with Gogs.io](https://eladnava.com/host-your-own-private-github-with-gogs-io/)
|
||||
- [使用 Gogs 搭建自己的 Git 服务器](https://mynook.info/blog/post/host-your-own-git-server-using-gogs) (Chinese)
|
||||
- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654) (Chinese)
|
||||
- [Installing Gogs on FreeBSD](https://www.codejam.info/2015/03/installing-gogs-on-freebsd.html)
|
||||
- [Gogs on Raspberry Pi](http://blog.meinside.pe.kr/Gogs-on-Raspberry-Pi/)
|
||||
- [Cloudflare Full SSL with GOGS (Go Git Service) using NGINX](http://www.listekconsulting.com/articles/cloudflare-full-ssl-with-gogs-go-git-service-using-nginx/)
|
||||
|
||||
### Screencasts
|
||||
|
||||
- [How to install Gogs on a Linux Server (DigitalOcean)](https://www.youtube.com/watch?v=deSfX0gqefE)
|
||||
- [Instalando Gogs no Ubuntu](https://www.youtube.com/watch?v=4UkHAR1F7ZA) (Português)
|
||||
|
||||
### Deploy to Cloud
|
||||
|
||||
- [OpenShift](https://github.com/tkisme/gogs-openshift)
|
||||
- [Cloudron](https://cloudron.io/appstore.html#io.gogs.cloudronapp)
|
||||
- [Scaleway](https://www.scaleway.com/imagehub/gogs/)
|
||||
- [Portal](https://portaldemo.xyz/cloud/)
|
||||
- [Sandstorm](https://github.com/cem/gogs-sandstorm)
|
||||
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
|
||||
- [YunoHost](https://github.com/mbugeia/gogs_ynh)
|
||||
- [DPlatform](https://github.com/j8r/DPlatform)
|
||||
|
||||
## Software and Service Support
|
||||
|
||||
- [Drone](https://github.com/drone/drone) (CI)
|
||||
- [Fabric8](http://fabric8.io/) (DevOps)
|
||||
- [Taiga](https://taiga.io/) (Project Management)
|
||||
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs) (IT)
|
||||
- [Kanboard](http://kanboard.net/plugin/gogs-webhook) (Project Management)
|
||||
- [BearyChat](https://bearychat.com/) (Team Communication)
|
||||
- [HiWork](http://www.hiwork.cc/) (Team Communication)
|
||||
|
||||
### Product Support
|
||||
|
||||
- [Synology](https://www.synology.com) (Docker)
|
||||
- [One Space](http://www.onespace.cc) (App Store)
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- Router and middleware mechanism of [Macaron](https://github.com/go-macaron/macaron).
|
||||
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
|
||||
- Thanks [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
||||
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
|
||||
- Thanks [DigitalOcean](https://www.digitalocean.com) for hosting home and demo sites.
|
||||
- Thanks [KeyCDN](https://www.keycdn.com/) and [QiNiu](http://www.qiniu.com/) for providing CDN service.
|
||||
## Docs
|
||||
|
||||
For further information or instructions how to install Gitea please take a look at our [documentation](https://docs.gitea.io/en-us/), if you can not find some specific information just head over to our [Gitter](https://gitter.im/go-gitea/gitea) channel to have a chat with us.
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -131,7 +39,7 @@ Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||
* [Translators](conf/locale/TRANSLATORS)
|
||||
* [Translators](options/locale/TRANSLATORS)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
48
README_ZH.md
Normal file
48
README_ZH.md
Normal file
@@ -0,0 +1,48 @@
|
||||
[English](https://github.com/go-gitea/gitea/blob/master/README.md)
|
||||
|
||||
# Gitea - Git with a cup of tea
|
||||
|
||||
[](http://drone.gitea.io/go-gitea/gitea)
|
||||
[](https://gitter.im/go-gitea/gitea?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||
[](https://coverage.gitea.io/go-gitea/gitea)
|
||||
[](https://goreportcard.com/report/code.gitea.io/gitea)
|
||||
[](https://godoc.org/code.gitea.io/gitea)
|
||||
[](https://github.com/go-gitea/gitea/releases/latest)
|
||||
|
||||
||||
|
||||
|:-------------:|:-------:|:-------:|
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
|
||||
## 目标
|
||||
|
||||
Gitea的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用Go作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了x86,amd64,还包括 ARM 和 PowerPC。
|
||||
|
||||
如果您想试用一下,请访问 [在线Demo](https://try.gitea.io/)!
|
||||
|
||||
## 提示
|
||||
|
||||
1. **开始贡献代码之前请确保你已经看过了 [贡献者向导(英文)](CONTRIBUTING.md)**.
|
||||
2. 所有的安全问题,请私下发送邮件给 **security@gitea.io**。谢谢!
|
||||
3. 如果你要使用API,请参见 [API 文档](https://godoc.org/code.gitea.io/sdk/gitea).
|
||||
|
||||
## 文档
|
||||
|
||||
关于如何安装请访问我们的 [文档站](https://docs.gitea.io/zh-cn/),如果没有找到对应的文档,你也可以通过 [Gitter - 英文](https://gitter.im/go-gitea/gitea) 和 QQ群 328432459 来和我们交流。
|
||||
|
||||
## 贡献流程
|
||||
|
||||
Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
## 作者
|
||||
|
||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||
* [Translators](options/locale/TRANSLATORS)
|
||||
|
||||
## 授权许可
|
||||
|
||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) 文件中。
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 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.
|
||||
|
||||
@@ -18,7 +19,7 @@ var (
|
||||
CmdAdmin = cli.Command{
|
||||
Name: "admin",
|
||||
Usage: "Preform admin operations on command line",
|
||||
Description: `Allow using internal logic of Gogs without hacking into the source code
|
||||
Description: `Allow using internal logic of Gitea without hacking into the source code
|
||||
to make automatic initialization process more smoothly`,
|
||||
Subcommands: []cli.Command{
|
||||
subcmdCreateUser,
|
||||
@@ -73,7 +74,9 @@ func runCreateUser(c *cli.Context) error {
|
||||
|
||||
setting.NewContext()
|
||||
models.LoadConfigs()
|
||||
models.SetEngine()
|
||||
if err := models.SetEngine(); err != nil {
|
||||
return fmt.Errorf("models.SetEngine: %v", err)
|
||||
}
|
||||
|
||||
if err := models.CreateUser(&models.User{
|
||||
Name: c.String("name"),
|
||||
|
||||
@@ -137,7 +137,7 @@ func runCert(ctx *cli.Context) error {
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Acme Co"},
|
||||
CommonName: "Gogs",
|
||||
CommonName: "Gitea",
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
74
cmd/dump.go
74
cmd/dump.go
@@ -1,4 +1,5 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 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.
|
||||
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
@@ -21,9 +23,9 @@ import (
|
||||
// CmdDump represents the available dump sub-command.
|
||||
var CmdDump = cli.Command{
|
||||
Name: "dump",
|
||||
Usage: "Dump Gogs files and database",
|
||||
Usage: "Dump Gitea files and database",
|
||||
Description: `Dump compresses all related files and database into zip file.
|
||||
It can be used for backup and capture Gogs server image to send to maintainer`,
|
||||
It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||
Action: runDump,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
@@ -48,6 +50,7 @@ func runDump(ctx *cli.Context) error {
|
||||
setting.CustomConf = ctx.String("config")
|
||||
}
|
||||
setting.NewContext()
|
||||
setting.NewServices() // cannot access session settings otherwise
|
||||
models.LoadConfigs()
|
||||
models.SetEngine()
|
||||
|
||||
@@ -55,14 +58,14 @@ func runDump(ctx *cli.Context) error {
|
||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||
log.Fatalf("Path does not exist: %s", tmpDir)
|
||||
}
|
||||
TmpWorkDir, err := ioutil.TempDir(tmpDir, "gogs-dump-")
|
||||
TmpWorkDir, err := ioutil.TempDir(tmpDir, "gitea-dump-")
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to create tmp work directory: %v", err)
|
||||
}
|
||||
log.Printf("Creating tmp work dir: %s", TmpWorkDir)
|
||||
|
||||
reposDump := path.Join(TmpWorkDir, "gogs-repo.zip")
|
||||
dbDump := path.Join(TmpWorkDir, "gogs-db.sql")
|
||||
reposDump := path.Join(TmpWorkDir, "gitea-repo.zip")
|
||||
dbDump := path.Join(TmpWorkDir, "gitea-db.sql")
|
||||
|
||||
log.Printf("Dumping local repositories...%s", setting.RepoRootPath)
|
||||
zip.Verbose = ctx.Bool("verbose")
|
||||
@@ -75,18 +78,18 @@ func runDump(ctx *cli.Context) error {
|
||||
log.Fatalf("Fail to dump database: %v", err)
|
||||
}
|
||||
|
||||
fileName := fmt.Sprintf("gogs-dump-%d.zip", time.Now().Unix())
|
||||
fileName := fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix())
|
||||
log.Printf("Packing dump files...")
|
||||
z, err := zip.Create(fileName)
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to create %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
if err := z.AddFile("gogs-repo.zip", reposDump); err != nil {
|
||||
log.Fatalf("Fail to include gogs-repo.zip: %v", err)
|
||||
if err := z.AddFile("gitea-repo.zip", reposDump); err != nil {
|
||||
log.Fatalf("Fail to include gitea-repo.zip: %v", err)
|
||||
}
|
||||
if err := z.AddFile("gogs-db.sql", dbDump); err != nil {
|
||||
log.Fatalf("Fail to include gogs-db.sql: %v", err)
|
||||
if err := z.AddFile("gitea-db.sql", dbDump); err != nil {
|
||||
log.Fatalf("Fail to include gitea-db.sql: %v", err)
|
||||
}
|
||||
customDir, err := os.Stat(setting.CustomPath)
|
||||
if err == nil && customDir.IsDir() {
|
||||
@@ -96,6 +99,20 @@ func runDump(ctx *cli.Context) error {
|
||||
} else {
|
||||
log.Printf("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
||||
}
|
||||
|
||||
log.Printf("Packing data directory...%s", setting.AppDataPath)
|
||||
var sessionAbsPath string
|
||||
if setting.SessionConfig.Provider == "file" {
|
||||
if len(setting.SessionConfig.ProviderConfig) == 0 {
|
||||
setting.SessionConfig.ProviderConfig = "data/sessions"
|
||||
}
|
||||
sessionAbsPath, _ = filepath.Abs(setting.SessionConfig.ProviderConfig)
|
||||
}
|
||||
|
||||
if err := zipAddDirectoryExclude(z, "data", setting.AppDataPath, sessionAbsPath); err != nil {
|
||||
log.Fatalf("Fail to include data directory: %v", err)
|
||||
}
|
||||
|
||||
if err := z.AddDir("log", setting.LogRootPath); err != nil {
|
||||
log.Fatalf("Fail to include log: %v", err)
|
||||
}
|
||||
@@ -118,3 +135,40 @@ func runDump(ctx *cli.Context) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// zipAddDirectoryExclude zips absPath to specified zipPath inside z excluding excludeAbsPath
|
||||
func zipAddDirectoryExclude(zip *zip.ZipArchive, zipPath, absPath string, excludeAbsPath string) error {
|
||||
absPath, err := filepath.Abs(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dir, err := os.Open(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
zip.AddEmptyDir(zipPath)
|
||||
|
||||
files, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
currentAbsPath := path.Join(absPath, file.Name())
|
||||
currentZipPath := path.Join(zipPath, file.Name())
|
||||
if file.IsDir() {
|
||||
if currentAbsPath != excludeAbsPath {
|
||||
if err = zipAddDirectoryExclude(zip, currentZipPath, currentAbsPath, excludeAbsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if err = zip.AddFile(currentZipPath, currentAbsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
15
cmd/serve.go
15
cmd/serve.go
@@ -1,4 +1,5 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 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.
|
||||
|
||||
@@ -76,7 +77,7 @@ var (
|
||||
)
|
||||
|
||||
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||
fmt.Fprintln(os.Stderr, "Gogs:", userMessage)
|
||||
fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
|
||||
|
||||
if len(logMessage) > 0 {
|
||||
if !setting.ProdMode {
|
||||
@@ -120,7 +121,7 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
|
||||
|
||||
// Ask for running deliver hook and test pull request tasks.
|
||||
reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
|
||||
strings.TrimPrefix(task.RefName, git.BRANCH_PREFIX) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
|
||||
strings.TrimPrefix(task.RefName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
|
||||
log.GitLogger.Trace("Trigger task: %s", reqURL)
|
||||
|
||||
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
||||
@@ -144,7 +145,7 @@ func runServ(c *cli.Context) error {
|
||||
setup("serv.log")
|
||||
|
||||
if setting.SSH.Disabled {
|
||||
println("Gogs: SSH has been disabled")
|
||||
println("Gitea: SSH has been disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -154,8 +155,8 @@ func runServ(c *cli.Context) error {
|
||||
|
||||
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||
if len(cmd) == 0 {
|
||||
println("Hi there, You've successfully authenticated, but Gogs does not provide shell access.")
|
||||
println("If this is unexpected, please log in with password and setup Gogs under another user.")
|
||||
println("Hi there, You've successfully authenticated, but Gitea does not provide shell access.")
|
||||
println("If this is unexpected, please log in with password and setup Gitea under another user.")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -255,11 +256,11 @@ func runServ(c *cli.Context) error {
|
||||
"User %s does not have level %v access to repository %s",
|
||||
user.Name, requestedMode, repoPath)
|
||||
}
|
||||
|
||||
os.Setenv("GITEA_PUSHER_NAME", user.Name)
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv("GITEA_PUSHER_NAME", user.Name)
|
||||
|
||||
uuid := gouuid.NewV4().String()
|
||||
os.Setenv("GITEA_UUID", uuid)
|
||||
// Keep the old env variable name for backward compability
|
||||
|
||||
89
cmd/web.go
89
cmd/web.go
@@ -7,7 +7,6 @@ package cmd
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
@@ -15,15 +14,14 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/bindata"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/options"
|
||||
"code.gitea.io/gitea/modules/public"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/template"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/admin"
|
||||
apiv1 "code.gitea.io/gitea/routers/api/v1"
|
||||
@@ -39,18 +37,15 @@ import (
|
||||
"github.com/go-macaron/i18n"
|
||||
"github.com/go-macaron/session"
|
||||
"github.com/go-macaron/toolbox"
|
||||
"github.com/go-xorm/xorm"
|
||||
version "github.com/mcuadros/go-version"
|
||||
"github.com/urfave/cli"
|
||||
ini "gopkg.in/ini.v1"
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// CmdWeb represents the available web sub-command.
|
||||
var CmdWeb = cli.Command{
|
||||
Name: "web",
|
||||
Usage: "Start Gogs web server",
|
||||
Description: `Gogs web server is the only thing you need to run,
|
||||
Usage: "Start Gitea web server",
|
||||
Description: `Gitea web server is the only thing you need to run,
|
||||
and it takes care of all the other things for you`,
|
||||
Action: runWeb,
|
||||
Flags: []cli.Flag{
|
||||
@@ -74,45 +69,6 @@ type VerChecker struct {
|
||||
Expected string
|
||||
}
|
||||
|
||||
// checkVersion checks if binary matches the version of templates files.
|
||||
func checkVersion() {
|
||||
// Templates.
|
||||
data, err := ioutil.ReadFile(setting.StaticRootPath + "/templates/.VERSION")
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err)
|
||||
}
|
||||
tplVer := string(data)
|
||||
if tplVer != setting.AppVer {
|
||||
if version.Compare(tplVer, setting.AppVer, ">") {
|
||||
log.Fatal(4, "Binary version is lower than template file version, did you forget to recompile Gogs?")
|
||||
} else {
|
||||
log.Fatal(4, "Binary version is higher than template file version, did you forget to update template files?")
|
||||
}
|
||||
}
|
||||
|
||||
// Check dependency version.
|
||||
checkers := []VerChecker{
|
||||
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.5.5"},
|
||||
{"github.com/go-macaron/binding", binding.Version, "0.3.2"},
|
||||
{"github.com/go-macaron/cache", cache.Version, "0.1.2"},
|
||||
{"github.com/go-macaron/csrf", csrf.Version, "0.1.0"},
|
||||
{"github.com/go-macaron/i18n", i18n.Version, "0.3.0"},
|
||||
{"github.com/go-macaron/session", session.Version, "0.1.6"},
|
||||
{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
|
||||
{"gopkg.in/ini.v1", ini.Version, "1.8.4"},
|
||||
{"gopkg.in/macaron.v1", macaron.Version, "1.1.7"},
|
||||
{"code.gitea.io/git", git.Version, "0.4.1"},
|
||||
}
|
||||
for _, c := range checkers {
|
||||
if !version.Compare(c.Version(), c.Expected, ">=") {
|
||||
log.Fatal(4, `Dependency outdated!
|
||||
Package '%s' current version (%s) is below requirement (%s),
|
||||
please use following command to update this package and recompile Gogs:
|
||||
go get -u %[1]s`, c.ImportPath, c.Version(), c.Expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// newMacaron initializes Macaron instance.
|
||||
func newMacaron() *macaron.Macaron {
|
||||
m := macaron.New()
|
||||
@@ -140,32 +96,32 @@ func newMacaron() *macaron.Macaron {
|
||||
},
|
||||
))
|
||||
|
||||
funcMap := template.NewFuncMap()
|
||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
||||
AppendDirectories: []string{path.Join(setting.CustomPath, "templates")},
|
||||
Funcs: funcMap,
|
||||
IndentJSON: macaron.Env != macaron.PROD,
|
||||
}))
|
||||
models.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"),
|
||||
path.Join(setting.CustomPath, "templates/mail"), funcMap)
|
||||
m.Use(templates.Renderer())
|
||||
models.InitMailRender(templates.Mailer())
|
||||
|
||||
localeNames, err := options.Dir("locale")
|
||||
|
||||
localeNames, err := bindata.AssetDir("conf/locale")
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to list locale files: %v", err)
|
||||
}
|
||||
|
||||
localFiles := make(map[string][]byte)
|
||||
|
||||
for _, name := range localeNames {
|
||||
localFiles[name] = bindata.MustAsset("conf/locale/" + name)
|
||||
localFiles[name], err = options.Locale(name)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(4, "Failed to load %s locale file. %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
m.Use(i18n.I18n(i18n.Options{
|
||||
SubURL: setting.AppSubURL,
|
||||
Files: localFiles,
|
||||
CustomDirectory: path.Join(setting.CustomPath, "conf/locale"),
|
||||
Langs: setting.Langs,
|
||||
Names: setting.Names,
|
||||
DefaultLang: "en-US",
|
||||
Redirect: true,
|
||||
SubURL: setting.AppSubURL,
|
||||
Files: localFiles,
|
||||
Langs: setting.Langs,
|
||||
Names: setting.Names,
|
||||
DefaultLang: "en-US",
|
||||
Redirect: true,
|
||||
}))
|
||||
m.Use(cache.Cacher(cache.Options{
|
||||
Adapter: setting.CacheAdapter,
|
||||
@@ -200,7 +156,6 @@ func runWeb(ctx *cli.Context) error {
|
||||
setting.CustomConf = ctx.String("config")
|
||||
}
|
||||
routers.GlobalInit()
|
||||
checkVersion()
|
||||
|
||||
m := newMacaron()
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
Execute following command in ROOT directory when anything is changed:
|
||||
|
||||
$ make bindata
|
||||
14
conf/app.ini
14
conf/app.ini
@@ -1,6 +1,3 @@
|
||||
# NEVER EVER MODIFY THIS FILE
|
||||
# PLEASE MAKE CHANGES ON CORRESPONDING CUSTOM CONFIG FILE
|
||||
|
||||
; App name that shows on every page title
|
||||
APP_NAME = Gitea: Git with a cup of tea
|
||||
; Change it if you run locally
|
||||
@@ -57,7 +54,7 @@ FEED_MAX_COMMIT_NUM = 5
|
||||
; Value of `theme-color` meta tag, used by Android >= 5.0
|
||||
; An invalid color like "none" or "disable" will have the default style
|
||||
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
|
||||
THEME_COLOR_META_TAG = `#ff5343`
|
||||
THEME_COLOR_META_TAG = `#6cc644`
|
||||
; Max size of files to be displayed (defaults is 8MiB)
|
||||
MAX_DISPLAY_FILE_SIZE = 8388608
|
||||
|
||||
@@ -103,6 +100,8 @@ DISABLE_SSH = false
|
||||
START_SSH_SERVER = false
|
||||
; Domain name to be exposed in clone URL
|
||||
SSH_DOMAIN = %(DOMAIN)s
|
||||
; Network interface builtin SSH server listens on
|
||||
SSH_LISTEN_HOST =
|
||||
; Port number to be exposed in clone URL
|
||||
SSH_PORT = 22
|
||||
; Port number builtin SSH server listens on
|
||||
@@ -336,7 +335,7 @@ HOST =
|
||||
; Mailer user name and password
|
||||
USER =
|
||||
PASSWD =
|
||||
; Receivers, can be one or more, e.g. ["1@example.com","2@example.com"]
|
||||
; Receivers, can be one or more, e.g. 1@example.com,2@example.com
|
||||
RECEIVERS =
|
||||
|
||||
; For "database" mode only
|
||||
@@ -400,8 +399,8 @@ DEFAULT_INTERVAL = 8
|
||||
MAX_RESPONSE_ITEMS = 50
|
||||
|
||||
[i18n]
|
||||
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE
|
||||
NAMES = English,简体中文,繁體中文(香港),繁體中文(台湾),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски,Svenska
|
||||
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR
|
||||
NAMES = English,简体中文,繁體中文(香港),繁體中文(台湾),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски,Svenska,한국어
|
||||
|
||||
; Used for datetimepicker
|
||||
[i18n.datelang]
|
||||
@@ -425,6 +424,7 @@ tr-TR = tr
|
||||
cs-CZ = cs-CZ
|
||||
sr-SP = sr
|
||||
sv-SE = sv
|
||||
ko-KR = ko
|
||||
|
||||
; Extension mapping to highlight class
|
||||
; e.g. .toml=ini
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /root > /dev/null
|
||||
exec su-exec root /usr/sbin/sshd -E /var/log/sshd.log -D
|
||||
exec su-exec root /usr/sbin/sshd -D
|
||||
popd
|
||||
|
||||
@@ -18,7 +18,6 @@ UseDNS no
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
PrintMotd no
|
||||
PrintLastLog no
|
||||
|
||||
PermitUserEnvironment yes
|
||||
PermitRootLogin no
|
||||
|
||||
4
main.go
4
main.go
@@ -1,5 +1,5 @@
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 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.
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
// Version holds the current Gitea version
|
||||
const Version = "0.9.99.0915"
|
||||
var Version = "1.0.2+dev"
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
@@ -494,12 +494,12 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
isNewBranch := false
|
||||
opType := ActionCommitRepo
|
||||
// Check it's tag push or branch.
|
||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
||||
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||
opType = ActionPushTag
|
||||
opts.Commits = &PushCommits{}
|
||||
} else {
|
||||
// if not the first commit, set the compare URL.
|
||||
if opts.OldCommitID == git.EMPTY_SHA {
|
||||
if opts.OldCommitID == git.EmptySHA {
|
||||
isNewBranch = true
|
||||
} else {
|
||||
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
||||
@@ -539,7 +539,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
}()
|
||||
|
||||
apiPusher := pusher.APIFormat()
|
||||
apiRepo := repo.APIFormat(nil)
|
||||
apiRepo := repo.APIFormat(AccessModeNone)
|
||||
|
||||
var shaSum string
|
||||
switch opType {
|
||||
@@ -562,7 +562,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err)
|
||||
}
|
||||
shaSum, err = gitRepo.GetBranchCommitID(opts.RefFullName)
|
||||
shaSum, err = gitRepo.GetBranchCommitID(refName)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommitID[%s]: %v", opts.RefFullName, err)
|
||||
}
|
||||
@@ -580,7 +580,7 @@ func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository[%s]: %v", repo.RepoPath(), err)
|
||||
}
|
||||
shaSum, err = gitRepo.GetTagCommitID(opts.RefFullName)
|
||||
shaSum, err = gitRepo.GetTagCommitID(refName)
|
||||
if err != nil {
|
||||
log.Error(4, "GetTagCommitID[%s]: %v", opts.RefFullName, err)
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ import (
|
||||
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/highlight"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/template/highlight"
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
"golang.org/x/net/html/charset"
|
||||
|
||||
@@ -87,13 +87,20 @@ func (issue *Issue) AfterSet(colName string, _ xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
func (issue *Issue) loadAttributes(e Engine) (err error) {
|
||||
func (issue *Issue) loadRepo(e Engine) (err error) {
|
||||
if issue.Repo == nil {
|
||||
issue.Repo, err = getRepositoryByID(e, issue.RepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getRepositoryByID [%d]: %v", issue.RepoID, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (issue *Issue) loadAttributes(e Engine) (err error) {
|
||||
if err := issue.loadRepo(e); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if issue.Poster == nil {
|
||||
issue.Poster, err = getUserByID(e, issue.PosterID)
|
||||
@@ -265,7 +272,7 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
|
||||
Action: api.HookIssueLabelUpdated,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(nil),
|
||||
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
}
|
||||
@@ -322,6 +329,16 @@ func (issue *Issue) removeLabel(e *xorm.Session, label *Label) error {
|
||||
|
||||
// RemoveLabel removes a label from issue by given ID.
|
||||
func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
|
||||
if err := issue.loadRepo(x); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if has, err := HasAccess(doer, issue.Repo, AccessModeWrite); err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrLabelNotExist{}
|
||||
}
|
||||
|
||||
if err := DeleteIssueLabel(issue, label); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -353,6 +370,16 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := issue.loadRepo(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if has, err := hasAccess(sess, doer, issue.Repo, AccessModeWrite); err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrLabelNotExist{}
|
||||
}
|
||||
|
||||
if err = issue.clearLabels(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -371,7 +398,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
|
||||
Action: api.HookIssueLabelCleared,
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(nil),
|
||||
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
}
|
||||
@@ -493,7 +520,7 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: repo.APIFormat(nil),
|
||||
Repository: repo.APIFormat(AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
if isClosed {
|
||||
@@ -531,7 +558,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||
},
|
||||
},
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(nil),
|
||||
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
}
|
||||
@@ -563,7 +590,7 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
|
||||
},
|
||||
},
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(nil),
|
||||
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
})
|
||||
}
|
||||
@@ -596,7 +623,7 @@ func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
|
||||
apiPullRequest := &api.PullRequestPayload{
|
||||
Index: issue.Index,
|
||||
PullRequest: issue.PullRequest.APIFormat(),
|
||||
Repository: issue.Repo.APIFormat(nil),
|
||||
Repository: issue.Repo.APIFormat(AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
if isRemoveAssignee {
|
||||
@@ -888,7 +915,10 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
|
||||
}
|
||||
|
||||
if len(opts.Labels) > 0 && opts.Labels != "0" {
|
||||
labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ","))
|
||||
labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(labelIDs) > 0 {
|
||||
sess.
|
||||
Join("INNER", "issue_label", "issue.id = issue_label.issue_id").
|
||||
@@ -1055,7 +1085,7 @@ func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int
|
||||
|
||||
// UpdateIssueMentions extracts mentioned people from content and
|
||||
// updates issue-user relations for them.
|
||||
func UpdateIssueMentions(issueID int64, mentions []string) error {
|
||||
func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
|
||||
if len(mentions) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -1065,7 +1095,7 @@ func UpdateIssueMentions(issueID int64, mentions []string) error {
|
||||
}
|
||||
users := make([]*User, 0, len(mentions))
|
||||
|
||||
if err := x.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil {
|
||||
if err := e.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil {
|
||||
return fmt.Errorf("find mentioned users: %v", err)
|
||||
}
|
||||
|
||||
@@ -1089,7 +1119,7 @@ func UpdateIssueMentions(issueID int64, mentions []string) error {
|
||||
ids = append(ids, memberIDs...)
|
||||
}
|
||||
|
||||
if err := UpdateIssueUsersByMentions(issueID, ids); err != nil {
|
||||
if err := UpdateIssueUsersByMentions(e, issueID, ids); err != nil {
|
||||
return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
|
||||
}
|
||||
|
||||
@@ -1144,10 +1174,11 @@ func GetIssueStats(opts *IssueStatsOptions) *IssueStats {
|
||||
And("is_pull = ?", opts.IsPull)
|
||||
|
||||
if len(opts.Labels) > 0 && opts.Labels != "0" {
|
||||
labelIDs := base.StringsToInt64s(strings.Split(opts.Labels, ","))
|
||||
if len(labelIDs) > 0 {
|
||||
sess.
|
||||
Join("INNER", "issue_label", "issue.id = issue_id").
|
||||
labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
|
||||
if err != nil {
|
||||
log.Warn("Malformed Labels argument: %s", opts.Labels)
|
||||
} else if len(labelIDs) > 0 {
|
||||
sess.Join("INNER", "issue_label", "issue.id = issue_id").
|
||||
In("label_id", labelIDs)
|
||||
}
|
||||
}
|
||||
@@ -1248,7 +1279,7 @@ func GetUserIssueStats(repoID, uid int64, repoIDs []int64, filterMode int, isPul
|
||||
func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen int64, numClosed int64) {
|
||||
countSession := func(isClosed, isPull bool, repoID int64) *xorm.Session {
|
||||
sess := x.
|
||||
Where("issue.repo_id = ?", isClosed).
|
||||
Where("is_closed = ?", isClosed).
|
||||
And("is_pull = ?", isPull).
|
||||
And("repo_id = ?", repoID)
|
||||
|
||||
@@ -1330,22 +1361,22 @@ func UpdateIssueUserByRead(uid, issueID int64) error {
|
||||
}
|
||||
|
||||
// UpdateIssueUsersByMentions updates issue-user pairs by mentioning.
|
||||
func UpdateIssueUsersByMentions(issueID int64, uids []int64) error {
|
||||
func UpdateIssueUsersByMentions(e Engine, issueID int64, uids []int64) error {
|
||||
for _, uid := range uids {
|
||||
iu := &IssueUser{
|
||||
UID: uid,
|
||||
IssueID: issueID,
|
||||
}
|
||||
has, err := x.Get(iu)
|
||||
has, err := e.Get(iu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iu.IsMentioned = true
|
||||
if has {
|
||||
_, err = x.Id(iu.ID).AllCols().Update(iu)
|
||||
_, err = e.Id(iu.ID).AllCols().Update(iu)
|
||||
} else {
|
||||
_, err = x.Insert(iu)
|
||||
_, err = e.Insert(iu)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -123,14 +123,55 @@ func (c *Comment) AfterDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
// HTMLURL formats a URL-string to the issue-comment
|
||||
func (c *Comment) HTMLURL() string {
|
||||
issue, err := GetIssueByID(c.IssueID)
|
||||
if err != nil { // Silently dropping errors :unamused:
|
||||
log.Error(4, "GetIssueByID(%d): %v", c.IssueID, err)
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s#issuecomment-%d", issue.HTMLURL(), c.ID)
|
||||
}
|
||||
|
||||
// IssueURL formats a URL-string to the issue
|
||||
func (c *Comment) IssueURL() string {
|
||||
issue, err := GetIssueByID(c.IssueID)
|
||||
if err != nil { // Silently dropping errors :unamused:
|
||||
log.Error(4, "GetIssueByID(%d): %v", c.IssueID, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if issue.IsPull {
|
||||
return ""
|
||||
}
|
||||
return issue.HTMLURL()
|
||||
}
|
||||
|
||||
// PRURL formats a URL-string to the pull-request
|
||||
func (c *Comment) PRURL() string {
|
||||
issue, err := GetIssueByID(c.IssueID)
|
||||
if err != nil { // Silently dropping errors :unamused:
|
||||
log.Error(4, "GetIssueByID(%d): %v", c.IssueID, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if !issue.IsPull {
|
||||
return ""
|
||||
}
|
||||
return issue.HTMLURL()
|
||||
}
|
||||
|
||||
// APIFormat converts a Comment to the api.Comment format
|
||||
func (c *Comment) APIFormat() *api.Comment {
|
||||
return &api.Comment{
|
||||
ID: c.ID,
|
||||
Poster: c.Poster.APIFormat(),
|
||||
Body: c.Content,
|
||||
Created: c.Created,
|
||||
Updated: c.Updated,
|
||||
ID: c.ID,
|
||||
Poster: c.Poster.APIFormat(),
|
||||
HTMLURL: c.HTMLURL(),
|
||||
IssueURL: c.IssueURL(),
|
||||
PRURL: c.PRURL(),
|
||||
Body: c.Content,
|
||||
Created: c.Created,
|
||||
Updated: c.Updated,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +187,9 @@ func (c *Comment) EventTag() string {
|
||||
|
||||
// MailParticipants sends new comment emails to repository watchers
|
||||
// and mentioned people.
|
||||
func (c *Comment) MailParticipants(opType ActionType, issue *Issue) (err error) {
|
||||
func (c *Comment) MailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
|
||||
mentions := markdown.FindAllMentions(c.Content)
|
||||
if err = UpdateIssueMentions(c.IssueID, mentions); err != nil {
|
||||
if err = UpdateIssueMentions(e, c.IssueID, mentions); err != nil {
|
||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
|
||||
}
|
||||
|
||||
@@ -262,7 +303,9 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
||||
if err = notifyWatchers(e, act); err != nil {
|
||||
log.Error(4, "notifyWatchers: %v", err)
|
||||
}
|
||||
comment.MailParticipants(act.OpType, opts.Issue)
|
||||
if err = comment.MailParticipants(e, act.OpType, opts.Issue); err != nil {
|
||||
log.Error(4, "MailParticipants: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return comment, nil
|
||||
@@ -375,6 +418,15 @@ func getCommentsByIssueIDSince(e Engine, issueID, since int64) ([]*Comment, erro
|
||||
return comments, sess.Find(&comments)
|
||||
}
|
||||
|
||||
func getCommentsByRepoIDSince(e Engine, repoID, since int64) ([]*Comment, error) {
|
||||
comments := make([]*Comment, 0, 10)
|
||||
sess := e.Where("issue.repo_id = ?", repoID).Join("INNER", "issue", "issue.id = comment.issue_id", repoID).Asc("created_unix")
|
||||
if since > 0 {
|
||||
sess.And("updated_unix >= ?", since)
|
||||
}
|
||||
return comments, sess.Find(&comments)
|
||||
}
|
||||
|
||||
func getCommentsByIssueID(e Engine, issueID int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueIDSince(e, issueID, -1)
|
||||
}
|
||||
@@ -389,6 +441,11 @@ func GetCommentsByIssueIDSince(issueID, since int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueIDSince(x, issueID, since)
|
||||
}
|
||||
|
||||
// GetCommentsByRepoIDSince returns a list of comments for all issues in a repo since a given time point.
|
||||
func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) {
|
||||
return getCommentsByRepoIDSince(x, repoID, since)
|
||||
}
|
||||
|
||||
// UpdateComment updates information of comment.
|
||||
func UpdateComment(c *Comment) error {
|
||||
_, err := x.Id(c.ID).AllCols().Update(c)
|
||||
|
||||
@@ -69,7 +69,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
|
||||
// and mentioned people.
|
||||
func (issue *Issue) MailParticipants() (err error) {
|
||||
mentions := markdown.FindAllMentions(issue.Content)
|
||||
if err = UpdateIssueMentions(issue.ID, mentions); err != nil {
|
||||
if err = UpdateIssueMentions(x, issue.ID, mentions); err != nil {
|
||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -548,9 +548,9 @@ func ExternalUserLogin(user *User, login, password string, source *LoginSource,
|
||||
func UserSignIn(username, password string) (*User, error) {
|
||||
var user *User
|
||||
if strings.Contains(username, "@") {
|
||||
user = &User{Email: strings.ToLower(username)}
|
||||
user = &User{Email: strings.ToLower(strings.TrimSpace(username))}
|
||||
} else {
|
||||
user = &User{LowerName: strings.ToLower(username)}
|
||||
user = &User{LowerName: strings.ToLower(strings.TrimSpace(username))}
|
||||
}
|
||||
|
||||
hasUser, err := x.Get(user)
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"path"
|
||||
|
||||
"gopkg.in/gomail.v2"
|
||||
"gopkg.in/macaron.v1"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/mailer"
|
||||
"code.gitea.io/gitea/modules/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"gopkg.in/gomail.v2"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -31,32 +31,16 @@ const (
|
||||
mailNotifyCollaborator base.TplName = "notify/collaborator"
|
||||
)
|
||||
|
||||
type mailRenderInterface interface {
|
||||
HTMLString(string, interface{}, ...macaron.HTMLOptions) (string, error)
|
||||
}
|
||||
|
||||
var mailRender mailRenderInterface
|
||||
var templates *template.Template
|
||||
|
||||
// InitMailRender initializes the macaron mail renderer
|
||||
func InitMailRender(dir, appendDir string, funcMap []template.FuncMap) {
|
||||
opt := &macaron.RenderOptions{
|
||||
Directory: dir,
|
||||
AppendDirectories: []string{appendDir},
|
||||
Funcs: funcMap,
|
||||
Extensions: []string{".tmpl", ".html"},
|
||||
}
|
||||
ts := macaron.NewTemplateSet()
|
||||
ts.Set(macaron.DEFAULT_TPL_SET_NAME, opt)
|
||||
|
||||
mailRender = &macaron.TplRender{
|
||||
TemplateSet: ts,
|
||||
Opt: opt,
|
||||
}
|
||||
func InitMailRender(tmpls *template.Template) {
|
||||
templates = tmpls
|
||||
}
|
||||
|
||||
// SendTestMail sends a test mail
|
||||
func SendTestMail(email string) error {
|
||||
return gomail.Send(&mailer.Sender{}, mailer.NewMessage([]string{email}, "Gogs Test Email!", "Gogs Test Email!").Message)
|
||||
return gomail.Send(&mailer.Sender{}, mailer.NewMessage([]string{email}, "Gitea Test Email!", "Gitea Test Email!").Message)
|
||||
}
|
||||
|
||||
// SendUserMail sends a mail to the user
|
||||
@@ -67,13 +51,15 @@ func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject,
|
||||
"ResetPwdCodeLives": setting.Service.ResetPwdCodeLives / 60,
|
||||
"Code": code,
|
||||
}
|
||||
body, err := mailRender.HTMLString(string(tpl), data)
|
||||
if err != nil {
|
||||
log.Error(3, "HTMLString: %v", err)
|
||||
|
||||
var content bytes.Buffer
|
||||
|
||||
if err := templates.ExecuteTemplate(&content, string(tpl), data); err != nil {
|
||||
log.Error(3, "Template: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg := mailer.NewMessage([]string{u.Email}, subject, body)
|
||||
msg := mailer.NewMessage([]string{u.Email}, subject, content.String())
|
||||
msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info)
|
||||
|
||||
mailer.SendAsync(msg)
|
||||
@@ -97,13 +83,15 @@ func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) {
|
||||
"Code": u.GenerateEmailActivateCode(email.Email),
|
||||
"Email": email.Email,
|
||||
}
|
||||
body, err := mailRender.HTMLString(string(mailAuthActivateEmail), data)
|
||||
if err != nil {
|
||||
log.Error(3, "HTMLString: %v", err)
|
||||
|
||||
var content bytes.Buffer
|
||||
|
||||
if err := templates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
|
||||
log.Error(3, "Template: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), body)
|
||||
msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), content.String())
|
||||
msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID)
|
||||
|
||||
mailer.SendAsync(msg)
|
||||
@@ -114,13 +102,15 @@ func SendRegisterNotifyMail(c *macaron.Context, u *User) {
|
||||
data := map[string]interface{}{
|
||||
"Username": u.DisplayName(),
|
||||
}
|
||||
body, err := mailRender.HTMLString(string(mailAuthRegisterNotify), data)
|
||||
if err != nil {
|
||||
log.Error(3, "HTMLString: %v", err)
|
||||
|
||||
var content bytes.Buffer
|
||||
|
||||
if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
|
||||
log.Error(3, "Template: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), body)
|
||||
msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), content.String())
|
||||
msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID)
|
||||
|
||||
mailer.SendAsync(msg)
|
||||
@@ -136,13 +126,15 @@ func SendCollaboratorMail(u, doer *User, repo *Repository) {
|
||||
"RepoName": repoName,
|
||||
"Link": repo.HTMLURL(),
|
||||
}
|
||||
body, err := mailRender.HTMLString(string(mailNotifyCollaborator), data)
|
||||
if err != nil {
|
||||
log.Error(3, "HTMLString: %v", err)
|
||||
|
||||
var content bytes.Buffer
|
||||
|
||||
if err := templates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
|
||||
log.Error(3, "Template: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
msg := mailer.NewMessage([]string{u.Email}, subject, body)
|
||||
msg := mailer.NewMessage([]string{u.Email}, subject, content.String())
|
||||
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
|
||||
|
||||
mailer.SendAsync(msg)
|
||||
@@ -161,11 +153,14 @@ func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []s
|
||||
body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
|
||||
data := composeTplData(subject, body, issue.HTMLURL())
|
||||
data["Doer"] = doer
|
||||
content, err := mailRender.HTMLString(string(tplName), data)
|
||||
if err != nil {
|
||||
log.Error(3, "HTMLString (%s): %v", tplName, err)
|
||||
|
||||
var content bytes.Buffer
|
||||
|
||||
if err := templates.ExecuteTemplate(&content, string(tplName), data); err != nil {
|
||||
log.Error(3, "Template: %v", err)
|
||||
}
|
||||
msg := mailer.NewMessageFrom(tos, fmt.Sprintf(`"%s" <%s>`, doer.DisplayName(), setting.MailService.FromEmail), subject, content)
|
||||
|
||||
msg := mailer.NewMessageFrom(tos, fmt.Sprintf(`"%s" <%s>`, doer.DisplayName(), setting.MailService.FromEmail), subject, content.String())
|
||||
msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
|
||||
return msg
|
||||
}
|
||||
|
||||
@@ -457,8 +457,12 @@ func generateOrgRandsAndSalt(x *xorm.Engine) (err error) {
|
||||
}
|
||||
|
||||
for _, org := range orgs {
|
||||
org.Rands = base.GetRandomString(10)
|
||||
org.Salt = base.GetRandomString(10)
|
||||
if org.Rands, err = base.GetRandomString(10); err != nil {
|
||||
return err
|
||||
}
|
||||
if org.Salt, err = base.GetRandomString(10); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = sess.Id(org.ID).Update(org); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -109,8 +109,12 @@ func CreateOrganization(org, owner *User) (err error) {
|
||||
}
|
||||
|
||||
org.LowerName = strings.ToLower(org.Name)
|
||||
org.Rands = GetUserSalt()
|
||||
org.Salt = GetUserSalt()
|
||||
if org.Rands, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
if org.Salt, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
org.UseCustomAvatar = true
|
||||
org.MaxRepoCreation = -1
|
||||
org.NumTeams = 1
|
||||
@@ -202,12 +206,9 @@ func Organizations(page, pageSize int) ([]*User, error) {
|
||||
|
||||
// DeleteOrganization completely and permanently deletes everything of organization.
|
||||
func DeleteOrganization(org *User) (err error) {
|
||||
if err := DeleteUser(org); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
defer sess.Close()
|
||||
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -224,7 +225,11 @@ func DeleteOrganization(org *User) (err error) {
|
||||
return fmt.Errorf("deleteUser: %v", err)
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
if err = sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return RewriteAllPublicKeys()
|
||||
}
|
||||
|
||||
// ________ ____ ___
|
||||
|
||||
@@ -160,14 +160,14 @@ func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||
Ref: pr.BaseBranch,
|
||||
Sha: baseCommit.ID.String(),
|
||||
RepoID: pr.BaseRepoID,
|
||||
Repository: pr.BaseRepo.APIFormat(nil),
|
||||
Repository: pr.BaseRepo.APIFormat(AccessModeNone),
|
||||
}
|
||||
apiHeadBranchInfo := &api.PRBranchInfo{
|
||||
Name: pr.HeadBranch,
|
||||
Ref: pr.HeadBranch,
|
||||
Sha: headCommit.ID.String(),
|
||||
RepoID: pr.HeadRepoID,
|
||||
Repository: pr.HeadRepo.APIFormat(nil),
|
||||
Repository: pr.HeadRepo.APIFormat(AccessModeNone),
|
||||
}
|
||||
apiPullRequest := &api.PullRequest{
|
||||
ID: pr.ID,
|
||||
@@ -355,7 +355,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
Action: api.HookIssueClosed,
|
||||
Index: pr.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
Repository: pr.Issue.Repo.APIFormat(nil),
|
||||
Repository: pr.Issue.Repo.APIFormat(AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error(4, "PrepareWebhooks: %v", err)
|
||||
@@ -380,12 +380,12 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
l.PushFront(mergeCommit)
|
||||
|
||||
p := &api.PushPayload{
|
||||
Ref: git.BRANCH_PREFIX + pr.BaseBranch,
|
||||
Ref: git.BranchPrefix + pr.BaseBranch,
|
||||
Before: pr.MergeBase,
|
||||
After: pr.MergedCommitID,
|
||||
CompareURL: setting.AppURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||
Commits: ListToPushCommits(l).ToAPIPayloadCommits(pr.BaseRepo.HTMLURL()),
|
||||
Repo: pr.BaseRepo.APIFormat(nil),
|
||||
Repo: pr.BaseRepo.APIFormat(AccessModeNone),
|
||||
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
@@ -404,7 +404,6 @@ var patchConflicts = []string{
|
||||
}
|
||||
|
||||
// testPatch checks if patch can be merged to base repository without conflict.
|
||||
// FIXME: make a mechanism to clean up stable local copies.
|
||||
func (pr *PullRequest) testPatch() (err error) {
|
||||
if pr.BaseRepo == nil {
|
||||
pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID)
|
||||
@@ -429,6 +428,9 @@ func (pr *PullRequest) testPatch() (err error) {
|
||||
|
||||
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
|
||||
|
||||
// Delete old temp local copy before we create a new temp local copy
|
||||
RemoveAllWithNotice("Deleting old local copy", pr.BaseRepo.LocalCopyPath())
|
||||
|
||||
if err := pr.BaseRepo.UpdateLocalCopyBranch(pr.BaseBranch); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopy: %v", err)
|
||||
}
|
||||
@@ -514,7 +516,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
Action: api.HookIssueOpened,
|
||||
Index: pull.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
Repository: repo.APIFormat(nil),
|
||||
Repository: repo.APIFormat(AccessModeNone),
|
||||
Sender: pull.Poster.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error(4, "PrepareWebhooks: %v", err)
|
||||
@@ -840,7 +842,7 @@ func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool
|
||||
Action: api.HookIssueSynchronized,
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: pr.Issue.PullRequest.APIFormat(),
|
||||
Repository: pr.Issue.Repo.APIFormat(nil),
|
||||
Repository: pr.Issue.Repo.APIFormat(AccessModeNone),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error(4, "PrepareWebhooks [pull_id: %v]: %v", pr.ID, err)
|
||||
@@ -927,7 +929,7 @@ func TestPullRequests() {
|
||||
|
||||
pr, err := GetPullRequestByID(com.StrTo(prID).MustInt64())
|
||||
if err != nil {
|
||||
log.Error(4, "GetPullRequestByID[%d]: %v", prID, err)
|
||||
log.Error(4, "GetPullRequestByID[%s]: %v", prID, err)
|
||||
continue
|
||||
} else if err = pr.testPatch(); err != nil {
|
||||
log.Error(4, "testPatch[%d]: %v", pr.ID, err)
|
||||
|
||||
@@ -189,7 +189,7 @@ func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) {
|
||||
}
|
||||
|
||||
// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
|
||||
func DeleteReleaseByID(id int64) error {
|
||||
func DeleteReleaseByID(id int64, u *User, delTag bool) error {
|
||||
rel, err := GetReleaseByID(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetReleaseByID: %v", err)
|
||||
@@ -200,11 +200,20 @@ func DeleteReleaseByID(id int64) error {
|
||||
return fmt.Errorf("GetRepositoryByID: %v", err)
|
||||
}
|
||||
|
||||
_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
|
||||
fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
|
||||
"git", "tag", "-d", rel.TagName)
|
||||
if err != nil && !strings.Contains(stderr, "not found") {
|
||||
return fmt.Errorf("git tag -d: %v - %s", err, stderr)
|
||||
has, err := HasAccess(u, repo, AccessModeWrite)
|
||||
if err != nil {
|
||||
return fmt.Errorf("HasAccess: %v", err)
|
||||
} else if !has {
|
||||
return fmt.Errorf("DeleteReleaseByID: permission denied")
|
||||
}
|
||||
|
||||
if delTag {
|
||||
_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
|
||||
fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
|
||||
"git", "tag", "-d", rel.TagName)
|
||||
if err != nil && !strings.Contains(stderr, "not found") {
|
||||
return fmt.Errorf("git tag -d: %v - %s", err, stderr)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
|
||||
|
||||
@@ -20,9 +20,9 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/modules/bindata"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markdown"
|
||||
"code.gitea.io/gitea/modules/options"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/sync"
|
||||
@@ -80,11 +80,11 @@ func LoadRepoConfig() {
|
||||
types := []string{"gitignore", "license", "readme", "label"}
|
||||
typeFiles := make([][]string, 4)
|
||||
for i, t := range types {
|
||||
files, err := bindata.AssetDir("conf/" + t)
|
||||
files, err := options.Dir(t)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to get %s files: %v", t, err)
|
||||
}
|
||||
customPath := path.Join(setting.CustomPath, "conf", t)
|
||||
customPath := path.Join(setting.CustomPath, "options", t)
|
||||
if com.IsDir(customPath) {
|
||||
customFiles, err := com.StatDir(customPath)
|
||||
if err != nil {
|
||||
@@ -276,9 +276,13 @@ func (repo *Repository) HTMLURL() string {
|
||||
}
|
||||
|
||||
// APIFormat converts a Repository to api.Repository
|
||||
// Arguments that are allowed to be nil: permission
|
||||
func (repo *Repository) APIFormat(permission *api.Permission) *api.Repository {
|
||||
func (repo *Repository) APIFormat(mode AccessMode) *api.Repository {
|
||||
cloneLink := repo.CloneLink()
|
||||
permission := &api.Permission{
|
||||
Admin: mode >= AccessModeAdmin,
|
||||
Push: mode >= AccessModeWrite,
|
||||
Pull: mode >= AccessModeRead,
|
||||
}
|
||||
return &api.Repository{
|
||||
ID: repo.ID,
|
||||
Owner: repo.Owner.APIFormat(),
|
||||
@@ -506,7 +510,7 @@ func (repo *Repository) DescriptionHTML() template.HTML {
|
||||
|
||||
// LocalCopyPath returns the local repository copy path
|
||||
func (repo *Repository) LocalCopyPath() string {
|
||||
return path.Join(setting.AppDataPath, "tmp/local-rpeo", com.ToStr(repo.ID))
|
||||
return path.Join(setting.AppDataPath, "tmp/local-repo", com.ToStr(repo.ID))
|
||||
}
|
||||
|
||||
// UpdateLocalCopyBranch pulls latest changes of given branch from repoPath to localPath.
|
||||
@@ -687,7 +691,6 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
|
||||
wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
|
||||
if len(wikiRemotePath) > 0 {
|
||||
|
||||
if err := os.RemoveAll(wikiPath); err != nil {
|
||||
return repo, fmt.Errorf("Fail to remove %s: %v", wikiPath, err)
|
||||
}
|
||||
@@ -696,8 +699,12 @@ func MigrateRepository(u *User, opts MigrateRepoOptions) (*Repository, error) {
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Branch: "master",
|
||||
}); err != nil {
|
||||
log.Info("Clone wiki: %v", err)
|
||||
log.Warn("Clone wiki: %v", err)
|
||||
if err := os.RemoveAll(wikiPath); err != nil {
|
||||
return repo, fmt.Errorf("Fail to remove %s: %v", wikiPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -823,14 +830,27 @@ type CreateRepoOptions struct {
|
||||
}
|
||||
|
||||
func getRepoInitFile(tp, name string) ([]byte, error) {
|
||||
relPath := path.Join("conf", tp, strings.TrimLeft(name, "./"))
|
||||
cleanedName := strings.TrimLeft(name, "./")
|
||||
relPath := path.Join("options", tp, cleanedName)
|
||||
|
||||
// Use custom file when available.
|
||||
customPath := path.Join(setting.CustomPath, relPath)
|
||||
if com.IsFile(customPath) {
|
||||
return ioutil.ReadFile(customPath)
|
||||
}
|
||||
return bindata.Asset(relPath)
|
||||
|
||||
switch tp {
|
||||
case "readme":
|
||||
return options.Readme(cleanedName)
|
||||
case "gitignore":
|
||||
return options.Gitignore(cleanedName)
|
||||
case "license":
|
||||
return options.License(cleanedName)
|
||||
case "label":
|
||||
return options.Labels(cleanedName)
|
||||
default:
|
||||
return []byte{}, fmt.Errorf("Invalid init file type")
|
||||
}
|
||||
}
|
||||
|
||||
func prepareRepoCommit(repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
|
||||
|
||||
@@ -158,13 +158,13 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
|
||||
}
|
||||
oldCommitID := opts.LastCommitID
|
||||
if opts.NewBranch != opts.OldBranch {
|
||||
oldCommitID = git.EMPTY_SHA
|
||||
oldCommitID = git.EmptySHA
|
||||
}
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: doer.Name,
|
||||
RepoOwnerID: repo.MustOwner().ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
||||
RefFullName: git.BranchPrefix + opts.NewBranch,
|
||||
OldCommitID: oldCommitID,
|
||||
NewCommitID: commit.ID.String(),
|
||||
Commits: pushCommits,
|
||||
@@ -297,7 +297,7 @@ func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (
|
||||
PusherName: doer.Name,
|
||||
RepoOwnerID: repo.MustOwner().ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
||||
RefFullName: git.BranchPrefix + opts.NewBranch,
|
||||
OldCommitID: opts.LastCommitID,
|
||||
NewCommitID: commit.ID.String(),
|
||||
Commits: pushCommits,
|
||||
@@ -533,7 +533,7 @@ func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions)
|
||||
PusherName: doer.Name,
|
||||
RepoOwnerID: repo.MustOwner().ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
||||
RefFullName: git.BranchPrefix + opts.NewBranch,
|
||||
OldCommitID: opts.LastCommitID,
|
||||
NewCommitID: commit.ID.String(),
|
||||
Commits: pushCommits,
|
||||
|
||||
@@ -231,7 +231,7 @@ func SyncMirrors() {
|
||||
|
||||
m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64())
|
||||
if err != nil {
|
||||
log.Error(4, "GetMirrorByRepoID [%d]: %v", repoID, err)
|
||||
log.Error(4, "GetMirrorByRepoID [%s]: %v", repoID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ func SyncMirrors() {
|
||||
|
||||
m.ScheduleNextUpdate()
|
||||
if err = UpdateMirror(m); err != nil {
|
||||
log.Error(4, "UpdateMirror [%d]: %v", repoID, err)
|
||||
log.Error(4, "UpdateMirror [%s]: %v", repoID, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,14 @@ func UpdateAccessToken(t *AccessToken) error {
|
||||
}
|
||||
|
||||
// DeleteAccessTokenByID deletes access token by given ID.
|
||||
func DeleteAccessTokenByID(id int64) error {
|
||||
_, err := x.Id(id).Delete(new(AccessToken))
|
||||
return err
|
||||
func DeleteAccessTokenByID(id, userID int64) error {
|
||||
cnt, err := x.Id(id).Delete(&AccessToken{
|
||||
UID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
} else if cnt != 1 {
|
||||
return ErrAccessTokenNotExist{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -91,10 +91,10 @@ type PushUpdateOptions struct {
|
||||
// PushUpdate must be called for any push actions in order to
|
||||
// generates necessary push action history feeds.
|
||||
func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||
isNewRef := opts.OldCommitID == git.EMPTY_SHA
|
||||
isDelRef := opts.NewCommitID == git.EMPTY_SHA
|
||||
isNewRef := opts.OldCommitID == git.EmptySHA
|
||||
isDelRef := opts.NewCommitID == git.EmptySHA
|
||||
if isNewRef && isDelRef {
|
||||
return fmt.Errorf("Old and new revisions are both %s", git.EMPTY_SHA)
|
||||
return fmt.Errorf("Old and new revisions are both %s", git.EmptySHA)
|
||||
}
|
||||
|
||||
repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
|
||||
@@ -106,7 +106,7 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||
}
|
||||
|
||||
if isDelRef {
|
||||
log.GitLogger.Info("Reference '%s' has been deleted from '%s/%s' by %d",
|
||||
log.GitLogger.Info("Reference '%s' has been deleted from '%s/%s' by %s",
|
||||
opts.RefFullName, opts.RepoUserName, opts.RepoName, opts.PusherName)
|
||||
return nil
|
||||
}
|
||||
@@ -127,7 +127,7 @@ func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||
}
|
||||
|
||||
// Push tags.
|
||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
||||
if strings.HasPrefix(opts.RefFullName, git.TagPrefix) {
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: opts.PusherName,
|
||||
RepoOwnerID: owner.ID,
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/nfnt/resize"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"code.gitea.io/git"
|
||||
api "code.gitea.io/sdk/gitea"
|
||||
@@ -361,7 +362,7 @@ func (u *User) NewGitSig() *git.Signature {
|
||||
|
||||
// EncodePasswd encodes password to safe format.
|
||||
func (u *User) EncodePasswd() {
|
||||
newPasswd := base.PBKDF2([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New)
|
||||
newPasswd := pbkdf2.Key([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New)
|
||||
u.Passwd = fmt.Sprintf("%x", newPasswd)
|
||||
}
|
||||
|
||||
@@ -531,7 +532,7 @@ func IsUserExist(uid int64, name string) (bool, error) {
|
||||
}
|
||||
|
||||
// GetUserSalt returns a ramdom user salt token.
|
||||
func GetUserSalt() string {
|
||||
func GetUserSalt() (string, error) {
|
||||
return base.GetRandomString(10)
|
||||
}
|
||||
|
||||
@@ -603,8 +604,12 @@ func CreateUser(u *User) (err error) {
|
||||
u.LowerName = strings.ToLower(u.Name)
|
||||
u.AvatarEmail = u.Email
|
||||
u.Avatar = base.HashEmail(u.AvatarEmail)
|
||||
u.Rands = GetUserSalt()
|
||||
u.Salt = GetUserSalt()
|
||||
if u.Rands, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
if u.Salt, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
u.EncodePasswd()
|
||||
u.MaxRepoCreation = -1
|
||||
|
||||
@@ -880,8 +885,10 @@ func deleteUser(e *xorm.Session, u *User) error {
|
||||
}
|
||||
|
||||
avatarPath := u.CustomAvatarPath()
|
||||
if err := os.Remove(avatarPath); err != nil {
|
||||
return fmt.Errorf("Fail to remove %s: %v", avatarPath, err)
|
||||
if com.IsExist(avatarPath) {
|
||||
if err := os.Remove(avatarPath); err != nil {
|
||||
return fmt.Errorf("Fail to remove %s: %v", avatarPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -5,10 +5,16 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrEmailAddressNotExist email address not exist
|
||||
ErrEmailAddressNotExist = errors.New("Email address does not exist")
|
||||
)
|
||||
|
||||
// EmailAddress is the list of all email addresses of a user. Can contain the
|
||||
// primary email address, but is not obligatory.
|
||||
type EmailAddress struct {
|
||||
@@ -116,7 +122,9 @@ func (email *EmailAddress) Activate() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user.Rands = GetUserSalt()
|
||||
if user.Rands, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
@@ -139,14 +147,25 @@ func (email *EmailAddress) Activate() error {
|
||||
|
||||
// DeleteEmailAddress deletes an email address of given user.
|
||||
func DeleteEmailAddress(email *EmailAddress) (err error) {
|
||||
if email.ID > 0 {
|
||||
_, err = x.Id(email.ID).Delete(new(EmailAddress))
|
||||
} else {
|
||||
_, err = x.
|
||||
Where("email=?", email.Email).
|
||||
Delete(new(EmailAddress))
|
||||
var deleted int64
|
||||
// ask to check UID
|
||||
var address = EmailAddress{
|
||||
UID: email.UID,
|
||||
}
|
||||
return err
|
||||
if email.ID > 0 {
|
||||
deleted, err = x.Id(email.ID).Delete(&address)
|
||||
} else {
|
||||
deleted, err = x.
|
||||
Where("email=?", email.Email).
|
||||
Delete(&address)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if deleted != 1 {
|
||||
return ErrEmailAddressNotExist
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEmailAddresses deletes multiple email addresses
|
||||
|
||||
@@ -634,7 +634,7 @@ func DeliverHooks() {
|
||||
|
||||
tasks = make([]*HookTask, 0, 5)
|
||||
if err := x.Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil {
|
||||
log.Error(4, "Get repository [%d] hook tasks: %v", repoID, err)
|
||||
log.Error(4, "Get repository [%s] hook tasks: %v", repoID, err)
|
||||
continue
|
||||
}
|
||||
for _, t := range tasks {
|
||||
|
||||
@@ -33,6 +33,7 @@ func ToWikiPageURL(name string) string {
|
||||
// that are not belong to wiki repository.
|
||||
func ToWikiPageName(urlString string) string {
|
||||
name, _ := url.QueryUnescape(strings.Replace(urlString, "-", " ", -1))
|
||||
name = strings.Replace(name, "\t", " ", -1)
|
||||
return strings.Replace(strings.TrimLeft(name, "./"), "/", " ", -1)
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ func discardLocalWikiChanges(localPath string) error {
|
||||
}
|
||||
|
||||
// updateWikiPage adds new page to repository wiki.
|
||||
func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, message string, isNew bool) (err error) {
|
||||
func (repo *Repository) updateWikiPage(doer *User, oldWikiPath, wikiPath, content, message string, isNew bool) (err error) {
|
||||
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
@@ -103,8 +104,8 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
|
||||
return fmt.Errorf("UpdateLocalWiki: %v", err)
|
||||
}
|
||||
|
||||
title = ToWikiPageName(title)
|
||||
filename := path.Join(localPath, title+".md")
|
||||
title := ToWikiPageName(wikiPath)
|
||||
filename := path.Join(localPath, wikiPath+".md")
|
||||
|
||||
// If not a new file, show perform update not create.
|
||||
if isNew {
|
||||
@@ -112,7 +113,7 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
|
||||
return ErrWikiAlreadyExist{filename}
|
||||
}
|
||||
} else {
|
||||
file := path.Join(localPath, oldTitle+".md")
|
||||
file := path.Join(localPath, oldWikiPath+".md")
|
||||
|
||||
if err := os.Remove(file); err != nil {
|
||||
return fmt.Errorf("Fail to remove %s: %v", file, err)
|
||||
@@ -148,19 +149,19 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddWikiPage adds a new wiki page with a given title.
|
||||
func (repo *Repository) AddWikiPage(doer *User, title, content, message string) error {
|
||||
return repo.updateWikiPage(doer, "", title, content, message, true)
|
||||
// AddWikiPage adds a new wiki page with a given wikiPath.
|
||||
func (repo *Repository) AddWikiPage(doer *User, wikiPath, content, message string) error {
|
||||
return repo.updateWikiPage(doer, "", wikiPath, content, message, true)
|
||||
}
|
||||
|
||||
// EditWikiPage updates a wiki page identified by its title,
|
||||
// optionally also changing title.
|
||||
func (repo *Repository) EditWikiPage(doer *User, oldTitle, title, content, message string) error {
|
||||
return repo.updateWikiPage(doer, oldTitle, title, content, message, false)
|
||||
// EditWikiPage updates a wiki page identified by its wikiPath,
|
||||
// optionally also changing wikiPath.
|
||||
func (repo *Repository) EditWikiPage(doer *User, oldWikiPath, wikiPath, content, message string) error {
|
||||
return repo.updateWikiPage(doer, oldWikiPath, wikiPath, content, message, false)
|
||||
}
|
||||
|
||||
// DeleteWikiPage deletes a wiki page identified by its title.
|
||||
func (repo *Repository) DeleteWikiPage(doer *User, title string) (err error) {
|
||||
// DeleteWikiPage deletes a wiki page identified by its wikiPath.
|
||||
func (repo *Repository) DeleteWikiPage(doer *User, wikiPath string) (err error) {
|
||||
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
@@ -171,13 +172,13 @@ func (repo *Repository) DeleteWikiPage(doer *User, title string) (err error) {
|
||||
return fmt.Errorf("UpdateLocalWiki: %v", err)
|
||||
}
|
||||
|
||||
title = ToWikiPageName(title)
|
||||
filename := path.Join(localPath, title+".md")
|
||||
filename := path.Join(localPath, wikiPath+".md")
|
||||
|
||||
if err := os.Remove(filename); err != nil {
|
||||
return fmt.Errorf("Fail to remove %s: %v", filename, err)
|
||||
}
|
||||
|
||||
title := ToWikiPageName(wikiPath)
|
||||
message := "Delete page '" + title + "'"
|
||||
|
||||
if err = git.AddChanges(localPath, true); err != nil {
|
||||
|
||||
@@ -151,6 +151,11 @@ func bindUser(l *ldap.Conn, userDN, passwd string) error {
|
||||
|
||||
// SearchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter
|
||||
func (ls *Source) SearchEntry(name, passwd string, directBind bool) (string, string, string, string, bool, bool) {
|
||||
// See https://tools.ietf.org/search/rfc4513#section-5.1.2
|
||||
if len(passwd) == 0 {
|
||||
log.Debug("Auth. failed for %s, password cannot be empty")
|
||||
return "", "", "", "", false, false
|
||||
}
|
||||
l, err := dial(ls)
|
||||
if err != nil {
|
||||
log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"html/template"
|
||||
"math"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -57,6 +56,9 @@ func DetectEncoding(content []byte) (string, error) {
|
||||
}
|
||||
|
||||
result, err := chardet.NewTextDetector().DetectBest(content)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if result.Charset != "UTF-8" && len(setting.Repository.AnsiCharset) > 0 {
|
||||
log.Debug("Using default AnsiCharset: %s", setting.Repository.AnsiCharset)
|
||||
return setting.Repository.AnsiCharset, err
|
||||
@@ -83,57 +85,31 @@ func BasicAuthEncode(username, password string) string {
|
||||
}
|
||||
|
||||
// GetRandomString generate random string by specify chars.
|
||||
func GetRandomString(n int, alphabets ...byte) string {
|
||||
func GetRandomString(n int) (string, error) {
|
||||
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
var bytes = make([]byte, n)
|
||||
rand.Read(bytes)
|
||||
for i, b := range bytes {
|
||||
if len(alphabets) == 0 {
|
||||
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||
} else {
|
||||
bytes[i] = alphabets[b%byte(len(alphabets))]
|
||||
|
||||
buffer := make([]byte, n)
|
||||
max := big.NewInt(int64(len(alphanum)))
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
index, err := randomInt(max)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buffer[i] = alphanum[index]
|
||||
}
|
||||
return string(bytes)
|
||||
|
||||
return string(buffer), nil
|
||||
}
|
||||
|
||||
// PBKDF2 http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
|
||||
// FIXME: use https://godoc.org/golang.org/x/crypto/pbkdf2?
|
||||
func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
||||
prf := hmac.New(h, password)
|
||||
hashLen := prf.Size()
|
||||
numBlocks := (keyLen + hashLen - 1) / hashLen
|
||||
|
||||
var buf [4]byte
|
||||
dk := make([]byte, 0, numBlocks*hashLen)
|
||||
U := make([]byte, hashLen)
|
||||
for block := 1; block <= numBlocks; block++ {
|
||||
// N.B.: || means concatenation, ^ means XOR
|
||||
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
|
||||
// U_1 = PRF(password, salt || uint(i))
|
||||
prf.Reset()
|
||||
prf.Write(salt)
|
||||
buf[0] = byte(block >> 24)
|
||||
buf[1] = byte(block >> 16)
|
||||
buf[2] = byte(block >> 8)
|
||||
buf[3] = byte(block)
|
||||
prf.Write(buf[:4])
|
||||
dk = prf.Sum(dk)
|
||||
T := dk[len(dk)-hashLen:]
|
||||
copy(U, T)
|
||||
|
||||
// U_n = PRF(password, U_(n-1))
|
||||
for n := 2; n <= iter; n++ {
|
||||
prf.Reset()
|
||||
prf.Write(U)
|
||||
U = U[:0]
|
||||
U = prf.Sum(U)
|
||||
for x := range U {
|
||||
T[x] ^= U[x]
|
||||
}
|
||||
}
|
||||
func randomInt(max *big.Int) (int, error) {
|
||||
rand, err := rand.Int(rand.Reader, max)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dk[:keyLen]
|
||||
|
||||
return int(rand.Int64()), nil
|
||||
}
|
||||
|
||||
// VerifyTimeLimitCode verify time limit code
|
||||
@@ -283,19 +259,25 @@ func computeTimeDiff(diff int64) (int64, string) {
|
||||
diffStr = "1 year"
|
||||
default:
|
||||
diffStr = fmt.Sprintf("%d years", diff/Year)
|
||||
diff = 0
|
||||
diff -= (diff / Year) * Year
|
||||
}
|
||||
return diff, diffStr
|
||||
}
|
||||
|
||||
// TimeSincePro calculates the time interval and generate full user-friendly string.
|
||||
func TimeSincePro(then time.Time) string {
|
||||
now := time.Now()
|
||||
return timeSincePro(then, time.Now())
|
||||
}
|
||||
|
||||
func timeSincePro(then, now time.Time) string {
|
||||
diff := now.Unix() - then.Unix()
|
||||
|
||||
if then.After(now) {
|
||||
return "future"
|
||||
}
|
||||
if diff == 0 {
|
||||
return "now"
|
||||
}
|
||||
|
||||
var timeStr, diffStr string
|
||||
for {
|
||||
@@ -309,9 +291,7 @@ func TimeSincePro(then time.Time) string {
|
||||
return strings.TrimPrefix(timeStr, ", ")
|
||||
}
|
||||
|
||||
func timeSince(then time.Time, lang string) string {
|
||||
now := time.Now()
|
||||
|
||||
func timeSince(then, now time.Time, lang string) string {
|
||||
lbl := i18n.Tr(lang, "tool.ago")
|
||||
diff := now.Unix() - then.Unix()
|
||||
if then.After(now) {
|
||||
@@ -322,7 +302,7 @@ func timeSince(then time.Time, lang string) string {
|
||||
switch {
|
||||
case diff <= 0:
|
||||
return i18n.Tr(lang, "tool.now")
|
||||
case diff <= 2:
|
||||
case diff <= 1:
|
||||
return i18n.Tr(lang, "tool.1s", lbl)
|
||||
case diff < 1*Minute:
|
||||
return i18n.Tr(lang, "tool.seconds", diff, lbl)
|
||||
@@ -361,12 +341,18 @@ func timeSince(then time.Time, lang string) string {
|
||||
|
||||
// RawTimeSince retrieves i18n key of time since t
|
||||
func RawTimeSince(t time.Time, lang string) string {
|
||||
return timeSince(t, lang)
|
||||
return timeSince(t, time.Now(), lang)
|
||||
}
|
||||
|
||||
// TimeSince calculates the time interval and generate user-friendly string.
|
||||
func TimeSince(t time.Time, lang string) template.HTML {
|
||||
return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, t.Format(setting.TimeFormat), timeSince(t, lang)))
|
||||
func TimeSince(then time.Time, lang string) template.HTML {
|
||||
return htmlTimeSince(then, time.Now(), lang)
|
||||
}
|
||||
|
||||
func htmlTimeSince(then, now time.Time, lang string) template.HTML {
|
||||
return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`,
|
||||
then.Format(setting.TimeFormat),
|
||||
timeSince(then, now, lang)))
|
||||
}
|
||||
|
||||
// Storage space size types
|
||||
@@ -451,10 +437,10 @@ func Subtract(left interface{}, right interface{}) interface{} {
|
||||
case int64:
|
||||
rright = right.(int64)
|
||||
case float32:
|
||||
fright = float64(left.(float32))
|
||||
fright = float64(right.(float32))
|
||||
isInt = false
|
||||
case float64:
|
||||
fleft = left.(float64)
|
||||
fright = right.(float64)
|
||||
isInt = false
|
||||
}
|
||||
|
||||
@@ -486,12 +472,16 @@ func TruncateString(str string, limit int) string {
|
||||
}
|
||||
|
||||
// StringsToInt64s converts a slice of string to a slice of int64.
|
||||
func StringsToInt64s(strs []string) []int64 {
|
||||
func StringsToInt64s(strs []string) ([]int64, error) {
|
||||
ints := make([]int64, len(strs))
|
||||
for i := range strs {
|
||||
ints[i] = com.StrTo(strs[i]).MustInt64()
|
||||
n, err := com.StrTo(strs[i]).Int64()
|
||||
if err != nil {
|
||||
return ints, err
|
||||
}
|
||||
ints[i] = n
|
||||
}
|
||||
return ints
|
||||
return ints, nil
|
||||
}
|
||||
|
||||
// Int64sToStrings converts a slice of int64 to a slice of string.
|
||||
@@ -535,3 +525,8 @@ func IsImageFile(data []byte) bool {
|
||||
func IsPDFFile(data []byte) bool {
|
||||
return strings.Index(http.DetectContentType(data), "application/pdf") != -1
|
||||
}
|
||||
|
||||
// IsVideoFile detectes if data is an video format
|
||||
func IsVideoFile(data []byte) bool {
|
||||
return strings.Index(http.DetectContentType(data), "video/") != -1
|
||||
}
|
||||
|
||||
@@ -4,10 +4,40 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/Unknwon/i18n"
|
||||
macaroni18n "github.com/go-macaron/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"strk.kbt.io/projects/go/libravatar"
|
||||
"time"
|
||||
)
|
||||
|
||||
var BaseDate time.Time
|
||||
|
||||
// time durations
|
||||
const (
|
||||
DayDur = 24 * time.Hour
|
||||
WeekDur = 7 * DayDur
|
||||
MonthDur = 30 * DayDur
|
||||
YearDur = 12 * MonthDur
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// setup
|
||||
macaroni18n.I18n(macaroni18n.Options{
|
||||
Directory: "../../options/locale/",
|
||||
DefaultLang: "en-US",
|
||||
Langs: []string{"en-US"},
|
||||
Names: []string{"english"},
|
||||
})
|
||||
BaseDate = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
// run the tests
|
||||
retVal := m.Run()
|
||||
|
||||
os.Exit(retVal)
|
||||
}
|
||||
|
||||
func TestEncodeMD5(t *testing.T) {
|
||||
assert.Equal(t,
|
||||
"3858f62230ac3c915f300c664312c63f",
|
||||
@@ -26,7 +56,41 @@ func TestShortSha(t *testing.T) {
|
||||
assert.Equal(t, "veryverylo", ShortSha("veryverylong"))
|
||||
}
|
||||
|
||||
// TODO: Test DetectEncoding()
|
||||
func TestDetectEncoding(t *testing.T) {
|
||||
testSuccess := func(b []byte, expected string) {
|
||||
encoding, err := DetectEncoding(b)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, encoding)
|
||||
}
|
||||
// utf-8
|
||||
b := []byte("just some ascii")
|
||||
testSuccess(b, "UTF-8")
|
||||
|
||||
// utf-8-sig: "hey" (with BOM)
|
||||
b = []byte{0xef, 0xbb, 0xbf, 0x68, 0x65, 0x79}
|
||||
testSuccess(b, "UTF-8")
|
||||
|
||||
// utf-16: "hey<accented G>"
|
||||
b = []byte{0xff, 0xfe, 0x68, 0x00, 0x65, 0x00, 0x79, 0x00, 0xf4, 0x01}
|
||||
testSuccess(b, "UTF-16LE")
|
||||
|
||||
// iso-8859-1: d<accented e>cor<newline>
|
||||
b = []byte{0x44, 0xe9, 0x63, 0x6f, 0x72, 0x0a}
|
||||
encoding, err := DetectEncoding(b)
|
||||
assert.NoError(t, err)
|
||||
// due to a race condition in `chardet` library, it could either detect
|
||||
// "ISO-8859-1" or "IS0-8859-2" here. Technically either is correct, so
|
||||
// we accept either.
|
||||
assert.Contains(t, encoding, "ISO-8859")
|
||||
|
||||
setting.Repository.AnsiCharset = "placeholder"
|
||||
testSuccess(b, "placeholder")
|
||||
|
||||
// invalid bytes
|
||||
b = []byte{0xfa}
|
||||
_, err = DetectEncoding(b)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestBasicAuthDecode(t *testing.T) {
|
||||
_, _, err := BasicAuthDecode("?")
|
||||
@@ -43,7 +107,9 @@ func TestBasicAuthEncode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetRandomString(t *testing.T) {
|
||||
assert.Len(t, GetRandomString(4), 4)
|
||||
randomString, err := GetRandomString(4)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, randomString, 4)
|
||||
}
|
||||
|
||||
// TODO: Test PBKDF2()
|
||||
@@ -86,11 +152,112 @@ func TestAvatarLink(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: computeTimeDiff()
|
||||
// TODO: TimeSincePro()
|
||||
// TODO: timeSince()
|
||||
// TODO: RawTimeSince()
|
||||
// TODO: TimeSince()
|
||||
func TestComputeTimeDiff(t *testing.T) {
|
||||
// test that for each offset in offsets,
|
||||
// computeTimeDiff(base + offset) == (offset, str)
|
||||
test := func(base int64, str string, offsets ...int64) {
|
||||
for _, offset := range offsets {
|
||||
diff, diffStr := computeTimeDiff(base + offset)
|
||||
assert.Equal(t, offset, diff)
|
||||
assert.Equal(t, str, diffStr)
|
||||
}
|
||||
}
|
||||
test(0, "now", 0)
|
||||
test(1, "1 second", 0)
|
||||
test(2, "2 seconds", 0)
|
||||
test(Minute, "1 minute", 0, 1, 30, Minute-1)
|
||||
test(2*Minute, "2 minutes", 0, Minute-1)
|
||||
test(Hour, "1 hour", 0, 1, Hour-1)
|
||||
test(5*Hour, "5 hours", 0, Hour-1)
|
||||
test(Day, "1 day", 0, 1, Day-1)
|
||||
test(5*Day, "5 days", 0, Day-1)
|
||||
test(Week, "1 week", 0, 1, Week-1)
|
||||
test(3*Week, "3 weeks", 0, 4*Day+25000)
|
||||
test(Month, "1 month", 0, 1, Month-1)
|
||||
test(10*Month, "10 months", 0, Month-1)
|
||||
test(Year, "1 year", 0, Year-1)
|
||||
test(3*Year, "3 years", 0, Year-1)
|
||||
}
|
||||
|
||||
func TestTimeSince(t *testing.T) {
|
||||
assert.Equal(t, "now", timeSince(BaseDate, BaseDate, "en"))
|
||||
|
||||
// test that each diff in `diffs` yields the expected string
|
||||
test := func(expected string, diffs ...time.Duration) {
|
||||
ago := i18n.Tr("en", "tool.ago")
|
||||
fromNow := i18n.Tr("en", "tool.from_now")
|
||||
for _, diff := range diffs {
|
||||
actual := timeSince(BaseDate, BaseDate.Add(diff), "en")
|
||||
assert.Equal(t, expected+" "+ago, actual)
|
||||
actual = timeSince(BaseDate.Add(diff), BaseDate, "en")
|
||||
assert.Equal(t, expected+" "+fromNow, actual)
|
||||
}
|
||||
}
|
||||
test("1 second", time.Second, time.Second+50*time.Millisecond)
|
||||
test("2 seconds", 2*time.Second, 2*time.Second+50*time.Millisecond)
|
||||
test("1 minute", time.Minute, time.Minute+30*time.Second)
|
||||
test("2 minutes", 2*time.Minute, 2*time.Minute+30*time.Second)
|
||||
test("1 hour", time.Hour, time.Hour+30*time.Minute)
|
||||
test("2 hours", 2*time.Hour, 2*time.Hour+30*time.Minute)
|
||||
test("1 day", DayDur, DayDur+12*time.Hour)
|
||||
test("2 days", 2*DayDur, 2*DayDur+12*time.Hour)
|
||||
test("1 week", WeekDur, WeekDur+3*DayDur)
|
||||
test("2 weeks", 2*WeekDur, 2*WeekDur+3*DayDur)
|
||||
test("1 month", MonthDur, MonthDur+15*DayDur)
|
||||
test("2 months", 2*MonthDur, 2*MonthDur+15*DayDur)
|
||||
test("1 year", YearDur, YearDur+6*MonthDur)
|
||||
test("2 years", 2*YearDur, 2*YearDur+6*MonthDur)
|
||||
}
|
||||
|
||||
func TestTimeSincePro(t *testing.T) {
|
||||
assert.Equal(t, "now", timeSincePro(BaseDate, BaseDate))
|
||||
|
||||
// test that a difference of `diff` yields the expected string
|
||||
test := func(expected string, diff time.Duration) {
|
||||
actual := timeSincePro(BaseDate, BaseDate.Add(diff))
|
||||
assert.Equal(t, expected, actual)
|
||||
assert.Equal(t, "future", timeSincePro(BaseDate.Add(diff), BaseDate))
|
||||
}
|
||||
test("1 second", time.Second)
|
||||
test("2 seconds", 2*time.Second)
|
||||
test("1 minute", time.Minute)
|
||||
test("1 minute, 1 second", time.Minute+time.Second)
|
||||
test("1 minute, 59 seconds", time.Minute+59*time.Second)
|
||||
test("2 minutes", 2*time.Minute)
|
||||
test("1 hour", time.Hour)
|
||||
test("1 hour, 1 second", time.Hour+time.Second)
|
||||
test("1 hour, 59 minutes, 59 seconds", time.Hour+59*time.Minute+59*time.Second)
|
||||
test("2 hours", 2*time.Hour)
|
||||
test("1 day", DayDur)
|
||||
test("1 day, 23 hours, 59 minutes, 59 seconds",
|
||||
DayDur+23*time.Hour+59*time.Minute+59*time.Second)
|
||||
test("2 days", 2*DayDur)
|
||||
test("1 week", WeekDur)
|
||||
test("2 weeks", 2*WeekDur)
|
||||
test("1 month", MonthDur)
|
||||
test("3 months", 3*MonthDur)
|
||||
test("1 year", YearDur)
|
||||
test("2 years, 3 months, 1 week, 2 days, 4 hours, 12 minutes, 17 seconds",
|
||||
2*YearDur+3*MonthDur+WeekDur+2*DayDur+4*time.Hour+
|
||||
12*time.Minute+17*time.Second)
|
||||
}
|
||||
|
||||
func TestHtmlTimeSince(t *testing.T) {
|
||||
setting.TimeFormat = time.UnixDate
|
||||
// test that `diff` yields a result containing `expected`
|
||||
test := func(expected string, diff time.Duration) {
|
||||
actual := htmlTimeSince(BaseDate, BaseDate.Add(diff), "en")
|
||||
assert.Contains(t, actual, `title="Sat Jan 1 00:00:00 UTC 2000"`)
|
||||
assert.Contains(t, actual, expected)
|
||||
}
|
||||
test("1 second", time.Second)
|
||||
test("3 minutes", 3*time.Minute+5*time.Second)
|
||||
test("1 day", DayDur+18*time.Hour)
|
||||
test("1 week", WeekDur+6*DayDur)
|
||||
test("3 months", 3*MonthDur+3*WeekDur)
|
||||
test("2 years", 2*YearDur)
|
||||
test("3 years", 3*YearDur+11*MonthDur+4*WeekDur)
|
||||
}
|
||||
|
||||
func TestFileSize(t *testing.T) {
|
||||
var size int64
|
||||
@@ -106,11 +273,48 @@ func TestFileSize(t *testing.T) {
|
||||
assert.Equal(t, "512TB", FileSize(size))
|
||||
size = size * 1024
|
||||
assert.Equal(t, "512PB", FileSize(size))
|
||||
//size = size * 1024 TODO: Fix bug for EB
|
||||
//assert.Equal(t, "512EB", FileSize(size))
|
||||
size = size * 4
|
||||
assert.Equal(t, "2.0EB", FileSize(size))
|
||||
}
|
||||
|
||||
// TODO: Subtract()
|
||||
func TestSubtract(t *testing.T) {
|
||||
toFloat64 := func(n interface{}) float64 {
|
||||
switch n.(type) {
|
||||
case int:
|
||||
return float64(n.(int))
|
||||
case int8:
|
||||
return float64(n.(int8))
|
||||
case int16:
|
||||
return float64(n.(int16))
|
||||
case int32:
|
||||
return float64(n.(int32))
|
||||
case int64:
|
||||
return float64(n.(int64))
|
||||
case float32:
|
||||
return float64(n.(float32))
|
||||
case float64:
|
||||
return n.(float64)
|
||||
default:
|
||||
return 0.0
|
||||
}
|
||||
}
|
||||
values := []interface{}{
|
||||
int(-3),
|
||||
int8(14),
|
||||
int16(81),
|
||||
int32(-156),
|
||||
int64(1528),
|
||||
float32(3.5),
|
||||
float64(-15.348),
|
||||
}
|
||||
for _, left := range values {
|
||||
for _, right := range values {
|
||||
expected := toFloat64(left) - toFloat64(right)
|
||||
sub := Subtract(left, right)
|
||||
assert.InDelta(t, expected, sub, 1e-3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEllipsisString(t *testing.T) {
|
||||
assert.Equal(t, "...", EllipsisString("foobar", 0))
|
||||
@@ -135,14 +339,18 @@ func TestTruncateString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringsToInt64s(t *testing.T) {
|
||||
assert.Equal(t, []int64{}, StringsToInt64s([]string{}))
|
||||
assert.Equal(t,
|
||||
[]int64{1, 4, 16, 64, 256},
|
||||
StringsToInt64s([]string{"1", "4", "16", "64", "256"}),
|
||||
)
|
||||
testSuccess := func(input []string, expected []int64) {
|
||||
result, err := StringsToInt64s(input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
testSuccess([]string{}, []int64{})
|
||||
testSuccess([]string{"-1234"}, []int64{-1234})
|
||||
testSuccess([]string{"1", "4", "16", "64", "256"},
|
||||
[]int64{1, 4, 16, 64, 256})
|
||||
|
||||
// TODO: StringsToInt64s should return ([]int64, error)
|
||||
assert.Equal(t, []int64{-1, 0, 0}, StringsToInt64s([]string{"-1", "a", "$"}))
|
||||
_, err := StringsToInt64s([]string{"-1", "a", "$"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestInt64sToStrings(t *testing.T) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -39,6 +39,17 @@ func NewLogger(bufLen int64, mode, config string) {
|
||||
}
|
||||
}
|
||||
|
||||
// DelLogger removes loggers that are for the given mode
|
||||
func DelLogger(mode string) error {
|
||||
for _, l := range loggers {
|
||||
if _, ok := l.outputs[mode]; ok {
|
||||
return l.DelLogger(mode)
|
||||
}
|
||||
}
|
||||
Trace("Log adapter %s not found, no need to delete", mode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewGitLogger create a logger for git
|
||||
// FIXME: use same log level as other loggers.
|
||||
func NewGitLogger(logPath string) {
|
||||
|
||||
99
modules/options/dynamic.go
Normal file
99
modules/options/dynamic.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// +build !bindata
|
||||
|
||||
// Copyright 2016 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 options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
var (
|
||||
directories = make(directorySet)
|
||||
)
|
||||
|
||||
// Dir returns all files from static or custom directory.
|
||||
func Dir(name string) ([]string, error) {
|
||||
if directories.Filled(name) {
|
||||
return directories.Get(name), nil
|
||||
}
|
||||
|
||||
var (
|
||||
result []string
|
||||
)
|
||||
|
||||
customDir := path.Join(setting.CustomPath, "options", name)
|
||||
|
||||
if com.IsDir(customDir) {
|
||||
files, err := com.StatDir(customDir, true)
|
||||
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("Failed to read custom directory. %v", err)
|
||||
}
|
||||
|
||||
result = append(result, files...)
|
||||
}
|
||||
|
||||
staticDir := path.Join(setting.StaticRootPath, "options", name)
|
||||
|
||||
if com.IsDir(staticDir) {
|
||||
files, err := com.StatDir(staticDir, true)
|
||||
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("Failed to read static directory. %v", err)
|
||||
}
|
||||
|
||||
result = append(result, files...)
|
||||
}
|
||||
|
||||
return directories.AddAndGet(name, result), nil
|
||||
}
|
||||
|
||||
// Locale reads the content of a specific locale from static or custom path.
|
||||
func Locale(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("locale", name))
|
||||
}
|
||||
|
||||
// Readme reads the content of a specific readme from static or custom path.
|
||||
func Readme(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("readme", name))
|
||||
}
|
||||
|
||||
// Gitignore reads the content of a specific gitignore from static or custom path.
|
||||
func Gitignore(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("gitignore", name))
|
||||
}
|
||||
|
||||
// License reads the content of a specific license from static or custom path.
|
||||
func License(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("license", name))
|
||||
}
|
||||
|
||||
// Labels eads the content of a specific labels from static or custom path.
|
||||
func Labels(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("label", name))
|
||||
}
|
||||
|
||||
// fileFromDir is a helper to read files from static or custom path.
|
||||
func fileFromDir(name string) ([]byte, error) {
|
||||
customPath := path.Join(setting.CustomPath, "options", name)
|
||||
|
||||
if com.IsFile(customPath) {
|
||||
return ioutil.ReadFile(customPath)
|
||||
}
|
||||
|
||||
staticPath := path.Join(setting.StaticRootPath, "options", name)
|
||||
|
||||
if com.IsFile(staticPath) {
|
||||
return ioutil.ReadFile(staticPath)
|
||||
}
|
||||
|
||||
return []byte{}, fmt.Errorf("Asset file does not exist: %s", name)
|
||||
}
|
||||
51
modules/options/options.go
Normal file
51
modules/options/options.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2016 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 options
|
||||
|
||||
//go:generate go-bindata -tags "bindata" -ignore "TRANSLATORS" -pkg "options" -o "bindata.go" ../../options/...
|
||||
//go:generate go fmt bindata.go
|
||||
//go:generate sed -i.bak s/..\/..\/options\/// bindata.go
|
||||
//go:generate rm -f bindata.go.bak
|
||||
|
||||
type directorySet map[string][]string
|
||||
|
||||
func (s directorySet) Add(key string, value []string) {
|
||||
_, ok := s[key]
|
||||
|
||||
if !ok {
|
||||
s[key] = make([]string, 0, len(value))
|
||||
}
|
||||
|
||||
s[key] = append(s[key], value...)
|
||||
}
|
||||
|
||||
func (s directorySet) Get(key string) []string {
|
||||
_, ok := s[key]
|
||||
|
||||
if ok {
|
||||
result := []string{}
|
||||
seen := map[string]string{}
|
||||
|
||||
for _, val := range s[key] {
|
||||
if _, ok := seen[val]; !ok {
|
||||
result = append(result, val)
|
||||
seen[val] = val
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (s directorySet) AddAndGet(key string, value []string) []string {
|
||||
s.Add(key, value)
|
||||
return s.Get(key)
|
||||
}
|
||||
|
||||
func (s directorySet) Filled(key string) bool {
|
||||
return len(s[key]) > 0
|
||||
}
|
||||
89
modules/options/static.go
Normal file
89
modules/options/static.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// +build bindata
|
||||
|
||||
// Copyright 2016 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 options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/Unknwon/com"
|
||||
)
|
||||
|
||||
var (
|
||||
directories = make(directorySet)
|
||||
)
|
||||
|
||||
// Dir returns all files from bindata or custom directory.
|
||||
func Dir(name string) ([]string, error) {
|
||||
if directories.Filled(name) {
|
||||
return directories.Get(name), nil
|
||||
}
|
||||
|
||||
var (
|
||||
result []string
|
||||
)
|
||||
|
||||
customDir := path.Join(setting.CustomPath, "options", name)
|
||||
|
||||
if com.IsDir(customDir) {
|
||||
files, err := com.StatDir(customDir, true)
|
||||
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("Failed to read custom directory. %v", err)
|
||||
}
|
||||
|
||||
result = append(result, files...)
|
||||
}
|
||||
|
||||
files, err := AssetDir(path.Join("..", "..", "options", name))
|
||||
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("Failed to read embedded directory. %v", err)
|
||||
}
|
||||
|
||||
result = append(result, files...)
|
||||
|
||||
return directories.AddAndGet(name, result), nil
|
||||
}
|
||||
|
||||
// Locale reads the content of a specific locale from bindata or custom path.
|
||||
func Locale(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("locale", name))
|
||||
}
|
||||
|
||||
// Readme reads the content of a specific readme from bindata or custom path.
|
||||
func Readme(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("readme", name))
|
||||
}
|
||||
|
||||
// Gitignore reads the content of a gitignore locale from bindata or custom path.
|
||||
func Gitignore(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("gitignore", name))
|
||||
}
|
||||
|
||||
// License reads the content of a specific license from bindata or custom path.
|
||||
func License(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("license", name))
|
||||
}
|
||||
|
||||
// Labels eads the content of a specific labels from static or custom path.
|
||||
func Labels(name string) ([]byte, error) {
|
||||
return fileFromDir(path.Join("label", name))
|
||||
}
|
||||
|
||||
// fileFromDir is a helper to read files from bindata or custom path.
|
||||
func fileFromDir(name string) ([]byte, error) {
|
||||
customPath := path.Join(setting.CustomPath, "options", name)
|
||||
|
||||
if com.IsFile(customPath) {
|
||||
return ioutil.ReadFile(customPath)
|
||||
}
|
||||
|
||||
return Asset(name)
|
||||
}
|
||||
@@ -6,6 +6,8 @@ package public
|
||||
|
||||
//go:generate go-bindata -tags "bindata" -ignore "\\.go|\\.less" -pkg "public" -o "bindata.go" ../../public/...
|
||||
//go:generate go fmt bindata.go
|
||||
//go:generate sed -i.bak s/..\/..\/public\/// bindata.go
|
||||
//go:generate rm -f bindata.go.bak
|
||||
|
||||
// Options represents the available options to configure the macaron handler.
|
||||
type Options struct {
|
||||
|
||||
@@ -22,7 +22,7 @@ func Static(opts *Options) macaron.Handler {
|
||||
AssetDir: AssetDir,
|
||||
AssetInfo: AssetInfo,
|
||||
AssetNames: AssetNames,
|
||||
Prefix: "../../public",
|
||||
Prefix: "",
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
10
modules/setting/defaults.go
Normal file
10
modules/setting/defaults.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultLangs = strings.Split("en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR", ",")
|
||||
defaultLangNames = strings.Split("English,简体中文,繁體中文(香港),繁體中文(台湾),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски,Svenska,한국어", ",")
|
||||
)
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/user"
|
||||
"github.com/Unknwon/com"
|
||||
_ "github.com/go-macaron/cache/memcache" // memcache plugin for cache
|
||||
_ "github.com/go-macaron/cache/redis"
|
||||
@@ -25,10 +27,6 @@ import (
|
||||
_ "github.com/kardianos/minwinsvc" // import minwinsvc for windows services
|
||||
"gopkg.in/ini.v1"
|
||||
"strk.kbt.io/projects/go/libravatar"
|
||||
|
||||
"code.gitea.io/gitea/modules/bindata"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/user"
|
||||
)
|
||||
|
||||
// Scheme describes protocol types
|
||||
@@ -53,10 +51,6 @@ const (
|
||||
|
||||
// settings
|
||||
var (
|
||||
// BuildTime information should only be set by -ldflags.
|
||||
BuildTime string
|
||||
BuildGitHash string
|
||||
|
||||
// AppVer settings
|
||||
AppVer string
|
||||
AppName string
|
||||
@@ -86,6 +80,7 @@ var (
|
||||
StartBuiltinServer bool `ini:"START_SSH_SERVER"`
|
||||
Domain string `ini:"SSH_DOMAIN"`
|
||||
Port int `ini:"SSH_PORT"`
|
||||
ListenHost string `ini:"SSH_LISTEN_HOST"`
|
||||
ListenPort int `ini:"SSH_LISTEN_PORT"`
|
||||
RootPath string `ini:"SSH_ROOT_PATH"`
|
||||
KeyTestPath string `ini:"SSH_KEY_TEST_PATH"`
|
||||
@@ -109,16 +104,21 @@ var (
|
||||
UseTiDB bool
|
||||
|
||||
// Webhook settings
|
||||
Webhook struct {
|
||||
Webhook = struct {
|
||||
QueueLength int
|
||||
DeliverTimeout int
|
||||
SkipTLSVerify bool
|
||||
Types []string
|
||||
PagingNum int
|
||||
}{
|
||||
QueueLength: 1000,
|
||||
DeliverTimeout: 5,
|
||||
SkipTLSVerify: false,
|
||||
PagingNum: 10,
|
||||
}
|
||||
|
||||
// Repository settings
|
||||
Repository struct {
|
||||
Repository = struct {
|
||||
AnsiCharset string
|
||||
ForcePrivate bool
|
||||
MaxCreationLimit int
|
||||
@@ -141,12 +141,44 @@ var (
|
||||
FileMaxSize int64
|
||||
MaxFiles int
|
||||
} `ini:"-"`
|
||||
}{
|
||||
AnsiCharset: "",
|
||||
ForcePrivate: false,
|
||||
MaxCreationLimit: -1,
|
||||
MirrorQueueLength: 1000,
|
||||
PullRequestQueueLength: 1000,
|
||||
PreferredLicenses: []string{"Apache License 2.0,MIT License"},
|
||||
DisableHTTPGit: false,
|
||||
|
||||
// Repository editor settings
|
||||
Editor: struct {
|
||||
LineWrapExtensions []string
|
||||
PreviewableFileModes []string
|
||||
}{
|
||||
LineWrapExtensions: strings.Split(".txt,.md,.markdown,.mdown,.mkd,", ","),
|
||||
PreviewableFileModes: []string{"markdown"},
|
||||
},
|
||||
|
||||
// Repository upload settings
|
||||
Upload: struct {
|
||||
Enabled bool
|
||||
TempPath string
|
||||
AllowedTypes []string `delim:"|"`
|
||||
FileMaxSize int64
|
||||
MaxFiles int
|
||||
}{
|
||||
Enabled: true,
|
||||
TempPath: "data/tmp/uploads",
|
||||
AllowedTypes: []string{},
|
||||
FileMaxSize: 3,
|
||||
MaxFiles: 5,
|
||||
},
|
||||
}
|
||||
RepoRootPath string
|
||||
ScriptType string
|
||||
ScriptType = "bash"
|
||||
|
||||
// UI settings
|
||||
UI struct {
|
||||
UI = struct {
|
||||
ExplorePagingNum int
|
||||
IssuePagingNum int
|
||||
FeedMaxCommitNum int
|
||||
@@ -162,13 +194,38 @@ var (
|
||||
User struct {
|
||||
RepoPagingNum int
|
||||
} `ini:"ui.user"`
|
||||
}{
|
||||
ExplorePagingNum: 20,
|
||||
IssuePagingNum: 10,
|
||||
FeedMaxCommitNum: 5,
|
||||
ThemeColorMetaTag: `#6cc644`,
|
||||
MaxDisplayFileSize: 8388608,
|
||||
Admin: struct {
|
||||
UserPagingNum int
|
||||
RepoPagingNum int
|
||||
NoticePagingNum int
|
||||
OrgPagingNum int
|
||||
}{
|
||||
UserPagingNum: 50,
|
||||
RepoPagingNum: 50,
|
||||
NoticePagingNum: 25,
|
||||
OrgPagingNum: 50,
|
||||
},
|
||||
User: struct {
|
||||
RepoPagingNum int
|
||||
}{
|
||||
RepoPagingNum: 15,
|
||||
},
|
||||
}
|
||||
|
||||
// Markdown sttings
|
||||
Markdown struct {
|
||||
Markdown = struct {
|
||||
EnableHardLineBreak bool
|
||||
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
|
||||
FileExtensions []string
|
||||
}{
|
||||
EnableHardLineBreak: false,
|
||||
FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
|
||||
}
|
||||
|
||||
// Picture settings
|
||||
@@ -203,7 +260,7 @@ var (
|
||||
CSRFCookieName = "_csrf"
|
||||
|
||||
// Cron tasks
|
||||
Cron struct {
|
||||
Cron = struct {
|
||||
UpdateMirror struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
@@ -221,10 +278,37 @@ var (
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
} `ini:"cron.check_repo_stats"`
|
||||
}{
|
||||
UpdateMirror: struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
}{
|
||||
Schedule: "@every 10m",
|
||||
},
|
||||
RepoHealthCheck: struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
Timeout time.Duration
|
||||
Args []string `delim:" "`
|
||||
}{
|
||||
Schedule: "@every 24h",
|
||||
Timeout: 60 * time.Second,
|
||||
Args: []string{},
|
||||
},
|
||||
CheckRepoStats: struct {
|
||||
Enabled bool
|
||||
RunAtStart bool
|
||||
Schedule string
|
||||
}{
|
||||
RunAtStart: true,
|
||||
Schedule: "@every 24h",
|
||||
},
|
||||
}
|
||||
|
||||
// Git settings
|
||||
Git struct {
|
||||
Git = struct {
|
||||
DisableDiffHighlight bool
|
||||
MaxGitDiffLines int
|
||||
MaxGitDiffLineCharacters int
|
||||
@@ -237,16 +321,39 @@ var (
|
||||
Pull int
|
||||
GC int `ini:"GC"`
|
||||
} `ini:"git.timeout"`
|
||||
}{
|
||||
DisableDiffHighlight: false,
|
||||
MaxGitDiffLines: 1000,
|
||||
MaxGitDiffLineCharacters: 500,
|
||||
MaxGitDiffFiles: 100,
|
||||
GCArgs: []string{},
|
||||
Timeout: struct {
|
||||
Migrate int
|
||||
Mirror int
|
||||
Clone int
|
||||
Pull int
|
||||
GC int `ini:"GC"`
|
||||
}{
|
||||
Migrate: 600,
|
||||
Mirror: 300,
|
||||
Clone: 300,
|
||||
Pull: 300,
|
||||
GC: 60,
|
||||
},
|
||||
}
|
||||
|
||||
// Mirror settings
|
||||
Mirror struct {
|
||||
Mirror = struct {
|
||||
DefaultInterval int
|
||||
}{
|
||||
DefaultInterval: 8,
|
||||
}
|
||||
|
||||
// API settings
|
||||
API struct {
|
||||
API = struct {
|
||||
MaxResponseItems int
|
||||
}{
|
||||
MaxResponseItems: 50,
|
||||
}
|
||||
|
||||
// I18n settings
|
||||
@@ -352,9 +459,10 @@ func NewContext() {
|
||||
log.Fatal(4, "Fail to get work directory: %v", err)
|
||||
}
|
||||
|
||||
Cfg, err = ini.Load(bindata.MustAsset("conf/app.ini"))
|
||||
Cfg = ini.Empty()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err)
|
||||
log.Fatal(4, "Fail to parse 'app.ini': %v", err)
|
||||
}
|
||||
|
||||
CustomPath = os.Getenv("GITEA_CUSTOM")
|
||||
@@ -447,6 +555,10 @@ please consider changing to GITEA_CUSTOM`)
|
||||
if err = Cfg.Section("server").MapTo(&SSH); err != nil {
|
||||
log.Fatal(4, "Fail to map SSH settings: %v", err)
|
||||
}
|
||||
|
||||
SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
|
||||
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
|
||||
|
||||
// When disable SSH, start builtin server value is ignored.
|
||||
if SSH.Disabled {
|
||||
SSH.StartBuiltinServer = false
|
||||
@@ -470,11 +582,11 @@ please consider changing to GITEA_CUSTOM`)
|
||||
}
|
||||
|
||||
sec = Cfg.Section("security")
|
||||
InstallLock = sec.Key("INSTALL_LOCK").MustBool()
|
||||
SecretKey = sec.Key("SECRET_KEY").String()
|
||||
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt()
|
||||
CookieUserName = sec.Key("COOKIE_USERNAME").String()
|
||||
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String()
|
||||
InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
|
||||
SecretKey = sec.Key("SECRET_KEY").MustString("!#@FDEWREWR&*(")
|
||||
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
|
||||
CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
|
||||
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
|
||||
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
|
||||
|
||||
sec = Cfg.Section("attachment")
|
||||
@@ -505,7 +617,7 @@ please consider changing to GITEA_CUSTOM`)
|
||||
"StampNano": time.StampNano,
|
||||
}[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")]
|
||||
|
||||
RunUser = Cfg.Section("").Key("RUN_USER").String()
|
||||
RunUser = Cfg.Section("").Key("RUN_USER").MustString(user.CurrentUsername())
|
||||
// Does not check run user when the install lock is off.
|
||||
if InstallLock {
|
||||
currentUser, match := IsRunUserMatchCurrentUser(RunUser)
|
||||
@@ -548,6 +660,8 @@ please consider changing to GITEA_CUSTOM`)
|
||||
GravatarSource = "http://gravatar.duoshuo.com/avatar/"
|
||||
case "gravatar":
|
||||
GravatarSource = "https://secure.gravatar.com/avatar/"
|
||||
case "libravatar":
|
||||
GravatarSource = "https://seccdn.libravatar.org/avatar/"
|
||||
default:
|
||||
GravatarSource = source
|
||||
}
|
||||
@@ -594,12 +708,18 @@ please consider changing to GITEA_CUSTOM`)
|
||||
}
|
||||
|
||||
Langs = Cfg.Section("i18n").Key("LANGS").Strings(",")
|
||||
if len(Langs) == 0 {
|
||||
Langs = defaultLangs
|
||||
}
|
||||
Names = Cfg.Section("i18n").Key("NAMES").Strings(",")
|
||||
if len(Names) == 0 {
|
||||
Names = defaultLangNames
|
||||
}
|
||||
dateLangs = Cfg.Section("i18n.datelang").KeysHash()
|
||||
|
||||
ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool()
|
||||
ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool()
|
||||
ShowFooterTemplateLoadTime = Cfg.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool()
|
||||
ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool(false)
|
||||
ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool(true)
|
||||
ShowFooterTemplateLoadTime = Cfg.Section("other").Key("SHOW_FOOTER_TEMPLATE_LOAD_TIME").MustBool(true)
|
||||
|
||||
HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt"))
|
||||
}
|
||||
@@ -640,21 +760,28 @@ var logLevels = map[string]string{
|
||||
}
|
||||
|
||||
func newLogService() {
|
||||
log.Info("%s %s", AppName, AppVer)
|
||||
log.Info("Gitea v%s", AppVer)
|
||||
|
||||
if len(BuildTime) > 0 {
|
||||
log.Info("Build Time: %s", BuildTime)
|
||||
log.Info("Build Git Hash: %s", BuildGitHash)
|
||||
}
|
||||
|
||||
// Get and check log mode.
|
||||
LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
|
||||
LogConfigs = make([]string, len(LogModes))
|
||||
|
||||
useConsole := false
|
||||
for _, mode := range LogModes {
|
||||
if mode == "console" {
|
||||
useConsole = true
|
||||
}
|
||||
}
|
||||
if (!useConsole) {
|
||||
log.DelLogger("console")
|
||||
}
|
||||
|
||||
for i, mode := range LogModes {
|
||||
mode = strings.TrimSpace(mode)
|
||||
|
||||
sec, err := Cfg.GetSection("log." + mode)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(4, "Unknown log mode: %s", mode)
|
||||
sec, _ = Cfg.NewSection("log." + mode)
|
||||
}
|
||||
|
||||
validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}
|
||||
@@ -692,11 +819,11 @@ func newLogService() {
|
||||
sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}),
|
||||
sec.Key("ADDR").MustString(":7020"))
|
||||
case "smtp":
|
||||
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,
|
||||
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":["%s"],"subject":"%s"}`, level,
|
||||
sec.Key("USER").MustString("example@example.com"),
|
||||
sec.Key("PASSWD").MustString("******"),
|
||||
sec.Key("HOST").MustString("127.0.0.1:25"),
|
||||
sec.Key("RECEIVERS").MustString("[]"),
|
||||
strings.Replace(sec.Key("RECEIVERS").MustString("example@example.com"), ",", "\",\"", -1),
|
||||
sec.Key("SUBJECT").MustString("Diagnostic message from serve"))
|
||||
case "database":
|
||||
LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,
|
||||
@@ -729,7 +856,7 @@ func newSessionService() {
|
||||
SessionConfig.ProviderConfig = strings.Trim(Cfg.Section("session").Key("PROVIDER_CONFIG").String(), "\" ")
|
||||
SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("i_like_gogits")
|
||||
SessionConfig.CookiePath = AppSubURL
|
||||
SessionConfig.Secure = Cfg.Section("session").Key("COOKIE_SECURE").MustBool()
|
||||
SessionConfig.Secure = Cfg.Section("session").Key("COOKIE_SECURE").MustBool(false)
|
||||
SessionConfig.Gclifetime = Cfg.Section("session").Key("GC_INTERVAL_TIME").MustInt64(86400)
|
||||
SessionConfig.Maxlifetime = Cfg.Section("session").Key("SESSION_LIFE_TIME").MustInt64(86400)
|
||||
|
||||
|
||||
@@ -110,10 +110,10 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) {
|
||||
}
|
||||
}
|
||||
|
||||
func listen(config *ssh.ServerConfig, port int) {
|
||||
listener, err := net.Listen("tcp", "0.0.0.0:"+com.ToStr(port))
|
||||
func listen(config *ssh.ServerConfig, host string, port int) {
|
||||
listener, err := net.Listen("tcp", host+":"+com.ToStr(port))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Fatal(4, "Fail to start SSH server: %v", err)
|
||||
}
|
||||
for {
|
||||
// Once a ServerConfig has been configured, connections can be accepted.
|
||||
@@ -148,7 +148,7 @@ func listen(config *ssh.ServerConfig, port int) {
|
||||
}
|
||||
|
||||
// Listen starts a SSH server listens on given port.
|
||||
func Listen(port int) {
|
||||
func Listen(host string, port int) {
|
||||
config := &ssh.ServerConfig{
|
||||
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
||||
pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key))))
|
||||
@@ -185,5 +185,5 @@ func Listen(port int) {
|
||||
}
|
||||
config.AddHostKey(private)
|
||||
|
||||
go listen(config, port)
|
||||
go listen(config, host, port)
|
||||
}
|
||||
|
||||
103
modules/templates/dynamic.go
Normal file
103
modules/templates/dynamic.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// +build !bindata
|
||||
|
||||
// Copyright 2016 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 templates
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/Unknwon/com"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
templates = template.New("")
|
||||
)
|
||||
|
||||
// Renderer implements the macaron handler for serving the templates.
|
||||
func Renderer() macaron.Handler {
|
||||
return macaron.Renderer(macaron.RenderOptions{
|
||||
Funcs: NewFuncMap(),
|
||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
||||
AppendDirectories: []string{
|
||||
path.Join(setting.CustomPath, "templates"),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Mailer provides the templates required for sending notification mails.
|
||||
func Mailer() *template.Template {
|
||||
for _, funcs := range NewFuncMap() {
|
||||
templates.Funcs(funcs)
|
||||
}
|
||||
|
||||
staticDir := path.Join(setting.StaticRootPath, "templates", "mail")
|
||||
|
||||
if com.IsDir(staticDir) {
|
||||
files, err := com.StatDir(staticDir)
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Failed to read %s templates dir. %v", staticDir, err)
|
||||
} else {
|
||||
for _, filePath := range files {
|
||||
if !strings.HasSuffix(filePath, ".tmpl") {
|
||||
continue
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(path.Join(staticDir, filePath))
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Failed to read static %s template. %v", filePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
templates.New(
|
||||
strings.TrimSuffix(
|
||||
filePath,
|
||||
".tmpl",
|
||||
),
|
||||
).Parse(string(content))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customDir := path.Join(setting.CustomPath, "templates", "mail")
|
||||
|
||||
if com.IsDir(customDir) {
|
||||
files, err := com.StatDir(customDir)
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Failed to read %s templates dir. %v", customDir, err)
|
||||
} else {
|
||||
for _, filePath := range files {
|
||||
if !strings.HasSuffix(filePath, ".tmpl") {
|
||||
continue
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(path.Join(customDir, filePath))
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Failed to read custom %s template. %v", filePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
templates.New(
|
||||
strings.TrimSuffix(
|
||||
filePath,
|
||||
".tmpl",
|
||||
),
|
||||
).Parse(string(content))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return templates
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package template
|
||||
package templates
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"golang.org/x/net/html/charset"
|
||||
"golang.org/x/text/transform"
|
||||
"gopkg.in/editorconfig/editorconfig-core-go.v1"
|
||||
@@ -61,6 +62,7 @@ func NewFuncMap() []template.FuncMap {
|
||||
},
|
||||
"AvatarLink": base.AvatarLink,
|
||||
"Safe": Safe,
|
||||
"Sanitize": bluemonday.UGCPolicy().Sanitize,
|
||||
"Str2html": Str2html,
|
||||
"TimeSince": base.TimeSince,
|
||||
"RawTimeSince": base.RawTimeSince,
|
||||
109
modules/templates/static.go
Normal file
109
modules/templates/static.go
Normal file
@@ -0,0 +1,109 @@
|
||||
// +build bindata
|
||||
|
||||
// Copyright 2016 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 templates
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-macaron/bindata"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
templates = template.New("")
|
||||
)
|
||||
|
||||
// Renderer implements the macaron handler for serving the templates.
|
||||
func Renderer() macaron.Handler {
|
||||
return macaron.Renderer(macaron.RenderOptions{
|
||||
Funcs: NewFuncMap(),
|
||||
AppendDirectories: []string{
|
||||
path.Join(setting.CustomPath, "templates"),
|
||||
},
|
||||
TemplateFileSystem: bindata.Templates(
|
||||
bindata.Options{
|
||||
Asset: Asset,
|
||||
AssetDir: AssetDir,
|
||||
AssetInfo: AssetInfo,
|
||||
AssetNames: AssetNames,
|
||||
Prefix: "",
|
||||
},
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
// Mailer provides the templates required for sending notification mails.
|
||||
func Mailer() *template.Template {
|
||||
for _, funcs := range NewFuncMap() {
|
||||
templates.Funcs(funcs)
|
||||
}
|
||||
|
||||
for _, assetPath := range AssetNames() {
|
||||
if !strings.HasPrefix(assetPath, "mail/") {
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(assetPath, ".tmpl") {
|
||||
continue
|
||||
}
|
||||
|
||||
content, err := Asset(assetPath)
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Failed to read embedded %s template. %v", assetPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
templates.New(
|
||||
strings.TrimPrefix(
|
||||
strings.TrimSuffix(
|
||||
assetPath,
|
||||
".tmpl",
|
||||
),
|
||||
"mail/",
|
||||
),
|
||||
).Parse(string(content))
|
||||
}
|
||||
|
||||
customDir := path.Join(setting.CustomPath, "templates", "mail")
|
||||
|
||||
if com.IsDir(customDir) {
|
||||
files, err := com.StatDir(customDir)
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Failed to read %s templates dir. %v", customDir, err)
|
||||
} else {
|
||||
for _, filePath := range files {
|
||||
if !strings.HasSuffix(filePath, ".tmpl") {
|
||||
continue
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(path.Join(customDir, filePath))
|
||||
|
||||
if err != nil {
|
||||
log.Warn("Failed to read custom %s template. %v", filePath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
templates.New(
|
||||
strings.TrimSuffix(
|
||||
filePath,
|
||||
".tmpl",
|
||||
),
|
||||
).Parse(string(content))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return templates
|
||||
}
|
||||
10
modules/templates/templates.go
Normal file
10
modules/templates/templates.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2016 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 templates
|
||||
|
||||
//go:generate go-bindata -tags "bindata" -ignore "\\.go" -pkg "templates" -o "bindata.go" ../../templates/...
|
||||
//go:generate go fmt bindata.go
|
||||
//go:generate sed -i.bak s/..\/..\/templates\/// bindata.go
|
||||
//go:generate rm -f bindata.go.bak
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user