mirror of
https://github.com/go-gitea/gitea.git
synced 2026-01-02 21:41:36 +00:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3afbbfe921 | ||
|
|
bfce841b04 | ||
|
|
139fc7cfee | ||
|
|
596eebb2b6 | ||
|
|
1d5d745851 | ||
|
|
3dabfd4933 | ||
|
|
6ee6731290 | ||
|
|
602fe45936 | ||
|
|
e2da9cd21f | ||
|
|
c0b917b7eb | ||
|
|
54ea58ddf0 | ||
|
|
0158725387 | ||
|
|
f3cacf1332 | ||
|
|
a15dc93011 | ||
|
|
66b31786d3 | ||
|
|
931ddfec6d | ||
|
|
7e0a5b17db | ||
|
|
07688231c2 | ||
|
|
21eaeb8418 | ||
|
|
9a929ad17f | ||
|
|
c19ac41b34 | ||
|
|
fd85d31cb4 | ||
|
|
c9e4d7a564 | ||
|
|
9990430e32 | ||
|
|
6f5656ab0e | ||
|
|
e4a876cee1 | ||
|
|
abb534ba7a | ||
|
|
65dceb6a40 | ||
|
|
db26f0aca9 | ||
|
|
76878fd69b | ||
|
|
3444fa2dd7 | ||
|
|
caa2aeaa52 | ||
|
|
11300ee582 | ||
|
|
c6b78c3d31 | ||
|
|
4c40aa5be9 | ||
|
|
50f2e90b76 | ||
|
|
5d11ccc9e1 | ||
|
|
93860af542 | ||
|
|
7bf5834f2c | ||
|
|
1fbdd9335f | ||
|
|
e9061a537c | ||
|
|
ed664a9e1d | ||
|
|
4cb18601ff | ||
|
|
3abb25166c | ||
|
|
9e6ad64d48 | ||
|
|
b51d7c459e | ||
|
|
d3b6f001fe | ||
|
|
e938f1d945 | ||
|
|
7284327a00 | ||
|
|
919f3f11e2 | ||
|
|
3cee15e6f9 | ||
|
|
34e3644ada | ||
|
|
14bd120cdc | ||
|
|
3e40f8bebc | ||
|
|
df5f1d9dca | ||
|
|
457ee1ab5a | ||
|
|
4f64688902 | ||
|
|
117dcf1c02 | ||
|
|
4529a262c0 | ||
|
|
ff24f81a05 | ||
|
|
e3cb4f9d0e | ||
|
|
9b7890f1cc | ||
|
|
eb064dfda2 | ||
|
|
f9e66e5a46 | ||
|
|
ef89260cf1 | ||
|
|
315d928626 | ||
|
|
5525452bdf | ||
|
|
987cd277f6 | ||
|
|
5cdfde2ebf | ||
|
|
1cd6233cef | ||
|
|
cb81e39f7a | ||
|
|
8efd6b32e2 | ||
|
|
c95d9603ea |
@@ -232,6 +232,7 @@ services:
|
||||
image: postgres:9.5
|
||||
environment:
|
||||
POSTGRES_DB: test
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
||||
- name: ldap
|
||||
pull: default
|
||||
@@ -386,7 +387,7 @@ steps:
|
||||
|
||||
- name: static
|
||||
pull: always
|
||||
image: techknowlogick/xgo:latest
|
||||
image: techknowlogick/xgo:go-1.13.x
|
||||
commands:
|
||||
- apt update && apt -y install curl
|
||||
- curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
|
||||
@@ -484,7 +485,7 @@ steps:
|
||||
|
||||
- name: static
|
||||
pull: always
|
||||
image: techknowlogick/xgo:latest
|
||||
image: techknowlogick/xgo:go-1.13.x
|
||||
commands:
|
||||
- apt update && apt -y install curl
|
||||
- curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -69,6 +69,7 @@ coverage.all
|
||||
/yarn.lock
|
||||
/public/js
|
||||
/public/css
|
||||
/VERSION
|
||||
|
||||
# Snapcraft
|
||||
snap/.snapcraft/
|
||||
|
||||
104
CHANGELOG.md
104
CHANGELOG.md
@@ -4,6 +4,86 @@ This changelog goes through all the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
|
||||
## [1.11.4](https://github.com/go-gitea/gitea/releases/tag/v1.11.4) - 2020-04-01
|
||||
|
||||
* BUGFIXES
|
||||
* Only update merge_base if not already merged (#10909)
|
||||
* Fix milestones too many SQL variables bug (#10880) (#10904)
|
||||
* Protect against NPEs in notifications list (#10879) (#10883)
|
||||
* Convert plumbing.ErrObjectNotFound to git.ErrNotExist in getCommit (#10862) (#10868)
|
||||
* Convert plumbing.ErrReferenceNotFound to git.ErrNotExist in GetRefCommitID (#10676) (#10797)
|
||||
* Account for empty lines in receive-hook message (#10773) (#10784)
|
||||
* Fix bug on branch API (#10767) (#10775)
|
||||
* Migrate to go-git/go-git v5.0.0 (#10735) (#10753)
|
||||
* Fix hiding of fields in authorization source page (#10734) (#10752)
|
||||
* Prevent default for linkAction (#10742) (#10743)
|
||||
|
||||
## [1.11.3](https://github.com/go-gitea/gitea/releases/tag/v1.11.3) - 2020-03-10
|
||||
|
||||
* BUGFIXES
|
||||
* Prevent panic in stopwatch (#10670) (#10673)
|
||||
* Fix bug on pull view when required status check no ci result (#10648) (#10651)
|
||||
* Build explicitly with Go 1.13 (#10684)
|
||||
|
||||
## [1.11.2](https://github.com/go-gitea/gitea/releases/tag/v1.11.2) - 2020-03-06
|
||||
|
||||
* BREAKING
|
||||
* Various fixes in login sources (#10428) (#10429)
|
||||
* SECURITY
|
||||
* Ensure only own addresses are updated (#10397) (#10399)
|
||||
* Logout POST action (#10582) (#10585)
|
||||
* Org action fixes and form cleanup (#10512) (#10514)
|
||||
* Change action GETs to POST (#10462) (#10464)
|
||||
* Fix admin notices (#10480) (#10483)
|
||||
* Change admin dashboard to POST (#10465) (#10466)
|
||||
* Update markbates/goth (#10444) (#10445)
|
||||
* Update crypto vendors (#10385) (#10398)
|
||||
* BUGFIXES
|
||||
* Allow users with write permissions to modify issue descriptions and comments. (#10623) (#10626)
|
||||
* Handle deleted base branch in PR (#10618) (#10619)
|
||||
* Delete dependencies when deleting a repository (#10608) (#10616)
|
||||
* Ensure executable bit is kept on the web editor (#10607) (#10614)
|
||||
* Update mergebase in pr checker (#10586) (#10605)
|
||||
* Fix release attachments being deleted while upgrading (#10572) (#10573)
|
||||
* Fix redirection path if Slack webhook channel is invalid (#10566)
|
||||
* Fix head.tmpl og:image picture location (#10531) (#10556)
|
||||
* Fix 404 after activating secondary email (#10547) (#10553)
|
||||
* Show Signer in commit lists and add basic trust (#10425 & #10511) (#10524)
|
||||
* Fix potential bugs (#10513) (#10518)
|
||||
* Use \[:space:\] instead of \\s (#10508) (#10509)
|
||||
* Avoid mailing users that have explicitly unwatched an issue (#10475) (#10500)
|
||||
* Handle push rejection message in Merge & Web Editor (#10373) (#10497)
|
||||
* Fix SQLite concurrency problems by using BEGIN IMMEDIATE (#10368) (#10493)
|
||||
* Fix double PR notification from API (#10482) (#10486)
|
||||
* Show the username as a fallback on feeds if full name is blank (#10461)
|
||||
* Trigger webhooks on issue label-change via API too (#10421) (#10439)
|
||||
* Fix git reference type in webhooks (#10427) (#10432)
|
||||
* Prevent panic on merge to PR (#10403) (#10408)
|
||||
* Fix wrong num closed issues on repository when close issue via commit… (#10364) (#10380)
|
||||
* Reading pull attachments should depend on read UnitTypePullRequests (#10346) (#10354)
|
||||
* Set max-width on review-box comment box (#10348) (#10353)
|
||||
* Prevent nil pointer in GetPullRequestCommitStatusState (#10342) (#10344)
|
||||
* Fix protected branch status check settings (#10341) (#10343)
|
||||
* Truncate long commit message header (#10301) (#10319)
|
||||
* Set the initial commit status to Success otherwise it will always be Pending (#10317) (#10318)
|
||||
* Don't manually replace whitespace during render (#10291) (#10315)
|
||||
* ENHANCEMENT
|
||||
* Admin page for managing user e-mail activation (#10557) (#10579)
|
||||
|
||||
## [1.11.1](https://github.com/go-gitea/gitea/releases/tag/v1.11.1) - 2020-02-15
|
||||
|
||||
* BUGFIXES
|
||||
* Repo name added to automatically generated commit message when merging (#9997) (#10285)
|
||||
* Fix Workerpool deadlock (#10283) (#10284)
|
||||
* Divide GetIssueStats query in smaller chunks (#10176) (#10282)
|
||||
* Fix reply on code review (#10257)
|
||||
* Stop hanging issue indexer initialisation from preventing shutdown (#10243) (#10249)
|
||||
* Fix filter label emoji width (#10241) (#10244)
|
||||
* Fix issue sidebar menus having an infinite height (#10239) (#10240)
|
||||
* Fix commit between two commits calculation if there is only last commit (#10225) (#10226)
|
||||
* Only check for conflicts/merging if the PR has not been merged in the interim (#10132) (#10206)
|
||||
* Blacklist manifest.json & milestones user (#10292) (#10293)
|
||||
|
||||
## [1.11.0](https://github.com/go-gitea/gitea/releases/tag/v1.11.0) - 2020-02-10
|
||||
* BREAKING
|
||||
* Fix followers and following tabs in profile (#10202) (#10203)
|
||||
@@ -453,6 +533,30 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
* Update CodeMirror to version 5.49.0 (#8381)
|
||||
* Wiki editor: enable side-by-side button (#7242)
|
||||
|
||||
## [1.10.6](https://github.com/go-gitea/gitea/releases/tag/v1.10.6) - 2020-03-10
|
||||
|
||||
This is a re-tag version of v1.10.5 and also explicitly built with Go 1.13.
|
||||
|
||||
WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be used.
|
||||
|
||||
## [1.10.5](https://github.com/go-gitea/gitea/releases/tag/v1.10.5) - 2020-03-06
|
||||
|
||||
* BUGFIXES
|
||||
* Fix release attachments being deleted while upgrading (#10572) (#10574)
|
||||
|
||||
## [1.10.4](https://github.com/go-gitea/gitea/releases/tag/v1.10.4) - 2020-02-16
|
||||
|
||||
* FEATURE
|
||||
* Prevent empty LDAP search from deactivating all users (#9879) (#9890)
|
||||
* BUGFIXES
|
||||
* Fix reply on code review (#10261) (#10227)
|
||||
* Fix branch page pull request title and link error (#10092) (#10098)
|
||||
* Fix milestone API state parameter unhandled (#10049) (#10053)
|
||||
* Fix wiki raw view on sub path (#10002) (#10041)
|
||||
* Fix RocketChat Webhook (#9908) (#9921) (#9925)
|
||||
* Fix bug about wrong dependencies permissions check and other wrong permissions check (#9884) (Partial backport #9842)
|
||||
* Ensure that 2fa is checked on reset-password (#9857) (#9877)
|
||||
|
||||
## [1.10.3](https://github.com/go-gitea/gitea/releases/tag/v1.10.3) - 2020-01-17
|
||||
* SECURITY
|
||||
* Hide credentials when submitting migration (#9102) (#9704)
|
||||
|
||||
54
Makefile
54
Makefile
@@ -29,6 +29,8 @@ EXTRA_GOFLAGS ?=
|
||||
|
||||
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
|
||||
|
||||
STORED_VERSION_FILE := VERSION
|
||||
|
||||
ifneq ($(DRONE_TAG),)
|
||||
VERSION ?= $(subst v,,$(DRONE_TAG))
|
||||
GITEA_VERSION ?= $(VERSION)
|
||||
@@ -38,7 +40,13 @@ else
|
||||
else
|
||||
VERSION ?= master
|
||||
endif
|
||||
GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
||||
|
||||
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
|
||||
ifneq ($(STORED_VERSION),)
|
||||
GITEA_VERSION ?= $(STORED_VERSION)
|
||||
else
|
||||
GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
||||
endif
|
||||
endif
|
||||
|
||||
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
||||
@@ -96,13 +104,15 @@ include docker/Makefile
|
||||
help:
|
||||
@echo "Make Routines:"
|
||||
@echo " - \"\" equivalent to \"build\""
|
||||
@echo " - build creates the entire project"
|
||||
@echo " - clean delete integration files and build files but not css and js files"
|
||||
@echo " - clean-all delete all generated files (integration test, build, css and js files)"
|
||||
@echo " - build build everything"
|
||||
@echo " - frontend build frontend files"
|
||||
@echo " - backend build backend files"
|
||||
@echo " - clean delete backend and integration files"
|
||||
@echo " - clean-all delete backend, frontend and integration files"
|
||||
@echo " - css rebuild only css files"
|
||||
@echo " - js rebuild only js files"
|
||||
@echo " - generate run \"make css js\" and \"go generate\""
|
||||
@echo " - fmt format the code"
|
||||
@echo " - generate run \"go generate\""
|
||||
@echo " - fmt format the Go code"
|
||||
@echo " - generate-swagger generate the swagger spec from code comments"
|
||||
@echo " - swagger-validate check if the swagger spec is valide"
|
||||
@echo " - revive run code linter revive"
|
||||
@@ -113,7 +123,7 @@ help:
|
||||
|
||||
.PHONY: go-check
|
||||
go-check:
|
||||
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell go version | grep -Eo '[0-9]+\.?[0-9]+?\.?[0-9]?\s' | tr '.' ' ');))
|
||||
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell go version | grep -Eo '[0-9]+\.?[0-9]+?\.?[0-9]?[[:space:]]' | tr '.' ' ');))
|
||||
@if [ "$(GO_VERSION)" -lt "001011000" ]; then \
|
||||
echo "Gitea requires Go 1.11.0 or greater to build. You can get it at https://golang.org/dl/"; \
|
||||
exit 1; \
|
||||
@@ -156,10 +166,6 @@ fmt:
|
||||
vet:
|
||||
$(GO) vet $(PACKAGES)
|
||||
|
||||
.PHONY: generate
|
||||
generate: js css
|
||||
GO111MODULE=on $(GO) generate -mod=vendor $(PACKAGES)
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger:
|
||||
$(SWAGGER) generate spec -o './$(SWAGGER_SPEC)'
|
||||
@@ -414,13 +420,23 @@ install: $(wildcard *.go)
|
||||
$(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
||||
|
||||
.PHONY: build
|
||||
build: go-check generate $(EXECUTABLE)
|
||||
build: frontend backend
|
||||
|
||||
.PHONY: frontend
|
||||
frontend: node-check js css
|
||||
|
||||
.PHONY: backend
|
||||
backend: go-check generate $(EXECUTABLE)
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
GO111MODULE=on $(GO) generate -mod=vendor $(PACKAGES)
|
||||
|
||||
$(EXECUTABLE): $(GO_SOURCES)
|
||||
GO111MODULE=on $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
.PHONY: release
|
||||
release: generate release-dirs release-windows release-linux release-darwin release-copy release-compress release-check
|
||||
release: frontend generate release-dirs release-windows release-linux release-darwin release-copy release-compress release-sources release-check
|
||||
|
||||
.PHONY: release-dirs
|
||||
release-dirs:
|
||||
@@ -431,7 +447,7 @@ release-windows:
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
xgo -go go-1.13 -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
@@ -441,7 +457,7 @@ release-linux:
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out gitea-$(VERSION) .
|
||||
xgo -go go-1.13 -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
@@ -451,7 +467,7 @@ release-darwin:
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
|
||||
xgo -go go-1.13 -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
@@ -471,6 +487,12 @@ release-compress:
|
||||
fi
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||
|
||||
.PHONY: release-sources
|
||||
release-sources: | node_modules
|
||||
echo $(VERSION) > $(STORED_VERSION_FILE)
|
||||
tar --exclude=./$(DIST) --exclude=./.git --exclude=./node_modules/.cache -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
||||
rm -f $(STORED_VERSION_FILE)
|
||||
|
||||
node_modules: package-lock.json
|
||||
npm install --no-save
|
||||
|
||||
|
||||
@@ -33,6 +33,15 @@ From the root of the source tree, run:
|
||||
|
||||
TAGS="bindata" make build
|
||||
|
||||
The `build` target is split into two sub-targets:
|
||||
|
||||
- `make backend` which requires [Go 1.11](https://golang.org/dl/) or greater.
|
||||
- `make frontend` which requires [Node.js 10.0.0](https://nodejs.org/en/download/) or greater.
|
||||
|
||||
If pre-built frontend files are present it is possible to only build the backend:
|
||||
|
||||
TAGS="bindata" make backend
|
||||
|
||||
More info: https://docs.gitea.io/en-us/install-from-source/
|
||||
|
||||
## Using
|
||||
|
||||
@@ -28,11 +28,11 @@ import (
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/routes"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
context2 "github.com/gorilla/context"
|
||||
"github.com/unknwon/com"
|
||||
"gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4/config"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/testfixtures.v2"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ params:
|
||||
description: Git with a cup of tea
|
||||
author: The Gitea Authors
|
||||
website: https://docs.gitea.io
|
||||
version: 1.11.0
|
||||
version: 1.11.2
|
||||
|
||||
outputs:
|
||||
home:
|
||||
|
||||
@@ -60,7 +60,7 @@ _Symbols used in table:_
|
||||
| Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ⁄ | ✓ |
|
||||
| Group Milestones | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Granular user roles (Code, Issues, Wiki etc) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Verified Committer | ✘ | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
|
||||
| Verified Committer | ⁄ | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
|
||||
| GPG Signed Commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Reject unsigned commits | [✘](https://github.com/go-gitea/gitea/issues/2770) | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ |
|
||||
| Repository Activity page | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
|
||||
@@ -114,6 +114,17 @@ recommended way to build from source is therefore:
|
||||
TAGS="bindata sqlite sqlite_unlock_notify" make build
|
||||
```
|
||||
|
||||
The `build` target is split into two sub-targets:
|
||||
|
||||
- `make backend` which requires [Go 1.11](https://golang.org/dl/) or greater.
|
||||
- `make frontend` which requires [Node.js 10.0.0](https://nodejs.org/en/download/) or greater.
|
||||
|
||||
If pre-built frontend files are present it is possible to only build the backend:
|
||||
|
||||
```bash
|
||||
TAGS="bindata" make backend
|
||||
``
|
||||
|
||||
## Test
|
||||
|
||||
After following the steps above, a `gitea` binary will be available in the working directory.
|
||||
|
||||
14
go.mod
14
go.mod
@@ -41,6 +41,8 @@ require (
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
|
||||
github.com/gliderlabs/ssh v0.2.2
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
|
||||
github.com/go-git/go-billy/v5 v5.0.0
|
||||
github.com/go-git/go-git/v5 v5.0.0
|
||||
github.com/go-openapi/jsonreference v0.19.3 // indirect
|
||||
github.com/go-redis/redis v6.15.2+incompatible
|
||||
github.com/go-sql-driver/mysql v1.4.1
|
||||
@@ -62,7 +64,7 @@ require (
|
||||
github.com/lib/pq v1.2.0
|
||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
github.com/mailru/easyjson v0.7.0 // indirect
|
||||
github.com/markbates/goth v1.56.0
|
||||
github.com/markbates/goth v1.61.2
|
||||
github.com/mattn/go-isatty v0.0.7
|
||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect
|
||||
github.com/mattn/go-sqlite3 v1.11.0
|
||||
@@ -80,7 +82,7 @@ require (
|
||||
github.com/quasoft/websspi v1.0.0
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/sergi/go-diff v1.0.0
|
||||
github.com/sergi/go-diff v1.1.0
|
||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 // indirect
|
||||
@@ -95,10 +97,10 @@ require (
|
||||
github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
|
||||
github.com/yuin/goldmark v1.1.19
|
||||
go.etcd.io/bbolt v1.3.3 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876
|
||||
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527
|
||||
golang.org/x/text v0.3.2
|
||||
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
@@ -106,8 +108,6 @@ require (
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.51.1
|
||||
gopkg.in/ldap.v3 v3.0.2
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/testfixtures.v2 v2.5.0
|
||||
mvdan.cc/xurls/v2 v2.1.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
|
||||
59
go.sum
59
go.sum
@@ -110,7 +110,7 @@ github.com/couchbase/vellum v0.0.0-20190829182332-ef2e028c01fd/go.mod h1:xbc8Ff/
|
||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 h1:1XjEY/gnjQ+AfXef2U6dxCquhiRzkEpxZuWqs+QxTL8=
|
||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
|
||||
@@ -164,6 +164,14 @@ github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a h1:FQqo
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
||||
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
|
||||
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
@@ -346,11 +354,13 @@ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lafriks/xormstore v1.3.2 h1:hqi3F8s/B4rz8GuEZZDuHuOxRjeuOpEI/cC7vcnWwH4=
|
||||
github.com/lafriks/xormstore v1.3.2/go.mod h1:mVNIwIa25QIr8rfR7YlVjrqN/apswHkVdtLCyVYBzXw=
|
||||
github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@@ -370,8 +380,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
|
||||
github.com/markbates/goth v1.56.0 h1:XEYedCgMNz5pi3ojXI8z2XUmXtBnMeuKUpx4Z6HlNj8=
|
||||
github.com/markbates/goth v1.56.0/go.mod h1:zZmAw0Es0Dpm7TT/4AdN14QrkiWLMrrU9Xei1o+/mdA=
|
||||
github.com/markbates/goth v1.61.2 h1:jDowrUH5qw8KGuQdKwFhLzkXkTYCIPfz3LHADJsiPIs=
|
||||
github.com/markbates/goth v1.61.2/go.mod h1:qh2QfwZoWRucQ+DR5KVKC6dUGkNCToWh4vS45GIzFsY=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d h1:m+dSK37rFf2fqppZhg15yI2IwC9BtucBiRwSDm9VL8g=
|
||||
@@ -402,6 +412,8 @@ github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc/go.mod h1:np1wUFZ6ty
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY=
|
||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/niklasfasching/go-org v0.1.8 h1:Kjvs6lP+LIILHhc9zIJ4Gu90a/pVY483if2Qmu8v4Fg=
|
||||
github.com/niklasfasching/go-org v0.1.8/go.mod h1:AsLD6X7djzRIz4/RFZu8vwRL0VGjUvGZCCH1Nz0VdrU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
@@ -416,7 +428,6 @@ github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
|
||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||
@@ -462,12 +473,11 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkNDhPdWN3REluVa46HQOVuVkjkd2sWnrABNQ=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b h1:4kg1wyftSKxLtnPAvcRWakIPpokB9w780/KwrNLnfPA=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
|
||||
@@ -502,13 +512,10 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 h1:JNEGSiWg6D3lcBCMCBqN3ELniXujt+0QNHLhNnO0w3s=
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2/go.mod h1:mjqs7N0Q6m5HpR7QfXVBZXZWSqTjQLeTujjA/xUp2uw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -532,7 +539,6 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/unknwon/cae v0.0.0-20190822084630-55a0b64484a1 h1:SpoCl3+Pta5/ubQyF+Fmx65obtpfkyzeaOIneCE3MTw=
|
||||
github.com/unknwon/cae v0.0.0-20190822084630-55a0b64484a1/go.mod h1:QaSeRctcea9fK6piJpAMCCPKxzJ01+xFcr2k1m3WRPU=
|
||||
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM=
|
||||
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||
@@ -578,11 +584,10 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
|
||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
|
||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
@@ -614,8 +619,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9 h1:DPz9iiH3YoKiKhX/ijjoZvT0VFwK2c6CWYWQ7Zyr8TU=
|
||||
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -645,15 +650,13 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2 h1:/J2nHFg1MTqaRLFO7M+J78ASNsJoz3r0cvHBPQ77fsE=
|
||||
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
@@ -675,7 +678,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 h1:kJQZhwFzSwJS2BxboKjdZzWczQOZx8VuH7Y8hhuGUtM=
|
||||
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -715,8 +717,9 @@ gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
@@ -731,12 +734,6 @@ gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w=
|
||||
gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/testfixtures.v2 v2.5.0 h1:N08B7l2GzFQenyYbzqthDnKAA+cmb17iAZhhFxr7JHw=
|
||||
gopkg.in/testfixtures.v2 v2.5.0/go.mod h1:vyAq+MYCgNpR29qitQdLZhdbLFf4mR/2MFJRFoQZZ2M=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
@@ -745,8 +742,9 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -760,7 +758,6 @@ xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
||||
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
||||
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||
xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM=
|
||||
xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
|
||||
xorm.io/xorm v0.8.1 h1:4f2KXuQxVdaX3RdI3Fw81NzMiSpZeyCZt8m3sEVeIkQ=
|
||||
xorm.io/xorm v0.8.1/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
|
||||
|
||||
@@ -28,6 +28,8 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) {
|
||||
var branch api.Branch
|
||||
DecodeJSON(t, resp, &branch)
|
||||
assert.EqualValues(t, branchName, branch.Name)
|
||||
assert.True(t, branch.UserCanPush)
|
||||
assert.True(t, branch.UserCanMerge)
|
||||
}
|
||||
|
||||
func TestAPIGetBranch(t *testing.T) {
|
||||
|
||||
@@ -39,12 +39,12 @@ func TestAPICreateIssue(t *testing.T) {
|
||||
defer prepareTestEnv(t)()
|
||||
const body, title = "apiTestBody", "apiTestTitle"
|
||||
|
||||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
||||
repoBefore := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repoBefore.OwnerID}).(*models.User)
|
||||
|
||||
session := loginUser(t, owner.Name)
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repo.Name, token)
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token)
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{
|
||||
Body: body,
|
||||
Title: title,
|
||||
@@ -57,19 +57,23 @@ func TestAPICreateIssue(t *testing.T) {
|
||||
assert.Equal(t, apiIssue.Title, title)
|
||||
|
||||
models.AssertExistsAndLoadBean(t, &models.Issue{
|
||||
RepoID: repo.ID,
|
||||
RepoID: repoBefore.ID,
|
||||
AssigneeID: owner.ID,
|
||||
Content: body,
|
||||
Title: title,
|
||||
})
|
||||
|
||||
repoAfter := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||
assert.Equal(t, repoBefore.NumIssues+1, repoAfter.NumIssues)
|
||||
assert.Equal(t, repoBefore.NumClosedIssues, repoAfter.NumClosedIssues)
|
||||
}
|
||||
|
||||
func TestAPIEditIssue(t *testing.T) {
|
||||
defer prepareTestEnv(t)()
|
||||
|
||||
issueBefore := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
|
||||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
|
||||
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
||||
repoBefore := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
|
||||
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repoBefore.OwnerID}).(*models.User)
|
||||
assert.NoError(t, issueBefore.LoadAttributes())
|
||||
assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
|
||||
assert.Equal(t, api.StateOpen, issueBefore.State())
|
||||
@@ -84,7 +88,7 @@ func TestAPIEditIssue(t *testing.T) {
|
||||
body := "new content!"
|
||||
title := "new title from api set"
|
||||
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repo.Name, issueBefore.Index, token)
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repoBefore.Name, issueBefore.Index, token)
|
||||
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
|
||||
State: &issueState,
|
||||
RemoveDeadline: &removeDeadline,
|
||||
@@ -99,6 +103,7 @@ func TestAPIEditIssue(t *testing.T) {
|
||||
DecodeJSON(t, resp, &apiIssue)
|
||||
|
||||
issueAfter := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
|
||||
repoAfter := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
|
||||
|
||||
// check deleted user
|
||||
assert.Equal(t, int64(500), issueAfter.PosterID)
|
||||
@@ -107,6 +112,9 @@ func TestAPIEditIssue(t *testing.T) {
|
||||
assert.Equal(t, int64(-1), issueBefore.PosterID)
|
||||
assert.Equal(t, int64(-1), apiIssue.Poster.ID)
|
||||
|
||||
// check repo change
|
||||
assert.Equal(t, repoBefore.NumClosedIssues+1, repoAfter.NumClosedIssues)
|
||||
|
||||
// API response
|
||||
assert.Equal(t, api.StateClosed, apiIssue.State)
|
||||
assert.Equal(t, milestone, apiIssue.Milestone.ID)
|
||||
|
||||
@@ -351,6 +351,17 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
|
||||
pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "protected", "unprotected")(t)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("GenerateCommit", func(t *testing.T) {
|
||||
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-")
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected-2"))
|
||||
var pr2 api.PullRequest
|
||||
t.Run("CreatePullRequest", func(t *testing.T) {
|
||||
pr2, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "unprotected", "unprotected-2")(t)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("MergePR2", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr2.Index))
|
||||
t.Run("MergePR", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index))
|
||||
t.Run("PullProtected", doGitPull(dstPath, "origin", "protected"))
|
||||
t.Run("ProtectProtectedBranchWhitelist", doProtectBranch(ctx, "protected", baseCtx.Username))
|
||||
|
||||
@@ -126,7 +126,7 @@ func restoreOldDB(t *testing.T, version string) bool {
|
||||
err := os.MkdirAll(path.Dir(setting.Database.Path), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
|
||||
db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d", setting.Database.Path, setting.Database.Timeout))
|
||||
db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate", setting.Database.Path, setting.Database.Timeout))
|
||||
assert.NoError(t, err)
|
||||
defer db.Close()
|
||||
|
||||
|
||||
@@ -105,8 +105,6 @@ func TestPullRebase(t *testing.T) {
|
||||
|
||||
func TestPullRebaseMerge(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
defer prepareTestEnv(t)()
|
||||
|
||||
hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
@@ -129,8 +127,6 @@ func TestPullRebaseMerge(t *testing.T) {
|
||||
|
||||
func TestPullSquash(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
defer prepareTestEnv(t)()
|
||||
|
||||
hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
@@ -154,10 +150,9 @@ func TestPullSquash(t *testing.T) {
|
||||
|
||||
func TestPullCleanUpAfterMerge(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
defer prepareTestEnv(t)()
|
||||
session := loginUser(t, "user1")
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
|
||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited - TestPullCleanUpAfterMerge)\n")
|
||||
|
||||
resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title")
|
||||
|
||||
@@ -190,7 +185,6 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
|
||||
|
||||
func TestCantMergeWorkInProgress(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
defer prepareTestEnv(t)()
|
||||
session := loginUser(t, "user1")
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||
@@ -212,7 +206,6 @@ func TestCantMergeWorkInProgress(t *testing.T) {
|
||||
|
||||
func TestCantMergeConflict(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
defer prepareTestEnv(t)()
|
||||
session := loginUser(t, "user1")
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n")
|
||||
@@ -258,7 +251,6 @@ func TestCantMergeConflict(t *testing.T) {
|
||||
|
||||
func TestCantMergeUnrelated(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
defer prepareTestEnv(t)()
|
||||
session := loginUser(t, "user1")
|
||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n")
|
||||
|
||||
@@ -20,7 +20,7 @@ func createNewRelease(t *testing.T, session *TestSession, repoURL, tag, title st
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
link, exists := htmlDoc.doc.Find("form").Attr("action")
|
||||
link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
|
||||
assert.True(t, exists, "The template has changed")
|
||||
|
||||
postData := map[string]string{
|
||||
|
||||
@@ -14,7 +14,7 @@ func TestSignOut(t *testing.T) {
|
||||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
req := NewRequest(t, "GET", "/user/logout")
|
||||
req := NewRequest(t, "POST", "/user/logout")
|
||||
session.MakeRequest(t, req, http.StatusFound)
|
||||
|
||||
// try to view a private repo, should fail
|
||||
|
||||
@@ -122,10 +122,13 @@ func (a *Action) ShortActUserName() string {
|
||||
return base.EllipsisString(a.GetActUserName(), 20)
|
||||
}
|
||||
|
||||
// GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME
|
||||
// GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank.
|
||||
func (a *Action) GetDisplayName() string {
|
||||
if setting.UI.DefaultShowFullName {
|
||||
return a.GetActFullName()
|
||||
trimmedFullName := strings.TrimSpace(a.GetActFullName())
|
||||
if len(trimmedFullName) > 0 {
|
||||
return trimmedFullName
|
||||
}
|
||||
}
|
||||
return a.ShortActUserName()
|
||||
}
|
||||
@@ -212,7 +215,7 @@ func (a *Action) getCommentLink(e Engine) string {
|
||||
return "#"
|
||||
}
|
||||
if a.Comment == nil && a.CommentID != 0 {
|
||||
a.Comment, _ = GetCommentByID(a.CommentID)
|
||||
a.Comment, _ = getCommentByID(e, a.CommentID)
|
||||
}
|
||||
if a.Comment != nil {
|
||||
return a.Comment.HTMLURL()
|
||||
|
||||
@@ -79,7 +79,11 @@ func (a *Attachment) LinkedRepository() (*Repository, UnitType, error) {
|
||||
return nil, UnitTypeIssues, err
|
||||
}
|
||||
repo, err := GetRepositoryByID(iss.RepoID)
|
||||
return repo, UnitTypeIssues, err
|
||||
unitType := UnitTypeIssues
|
||||
if iss.IsPull {
|
||||
unitType = UnitTypePullRequests
|
||||
}
|
||||
return repo, unitType, err
|
||||
} else if a.ReleaseID != 0 {
|
||||
rel, err := GetReleaseByID(a.ReleaseID)
|
||||
if err != nil {
|
||||
@@ -195,7 +199,7 @@ func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
|
||||
|
||||
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
|
||||
attachments := make([]*Attachment, 0, 10)
|
||||
return attachments, x.Where("comment_id=?", commentID).Find(&attachments)
|
||||
return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
|
||||
}
|
||||
|
||||
// getAttachmentByReleaseIDFileName return a file based on the the following infos:
|
||||
|
||||
@@ -138,7 +138,7 @@ func TestLinkedRepository(t *testing.T) {
|
||||
expectedUnitType UnitType
|
||||
}{
|
||||
{"LinkedIssue", 1, &Repository{ID: 1}, UnitTypeIssues},
|
||||
{"LinkedComment", 3, &Repository{ID: 1}, UnitTypeIssues},
|
||||
{"LinkedComment", 3, &Repository{ID: 1}, UnitTypePullRequests},
|
||||
{"LinkedRelease", 9, &Repository{ID: 1}, UnitTypeReleases},
|
||||
{"Notlinked", 10, nil, -1},
|
||||
}
|
||||
|
||||
@@ -113,6 +113,28 @@ func (protectBranch *ProtectedBranch) CanUserMerge(userID int64) bool {
|
||||
return in
|
||||
}
|
||||
|
||||
// IsUserMergeWhitelisted checks if some user is whitelisted to merge to this branch
|
||||
func (protectBranch *ProtectedBranch) IsUserMergeWhitelisted(userID int64) bool {
|
||||
if !protectBranch.EnableMergeWhitelist {
|
||||
return true
|
||||
}
|
||||
|
||||
if base.Int64sContains(protectBranch.MergeWhitelistUserIDs, userID) {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(protectBranch.MergeWhitelistTeamIDs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
in, err := IsUserInTeams(userID, protectBranch.MergeWhitelistTeamIDs)
|
||||
if err != nil {
|
||||
log.Error("IsUserInTeams: %v", err)
|
||||
return false
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
// IsUserOfficialReviewer check if user is official reviewer for the branch (counts towards required approvals)
|
||||
func (protectBranch *ProtectedBranch) IsUserOfficialReviewer(user *User) (bool, error) {
|
||||
return protectBranch.isUserOfficialReviewer(x, user)
|
||||
|
||||
@@ -7,6 +7,7 @@ package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
)
|
||||
@@ -56,6 +57,21 @@ func (err ErrNamePatternNotAllowed) Error() string {
|
||||
return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern)
|
||||
}
|
||||
|
||||
// ErrNameCharsNotAllowed represents a "character not allowed in name" error.
|
||||
type ErrNameCharsNotAllowed struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrNameCharsNotAllowed checks if an error is an ErrNameCharsNotAllowed.
|
||||
func IsErrNameCharsNotAllowed(err error) bool {
|
||||
_, ok := err.(ErrNameCharsNotAllowed)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrNameCharsNotAllowed) Error() string {
|
||||
return fmt.Sprintf("User name is invalid [%s]: must be valid alpha or numeric or dash(-_) or dot characters", err.Name)
|
||||
}
|
||||
|
||||
// ErrSSHDisabled represents an "SSH disabled" error.
|
||||
type ErrSSHDisabled struct {
|
||||
}
|
||||
@@ -1355,6 +1371,53 @@ func (err ErrMergePushOutOfDate) Error() string {
|
||||
return fmt.Sprintf("Merge PushOutOfDate Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||
}
|
||||
|
||||
// ErrPushRejected represents an error if merging fails due to rejection from a hook
|
||||
type ErrPushRejected struct {
|
||||
Style MergeStyle
|
||||
Message string
|
||||
StdOut string
|
||||
StdErr string
|
||||
Err error
|
||||
}
|
||||
|
||||
// IsErrPushRejected checks if an error is a ErrPushRejected.
|
||||
func IsErrPushRejected(err error) bool {
|
||||
_, ok := err.(ErrPushRejected)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrPushRejected) Error() string {
|
||||
return fmt.Sprintf("Merge PushRejected Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||
}
|
||||
|
||||
// GenerateMessage generates the remote message from the stderr
|
||||
func (err *ErrPushRejected) GenerateMessage() {
|
||||
messageBuilder := &strings.Builder{}
|
||||
i := strings.Index(err.StdErr, "remote: ")
|
||||
if i < 0 {
|
||||
err.Message = ""
|
||||
return
|
||||
}
|
||||
for {
|
||||
if len(err.StdErr) <= i+8 {
|
||||
break
|
||||
}
|
||||
if err.StdErr[i:i+8] != "remote: " {
|
||||
break
|
||||
}
|
||||
i += 8
|
||||
nl := strings.IndexByte(err.StdErr[i:], '\n')
|
||||
if nl >= 0 {
|
||||
messageBuilder.WriteString(err.StdErr[i : i+nl+1])
|
||||
i = i + nl + 1
|
||||
} else {
|
||||
messageBuilder.WriteString(err.StdErr[i:])
|
||||
i = len(err.StdErr)
|
||||
}
|
||||
}
|
||||
err.Message = strings.TrimSpace(messageBuilder.String())
|
||||
}
|
||||
|
||||
// ErrRebaseConflicts represents an error if rebase fails with a conflict
|
||||
type ErrRebaseConflicts struct {
|
||||
Style MergeStyle
|
||||
|
||||
@@ -369,6 +369,7 @@ type CommitVerification struct {
|
||||
CommittingUser *User
|
||||
SigningEmail string
|
||||
SigningKey *GPGKey
|
||||
TrustStatus string
|
||||
}
|
||||
|
||||
// SignCommit represents a commit with validation of signature.
|
||||
@@ -754,18 +755,54 @@ func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature,
|
||||
}
|
||||
|
||||
// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
|
||||
func ParseCommitsWithSignature(oldCommits *list.List) *list.List {
|
||||
func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *list.List {
|
||||
var (
|
||||
newCommits = list.New()
|
||||
e = oldCommits.Front()
|
||||
)
|
||||
memberMap := map[int64]bool{}
|
||||
|
||||
for e != nil {
|
||||
c := e.Value.(UserCommit)
|
||||
newCommits.PushBack(SignCommit{
|
||||
signCommit := SignCommit{
|
||||
UserCommit: &c,
|
||||
Verification: ParseCommitWithSignature(c.Commit),
|
||||
})
|
||||
}
|
||||
|
||||
_ = CalculateTrustStatus(signCommit.Verification, repository, &memberMap)
|
||||
|
||||
newCommits.PushBack(signCommit)
|
||||
e = e.Next()
|
||||
}
|
||||
return newCommits
|
||||
}
|
||||
|
||||
// CalculateTrustStatus will calculate the TrustStatus for a commit verification within a repository
|
||||
func CalculateTrustStatus(verification *CommitVerification, repository *Repository, memberMap *map[int64]bool) (err error) {
|
||||
if verification.Verified {
|
||||
verification.TrustStatus = "trusted"
|
||||
if verification.SigningUser.ID != 0 {
|
||||
var isMember bool
|
||||
if memberMap != nil {
|
||||
var has bool
|
||||
isMember, has = (*memberMap)[verification.SigningUser.ID]
|
||||
if !has {
|
||||
isMember, err = repository.IsOwnerMemberCollaborator(verification.SigningUser.ID)
|
||||
(*memberMap)[verification.SigningUser.ID] = isMember
|
||||
}
|
||||
} else {
|
||||
isMember, err = repository.IsOwnerMemberCollaborator(verification.SigningUser.ID)
|
||||
}
|
||||
|
||||
if !isMember {
|
||||
verification.TrustStatus = "untrusted"
|
||||
if verification.CommittingUser.ID != verification.SigningUser.ID {
|
||||
// The committing user and the signing user are not the same and are not the default key
|
||||
// This should be marked as questionable unless the signing user is a collaborator/team member etc.
|
||||
verification.TrustStatus = "unmatched"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -673,6 +673,10 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (*C
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := issue.updateClosedNum(e); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// New action comment
|
||||
cmtType := CommentTypeClose
|
||||
if !issue.IsClosed {
|
||||
@@ -1336,6 +1340,36 @@ type IssueStatsOptions struct {
|
||||
|
||||
// GetIssueStats returns issue statistic information by given conditions.
|
||||
func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
|
||||
if len(opts.IssueIDs) <= maxQueryParameters {
|
||||
return getIssueStatsChunk(opts, opts.IssueIDs)
|
||||
}
|
||||
|
||||
// If too long a list of IDs is provided, we get the statistics in
|
||||
// smaller chunks and get accumulates. Note: this could potentially
|
||||
// get us invalid results. The alternative is to insert the list of
|
||||
// ids in a temporary table and join from them.
|
||||
accum := &IssueStats{}
|
||||
for i := 0; i < len(opts.IssueIDs); {
|
||||
chunk := i + maxQueryParameters
|
||||
if chunk > len(opts.IssueIDs) {
|
||||
chunk = len(opts.IssueIDs)
|
||||
}
|
||||
stats, err := getIssueStatsChunk(opts, opts.IssueIDs[i:chunk])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accum.OpenCount += stats.OpenCount
|
||||
accum.ClosedCount += stats.ClosedCount
|
||||
accum.YourRepositoriesCount += stats.YourRepositoriesCount
|
||||
accum.AssignCount += stats.AssignCount
|
||||
accum.CreateCount += stats.CreateCount
|
||||
accum.OpenCount += stats.MentionCount
|
||||
i = chunk
|
||||
}
|
||||
return accum, nil
|
||||
}
|
||||
|
||||
func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, error) {
|
||||
stats := &IssueStats{}
|
||||
|
||||
countSession := func(opts *IssueStatsOptions) *xorm.Session {
|
||||
|
||||
@@ -749,8 +749,12 @@ func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commi
|
||||
|
||||
// GetCommentByID returns the comment by given ID.
|
||||
func GetCommentByID(id int64) (*Comment, error) {
|
||||
return getCommentByID(x, id)
|
||||
}
|
||||
|
||||
func getCommentByID(e Engine, id int64) (*Comment, error) {
|
||||
c := new(Comment)
|
||||
has, err := x.ID(id).Get(c)
|
||||
has, err := e.ID(id).Get(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
|
||||
@@ -521,10 +521,12 @@ func DeleteMilestoneByRepoID(repoID, id int64) error {
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// CountMilestonesByRepoIDs map from repoIDs to number of milestones matching the options`
|
||||
func CountMilestonesByRepoIDs(repoIDs []int64, isClosed bool) (map[int64]int64, error) {
|
||||
// CountMilestones map from repo conditions to number of milestones matching the options`
|
||||
func CountMilestones(repoCond builder.Cond, isClosed bool) (map[int64]int64, error) {
|
||||
sess := x.Where("is_closed = ?", isClosed)
|
||||
sess.In("repo_id", repoIDs)
|
||||
if repoCond.IsValid() {
|
||||
sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond))
|
||||
}
|
||||
|
||||
countsSlice := make([]*struct {
|
||||
RepoID int64
|
||||
@@ -544,11 +546,21 @@ func CountMilestonesByRepoIDs(repoIDs []int64, isClosed bool) (map[int64]int64,
|
||||
return countMap, nil
|
||||
}
|
||||
|
||||
// GetMilestonesByRepoIDs returns a list of milestones of given repositories and status.
|
||||
func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType string) (MilestoneList, error) {
|
||||
// CountMilestonesByRepoIDs map from repoIDs to number of milestones matching the options`
|
||||
func CountMilestonesByRepoIDs(repoIDs []int64, isClosed bool) (map[int64]int64, error) {
|
||||
return CountMilestones(
|
||||
builder.In("repo_id", repoIDs),
|
||||
isClosed,
|
||||
)
|
||||
}
|
||||
|
||||
// SearchMilestones search milestones
|
||||
func SearchMilestones(repoCond builder.Cond, page int, isClosed bool, sortType string) (MilestoneList, error) {
|
||||
miles := make([]*Milestone, 0, setting.UI.IssuePagingNum)
|
||||
sess := x.Where("is_closed = ?", isClosed)
|
||||
sess.In("repo_id", repoIDs)
|
||||
if repoCond.IsValid() {
|
||||
sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond))
|
||||
}
|
||||
if page > 0 {
|
||||
sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum)
|
||||
}
|
||||
@@ -570,25 +582,45 @@ func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType s
|
||||
return miles, sess.Find(&miles)
|
||||
}
|
||||
|
||||
// GetMilestonesByRepoIDs returns a list of milestones of given repositories and status.
|
||||
func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType string) (MilestoneList, error) {
|
||||
return SearchMilestones(
|
||||
builder.In("repo_id", repoIDs),
|
||||
page,
|
||||
isClosed,
|
||||
sortType,
|
||||
)
|
||||
}
|
||||
|
||||
// MilestonesStats represents milestone statistic information.
|
||||
type MilestonesStats struct {
|
||||
OpenCount, ClosedCount int64
|
||||
}
|
||||
|
||||
// Total returns the total counts of milestones
|
||||
func (m MilestonesStats) Total() int64 {
|
||||
return m.OpenCount + m.ClosedCount
|
||||
}
|
||||
|
||||
// GetMilestonesStats returns milestone statistic information for dashboard by given conditions.
|
||||
func GetMilestonesStats(userRepoIDs []int64) (*MilestonesStats, error) {
|
||||
func GetMilestonesStats(repoCond builder.Cond) (*MilestonesStats, error) {
|
||||
var err error
|
||||
stats := &MilestonesStats{}
|
||||
|
||||
stats.OpenCount, err = x.Where("is_closed = ?", false).
|
||||
And(builder.In("repo_id", userRepoIDs)).
|
||||
Count(new(Milestone))
|
||||
sess := x.Where("is_closed = ?", false)
|
||||
if repoCond.IsValid() {
|
||||
sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond)))
|
||||
}
|
||||
stats.OpenCount, err = sess.Count(new(Milestone))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats.ClosedCount, err = x.Where("is_closed = ?", true).
|
||||
And(builder.In("repo_id", userRepoIDs)).
|
||||
Count(new(Milestone))
|
||||
|
||||
sess = x.Where("is_closed = ?", true)
|
||||
if repoCond.IsValid() {
|
||||
sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond)))
|
||||
}
|
||||
stats.ClosedCount, err = sess.Count(new(Milestone))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"xorm.io/builder"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -370,7 +371,7 @@ func TestGetMilestonesStats(t *testing.T) {
|
||||
repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
|
||||
repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
|
||||
|
||||
milestoneStats, err := GetMilestonesStats([]int64{repo1.ID, repo2.ID})
|
||||
milestoneStats, err := GetMilestonesStats(builder.In("repo_id", []int64{repo1.ID, repo2.ID}))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, repo1.NumOpenMilestones+repo2.NumOpenMilestones, milestoneStats.OpenCount)
|
||||
assert.EqualValues(t, repo1.NumClosedMilestones+repo2.NumClosedMilestones, milestoneStats.ClosedCount)
|
||||
|
||||
@@ -64,14 +64,18 @@ func getIssueWatch(e Engine, userID, issueID int64) (iw *IssueWatch, exists bool
|
||||
return
|
||||
}
|
||||
|
||||
// GetIssueWatchersIDs returns IDs of subscribers to a given issue id
|
||||
// GetIssueWatchersIDs returns IDs of subscribers or explicit unsubscribers to a given issue id
|
||||
// but avoids joining with `user` for performance reasons
|
||||
// User permissions must be verified elsewhere if required
|
||||
func GetIssueWatchersIDs(issueID int64) ([]int64, error) {
|
||||
func GetIssueWatchersIDs(issueID int64, watching bool) ([]int64, error) {
|
||||
return getIssueWatchersIDs(x, issueID, watching)
|
||||
}
|
||||
|
||||
func getIssueWatchersIDs(e Engine, issueID int64, watching bool) ([]int64, error) {
|
||||
ids := make([]int64, 0, 64)
|
||||
return ids, x.Table("issue_watch").
|
||||
return ids, e.Table("issue_watch").
|
||||
Where("issue_id=?", issueID).
|
||||
And("is_watching = ?", true).
|
||||
And("is_watching = ?", watching).
|
||||
Select("user_id").
|
||||
Find(&ids)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"net/textproto"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/auth/ldap"
|
||||
@@ -455,10 +454,6 @@ func composeFullName(firstname, surname, username string) string {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
)
|
||||
|
||||
// LoginViaLDAP queries if login/password is valid against the LDAP directory pool,
|
||||
// and create a local user if success when enabled.
|
||||
func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*User, error) {
|
||||
@@ -503,10 +498,6 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*Use
|
||||
if len(sr.Username) == 0 {
|
||||
sr.Username = login
|
||||
}
|
||||
// Validate username make sure it satisfies requirement.
|
||||
if alphaDashDotPattern.MatchString(sr.Username) {
|
||||
return nil, fmt.Errorf("Invalid pattern for attribute 'username' [%s]: must be valid alpha or numeric or dash(-_) or dot characters", sr.Username)
|
||||
}
|
||||
|
||||
if len(sr.Mail) == 0 {
|
||||
sr.Mail = fmt.Sprintf("%s@localhost", sr.Username)
|
||||
@@ -666,7 +657,8 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
|
||||
// LoginViaPAM queries if login/password is valid against the PAM,
|
||||
// and create a local user if success when enabled.
|
||||
func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMConfig) (*User, error) {
|
||||
if err := pam.Auth(cfg.ServiceName, login, password); err != nil {
|
||||
pamLogin, err := pam.Auth(cfg.ServiceName, login, password)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "Authentication failure") {
|
||||
return nil, ErrUserNotExist{0, login, 0}
|
||||
}
|
||||
@@ -677,14 +669,21 @@ func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMCon
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// Allow PAM sources with `@` in their name, like from Active Directory
|
||||
username := pamLogin
|
||||
idx := strings.Index(pamLogin, "@")
|
||||
if idx > -1 {
|
||||
username = pamLogin[:idx]
|
||||
}
|
||||
|
||||
user = &User{
|
||||
LowerName: strings.ToLower(login),
|
||||
Name: login,
|
||||
Email: login,
|
||||
LowerName: strings.ToLower(username),
|
||||
Name: username,
|
||||
Email: pamLogin,
|
||||
Passwd: password,
|
||||
LoginType: LoginPAM,
|
||||
LoginSource: sourceID,
|
||||
LoginName: login,
|
||||
LoginName: login, // This is what the user typed in
|
||||
IsActive: true,
|
||||
}
|
||||
return user, CreateUser(user)
|
||||
|
||||
@@ -26,23 +26,38 @@ func deleteOrphanedAttachments(x *xorm.Engine) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
err := sess.BufferSize(setting.Database.IterateBufferSize).
|
||||
Where("`issue_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))").Cols("uuid").
|
||||
Iterate(new(Attachment),
|
||||
func(idx int, bean interface{}) error {
|
||||
attachment := bean.(*Attachment)
|
||||
|
||||
if err := os.RemoveAll(models.AttachmentLocalPath(attachment.UUID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := sess.ID(attachment.ID).NoAutoCondition().Delete(attachment)
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
var limit = setting.Database.IterateBufferSize
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
for {
|
||||
attachements := make([]Attachment, 0, limit)
|
||||
if err := sess.Where("`issue_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))").
|
||||
Cols("id, uuid").Limit(limit).
|
||||
Asc("id").
|
||||
Find(&attachements); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(attachements) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ids = make([]int64, 0, limit)
|
||||
for _, attachment := range attachements {
|
||||
ids = append(ids, attachment.ID)
|
||||
}
|
||||
if _, err := sess.In("id", ids).Delete(new(Attachment)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, attachment := range attachements {
|
||||
if err := os.RemoveAll(models.AttachmentLocalPath(attachment.UUID)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(attachements) < limit {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,12 @@ type Engine interface {
|
||||
Asc(colNames ...string) *xorm.Session
|
||||
}
|
||||
|
||||
const (
|
||||
// When queries are broken down in parts because of the number
|
||||
// of parameters, attempt to break by this amount
|
||||
maxQueryParameters = 300
|
||||
)
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []interface{}
|
||||
|
||||
@@ -7,6 +7,7 @@ package models
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
)
|
||||
|
||||
@@ -281,9 +282,9 @@ func (nl NotificationList) getPendingRepoIDs() []int64 {
|
||||
}
|
||||
|
||||
// LoadRepos loads repositories from database
|
||||
func (nl NotificationList) LoadRepos() (RepositoryList, error) {
|
||||
func (nl NotificationList) LoadRepos() (RepositoryList, []int, error) {
|
||||
if len(nl) == 0 {
|
||||
return RepositoryList{}, nil
|
||||
return RepositoryList{}, []int{}, nil
|
||||
}
|
||||
|
||||
var repoIDs = nl.getPendingRepoIDs()
|
||||
@@ -298,7 +299,7 @@ func (nl NotificationList) LoadRepos() (RepositoryList, error) {
|
||||
In("id", repoIDs[:limit]).
|
||||
Rows(new(Repository))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
@@ -306,7 +307,7 @@ func (nl NotificationList) LoadRepos() (RepositoryList, error) {
|
||||
err = rows.Scan(&repo)
|
||||
if err != nil {
|
||||
rows.Close()
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
repos[repo.ID] = &repo
|
||||
@@ -317,14 +318,21 @@ func (nl NotificationList) LoadRepos() (RepositoryList, error) {
|
||||
repoIDs = repoIDs[limit:]
|
||||
}
|
||||
|
||||
failed := []int{}
|
||||
|
||||
var reposList = make(RepositoryList, 0, len(repoIDs))
|
||||
for _, notification := range nl {
|
||||
for i, notification := range nl {
|
||||
if notification.Repository == nil {
|
||||
notification.Repository = repos[notification.RepoID]
|
||||
}
|
||||
if notification.Repository == nil {
|
||||
log.Error("Notification[%d]: RepoID: %d not found", notification.ID, notification.RepoID)
|
||||
failed = append(failed, i)
|
||||
continue
|
||||
}
|
||||
var found bool
|
||||
for _, r := range reposList {
|
||||
if r.ID == notification.Repository.ID {
|
||||
if r.ID == notification.RepoID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
@@ -333,7 +341,7 @@ func (nl NotificationList) LoadRepos() (RepositoryList, error) {
|
||||
reposList = append(reposList, notification.Repository)
|
||||
}
|
||||
}
|
||||
return reposList, nil
|
||||
return reposList, failed, nil
|
||||
}
|
||||
|
||||
func (nl NotificationList) getPendingIssueIDs() []int64 {
|
||||
@@ -350,9 +358,9 @@ func (nl NotificationList) getPendingIssueIDs() []int64 {
|
||||
}
|
||||
|
||||
// LoadIssues loads issues from database
|
||||
func (nl NotificationList) LoadIssues() error {
|
||||
func (nl NotificationList) LoadIssues() ([]int, error) {
|
||||
if len(nl) == 0 {
|
||||
return nil
|
||||
return []int{}, nil
|
||||
}
|
||||
|
||||
var issueIDs = nl.getPendingIssueIDs()
|
||||
@@ -367,7 +375,7 @@ func (nl NotificationList) LoadIssues() error {
|
||||
In("id", issueIDs[:limit]).
|
||||
Rows(new(Issue))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
@@ -375,7 +383,7 @@ func (nl NotificationList) LoadIssues() error {
|
||||
err = rows.Scan(&issue)
|
||||
if err != nil {
|
||||
rows.Close()
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
issues[issue.ID] = &issue
|
||||
@@ -386,13 +394,38 @@ func (nl NotificationList) LoadIssues() error {
|
||||
issueIDs = issueIDs[limit:]
|
||||
}
|
||||
|
||||
for _, notification := range nl {
|
||||
failures := []int{}
|
||||
|
||||
for i, notification := range nl {
|
||||
if notification.Issue == nil {
|
||||
notification.Issue = issues[notification.IssueID]
|
||||
if notification.Issue == nil {
|
||||
log.Error("Notification[%d]: IssueID: %d Not Found", notification.ID, notification.IssueID)
|
||||
failures = append(failures, i)
|
||||
continue
|
||||
}
|
||||
notification.Issue.Repo = notification.Repository
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return failures, nil
|
||||
}
|
||||
|
||||
// Without returns the notification list without the failures
|
||||
func (nl NotificationList) Without(failures []int) NotificationList {
|
||||
if len(failures) == 0 {
|
||||
return nl
|
||||
}
|
||||
remaining := make([]*Notification, 0, len(nl))
|
||||
last := -1
|
||||
var i int
|
||||
for _, i = range failures {
|
||||
remaining = append(remaining, nl[last+1:i]...)
|
||||
last = i
|
||||
}
|
||||
if len(nl) > i {
|
||||
remaining = append(remaining, nl[i+1:]...)
|
||||
}
|
||||
return remaining
|
||||
}
|
||||
|
||||
func (nl NotificationList) getPendingCommentIDs() []int64 {
|
||||
@@ -409,9 +442,9 @@ func (nl NotificationList) getPendingCommentIDs() []int64 {
|
||||
}
|
||||
|
||||
// LoadComments loads comments from database
|
||||
func (nl NotificationList) LoadComments() error {
|
||||
func (nl NotificationList) LoadComments() ([]int, error) {
|
||||
if len(nl) == 0 {
|
||||
return nil
|
||||
return []int{}, nil
|
||||
}
|
||||
|
||||
var commentIDs = nl.getPendingCommentIDs()
|
||||
@@ -426,7 +459,7 @@ func (nl NotificationList) LoadComments() error {
|
||||
In("id", commentIDs[:limit]).
|
||||
Rows(new(Comment))
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
@@ -434,7 +467,7 @@ func (nl NotificationList) LoadComments() error {
|
||||
err = rows.Scan(&comment)
|
||||
if err != nil {
|
||||
rows.Close()
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comments[comment.ID] = &comment
|
||||
@@ -445,13 +478,19 @@ func (nl NotificationList) LoadComments() error {
|
||||
commentIDs = commentIDs[limit:]
|
||||
}
|
||||
|
||||
for _, notification := range nl {
|
||||
failures := []int{}
|
||||
for i, notification := range nl {
|
||||
if notification.CommentID > 0 && notification.Comment == nil && comments[notification.CommentID] != nil {
|
||||
notification.Comment = comments[notification.CommentID]
|
||||
if notification.Comment == nil {
|
||||
log.Error("Notification[%d]: CommentID[%d] failed to load", notification.ID, notification.CommentID)
|
||||
failures = append(failures, i)
|
||||
continue
|
||||
}
|
||||
notification.Comment.Issue = notification.Issue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return failures, nil
|
||||
}
|
||||
|
||||
// GetNotificationCount returns the notification count for user
|
||||
|
||||
@@ -399,7 +399,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
|
||||
|
||||
// Create org.
|
||||
org := &User{
|
||||
Name: "All repo",
|
||||
Name: "All_repo",
|
||||
IsActive: true,
|
||||
Type: UserTypeOrganization,
|
||||
Visibility: structs.VisibleTypePublic,
|
||||
|
||||
@@ -180,7 +180,16 @@ func (pr *PullRequest) GetDefaultMergeMessage() string {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.MustHeadUserName(), pr.HeadRepo.Name, pr.BaseBranch)
|
||||
if err := pr.LoadIssue(); err != nil {
|
||||
log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
if pr.BaseRepoID == pr.HeadRepoID {
|
||||
return fmt.Sprintf("Merge pull request '%s' (#%d) from %s into %s", pr.Issue.Title, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Merge pull request '%s' (#%d) from %s:%s into %s", pr.Issue.Title, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch)
|
||||
}
|
||||
|
||||
// GetCommitMessages returns the commit messages between head and merge base (if there is one)
|
||||
@@ -649,44 +658,66 @@ func (pr *PullRequest) CheckUserAllowedToMerge(doer *User) (err error) {
|
||||
}
|
||||
|
||||
// SetMerged sets a pull request to merged and closes the corresponding issue
|
||||
func (pr *PullRequest) SetMerged() (err error) {
|
||||
func (pr *PullRequest) SetMerged() (bool, error) {
|
||||
if pr.HasMerged {
|
||||
return fmt.Errorf("PullRequest[%d] already merged", pr.Index)
|
||||
return false, fmt.Errorf("PullRequest[%d] already merged", pr.Index)
|
||||
}
|
||||
if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
|
||||
return fmt.Errorf("Unable to merge PullRequest[%d], some required fields are empty", pr.Index)
|
||||
return false, fmt.Errorf("Unable to merge PullRequest[%d], some required fields are empty", pr.Index)
|
||||
}
|
||||
|
||||
pr.HasMerged = true
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
if err := sess.Begin(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err = pr.loadIssue(sess); err != nil {
|
||||
return err
|
||||
if _, err := sess.Exec("UPDATE `issue` SET `repo_id` = `repo_id` WHERE `id` = ?", pr.IssueID); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err = pr.Issue.loadRepo(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = pr.Issue.Repo.getOwner(sess); err != nil {
|
||||
return err
|
||||
if _, err := sess.Exec("UPDATE `pull_request` SET `issue_id` = `issue_id` WHERE `id` = ?", pr.ID); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if _, err = pr.Issue.changeStatus(sess, pr.Merger, true); err != nil {
|
||||
return fmt.Errorf("Issue.changeStatus: %v", err)
|
||||
}
|
||||
if _, err = sess.ID(pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
|
||||
return fmt.Errorf("update pull request: %v", err)
|
||||
pr.Issue = nil
|
||||
if err := pr.loadIssue(sess); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err = sess.Commit(); err != nil {
|
||||
return fmt.Errorf("Commit: %v", err)
|
||||
if tmpPr, err := getPullRequestByID(sess, pr.ID); err != nil {
|
||||
return false, err
|
||||
} else if tmpPr.HasMerged {
|
||||
if pr.Issue.IsClosed {
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("PullRequest[%d] already merged but it's associated issue [%d] is not closed", pr.Index, pr.IssueID)
|
||||
} else if pr.Issue.IsClosed {
|
||||
return false, fmt.Errorf("PullRequest[%d] already closed", pr.Index)
|
||||
}
|
||||
return nil
|
||||
|
||||
if err := pr.Issue.loadRepo(sess); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := pr.Issue.Repo.getOwner(sess); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if _, err := pr.Issue.changeStatus(sess, pr.Merger, true); err != nil {
|
||||
return false, fmt.Errorf("Issue.changeStatus: %v", err)
|
||||
}
|
||||
|
||||
if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
|
||||
return false, fmt.Errorf("Failed to update pr[%d]: %v", pr.ID, err)
|
||||
}
|
||||
|
||||
if err := sess.Commit(); err != nil {
|
||||
return false, fmt.Errorf("Commit: %v", err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// NewPullRequest creates new pull request with labels for repository.
|
||||
@@ -845,6 +876,12 @@ func (pr *PullRequest) UpdateCols(cols ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateColsIfNotMerged updates specific fields of a pull request if it has not been merged
|
||||
func (pr *PullRequest) UpdateColsIfNotMerged(cols ...string) error {
|
||||
_, err := x.Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
|
||||
return err
|
||||
}
|
||||
|
||||
// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
|
||||
func (pr *PullRequest) IsWorkInProgress() bool {
|
||||
if err := pr.LoadIssue(); err != nil {
|
||||
|
||||
@@ -1856,6 +1856,18 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Dependencies for issues in this repository
|
||||
if _, err = sess.In("issue_id", deleteCond).
|
||||
Delete(&IssueDependency{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete dependencies for issues in other repositories
|
||||
if _, err = sess.In("dependency_id", deleteCond).
|
||||
Delete(&IssueDependency{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.In("issue_id", deleteCond).
|
||||
Delete(&IssueUser{}); err != nil {
|
||||
return err
|
||||
|
||||
@@ -202,3 +202,23 @@ func (repo *Repository) getRepoTeams(e Engine) (teams []*Team, err error) {
|
||||
func (repo *Repository) GetRepoTeams() ([]*Team, error) {
|
||||
return repo.getRepoTeams(x)
|
||||
}
|
||||
|
||||
// IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository
|
||||
func (repo *Repository) IsOwnerMemberCollaborator(userID int64) (bool, error) {
|
||||
if repo.OwnerID == userID {
|
||||
return true, nil
|
||||
}
|
||||
teamMember, err := x.Join("INNER", "team_repo", "team_repo.team_id = team_user.team_id").
|
||||
Join("INNER", "team_unit", "team_unit.team_id = team_user.team_id").
|
||||
Where("team_repo.repo_id = ?", repo.ID).
|
||||
And("team_unit.`type` = ?", UnitTypeCode).
|
||||
And("team_user.uid = ?", userID).Table("team_user").Exist(&TeamUser{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if teamMember {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return x.Get(&Collaboration{RepoID: repo.ID, UserID: userID})
|
||||
}
|
||||
|
||||
@@ -144,6 +144,10 @@ type SearchRepoOptions struct {
|
||||
TopicOnly bool
|
||||
// include description in keyword search
|
||||
IncludeDescription bool
|
||||
// None -> include has milestones AND has no milestone
|
||||
// True -> include just has milestones
|
||||
// False -> include just has no milestone
|
||||
HasMilestones util.OptionalBool
|
||||
}
|
||||
|
||||
//SearchOrderBy is used to sort the result
|
||||
@@ -171,12 +175,9 @@ const (
|
||||
SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
|
||||
)
|
||||
|
||||
// SearchRepository returns repositories based on search options,
|
||||
// SearchRepositoryCondition returns repositories based on search options,
|
||||
// it returns results in given range and number of total results.
|
||||
func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
|
||||
if opts.Page <= 0 {
|
||||
opts.Page = 1
|
||||
}
|
||||
func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
||||
var cond = builder.NewCond()
|
||||
|
||||
if opts.Private {
|
||||
@@ -276,6 +277,29 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
|
||||
cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
|
||||
}
|
||||
|
||||
switch opts.HasMilestones {
|
||||
case util.OptionalBoolTrue:
|
||||
cond = cond.And(builder.Gt{"num_milestones": 0})
|
||||
case util.OptionalBoolFalse:
|
||||
cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
// SearchRepository returns repositories based on search options,
|
||||
// it returns results in given range and number of total results.
|
||||
func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
|
||||
cond := SearchRepositoryCondition(opts)
|
||||
return SearchRepositoryByCondition(opts, cond)
|
||||
}
|
||||
|
||||
// SearchRepositoryByCondition search repositories by condition
|
||||
func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond) (RepositoryList, int64, error) {
|
||||
if opts.Page <= 0 {
|
||||
opts.Page = 1
|
||||
}
|
||||
|
||||
if len(opts.OrderBy) == 0 {
|
||||
opts.OrderBy = SearchOrderByAlphabetically
|
||||
}
|
||||
@@ -296,11 +320,11 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
|
||||
}
|
||||
|
||||
repos := make(RepositoryList, 0, opts.PageSize)
|
||||
if err = sess.
|
||||
Where(cond).
|
||||
OrderBy(opts.OrderBy.String()).
|
||||
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
|
||||
Find(&repos); err != nil {
|
||||
sess.Where(cond).OrderBy(opts.OrderBy.String())
|
||||
if opts.PageSize > 0 {
|
||||
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
|
||||
}
|
||||
if err = sess.Find(&repos); err != nil {
|
||||
return nil, 0, fmt.Errorf("Repo: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ func MainTest(m *testing.M, pathToGiteaRoot string) {
|
||||
|
||||
func createTestEngine(fixturesDir string) error {
|
||||
var err error
|
||||
x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
|
||||
x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared&_txlock=immediate")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"image/png"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -87,6 +88,9 @@ var (
|
||||
|
||||
// ErrUnsupportedLoginType login source is unknown error
|
||||
ErrUnsupportedLoginType = errors.New("Login source is unknown")
|
||||
|
||||
// Characters prohibited in a user name (anything except A-Za-z0-9_.-)
|
||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
)
|
||||
|
||||
// User represents the object of individual and member of organization.
|
||||
@@ -708,9 +712,11 @@ func (u *User) DisplayName() string {
|
||||
// GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set,
|
||||
// returns username otherwise.
|
||||
func (u *User) GetDisplayName() string {
|
||||
trimmed := strings.TrimSpace(u.FullName)
|
||||
if len(trimmed) > 0 && setting.UI.DefaultShowFullName {
|
||||
return trimmed
|
||||
if setting.UI.DefaultShowFullName {
|
||||
trimmed := strings.TrimSpace(u.FullName)
|
||||
if len(trimmed) > 0 {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
return u.Name
|
||||
}
|
||||
@@ -819,7 +825,9 @@ var (
|
||||
"issues",
|
||||
"js",
|
||||
"less",
|
||||
"manifest.json",
|
||||
"metrics",
|
||||
"milestones",
|
||||
"new",
|
||||
"notifications",
|
||||
"org",
|
||||
@@ -868,6 +876,11 @@ func isUsableName(names, patterns []string, name string) error {
|
||||
|
||||
// IsUsableUsername returns an error when a username is reserved
|
||||
func IsUsableUsername(name string) error {
|
||||
// Validate username make sure it satisfies requirement.
|
||||
if alphaDashDotPattern.MatchString(name) {
|
||||
// Note: usually this error is normally caught up earlier in the UI
|
||||
return ErrNameCharsNotAllowed{Name: name}
|
||||
}
|
||||
return isUsableName(reservedUsernames, reservedUserPatterns, name)
|
||||
}
|
||||
|
||||
@@ -987,7 +1000,7 @@ func VerifyActiveEmailCode(code, email string) *EmailAddress {
|
||||
data := com.ToStr(user.ID) + email + user.LowerName + user.Passwd + user.Rands
|
||||
|
||||
if base.VerifyTimeLimitCode(data, minutes, prefix) {
|
||||
emailAddress := &EmailAddress{Email: email}
|
||||
emailAddress := &EmailAddress{UID: user.ID, Email: email}
|
||||
if has, _ := x.Get(emailAddress); has {
|
||||
return emailAddress
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 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.
|
||||
|
||||
@@ -8,6 +9,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -54,13 +61,66 @@ func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
|
||||
if !isPrimaryFound {
|
||||
emails = append(emails, &EmailAddress{
|
||||
Email: u.Email,
|
||||
IsActivated: true,
|
||||
IsActivated: u.IsActive,
|
||||
IsPrimary: true,
|
||||
})
|
||||
}
|
||||
return emails, nil
|
||||
}
|
||||
|
||||
// GetEmailAddressByID gets a user's email address by ID
|
||||
func GetEmailAddressByID(uid, id int64) (*EmailAddress, error) {
|
||||
// User ID is required for security reasons
|
||||
email := &EmailAddress{ID: id, UID: uid}
|
||||
if has, err := x.Get(email); err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, nil
|
||||
}
|
||||
return email, nil
|
||||
}
|
||||
|
||||
func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) {
|
||||
if len(email) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Can't filter by boolean field unless it's explicit
|
||||
cond := builder.NewCond()
|
||||
cond = cond.And(builder.Eq{"email": email}, builder.Neq{"id": emailID})
|
||||
if setting.Service.RegisterEmailConfirm {
|
||||
// Inactive (unvalidated) addresses don't count as active if email validation is required
|
||||
cond = cond.And(builder.Eq{"is_activated": true})
|
||||
}
|
||||
|
||||
em := EmailAddress{}
|
||||
|
||||
if has, err := e.Where(cond).Get(&em); has || err != nil {
|
||||
if has {
|
||||
log.Info("isEmailActive('%s',%d,%d) found duplicate in email ID %d", email, userID, emailID, em.ID)
|
||||
}
|
||||
return has, err
|
||||
}
|
||||
|
||||
// Can't filter by boolean field unless it's explicit
|
||||
cond = builder.NewCond()
|
||||
cond = cond.And(builder.Eq{"email": email}, builder.Neq{"id": userID})
|
||||
if setting.Service.RegisterEmailConfirm {
|
||||
cond = cond.And(builder.Eq{"is_active": true})
|
||||
}
|
||||
|
||||
us := User{}
|
||||
|
||||
if has, err := e.Where(cond).Get(&us); has || err != nil {
|
||||
if has {
|
||||
log.Info("isEmailActive('%s',%d,%d) found duplicate in user ID %d", email, userID, emailID, us.ID)
|
||||
}
|
||||
return has, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isEmailUsed(e Engine, email string) (bool, error) {
|
||||
if len(email) == 0 {
|
||||
return true, nil
|
||||
@@ -118,31 +178,30 @@ func AddEmailAddresses(emails []*EmailAddress) error {
|
||||
|
||||
// Activate activates the email address to given user.
|
||||
func (email *EmailAddress) Activate() error {
|
||||
user, err := GetUserByID(email.UID)
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := email.updateActivation(sess, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func (email *EmailAddress) updateActivation(e Engine, activate bool) error {
|
||||
user, err := getUserByID(e, email.UID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if user.Rands, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
email.IsActivated = activate
|
||||
if _, err := e.ID(email.ID).Cols("is_activated").Update(email); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
email.IsActivated = true
|
||||
if _, err := sess.
|
||||
ID(email.ID).
|
||||
Cols("is_activated").
|
||||
Update(email); err != nil {
|
||||
return err
|
||||
} else if err = updateUserCols(sess, user, "rands"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
return updateUserCols(e, user, "rands")
|
||||
}
|
||||
|
||||
// DeleteEmailAddress deletes an email address of given user.
|
||||
@@ -201,7 +260,7 @@ func MakeEmailPrimary(email *EmailAddress) error {
|
||||
}
|
||||
|
||||
// Make sure the former primary email doesn't disappear.
|
||||
formerPrimaryEmail := &EmailAddress{Email: user.Email}
|
||||
formerPrimaryEmail := &EmailAddress{UID: user.ID, Email: user.Email}
|
||||
has, err = x.Get(formerPrimaryEmail)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -228,3 +287,199 @@ func MakeEmailPrimary(email *EmailAddress) error {
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// SearchEmailOrderBy is used to sort the results from SearchEmails()
|
||||
type SearchEmailOrderBy string
|
||||
|
||||
func (s SearchEmailOrderBy) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Strings for sorting result
|
||||
const (
|
||||
SearchEmailOrderByEmail SearchEmailOrderBy = "emails.email ASC, is_primary DESC, sortid ASC"
|
||||
SearchEmailOrderByEmailReverse SearchEmailOrderBy = "emails.email DESC, is_primary ASC, sortid DESC"
|
||||
SearchEmailOrderByName SearchEmailOrderBy = "`user`.lower_name ASC, is_primary DESC, sortid ASC"
|
||||
SearchEmailOrderByNameReverse SearchEmailOrderBy = "`user`.lower_name DESC, is_primary ASC, sortid DESC"
|
||||
)
|
||||
|
||||
// SearchEmailOptions are options to search e-mail addresses for the admin panel
|
||||
type SearchEmailOptions struct {
|
||||
Page int
|
||||
PageSize int // Can be smaller than or equal to setting.UI.ExplorePagingNum
|
||||
Keyword string
|
||||
SortType SearchEmailOrderBy
|
||||
IsPrimary util.OptionalBool
|
||||
IsActivated util.OptionalBool
|
||||
}
|
||||
|
||||
// SearchEmailResult is an e-mail address found in the user or email_address table
|
||||
type SearchEmailResult struct {
|
||||
UID int64
|
||||
Email string
|
||||
IsActivated bool
|
||||
IsPrimary bool
|
||||
// From User
|
||||
Name string
|
||||
FullName string
|
||||
}
|
||||
|
||||
// SearchEmails takes options i.e. keyword and part of email name to search,
|
||||
// it returns results in given range and number of total results.
|
||||
func SearchEmails(opts *SearchEmailOptions) ([]*SearchEmailResult, int64, error) {
|
||||
// Unfortunately, UNION support for SQLite in xorm is currently broken, so we must
|
||||
// build the SQL ourselves.
|
||||
where := make([]string, 0, 5)
|
||||
args := make([]interface{}, 0, 5)
|
||||
|
||||
emailsSQL := "(SELECT id as sortid, uid, email, is_activated, 0 as is_primary " +
|
||||
"FROM email_address " +
|
||||
"UNION ALL " +
|
||||
"SELECT id as sortid, id AS uid, email, is_active AS is_activated, 1 as is_primary " +
|
||||
"FROM `user` " +
|
||||
"WHERE type = ?) AS emails"
|
||||
args = append(args, UserTypeIndividual)
|
||||
|
||||
if len(opts.Keyword) > 0 {
|
||||
// Note: % can be injected in the Keyword parameter, but it won't do any harm.
|
||||
where = append(where, "(lower(`user`.full_name) LIKE ? OR `user`.lower_name LIKE ? OR emails.email LIKE ?)")
|
||||
likeStr := "%" + strings.ToLower(opts.Keyword) + "%"
|
||||
args = append(args, likeStr)
|
||||
args = append(args, likeStr)
|
||||
args = append(args, likeStr)
|
||||
}
|
||||
|
||||
switch {
|
||||
case opts.IsPrimary.IsTrue():
|
||||
where = append(where, "emails.is_primary = ?")
|
||||
args = append(args, true)
|
||||
case opts.IsPrimary.IsFalse():
|
||||
where = append(where, "emails.is_primary = ?")
|
||||
args = append(args, false)
|
||||
}
|
||||
|
||||
switch {
|
||||
case opts.IsActivated.IsTrue():
|
||||
where = append(where, "emails.is_activated = ?")
|
||||
args = append(args, true)
|
||||
case opts.IsActivated.IsFalse():
|
||||
where = append(where, "emails.is_activated = ?")
|
||||
args = append(args, false)
|
||||
}
|
||||
|
||||
var whereStr string
|
||||
if len(where) > 0 {
|
||||
whereStr = "WHERE " + strings.Join(where, " AND ")
|
||||
}
|
||||
|
||||
joinSQL := "FROM " + emailsSQL + " INNER JOIN `user` ON `user`.id = emails.uid " + whereStr
|
||||
|
||||
count, err := x.SQL("SELECT count(*) "+joinSQL, args...).Count()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Count: %v", err)
|
||||
}
|
||||
|
||||
orderby := opts.SortType.String()
|
||||
if orderby == "" {
|
||||
orderby = SearchEmailOrderByEmail.String()
|
||||
}
|
||||
|
||||
querySQL := "SELECT emails.uid, emails.email, emails.is_activated, emails.is_primary, " +
|
||||
"`user`.name, `user`.full_name " + joinSQL + " ORDER BY " + orderby
|
||||
|
||||
if opts.PageSize == 0 || opts.PageSize > setting.UI.ExplorePagingNum {
|
||||
opts.PageSize = setting.UI.ExplorePagingNum
|
||||
}
|
||||
if opts.Page <= 0 {
|
||||
opts.Page = 1
|
||||
}
|
||||
|
||||
rows, err := x.SQL(querySQL, args...).Rows(new(SearchEmailResult))
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Emails: %v", err)
|
||||
}
|
||||
|
||||
// Page manually because xorm can't handle Limit() with raw SQL
|
||||
defer rows.Close()
|
||||
|
||||
emails := make([]*SearchEmailResult, 0, opts.PageSize)
|
||||
skip := (opts.Page - 1) * opts.PageSize
|
||||
|
||||
for rows.Next() {
|
||||
var email SearchEmailResult
|
||||
if err := rows.Scan(&email); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if skip > 0 {
|
||||
skip--
|
||||
continue
|
||||
}
|
||||
emails = append(emails, &email)
|
||||
if len(emails) == opts.PageSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return emails, count, err
|
||||
}
|
||||
|
||||
// ActivateUserEmail will change the activated state of an email address,
|
||||
// either primary (in the user table) or secondary (in the email_address table)
|
||||
func ActivateUserEmail(userID int64, email string, primary, activate bool) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if primary {
|
||||
// Activate/deactivate a user's primary email address
|
||||
user := User{ID: userID, Email: email}
|
||||
if has, err := sess.Get(&user); err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return fmt.Errorf("no such user: %d (%s)", userID, email)
|
||||
}
|
||||
if user.IsActive == activate {
|
||||
// Already in the desired state; no action
|
||||
return nil
|
||||
}
|
||||
if activate {
|
||||
if used, err := isEmailActive(sess, email, userID, 0); err != nil {
|
||||
return fmt.Errorf("isEmailActive(): %v", err)
|
||||
} else if used {
|
||||
return ErrEmailAlreadyUsed{Email: email}
|
||||
}
|
||||
}
|
||||
user.IsActive = activate
|
||||
if user.Rands, err = GetUserSalt(); err != nil {
|
||||
return fmt.Errorf("generate salt: %v", err)
|
||||
}
|
||||
if err = updateUserCols(sess, &user, "is_active", "rands"); err != nil {
|
||||
return fmt.Errorf("updateUserCols(): %v", err)
|
||||
}
|
||||
} else {
|
||||
// Activate/deactivate a user's secondary email address
|
||||
// First check if there's another user active with the same address
|
||||
addr := EmailAddress{UID: userID, Email: email}
|
||||
if has, err := sess.Get(&addr); err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return fmt.Errorf("no such email: %d (%s)", userID, email)
|
||||
}
|
||||
if addr.IsActivated == activate {
|
||||
// Already in the desired state; no action
|
||||
return nil
|
||||
}
|
||||
if activate {
|
||||
if used, err := isEmailActive(sess, email, 0, addr.ID); err != nil {
|
||||
return fmt.Errorf("isEmailActive(): %v", err)
|
||||
} else if used {
|
||||
return ErrEmailAlreadyUsed{Email: email}
|
||||
}
|
||||
}
|
||||
if err = addr.updateActivation(sess, activate); err != nil {
|
||||
return fmt.Errorf("updateActivation(): %v", err)
|
||||
}
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ package models
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -169,3 +171,65 @@ func TestActivate(t *testing.T) {
|
||||
assert.True(t, emails[2].IsActivated)
|
||||
assert.True(t, emails[2].IsPrimary)
|
||||
}
|
||||
|
||||
func TestListEmails(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
// Must find all users and their emails
|
||||
opts := &SearchEmailOptions{}
|
||||
emails, count, err := SearchEmails(opts)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, int64(0), count)
|
||||
assert.True(t, count > 5)
|
||||
|
||||
contains := func(match func(s *SearchEmailResult) bool) bool {
|
||||
for _, v := range emails {
|
||||
if match(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 18 }))
|
||||
// 'user3' is an organization
|
||||
assert.False(t, contains(func(s *SearchEmailResult) bool { return s.UID == 3 }))
|
||||
|
||||
// Must find no records
|
||||
opts = &SearchEmailOptions{Keyword: "NOTFOUND"}
|
||||
emails, count, err = SearchEmails(opts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(0), count)
|
||||
|
||||
// Must find users 'user2', 'user28', etc.
|
||||
opts = &SearchEmailOptions{Keyword: "user2"}
|
||||
emails, count, err = SearchEmails(opts)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, int64(0), count)
|
||||
assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 2 }))
|
||||
assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 27 }))
|
||||
|
||||
// Must find only primary addresses (i.e. from the `user` table)
|
||||
opts = &SearchEmailOptions{IsPrimary: util.OptionalBoolTrue}
|
||||
emails, count, err = SearchEmails(opts)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, contains(func(s *SearchEmailResult) bool { return s.IsPrimary }))
|
||||
assert.False(t, contains(func(s *SearchEmailResult) bool { return !s.IsPrimary }))
|
||||
|
||||
// Must find only inactive addresses (i.e. not validated)
|
||||
opts = &SearchEmailOptions{IsActivated: util.OptionalBoolFalse}
|
||||
emails, count, err = SearchEmails(opts)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, contains(func(s *SearchEmailResult) bool { return !s.IsActivated }))
|
||||
assert.False(t, contains(func(s *SearchEmailResult) bool { return s.IsActivated }))
|
||||
|
||||
// Must find more than one page, but retrieve only one
|
||||
opts = &SearchEmailOptions{
|
||||
PageSize: 5,
|
||||
Page: 1,
|
||||
}
|
||||
emails, count, err = SearchEmails(opts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(emails))
|
||||
assert.True(t, count > int64(len(emails)))
|
||||
}
|
||||
|
||||
@@ -47,3 +47,13 @@ type AdminEditUserForm struct {
|
||||
func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// AdminDashboardForm form for admin dashboard operations
|
||||
type AdminDashboardForm struct {
|
||||
Op int `binding:"required"`
|
||||
}
|
||||
|
||||
// Validate validates form fields
|
||||
func (f *AdminDashboardForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// Auth pam auth service
|
||||
func Auth(serviceName, userName, passwd string) error {
|
||||
func Auth(serviceName, userName, passwd string) (string, error) {
|
||||
t, err := pam.StartFunc(serviceName, userName, func(s pam.Style, msg string) (string, error) {
|
||||
switch s {
|
||||
case pam.PromptEchoOff:
|
||||
@@ -25,12 +25,14 @@ func Auth(serviceName, userName, passwd string) error {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = t.Authenticate(0); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
return nil
|
||||
// PAM login names might suffer transformations in the PAM stack.
|
||||
// We should take whatever the PAM stack returns for it.
|
||||
return t.GetItem(pam.User)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@ import (
|
||||
)
|
||||
|
||||
// Auth not supported lack of pam tag
|
||||
func Auth(serviceName, userName, passwd string) error {
|
||||
return errors.New("PAM not supported")
|
||||
func Auth(serviceName, userName, passwd string) (string, error) {
|
||||
return "", errors.New("PAM not supported")
|
||||
}
|
||||
|
||||
@@ -30,8 +30,17 @@ func ToEmail(email *models.EmailAddress) *api.Email {
|
||||
}
|
||||
|
||||
// ToBranch convert a git.Commit and git.Branch to an api.Branch
|
||||
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User) *api.Branch {
|
||||
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User) (*api.Branch, error) {
|
||||
if bp == nil {
|
||||
var hasPerm bool
|
||||
var err error
|
||||
if user != nil {
|
||||
hasPerm, err = models.HasAccessUnit(user, repo, models.UnitTypeCode, models.AccessModeWrite)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &api.Branch{
|
||||
Name: b.Name,
|
||||
Commit: ToCommit(repo, c),
|
||||
@@ -39,20 +48,25 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.
|
||||
RequiredApprovals: 0,
|
||||
EnableStatusCheck: false,
|
||||
StatusCheckContexts: []string{},
|
||||
UserCanPush: true,
|
||||
UserCanMerge: true,
|
||||
}
|
||||
UserCanPush: hasPerm,
|
||||
UserCanMerge: hasPerm,
|
||||
}, nil
|
||||
}
|
||||
return &api.Branch{
|
||||
|
||||
branch := &api.Branch{
|
||||
Name: b.Name,
|
||||
Commit: ToCommit(repo, c),
|
||||
Protected: true,
|
||||
RequiredApprovals: bp.RequiredApprovals,
|
||||
EnableStatusCheck: bp.EnableStatusCheck,
|
||||
StatusCheckContexts: bp.StatusCheckContexts,
|
||||
UserCanPush: bp.CanUserPush(user.ID),
|
||||
UserCanMerge: bp.CanUserMerge(user.ID),
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
branch.UserCanPush = bp.CanUserPush(user.ID)
|
||||
branch.UserCanMerge = bp.IsUserMergeWhitelisted(user.ID)
|
||||
}
|
||||
return branch, nil
|
||||
}
|
||||
|
||||
// ToTag convert a git.Tag to an api.Tag
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
// Blob represents a Git object.
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
|
||||
// Commit represents a git commit.
|
||||
|
||||
@@ -6,9 +6,9 @@ package git
|
||||
|
||||
import (
|
||||
"github.com/emirpasic/gods/trees/binaryheap"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
cgobject "gopkg.in/src-d/go-git.v4/plumbing/object/commitgraph"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
|
||||
)
|
||||
|
||||
// GetCommitsInfo gets information of all commits that are corresponding to these entries
|
||||
|
||||
@@ -7,7 +7,7 @@ package git
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
|
||||
// NotesRef is the git ref where Gitea will look for git-notes data.
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
|
||||
// ParseTreeEntries parses the output of a `git ls-tree` command.
|
||||
|
||||
@@ -7,9 +7,9 @@ package git
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
)
|
||||
|
||||
func TestParseTreeEntries(t *testing.T) {
|
||||
|
||||
@@ -18,11 +18,11 @@ import (
|
||||
"time"
|
||||
|
||||
gitealog "code.gitea.io/gitea/modules/log"
|
||||
"github.com/go-git/go-billy/v5/osfs"
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/cache"
|
||||
"github.com/go-git/go-git/v5/storage/filesystem"
|
||||
"github.com/unknwon/com"
|
||||
"gopkg.in/src-d/go-billy.v4/osfs"
|
||||
gogit "gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/cache"
|
||||
"gopkg.in/src-d/go-git.v4/storage/filesystem"
|
||||
)
|
||||
|
||||
// Repository represents a Git repository.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
func (repo *Repository) getBlob(id SHA1) (*Blob, error) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
// BranchPrefix base dir of the branch information file store on git
|
||||
|
||||
@@ -12,15 +12,20 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/mcuadros/go-version"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
)
|
||||
|
||||
// GetRefCommitID returns the last commit ID string of given reference (branch or tag).
|
||||
func (repo *Repository) GetRefCommitID(name string) (string, error) {
|
||||
ref, err := repo.gogitRepo.Reference(plumbing.ReferenceName(name), true)
|
||||
if err != nil {
|
||||
if err == plumbing.ErrReferenceNotFound {
|
||||
return "", ErrNotExist{
|
||||
ID: name,
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -89,9 +94,15 @@ func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
|
||||
gogitCommit, err := repo.gogitRepo.CommitObject(id)
|
||||
if err == plumbing.ErrObjectNotFound {
|
||||
tagObject, err = repo.gogitRepo.TagObject(id)
|
||||
if err == plumbing.ErrObjectNotFound {
|
||||
return nil, ErrNotExist{
|
||||
ID: id.String(),
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
gogitCommit, err = repo.gogitRepo.CommitObject(tagObject.Target)
|
||||
}
|
||||
// if we get a plumbing.ErrObjectNotFound here then the repository is broken and it should be 500
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -318,7 +329,7 @@ func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List
|
||||
var stdout []byte
|
||||
var err error
|
||||
if before == nil {
|
||||
stdout, err = NewCommand("rev-list", before.ID.String()).RunInDirBytes(repo.Path)
|
||||
stdout, err = NewCommand("rev-list", last.ID.String()).RunInDirBytes(repo.Path)
|
||||
} else {
|
||||
stdout, err = NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
|
||||
gitealog "code.gitea.io/gitea/modules/log"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/format/commitgraph"
|
||||
cgobject "gopkg.in/src-d/go-git.v4/plumbing/object/commitgraph"
|
||||
"github.com/go-git/go-git/v5/plumbing/format/commitgraph"
|
||||
cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
|
||||
)
|
||||
|
||||
// CommitNodeIndex returns the index for walking commit graph
|
||||
|
||||
@@ -7,8 +7,8 @@ package git
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
// GetRefs returns all references of the repository.
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/mcuadros/go-version"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
)
|
||||
|
||||
// TagPrefix tags prefix path on the repository
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
// EmptySHA defines empty git SHA
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
|
||||
// Signature represents the Author or Committer information.
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
|
||||
// Tree represents a flat directory listing.
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
|
||||
// GetTreeEntryByPath get the tree entries according the sub dir
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
|
||||
// EntryMode the type of the object in the git tree
|
||||
|
||||
@@ -7,9 +7,9 @@ package git
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
)
|
||||
|
||||
func getTestEntries() Entries {
|
||||
|
||||
@@ -143,25 +143,23 @@ func InitIssueIndexer(syncReindex bool) {
|
||||
var populate bool
|
||||
switch setting.Indexer.IssueType {
|
||||
case "bleve":
|
||||
graceful.GetManager().RunWithShutdownFns(func(_, atTerminate func(context.Context, func())) {
|
||||
issueIndexer := NewBleveIndexer(setting.Indexer.IssuePath)
|
||||
exist, err := issueIndexer.Init()
|
||||
if err != nil {
|
||||
holder.cancel()
|
||||
log.Fatal("Unable to initialize Bleve Issue Indexer: %v", err)
|
||||
issueIndexer := NewBleveIndexer(setting.Indexer.IssuePath)
|
||||
exist, err := issueIndexer.Init()
|
||||
if err != nil {
|
||||
holder.cancel()
|
||||
log.Fatal("Unable to initialize Bleve Issue Indexer: %v", err)
|
||||
}
|
||||
populate = !exist
|
||||
holder.set(issueIndexer)
|
||||
graceful.GetManager().RunAtTerminate(context.Background(), func() {
|
||||
log.Debug("Closing issue indexer")
|
||||
issueIndexer := holder.get()
|
||||
if issueIndexer != nil {
|
||||
issueIndexer.Close()
|
||||
}
|
||||
populate = !exist
|
||||
holder.set(issueIndexer)
|
||||
atTerminate(context.Background(), func() {
|
||||
log.Debug("Closing issue indexer")
|
||||
issueIndexer := holder.get()
|
||||
if issueIndexer != nil {
|
||||
issueIndexer.Close()
|
||||
}
|
||||
log.Info("PID: %d Issue Indexer closed", os.Getpid())
|
||||
})
|
||||
log.Debug("Created Bleve Indexer")
|
||||
log.Info("PID: %d Issue Indexer closed", os.Getpid())
|
||||
})
|
||||
log.Debug("Created Bleve Indexer")
|
||||
case "db":
|
||||
issueIndexer := &DBIndexer{}
|
||||
holder.set(issueIndexer)
|
||||
|
||||
@@ -108,7 +108,7 @@ func (s *linkifyParser) Parse(parent ast.Node, block text.Reader, pc parser.Cont
|
||||
}
|
||||
at := bytes.IndexByte(line, '@')
|
||||
m = []int{0, stop, at, stop - 1}
|
||||
if m == nil || bytes.IndexByte(line[m[2]:m[3]], '.') < 0 {
|
||||
if bytes.IndexByte(line[m[2]:m[3]], '.') < 0 {
|
||||
return nil
|
||||
}
|
||||
lastChar := line[m[1]-1]
|
||||
|
||||
@@ -52,7 +52,6 @@ func (g *GiteaASTTransformer) Transform(node *ast.Document, reader text.Reader,
|
||||
|
||||
lnk := string(link)
|
||||
lnk = giteautil.URLJoin(prefix, lnk)
|
||||
lnk = strings.Replace(lnk, " ", "+", -1)
|
||||
link = []byte(lnk)
|
||||
}
|
||||
v.Destination = link
|
||||
|
||||
@@ -81,7 +81,6 @@ func RenderWiki(filename string, rawBytes []byte, urlPrefix string, metas map[st
|
||||
}
|
||||
|
||||
func render(parser Parser, rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte {
|
||||
urlPrefix = strings.Replace(urlPrefix, " ", "+", -1)
|
||||
result := parser.Render(rawBytes, urlPrefix, metas, isWiki)
|
||||
// TODO: one day the error should be returned.
|
||||
result, err := PostProcess(result, urlPrefix, metas, isWiki)
|
||||
|
||||
@@ -689,12 +689,12 @@ func (m *webhookNotifier) NotifyDeleteRef(pusher *models.User, repo *models.Repo
|
||||
|
||||
if err := webhook_module.PrepareWebhooks(repo, models.HookEventDelete, &api.DeletePayload{
|
||||
Ref: refName,
|
||||
RefType: "branch",
|
||||
RefType: refType,
|
||||
PusherType: api.PusherTypeUser,
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks.(delete branch): %v", err)
|
||||
log.Error("PrepareWebhooks.(delete %s): %v", refType, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,13 @@ func TestChannelQueue(t *testing.T) {
|
||||
|
||||
queue, err := NewChannelQueue(handle,
|
||||
ChannelQueueConfiguration{
|
||||
QueueLength: 20,
|
||||
Workers: 1,
|
||||
QueueLength: 0,
|
||||
MaxWorkers: 10,
|
||||
BlockTimeout: 1 * time.Second,
|
||||
BoostTimeout: 5 * time.Minute,
|
||||
BoostWorkers: 5,
|
||||
Workers: 0,
|
||||
Name: "TestChannelQueue",
|
||||
}, &testData{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
||||
@@ -96,8 +96,8 @@ func (p *WorkerPool) pushBoost(data Data) {
|
||||
p.blockTimeout /= 2
|
||||
p.lock.Unlock()
|
||||
}()
|
||||
p.addWorkers(ctx, boost)
|
||||
p.lock.Unlock()
|
||||
p.addWorkers(ctx, boost)
|
||||
p.dataChan <- data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,10 +242,30 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models
|
||||
func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error {
|
||||
// Because calls hooks we need to pass in the environment
|
||||
env := models.PushingEnvironment(doer, t.repo)
|
||||
stdout := &strings.Builder{}
|
||||
stderr := &strings.Builder{}
|
||||
|
||||
if _, err := git.NewCommand("push", t.repo.RepoPath(), strings.TrimSpace(commitHash)+":refs/heads/"+strings.TrimSpace(branch)).RunInDirWithEnv(t.basePath, env); err != nil {
|
||||
log.Error("Unable to push back to repo from temporary repo: %s (%s) Error: %v",
|
||||
t.repo.FullName(), t.basePath, err)
|
||||
if err := git.NewCommand("push", t.repo.RepoPath(), strings.TrimSpace(commitHash)+":refs/heads/"+strings.TrimSpace(branch)).RunInDirTimeoutEnvPipeline(env, -1, t.basePath, stdout, stderr); err != nil {
|
||||
errString := stderr.String()
|
||||
if strings.Contains(errString, "non-fast-forward") {
|
||||
return models.ErrMergePushOutOfDate{
|
||||
StdOut: stdout.String(),
|
||||
StdErr: errString,
|
||||
Err: err,
|
||||
}
|
||||
} else if strings.Contains(errString, "! [remote rejected]") {
|
||||
log.Error("Unable to push back to repo from temporary repo due to rejection: %s (%s)\nStdout: %s\nStderr: %s\nError: %v",
|
||||
t.repo.FullName(), t.basePath, stdout, errString, err)
|
||||
err := models.ErrPushRejected{
|
||||
StdOut: stdout.String(),
|
||||
StdErr: errString,
|
||||
Err: err,
|
||||
}
|
||||
err.GenerateMessage()
|
||||
return err
|
||||
}
|
||||
log.Error("Unable to push back to repo from temporary repo: %s (%s)\nStdout: %s\nError: %v",
|
||||
t.repo.FullName(), t.basePath, stdout, err)
|
||||
return fmt.Errorf("Unable to push back to repo from temporary repo: %s (%s) Error: %v",
|
||||
t.repo.FullName(), t.basePath, err)
|
||||
}
|
||||
|
||||
@@ -210,6 +210,7 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
|
||||
|
||||
encoding := "UTF-8"
|
||||
bom := false
|
||||
executable := false
|
||||
|
||||
if !opts.IsNewFile {
|
||||
fromEntry, err := commit.GetTreeEntryByPath(fromTreePath)
|
||||
@@ -245,6 +246,7 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
|
||||
return nil, models.ErrSHAOrCommitIDNotProvided{}
|
||||
}
|
||||
encoding, bom = detectEncodingAndBOM(fromEntry, repo)
|
||||
executable = fromEntry.IsExecutable()
|
||||
}
|
||||
|
||||
// For the path where this file will be created/updated, we need to make
|
||||
@@ -368,8 +370,14 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
|
||||
}
|
||||
|
||||
// Add the object to the index
|
||||
if err := t.AddObjectToIndex("100644", objectHash, treePath); err != nil {
|
||||
return nil, err
|
||||
if executable {
|
||||
if err := t.AddObjectToIndex("100755", objectHash, treePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := t.AddObjectToIndex("100644", objectHash, treePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Now write the tree
|
||||
|
||||
@@ -124,7 +124,7 @@ func DBConnStr() (string, error) {
|
||||
if err := os.MkdirAll(path.Dir(Database.Path), os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("Failed to create directories: %v", err)
|
||||
}
|
||||
connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d", Database.Path, Database.Timeout)
|
||||
connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate", Database.Path, Database.Timeout)
|
||||
default:
|
||||
return "", fmt.Errorf("Unknown database type: %s", Database.Type)
|
||||
}
|
||||
|
||||
@@ -374,6 +374,7 @@ user_bio = Biography
|
||||
|
||||
form.name_reserved = The username '%s' is reserved.
|
||||
form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username.
|
||||
form.name_chars_not_allowed = User name '%s' contains invalid characters.
|
||||
|
||||
[settings]
|
||||
profile = Profile
|
||||
@@ -434,7 +435,11 @@ manage_openid = Manage OpenID Addresses
|
||||
email_desc = Your primary email address will be used for notifications and other operations.
|
||||
theme_desc = This will be your default theme across the site.
|
||||
primary = Primary
|
||||
activated = Activated
|
||||
requires_activation = Requires activation
|
||||
primary_email = Make Primary
|
||||
activate_email = Send Activation
|
||||
activations_pending = Activations Pending
|
||||
delete_email = Remove
|
||||
email_deletion = Remove Email Address
|
||||
email_deletion_desc = The email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue?
|
||||
@@ -775,6 +780,8 @@ editor.commit_empty_file_header = Commit an empty file
|
||||
editor.commit_empty_file_text = The file you're about commit is empty. Proceed?
|
||||
editor.no_changes_to_show = There are no changes to show.
|
||||
editor.fail_to_update_file = Failed to update/create file '%s' with error: %v
|
||||
editor.push_rejected_no_message = The change was rejected by the server without a message. Please check githooks.
|
||||
editor.push_rejected = The change was rejected by the server with the following message:<br>%s<br> Please check githooks.
|
||||
editor.add_subdir = Add a directory…
|
||||
editor.unable_to_upload_files = Failed to upload files to '%s' with error: %v
|
||||
editor.upload_file_is_locked = File '%s' is locked by %s.
|
||||
@@ -794,6 +801,8 @@ commits.date = Date
|
||||
commits.older = Older
|
||||
commits.newer = Newer
|
||||
commits.signed_by = Signed by
|
||||
commits.signed_by_untrusted_user = Signed by untrusted user
|
||||
commits.signed_by_untrusted_user_unmatched = Signed by untrusted user who does not match committer
|
||||
commits.gpg_key_id = GPG Key ID
|
||||
|
||||
ext_issues = Ext. Issues
|
||||
@@ -1074,6 +1083,8 @@ pulls.merge_conflict = Merge Failed: There was a conflict whilst merging: %[1]s<
|
||||
pulls.rebase_conflict = Merge Failed: There was a conflict whilst rebasing commit: %[1]s<br>%[2]s<br>%[3]s<br>Hint:Try a different strategy
|
||||
pulls.unrelated_histories = Merge Failed: The merge head and base do not share a common history. Hint: Try a different strategy
|
||||
pulls.merge_out_of_date = Merge Failed: Whilst generating the merge, the base was updated. Hint: Try again.
|
||||
pulls.push_rejected = Merge Failed: The push was rejected with the following message:<br>%s<br>Review the githooks for this repository
|
||||
pulls.push_rejected_no_message = Merge Failed: The push was rejected but there was no remote message.<br>Review the githooks for this repository
|
||||
pulls.open_unmerged_pull_exists = `You cannot perform a reopen operation because there is a pending pull request (#%d) with identical properties.`
|
||||
pulls.status_checking = Some checks are pending
|
||||
pulls.status_checks_success = All checks were successful
|
||||
@@ -1681,6 +1692,7 @@ organizations = Organizations
|
||||
repositories = Repositories
|
||||
hooks = Default Webhooks
|
||||
authentication = Authentication Sources
|
||||
emails = User Emails
|
||||
config = Configuration
|
||||
notices = System Notices
|
||||
monitor = Monitoring
|
||||
@@ -1750,6 +1762,7 @@ dashboard.gc_times = GC Times
|
||||
users.user_manage_panel = User Account Management
|
||||
users.new_account = Create User Account
|
||||
users.name = Username
|
||||
users.full_name = Full Name
|
||||
users.activated = Activated
|
||||
users.admin = Admin
|
||||
users.restricted = Restricted
|
||||
@@ -1781,6 +1794,19 @@ users.still_own_repo = This user still owns one or more repositories. Delete or
|
||||
users.still_has_org = This user is a member of an organization. Remove the user from any organizations first.
|
||||
users.deletion_success = The user account has been deleted.
|
||||
|
||||
emails.email_manage_panel = User Email Management
|
||||
emails.primary = Primary
|
||||
emails.activated = Activated
|
||||
emails.filter_sort.email = Email
|
||||
emails.filter_sort.email_reverse = Email (reverse)
|
||||
emails.filter_sort.name = User Name
|
||||
emails.filter_sort.name_reverse = User Name (reverse)
|
||||
emails.updated = Email updated
|
||||
emails.not_updated = Failed to update the requested email address: %v
|
||||
emails.duplicate_active = This email address is already active for a different user.
|
||||
emails.change_email_header = Update Email Properties
|
||||
emails.change_email_text = Are your sure you want to update this email address?
|
||||
|
||||
orgs.org_manage_panel = Organization Management
|
||||
orgs.name = Name
|
||||
orgs.teams = Teams
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/cron"
|
||||
@@ -30,7 +31,6 @@ import (
|
||||
|
||||
"gitea.com/macaron/macaron"
|
||||
"gitea.com/macaron/session"
|
||||
"github.com/unknwon/com"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -144,14 +144,28 @@ func Dashboard(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("admin.dashboard")
|
||||
ctx.Data["PageIsAdmin"] = true
|
||||
ctx.Data["PageIsAdminDashboard"] = true
|
||||
ctx.Data["Stats"] = models.GetStatistic()
|
||||
// FIXME: update periodically
|
||||
updateSystemStatus()
|
||||
ctx.Data["SysStatus"] = sysStatus
|
||||
ctx.HTML(200, tplDashboard)
|
||||
}
|
||||
|
||||
// DashboardPost run an admin operation
|
||||
func DashboardPost(ctx *context.Context, form auth.AdminDashboardForm) {
|
||||
ctx.Data["Title"] = ctx.Tr("admin.dashboard")
|
||||
ctx.Data["PageIsAdmin"] = true
|
||||
ctx.Data["PageIsAdminDashboard"] = true
|
||||
ctx.Data["Stats"] = models.GetStatistic()
|
||||
updateSystemStatus()
|
||||
ctx.Data["SysStatus"] = sysStatus
|
||||
|
||||
// Run operation.
|
||||
op, _ := com.StrTo(ctx.Query("op")).Int()
|
||||
if op > 0 {
|
||||
if form.Op > 0 {
|
||||
var err error
|
||||
var success string
|
||||
|
||||
switch Operation(op) {
|
||||
switch Operation(form.Op) {
|
||||
case cleanInactivateUser:
|
||||
success = ctx.Tr("admin.dashboard.delete_inactivate_accounts_success")
|
||||
err = models.DeleteInactivateUsers()
|
||||
@@ -189,15 +203,9 @@ func Dashboard(ctx *context.Context) {
|
||||
} else {
|
||||
ctx.Flash.Success(success)
|
||||
}
|
||||
ctx.Redirect(setting.AppSubURL + "/admin")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Stats"] = models.GetStatistic()
|
||||
// FIXME: update periodically
|
||||
updateSystemStatus()
|
||||
ctx.Data["SysStatus"] = sysStatus
|
||||
ctx.HTML(200, tplDashboard)
|
||||
ctx.Redirect(setting.AppSubURL + "/admin")
|
||||
}
|
||||
|
||||
// SendTestMail send test mail to confirm mail service is OK
|
||||
|
||||
155
routers/admin/emails.go
Normal file
155
routers/admin/emails.go
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2020 The Gitea Authors.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/url"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
)
|
||||
|
||||
const (
|
||||
tplEmails base.TplName = "admin/emails/list"
|
||||
)
|
||||
|
||||
// Emails show all emails
|
||||
func Emails(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("admin.emails")
|
||||
ctx.Data["PageIsAdmin"] = true
|
||||
ctx.Data["PageIsAdminEmails"] = true
|
||||
|
||||
opts := &models.SearchEmailOptions{
|
||||
PageSize: setting.UI.Admin.UserPagingNum,
|
||||
Page: ctx.QueryInt("page"),
|
||||
}
|
||||
|
||||
if opts.Page <= 1 {
|
||||
opts.Page = 1
|
||||
}
|
||||
|
||||
type ActiveEmail struct {
|
||||
models.SearchEmailResult
|
||||
CanChange bool
|
||||
}
|
||||
|
||||
var (
|
||||
baseEmails []*models.SearchEmailResult
|
||||
emails []ActiveEmail
|
||||
count int64
|
||||
err error
|
||||
orderBy models.SearchEmailOrderBy
|
||||
)
|
||||
|
||||
ctx.Data["SortType"] = ctx.Query("sort")
|
||||
switch ctx.Query("sort") {
|
||||
case "email":
|
||||
orderBy = models.SearchEmailOrderByEmail
|
||||
case "reverseemail":
|
||||
orderBy = models.SearchEmailOrderByEmailReverse
|
||||
case "username":
|
||||
orderBy = models.SearchEmailOrderByName
|
||||
case "reverseusername":
|
||||
orderBy = models.SearchEmailOrderByNameReverse
|
||||
default:
|
||||
ctx.Data["SortType"] = "email"
|
||||
orderBy = models.SearchEmailOrderByEmail
|
||||
}
|
||||
|
||||
opts.Keyword = ctx.QueryTrim("q")
|
||||
opts.SortType = orderBy
|
||||
if len(ctx.Query("is_activated")) != 0 {
|
||||
opts.IsActivated = util.OptionalBoolOf(ctx.QueryBool("activated"))
|
||||
}
|
||||
if len(ctx.Query("is_primary")) != 0 {
|
||||
opts.IsPrimary = util.OptionalBoolOf(ctx.QueryBool("primary"))
|
||||
}
|
||||
|
||||
if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) {
|
||||
baseEmails, count, err = models.SearchEmails(opts)
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchEmails", err)
|
||||
return
|
||||
}
|
||||
emails = make([]ActiveEmail, len(baseEmails))
|
||||
for i := range baseEmails {
|
||||
emails[i].SearchEmailResult = *baseEmails[i]
|
||||
// Don't let the admin deactivate its own primary email address
|
||||
// We already know the user is admin
|
||||
emails[i].CanChange = ctx.User.ID != emails[i].UID || !emails[i].IsPrimary
|
||||
}
|
||||
}
|
||||
ctx.Data["Keyword"] = opts.Keyword
|
||||
ctx.Data["Total"] = count
|
||||
ctx.Data["Emails"] = emails
|
||||
|
||||
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
|
||||
pager.SetDefaultParams(ctx)
|
||||
ctx.Data["Page"] = pager
|
||||
|
||||
ctx.HTML(200, tplEmails)
|
||||
}
|
||||
|
||||
var (
|
||||
nullByte = []byte{0x00}
|
||||
)
|
||||
|
||||
func isKeywordValid(keyword string) bool {
|
||||
return !bytes.Contains([]byte(keyword), nullByte)
|
||||
}
|
||||
|
||||
// ActivateEmail serves a POST request for activating/deactivating a user's email
|
||||
func ActivateEmail(ctx *context.Context) {
|
||||
|
||||
truefalse := map[string]bool{"1": true, "0": false}
|
||||
|
||||
uid := com.StrTo(ctx.Query("uid")).MustInt64()
|
||||
email := ctx.Query("email")
|
||||
primary, okp := truefalse[ctx.Query("primary")]
|
||||
activate, oka := truefalse[ctx.Query("activate")]
|
||||
|
||||
if uid == 0 || len(email) == 0 || !okp || !oka {
|
||||
ctx.Error(400)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("Changing activation for User ID: %d, email: %s, primary: %v to %v", uid, email, primary, activate)
|
||||
|
||||
if err := models.ActivateUserEmail(uid, email, primary, activate); err != nil {
|
||||
log.Error("ActivateUserEmail(%v,%v,%v,%v): %v", uid, email, primary, activate, err)
|
||||
if models.IsErrEmailAlreadyUsed(err) {
|
||||
ctx.Flash.Error(ctx.Tr("admin.emails.duplicate_active"))
|
||||
} else {
|
||||
ctx.Flash.Error(ctx.Tr("admin.emails.not_updated", err))
|
||||
}
|
||||
} else {
|
||||
log.Info("Activation for User ID: %d, email: %s, primary: %v changed to %v", uid, email, primary, activate)
|
||||
ctx.Flash.Info(ctx.Tr("admin.emails.updated"))
|
||||
}
|
||||
|
||||
redirect, _ := url.Parse(setting.AppSubURL + "/admin/emails")
|
||||
q := url.Values{}
|
||||
if val := ctx.QueryTrim("q"); len(val) > 0 {
|
||||
q.Set("q", val)
|
||||
}
|
||||
if val := ctx.QueryTrim("sort"); len(val) > 0 {
|
||||
q.Set("sort", val)
|
||||
}
|
||||
if val := ctx.QueryTrim("is_primary"); len(val) > 0 {
|
||||
q.Set("is_primary", val)
|
||||
}
|
||||
if val := ctx.QueryTrim("is_activated"); len(val) > 0 {
|
||||
q.Set("is_activated", val)
|
||||
}
|
||||
redirect.RawQuery = q.Encode()
|
||||
ctx.Redirect(redirect.String())
|
||||
}
|
||||
@@ -121,6 +121,9 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
|
||||
case models.IsErrNamePatternNotAllowed(err):
|
||||
ctx.Data["Err_UserName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplUserNew, &form)
|
||||
case models.IsErrNameCharsNotAllowed(err):
|
||||
ctx.Data["Err_UserName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(models.ErrNameCharsNotAllowed).Name), tplUserNew, &form)
|
||||
default:
|
||||
ctx.ServerError("CreateUser", err)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
if err := models.CreateOrganization(org, u); err != nil {
|
||||
if models.IsErrUserAlreadyExist(err) ||
|
||||
models.IsErrNameReserved(err) ||
|
||||
models.IsErrNameCharsNotAllowed(err) ||
|
||||
models.IsErrNamePatternNotAllowed(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
} else {
|
||||
|
||||
@@ -90,6 +90,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
|
||||
if models.IsErrUserAlreadyExist(err) ||
|
||||
models.IsErrEmailAlreadyUsed(err) ||
|
||||
models.IsErrNameReserved(err) ||
|
||||
models.IsErrNameCharsNotAllowed(err) ||
|
||||
models.IsErrNamePatternNotAllowed(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
} else {
|
||||
|
||||
@@ -112,6 +112,7 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
if err := models.CreateOrganization(org, ctx.User); err != nil {
|
||||
if models.IsErrUserAlreadyExist(err) ||
|
||||
models.IsErrNameReserved(err) ||
|
||||
models.IsErrNameCharsNotAllowed(err) ||
|
||||
models.IsErrNamePatternNotAllowed(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
} else {
|
||||
|
||||
@@ -70,7 +70,13 @@ func GetBranch(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToBranch(ctx.Repo.Repository, branch, c, branchProtection, ctx.User))
|
||||
br, err := convert.ToBranch(ctx.Repo.Repository, branch, c, branchProtection, ctx.User)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, br)
|
||||
}
|
||||
|
||||
// ListBranches list all the branches of a repository
|
||||
@@ -113,7 +119,14 @@ func ListBranches(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
|
||||
return
|
||||
}
|
||||
apiBranches[i] = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User)
|
||||
|
||||
br, err := convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||
return
|
||||
}
|
||||
|
||||
apiBranches[i] = br
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, &apiBranches)
|
||||
|
||||
@@ -102,24 +102,8 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
issue, labels, err := prepareForReplaceOrAdd(ctx, form)
|
||||
if err != nil {
|
||||
if models.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetLabelsInRepoByIDs", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -204,7 +188,7 @@ func DeleteIssueLabel(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := models.DeleteIssueLabel(issue, label, ctx.User); err != nil {
|
||||
if err := issue_service.RemoveLabel(issue, ctx.User, label); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteIssueLabel", err)
|
||||
return
|
||||
}
|
||||
@@ -248,28 +232,12 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
|
||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
issue, labels, err := prepareForReplaceOrAdd(ctx, form)
|
||||
if err != nil {
|
||||
if models.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetLabelsInRepoByIDs", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := issue.ReplaceLabels(labels, ctx.User); err != nil {
|
||||
if err := issue_service.ReplaceLabels(issue, ctx.User, labels); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "ReplaceLabels", err)
|
||||
return
|
||||
}
|
||||
@@ -339,3 +307,28 @@ func ClearIssueLabels(ctx *context.APIContext) {
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) (issue *models.Issue, labels []*models.Label, err error) {
|
||||
issue, err = models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
if models.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
labels, err = models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetLabelsInRepoByIDs", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
@@ -172,19 +173,21 @@ func prepareIssueStopwatch(ctx *context.APIContext, shouldExist bool) (*models.I
|
||||
|
||||
if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return nil, err
|
||||
return nil, errors.New("Unable to write to PRs")
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return nil, err
|
||||
return nil, errors.New("Cannot use time tracker")
|
||||
}
|
||||
|
||||
if models.StopwatchExists(ctx.User.ID, issue.ID) != shouldExist {
|
||||
if shouldExist {
|
||||
ctx.Error(http.StatusConflict, "StopwatchExists", "cannot stop/cancel a non existent stopwatch")
|
||||
err = errors.New("cannot stop/cancel a non existent stopwatch")
|
||||
} else {
|
||||
ctx.Error(http.StatusConflict, "StopwatchExists", "cannot start a stopwatch again if it already exists")
|
||||
err = errors.New("cannot start a stopwatch again if it already exists")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
issue_service "code.gitea.io/gitea/services/issue"
|
||||
@@ -316,8 +315,6 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
return
|
||||
}
|
||||
|
||||
notification.NotifyNewPullRequest(pr)
|
||||
|
||||
log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
|
||||
ctx.JSON(http.StatusCreated, pr.APIFormat())
|
||||
}
|
||||
@@ -648,6 +645,14 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
|
||||
} else if models.IsErrMergePushOutOfDate(err) {
|
||||
ctx.Error(http.StatusConflict, "Merge", "merge push out of date")
|
||||
return
|
||||
} else if models.IsErrPushRejected(err) {
|
||||
errPushRej := err.(models.ErrPushRejected)
|
||||
if len(errPushRej.Message) == 0 {
|
||||
ctx.Error(http.StatusConflict, "Merge", "PushRejected without remote error message")
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusConflict, "Merge", "PushRejected with remote message: "+errPushRej.Message)
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "Merge", err)
|
||||
return
|
||||
|
||||
@@ -511,6 +511,8 @@ func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteA
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
|
||||
case models.IsErrNameReserved(err):
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
|
||||
case models.IsErrNameCharsNotAllowed(err):
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' contains invalid characters.", err.(models.ErrNameCharsNotAllowed).Name))
|
||||
case models.IsErrNamePatternNotAllowed(err):
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern))
|
||||
default:
|
||||
|
||||
@@ -70,14 +70,14 @@ func UploadAttachment(ctx *context.Context) {
|
||||
func DeleteAttachment(ctx *context.Context) {
|
||||
file := ctx.Query("file")
|
||||
attach, err := models.GetAttachmentByUUID(file)
|
||||
if !ctx.IsSigned || (ctx.User.ID != attach.UploaderID) {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
ctx.Error(400, err.Error())
|
||||
return
|
||||
}
|
||||
if !ctx.IsSigned || (ctx.User.ID != attach.UploaderID) {
|
||||
ctx.Error(403)
|
||||
return
|
||||
}
|
||||
err = models.DeleteAttachment(attach, true)
|
||||
if err != nil {
|
||||
ctx.Error(500, fmt.Sprintf("DeleteAttachment: %v", err))
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/repofiles"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -252,7 +251,7 @@ func loadBranches(ctx *context.Context) []*Branch {
|
||||
repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo
|
||||
}
|
||||
pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
|
||||
if err != nil && err != plumbing.ErrReferenceNotFound {
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
ctx.ServerError("GetBranchCommitID", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func Commits(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
commits = models.ValidateCommitsWithEmails(commits)
|
||||
commits = models.ParseCommitsWithSignature(commits)
|
||||
commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
|
||||
commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
|
||||
ctx.Data["Commits"] = commits
|
||||
|
||||
@@ -134,7 +134,7 @@ func SearchCommits(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
commits = models.ValidateCommitsWithEmails(commits)
|
||||
commits = models.ParseCommitsWithSignature(commits)
|
||||
commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
|
||||
commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
|
||||
ctx.Data["Commits"] = commits
|
||||
|
||||
@@ -180,7 +180,7 @@ func FileHistory(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
commits = models.ValidateCommitsWithEmails(commits)
|
||||
commits = models.ParseCommitsWithSignature(commits)
|
||||
commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
|
||||
commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
|
||||
ctx.Data["Commits"] = commits
|
||||
|
||||
@@ -237,11 +237,11 @@ func Diff(ctx *context.Context) {
|
||||
parents := make([]string, commit.ParentCount())
|
||||
for i := 0; i < commit.ParentCount(); i++ {
|
||||
sha, err := commit.ParentID(i)
|
||||
parents[i] = sha.String()
|
||||
if err != nil {
|
||||
ctx.NotFound("repo.Diff", err)
|
||||
return
|
||||
}
|
||||
parents[i] = sha.String()
|
||||
}
|
||||
|
||||
ctx.Data["CommitID"] = commitID
|
||||
@@ -262,12 +262,18 @@ func Diff(ctx *context.Context) {
|
||||
setPathsCompareContext(ctx, parentCommit, commit, headTarget)
|
||||
ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
|
||||
ctx.Data["Commit"] = commit
|
||||
ctx.Data["Verification"] = models.ParseCommitWithSignature(commit)
|
||||
verification := models.ParseCommitWithSignature(commit)
|
||||
ctx.Data["Verification"] = verification
|
||||
ctx.Data["Author"] = models.ValidateCommitWithEmail(commit)
|
||||
ctx.Data["Diff"] = diff
|
||||
ctx.Data["Parents"] = parents
|
||||
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
|
||||
|
||||
if err := models.CalculateTrustStatus(verification, ctx.Repo.Repository, nil); err != nil {
|
||||
ctx.ServerError("CalculateTrustStatus", err)
|
||||
return
|
||||
}
|
||||
|
||||
note := &git.Note{}
|
||||
err = git.GetNote(ctx.Repo.GitRepo, commitID, note)
|
||||
if err == nil {
|
||||
|
||||
@@ -316,7 +316,7 @@ func PrepareCompareDiff(
|
||||
}
|
||||
|
||||
compareInfo.Commits = models.ValidateCommitsWithEmails(compareInfo.Commits)
|
||||
compareInfo.Commits = models.ParseCommitsWithSignature(compareInfo.Commits)
|
||||
compareInfo.Commits = models.ParseCommitsWithSignature(compareInfo.Commits, headRepo)
|
||||
compareInfo.Commits = models.ParseCommitsWithStatus(compareInfo.Commits, headRepo)
|
||||
ctx.Data["Commits"] = compareInfo.Commits
|
||||
ctx.Data["CommitCount"] = compareInfo.Commits.Len()
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/upload"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -262,10 +263,17 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo
|
||||
} else {
|
||||
ctx.Error(500, err.Error())
|
||||
}
|
||||
} else if models.IsErrCommitIDDoesNotMatch(err) {
|
||||
} else if models.IsErrCommitIDDoesNotMatch(err) || models.IsErrMergePushOutOfDate(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplEditFile, &form)
|
||||
} else if models.IsErrPushRejected(err) {
|
||||
errPushRej := err.(models.ErrPushRejected)
|
||||
if len(errPushRej.Message) == 0 {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplEditFile, &form)
|
||||
} else {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected", utils.SanitizeFlashErrorString(errPushRej.Message)), tplEditFile, &form)
|
||||
}
|
||||
} else {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.fail_to_update_file", form.TreePath, err), tplEditFile, &form)
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.fail_to_update_file", form.TreePath, utils.SanitizeFlashErrorString(err.Error())), tplEditFile, &form)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,8 +434,15 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
|
||||
} else {
|
||||
ctx.Error(500, err.Error())
|
||||
}
|
||||
} else if models.IsErrCommitIDDoesNotMatch(err) {
|
||||
} else if models.IsErrCommitIDDoesNotMatch(err) || models.IsErrMergePushOutOfDate(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplDeleteFile, &form)
|
||||
} else if models.IsErrPushRejected(err) {
|
||||
errPushRej := err.(models.ErrPushRejected)
|
||||
if len(errPushRej.Message) == 0 {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplDeleteFile, &form)
|
||||
} else {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected", utils.SanitizeFlashErrorString(errPushRej.Message)), tplDeleteFile, &form)
|
||||
}
|
||||
} else {
|
||||
ctx.ServerError("DeleteRepoFile", err)
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/mcuadros/go-version"
|
||||
"github.com/unknwon/com"
|
||||
gogit "gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"container/list"
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -25,6 +24,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/repofiles"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers/utils"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
@@ -354,6 +354,16 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
|
||||
return nil
|
||||
}
|
||||
defer baseGitRepo.Close()
|
||||
|
||||
if !baseGitRepo.IsBranchExist(pull.BaseBranch) {
|
||||
ctx.Data["IsPullRequestBroken"] = true
|
||||
ctx.Data["BaseTarget"] = pull.BaseBranch
|
||||
ctx.Data["HeadTarget"] = pull.HeadBranch
|
||||
ctx.Data["NumCommits"] = 0
|
||||
ctx.Data["NumFiles"] = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
var headBranchExist bool
|
||||
var headBranchSha string
|
||||
// HeadRepo may be missing
|
||||
@@ -477,7 +487,7 @@ func ViewPullCommits(ctx *context.Context) {
|
||||
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||
commits = prInfo.Commits
|
||||
commits = models.ValidateCommitsWithEmails(commits)
|
||||
commits = models.ParseCommitsWithSignature(commits)
|
||||
commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
|
||||
commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
|
||||
ctx.Data["Commits"] = commits
|
||||
ctx.Data["CommitCount"] = commits.Len()
|
||||
@@ -586,6 +596,8 @@ func ViewPullFiles(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
getBranchData(ctx, issue)
|
||||
ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID)
|
||||
ctx.Data["IsIssueWriter"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
|
||||
ctx.HTML(200, tplPullFiles)
|
||||
}
|
||||
|
||||
@@ -663,27 +675,18 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) {
|
||||
}
|
||||
|
||||
if err = pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
|
||||
sanitize := func(x string) string {
|
||||
runes := []rune(x)
|
||||
|
||||
if len(runes) > 512 {
|
||||
x = "..." + string(runes[len(runes)-512:])
|
||||
}
|
||||
|
||||
return strings.Replace(html.EscapeString(x), "\n", "<br>", -1)
|
||||
}
|
||||
if models.IsErrInvalidMergeStyle(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
|
||||
return
|
||||
} else if models.IsErrMergeConflicts(err) {
|
||||
conflictError := err.(models.ErrMergeConflicts)
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.merge_conflict", sanitize(conflictError.StdErr), sanitize(conflictError.StdOut)))
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.merge_conflict", utils.SanitizeFlashErrorString(conflictError.StdErr), utils.SanitizeFlashErrorString(conflictError.StdOut)))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
|
||||
return
|
||||
} else if models.IsErrRebaseConflicts(err) {
|
||||
conflictError := err.(models.ErrRebaseConflicts)
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.rebase_conflict", sanitize(conflictError.CommitSHA), sanitize(conflictError.StdErr), sanitize(conflictError.StdOut)))
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA), utils.SanitizeFlashErrorString(conflictError.StdErr), utils.SanitizeFlashErrorString(conflictError.StdOut)))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
|
||||
return
|
||||
} else if models.IsErrMergeUnrelatedHistories(err) {
|
||||
@@ -696,6 +699,17 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
|
||||
return
|
||||
} else if models.IsErrPushRejected(err) {
|
||||
log.Debug("MergePushRejected error: %v", err)
|
||||
pushrejErr := err.(models.ErrPushRejected)
|
||||
message := pushrejErr.Message
|
||||
if len(message) == 0 {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.push_rejected_no_message"))
|
||||
} else {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.push_rejected", utils.SanitizeFlashErrorString(pushrejErr.Message)))
|
||||
}
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
|
||||
return
|
||||
}
|
||||
ctx.ServerError("Merge", err)
|
||||
return
|
||||
|
||||
@@ -181,7 +181,14 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
||||
// Show latest commit info of repository in table header,
|
||||
// or of directory if not in root directory.
|
||||
ctx.Data["LatestCommit"] = latestCommit
|
||||
ctx.Data["LatestCommitVerification"] = models.ParseCommitWithSignature(latestCommit)
|
||||
verification := models.ParseCommitWithSignature(latestCommit)
|
||||
|
||||
if err := models.CalculateTrustStatus(verification, ctx.Repo.Repository, nil); err != nil {
|
||||
ctx.ServerError("CalculateTrustStatus", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["LatestCommitVerification"] = verification
|
||||
|
||||
ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit)
|
||||
|
||||
statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository, ctx.Repo.Commit.ID.String(), 0)
|
||||
|
||||
@@ -448,7 +448,7 @@ func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
|
||||
|
||||
if form.HasInvalidChannel() {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
|
||||
ctx.Redirect(orCtx.Link + "/settings/hooks/slack/new")
|
||||
ctx.Redirect(orCtx.Link + "/slack/new")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -642,7 +642,7 @@ func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
|
||||
|
||||
if form.HasInvalidChannel() {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
|
||||
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
|
||||
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
|
||||
return nil, nil
|
||||
}
|
||||
commitsHistory = models.ValidateCommitsWithEmails(commitsHistory)
|
||||
commitsHistory = models.ParseCommitsWithSignature(commitsHistory)
|
||||
commitsHistory = models.ParseCommitsWithSignature(commitsHistory, ctx.Repo.Repository)
|
||||
|
||||
ctx.Data["Commits"] = commitsHistory
|
||||
|
||||
|
||||
@@ -399,7 +399,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Post("/recover_account", user.ResetPasswdPost)
|
||||
m.Get("/forgot_password", user.ForgotPasswd)
|
||||
m.Post("/forgot_password", user.ForgotPasswdPost)
|
||||
m.Get("/logout", user.SignOut)
|
||||
m.Post("/logout", user.SignOut)
|
||||
})
|
||||
// ***** END: User *****
|
||||
|
||||
@@ -408,6 +408,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
// ***** START: Admin *****
|
||||
m.Group("/admin", func() {
|
||||
m.Get("", adminReq, admin.Dashboard)
|
||||
m.Post("", adminReq, bindIgnErr(auth.AdminDashboardForm{}), admin.DashboardPost)
|
||||
m.Get("/config", admin.Config)
|
||||
m.Post("/config/test_mail", admin.SendTestMail)
|
||||
m.Group("/monitor", func() {
|
||||
@@ -428,6 +429,11 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Post("/:userid/delete", admin.DeleteUser)
|
||||
})
|
||||
|
||||
m.Group("/emails", func() {
|
||||
m.Get("", admin.Emails)
|
||||
m.Post("/activate", admin.ActivateEmail)
|
||||
})
|
||||
|
||||
m.Group("/orgs", func() {
|
||||
m.Get("", admin.Organizations)
|
||||
})
|
||||
@@ -469,7 +475,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Group("/notices", func() {
|
||||
m.Get("", admin.Notices)
|
||||
m.Post("/delete", admin.DeleteNotices)
|
||||
m.Get("/empty", admin.EmptyNotices)
|
||||
m.Post("/empty", admin.EmptyNotices)
|
||||
})
|
||||
}, adminReq)
|
||||
// ***** END: Admin *****
|
||||
@@ -485,7 +491,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
}, reqSignIn)
|
||||
|
||||
m.Group("/:username", func() {
|
||||
m.Get("/action/:action", user.Action)
|
||||
m.Post("/action/:action", user.Action)
|
||||
}, reqSignIn)
|
||||
|
||||
if macaron.Env == macaron.DEV {
|
||||
@@ -517,7 +523,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Get("/^:type(issues|pulls)$", user.Issues)
|
||||
m.Get("/milestones", reqMilestonesDashboardPageEnabled, user.Milestones)
|
||||
m.Get("/members", org.Members)
|
||||
m.Get("/members/action/:action", org.MembersAction)
|
||||
m.Post("/members/action/:action", org.MembersAction)
|
||||
|
||||
m.Get("/teams", org.Teams)
|
||||
}, context.OrgAssignment(true))
|
||||
@@ -525,8 +531,8 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Group("/:org", func() {
|
||||
m.Get("/teams/:team", org.TeamMembers)
|
||||
m.Get("/teams/:team/repositories", org.TeamRepositories)
|
||||
m.Route("/teams/:team/action/:action", "GET,POST", org.TeamsAction)
|
||||
m.Route("/teams/:team/action/repo/:action", "GET,POST", org.TeamsRepoAction)
|
||||
m.Post("/teams/:team/action/:action", org.TeamsAction)
|
||||
m.Post("/teams/:team/action/repo/:action", org.TeamsRepoAction)
|
||||
}, context.OrgAssignment(true, false, true))
|
||||
|
||||
m.Group("/:org", func() {
|
||||
@@ -660,7 +666,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
})
|
||||
}, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef())
|
||||
|
||||
m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action)
|
||||
m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Group("/issues", func() {
|
||||
@@ -714,7 +720,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
|
||||
m.Get("/:id/edit", repo.EditMilestone)
|
||||
m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost)
|
||||
m.Get("/:id/:action", repo.ChangeMilestonStatus)
|
||||
m.Post("/:id/:action", repo.ChangeMilestonStatus)
|
||||
m.Post("/delete", repo.DeleteMilestone)
|
||||
}, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef())
|
||||
m.Group("/milestone", func() {
|
||||
|
||||
@@ -928,6 +928,7 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
|
||||
LoginName: gothUser.(goth.User).UserID,
|
||||
}
|
||||
|
||||
//nolint: dupl
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
switch {
|
||||
case models.IsErrUserAlreadyExist(err):
|
||||
@@ -942,6 +943,9 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
|
||||
case models.IsErrNamePatternNotAllowed(err):
|
||||
ctx.Data["Err_UserName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplLinkAccount, &form)
|
||||
case models.IsErrNameCharsNotAllowed(err):
|
||||
ctx.Data["Err_UserName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(models.ErrNameCharsNotAllowed).Name), tplLinkAccount, &form)
|
||||
default:
|
||||
ctx.ServerError("CreateUser", err)
|
||||
}
|
||||
@@ -1213,9 +1217,19 @@ func ActivateEmail(ctx *context.Context) {
|
||||
|
||||
log.Trace("Email activated: %s", email.Email)
|
||||
ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
|
||||
|
||||
if u, err := models.GetUserByID(email.UID); err != nil {
|
||||
log.Warn("GetUserByID: %d", email.UID)
|
||||
} else {
|
||||
// Allow user to validate more emails
|
||||
_ = ctx.Cache.Delete("MailResendLimit_" + u.LowerName)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/email")
|
||||
// FIXME: e-mail verification does not require the user to be logged in,
|
||||
// so this could be redirecting to the login page.
|
||||
// Should users be logged in automatically here? (consider 2FA requirements, etc.)
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||
}
|
||||
|
||||
// ForgotPasswd render the forget pasword page
|
||||
|
||||
@@ -400,6 +400,7 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si
|
||||
Passwd: password,
|
||||
IsActive: !setting.Service.RegisterEmailConfirm,
|
||||
}
|
||||
//nolint: dupl
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
switch {
|
||||
case models.IsErrUserAlreadyExist(err):
|
||||
@@ -414,6 +415,9 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si
|
||||
case models.IsErrNamePatternNotAllowed(err):
|
||||
ctx.Data["Err_UserName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplSignUpOID, &form)
|
||||
case models.IsErrNameCharsNotAllowed(err):
|
||||
ctx.Data["Err_UserName"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(models.ErrNameCharsNotAllowed).Name), tplSignUpOID, &form)
|
||||
default:
|
||||
ctx.ServerError("CreateUser", err)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user