mirror of
https://github.com/go-gitea/gitea.git
synced 2026-04-13 11:06:14 +00:00
Compare commits
63 Commits
v1.16.6
...
release/v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9aa13c585e | ||
|
|
eeaa9250e0 | ||
|
|
713bc6c8dc | ||
|
|
6b7e860b0f | ||
|
|
0f89417d75 | ||
|
|
7c80a0b630 | ||
|
|
b42df3105d | ||
|
|
6162fb0a19 | ||
|
|
df0b330af7 | ||
|
|
51db7b03dd | ||
|
|
a7b1e20b76 | ||
|
|
de79d2a235 | ||
|
|
4b7f0c6c38 | ||
|
|
ae91913132 | ||
|
|
0e7791174d | ||
|
|
736b7b25a4 | ||
|
|
daf14b275a | ||
|
|
cf6694e815 | ||
|
|
704f809e90 | ||
|
|
0e9499ada7 | ||
|
|
675f658721 | ||
|
|
ccc11c1e77 | ||
|
|
336e1ac779 | ||
|
|
be99eb26a2 | ||
|
|
fe9458591a | ||
|
|
57e816311b | ||
|
|
09b76295f1 | ||
|
|
38acce2f3f | ||
|
|
8f44d00f22 | ||
|
|
4386eb751f | ||
|
|
d6aab069ed | ||
|
|
f4fb8dbc87 | ||
|
|
c7c18e0eb2 | ||
|
|
0a2d618d85 | ||
|
|
c8a83ace59 | ||
|
|
59d132f0b3 | ||
|
|
18dd49a4ab | ||
|
|
46637b1164 | ||
|
|
7b18c67ac9 | ||
|
|
6eb3c05cb7 | ||
|
|
82f24bedc2 | ||
|
|
88da50674f | ||
|
|
35a7db49b4 | ||
|
|
f4729e2418 | ||
|
|
f7330fd027 | ||
|
|
755d8e21ad | ||
|
|
7c0bf06d96 | ||
|
|
0d196e29e8 | ||
|
|
b86606fa38 | ||
|
|
74602bb487 | ||
|
|
1465e0cbb2 | ||
|
|
928b603d19 | ||
|
|
8ff542c1a2 | ||
|
|
39a0db6ecf | ||
|
|
9cc93c05cd | ||
|
|
b31418edd9 | ||
|
|
242f7f1a52 | ||
|
|
8d7f1e430a | ||
|
|
a6b32adc45 | ||
|
|
1f0dca4614 | ||
|
|
1d665da32f | ||
|
|
09adc26eb6 | ||
|
|
297346a762 |
34
.drone.yml
34
.drone.yml
@@ -235,6 +235,7 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
when:
|
||||
event:
|
||||
@@ -427,6 +428,7 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
when:
|
||||
event:
|
||||
@@ -628,6 +630,7 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: deps-frontend
|
||||
@@ -746,6 +749,7 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: deps-frontend
|
||||
@@ -891,14 +895,18 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: publish
|
||||
image: techknowlogick/drone-docker:latest
|
||||
pull: always
|
||||
settings:
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
auto_tag_suffix: linux-amd64
|
||||
tags:
|
||||
- ${DRONE_TAG##v}-linux-amd64
|
||||
- ${DRONE_TAG:1:4}-linux-amd64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
@@ -915,8 +923,11 @@ steps:
|
||||
image: techknowlogick/drone-docker:latest
|
||||
settings:
|
||||
dockerfile: Dockerfile.rootless
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
auto_tag_suffix: linux-amd64-rootless
|
||||
tags:
|
||||
- ${DRONE_TAG##v}-linux-amd64-rootless
|
||||
- ${DRONE_TAG:1:4}-linux-amd64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
@@ -954,6 +965,7 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: publish
|
||||
@@ -1016,6 +1028,7 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: publish
|
||||
@@ -1112,14 +1125,18 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: publish
|
||||
image: techknowlogick/drone-docker:latest
|
||||
pull: always
|
||||
settings:
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
auto_tag_suffix: linux-arm64
|
||||
tags:
|
||||
- ${DRONE_TAG##v}-linux-arm64
|
||||
- ${DRONE_TAG:1:4}-linux-arm64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
@@ -1136,8 +1153,11 @@ steps:
|
||||
image: techknowlogick/drone-docker:latest
|
||||
settings:
|
||||
dockerfile: Dockerfile.rootless
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
auto_tag_suffix: linux-arm64-rootless
|
||||
tags:
|
||||
- ${DRONE_TAG##v}-linux-arm64-rootless
|
||||
- ${DRONE_TAG:1:4}-linux-arm64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
@@ -1175,6 +1195,7 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: publish
|
||||
@@ -1237,6 +1258,7 @@ steps:
|
||||
image: docker:git
|
||||
pull: always
|
||||
commands:
|
||||
- git config --global --add safe.directory /drone/src
|
||||
- git fetch --tags --force
|
||||
|
||||
- name: publish
|
||||
@@ -1289,7 +1311,7 @@ steps:
|
||||
image: plugins/manifest
|
||||
pull: always
|
||||
settings:
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
ignore_missing: true
|
||||
spec: docker/manifest.rootless.tmpl
|
||||
password:
|
||||
@@ -1300,7 +1322,7 @@ steps:
|
||||
- name: manifest
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
ignore_missing: true
|
||||
spec: docker/manifest.tmpl
|
||||
password:
|
||||
|
||||
65
CHANGELOG.md
65
CHANGELOG.md
@@ -4,6 +4,71 @@ 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.16.9](https://github.com/go-gitea/gitea/releases/tag/v1.16.9) - 2022-07-12
|
||||
|
||||
* SECURITY
|
||||
* Add write check for creating Commit status (#20332) (#20334)
|
||||
* Check for permission when fetching user controlled issues (#20133) (#20196)
|
||||
* BUGFIXES
|
||||
* Hide notify mail setting ui if not enabled (#20138) (#20337)
|
||||
* Add write check for creating Commit status (#20332) (#20334)
|
||||
* Only show Followers that current user can access (#20220) (#20253)
|
||||
* Release page show all tags in compare dropdown (#20070) (#20071)
|
||||
* Fix permission check for delete tag (#19985) (#20001)
|
||||
* Only log non ErrNotExist errors in git.GetNote (#19884) (#19905)
|
||||
* Use exact search instead of fuzzy search for branch filter dropdown (#19885) (#19893)
|
||||
* Set Setpgid on child git processes (#19865) (#19881)
|
||||
* Import git from alpine 3.16 repository as 2.30.4 is needed for `safe.directory = '*'` to work but alpine 3.13 has 2.30.3 (#19876)
|
||||
* Ensure responses are context.ResponseWriters (#19843) (#19859)
|
||||
* Fix incorrect usage of `Count` function (#19850)
|
||||
* Fix raw endpoint PDF file headers (#19825) (#19826)
|
||||
* Make WIP prefixes case insensitive, e.g. allow `Draft` as a WIP prefix (#19780) (#19811)
|
||||
* Don't return 500 on NotificationUnreadCount (#19802)
|
||||
* Prevent NPE when cache service is disabled (#19703) (#19783)
|
||||
* Detect truncated utf-8 characters at the end of content as still representing utf-8 (#19773) (#19774)
|
||||
* Fix doctor pq: syntax error at or near "." quote user table name (#19765) (#19770)
|
||||
* Fix bug with assigneees (#19757)
|
||||
|
||||
## [1.16.8](https://github.com/go-gitea/gitea/releases/tag/v1.16.8) - 2022-05-16
|
||||
|
||||
* ENHANCEMENTS
|
||||
* Add doctor check/fix for bogus action rows (#19656) (#19669)
|
||||
* Make .cs highlighting legible on dark themes. (#19604) (#19605)
|
||||
* BUGFIXES
|
||||
* Fix oauth setting list bug (#19681)
|
||||
* Delete user related oauth stuff on user deletion too (#19677) (#19680)
|
||||
* Fix new release from tags list UI (#19670) (#19673)
|
||||
* Prevent NPE when checking repo units if the user is nil (#19625) (#19630)
|
||||
* GetFeeds must always discard actions with dangling repo_id (#19598) (#19629)
|
||||
* Call MultipartForm.RemoveAll when request finishes (#19606) (#19607)
|
||||
* Avoid MoreThanOne error when creating a branch whose name conflicts with other ref names (#19557) (#19591)
|
||||
* Fix sending empty notifications (#19589) (#19590)
|
||||
* Ignore DNS error when doing migration allow/block check (#19566) (#19567)
|
||||
* Fix issue overview for teams (#19652) (#19653)
|
||||
|
||||
## [1.16.7](https://github.com/go-gitea/gitea/releases/tag/v1.16.7) - 2022-05-02
|
||||
|
||||
* SECURITY
|
||||
* Escape git fetch remote (#19487) (#19490)
|
||||
* BUGFIXES
|
||||
* Don't overwrite err with nil (#19572) (#19574)
|
||||
* On Migrations, only write commit-graph if wiki clone was successful (#19563) (#19568)
|
||||
* Respect DefaultUserIsRestricted system default when creating new user (#19310) (#19560)
|
||||
* Don't error when branch's commit doesn't exist (#19547) (#19548)
|
||||
* Support `hostname:port` to pass host matcher's check (#19543) (#19544)
|
||||
* Prevent intermittent race in attribute reader close (#19537) (#19539)
|
||||
* Fix 64-bit atomic operations on 32-bit machines (#19531) (#19532)
|
||||
* Prevent dangling archiver goroutine (#19516) (#19526)
|
||||
* Fix migrate release from github (#19510) (#19523)
|
||||
* When view _Siderbar or _Footer, just display once (#19501) (#19522)
|
||||
* Fix blame page select range error and some typos (#19503)
|
||||
* Fix name of doctor fix "authorized-keys" in hints (#19464) (#19484)
|
||||
* User specific repoID or xorm builder conditions for issue search (#19475) (#19476)
|
||||
* Prevent dangling cat-file calls (goroutine alternative) (#19454) (#19466)
|
||||
* RepoAssignment ensure to close before overwrite (#19449) (#19460)
|
||||
* Set correct PR status on 3way on conflict checking (#19457) (#19458)
|
||||
* Mark TemplateLoading error as "UnprocessableEntity" (#19445) (#19446)
|
||||
|
||||
## [1.16.6](https://github.com/go-gitea/gitea/releases/tag/v1.16.6) - 2022-04-20
|
||||
|
||||
* ENHANCEMENTS
|
||||
|
||||
@@ -33,7 +33,6 @@ RUN apk --no-cache add \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gettext \
|
||||
git \
|
||||
linux-pam \
|
||||
openssh \
|
||||
s6 \
|
||||
@@ -41,6 +40,8 @@ RUN apk --no-cache add \
|
||||
su-exec \
|
||||
gnupg
|
||||
|
||||
RUN apk add git --repository=http://dl-cdn.alpinelinux.org/alpine/v3.16/main
|
||||
|
||||
RUN addgroup \
|
||||
-S -g 1000 \
|
||||
git && \
|
||||
|
||||
@@ -32,10 +32,11 @@ RUN apk --no-cache add \
|
||||
bash \
|
||||
ca-certificates \
|
||||
gettext \
|
||||
git \
|
||||
curl \
|
||||
gnupg
|
||||
|
||||
RUN apk add git --repository=http://dl-cdn.alpinelinux.org/alpine/v3.16/main
|
||||
|
||||
RUN addgroup \
|
||||
-S -g 1000 \
|
||||
git && \
|
||||
|
||||
20
cmd/admin.go
20
cmd/admin.go
@@ -25,6 +25,7 @@ import (
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
auth_service "code.gitea.io/gitea/services/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
||||
@@ -113,6 +114,10 @@ var (
|
||||
Name: "access-token",
|
||||
Usage: "Generate access token for the user",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "restricted",
|
||||
Usage: "Make a restricted user account",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -537,17 +542,26 @@ func runCreateUser(c *cli.Context) error {
|
||||
changePassword = c.Bool("must-change-password")
|
||||
}
|
||||
|
||||
restricted := util.OptionalBoolNone
|
||||
|
||||
if c.IsSet("restricted") {
|
||||
restricted = util.OptionalBoolOf(c.Bool("restricted"))
|
||||
}
|
||||
|
||||
u := &user_model.User{
|
||||
Name: username,
|
||||
Email: c.String("email"),
|
||||
Passwd: password,
|
||||
IsActive: true,
|
||||
IsAdmin: c.Bool("admin"),
|
||||
MustChangePassword: changePassword,
|
||||
Theme: setting.UI.DefaultTheme,
|
||||
}
|
||||
|
||||
if err := user_model.CreateUser(u); err != nil {
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
IsRestricted: restricted,
|
||||
}
|
||||
|
||||
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
||||
return fmt.Errorf("CreateUser: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/pprof"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/lfs"
|
||||
|
||||
@@ -247,7 +248,7 @@ func runServ(c *cli.Context) error {
|
||||
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
|
||||
os.Setenv(models.EnvAppURL, setting.AppURL)
|
||||
|
||||
//LFS token authentication
|
||||
// LFS token authentication
|
||||
if verb == lfsAuthenticateVerb {
|
||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
||||
|
||||
@@ -306,6 +307,7 @@ func runServ(c *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
process.SetSysProcAttribute(gitcmd)
|
||||
gitcmd.Dir = setting.RepoRootPath
|
||||
gitcmd.Stdout = os.Stdout
|
||||
gitcmd.Stdin = os.Stdin
|
||||
|
||||
@@ -880,7 +880,7 @@ PATH =
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; List of prefixes used in Pull Request title to mark them as Work In Progress
|
||||
;; List of prefixes used in Pull Request title to mark them as Work In Progress (matched in a case-insensitive manner)
|
||||
;WORK_IN_PROGRESS_PREFIXES = WIP:,[WIP]
|
||||
;;
|
||||
;; List of keywords used in Pull Request comments to automatically close a related issue
|
||||
|
||||
@@ -4,7 +4,6 @@ tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}-rootless
|
||||
{{/each}}
|
||||
- "latest-rootless"
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
|
||||
@@ -4,7 +4,6 @@ tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
- "latest"
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
|
||||
@@ -87,7 +87,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
||||
### Repository - Pull Request (`repository.pull-request`)
|
||||
|
||||
- `WORK_IN_PROGRESS_PREFIXES`: **WIP:,\[WIP\]**: List of prefixes used in Pull Request
|
||||
title to mark them as Work In Progress
|
||||
title to mark them as Work In Progress. These are matched in a case-insensitive manner.
|
||||
- `CLOSE_KEYWORDS`: **close**, **closes**, **closed**, **fix**, **fixes**, **fixed**, **resolve**, **resolves**, **resolved**: List of
|
||||
keywords used in Pull Request comments to automatically close a related issue
|
||||
- `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: List of keywords used in Pull Request comments to automatically reopen
|
||||
|
||||
@@ -43,7 +43,7 @@ Vous devriez avoir une instance fonctionnelle de Gitea. Pour accèder à l'inter
|
||||
|
||||
## Named Volumes
|
||||
|
||||
Ce guide aboutira à une installation avec les données Gita et PostgreSQL stockées dans des volumes nommés. Cela permet une sauvegarde, une restauration et des mises à niveau en toute simplicité.
|
||||
Ce guide aboutira à une installation avec les données Gitea et PostgreSQL stockées dans des volumes nommés. Cela permet une sauvegarde, une restauration et des mises à niveau en toute simplicité.
|
||||
|
||||
### The Database
|
||||
|
||||
|
||||
4
go.mod
4
go.mod
@@ -37,7 +37,7 @@ require (
|
||||
github.com/go-swagger/go-swagger v0.27.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.6.1
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v4 v4.3.0
|
||||
@@ -102,7 +102,7 @@ require (
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
mvdan.cc/xurls/v2 v2.2.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
xorm.io/builder v0.3.9
|
||||
xorm.io/builder v0.3.10
|
||||
xorm.io/xorm v1.2.5
|
||||
)
|
||||
|
||||
|
||||
7
go.sum
7
go.sum
@@ -649,8 +649,8 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 h1:gBeyun7mySAKWg7Fb0GOcv0upX9bdaZScs8QcRo8mEY=
|
||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQdcMdzjbqqXMEnHfq0Or6p8=
|
||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0=
|
||||
@@ -2336,7 +2336,8 @@ sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs=
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
|
||||
xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc=
|
||||
xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
xorm.io/builder v0.3.10 h1:Rvkncad3Lo9YIVqCbgIf6QnpR/HcW3IEr0AANNpuyMQ=
|
||||
xorm.io/builder v0.3.10/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
xorm.io/xorm v1.2.5 h1:tqN7OhN8P9xi52qBb76I8m5maAJMz/SSbgK2RGPCPbo=
|
||||
xorm.io/xorm v1.2.5/go.mod h1:fTG8tSjk6O1BYxwuohZUK+S1glnRycsCF05L1qQyEU0=
|
||||
|
||||
@@ -112,6 +112,13 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
}
|
||||
|
||||
os.Unsetenv("GIT_AUTHOR_NAME")
|
||||
os.Unsetenv("GIT_AUTHOR_EMAIL")
|
||||
os.Unsetenv("GIT_AUTHOR_DATE")
|
||||
os.Unsetenv("GIT_COMMITTER_NAME")
|
||||
os.Unsetenv("GIT_COMMITTER_EMAIL")
|
||||
os.Unsetenv("GIT_COMMITTER_DATE")
|
||||
|
||||
err := unittest.InitFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/services/pull"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
files_service "code.gitea.io/gitea/services/repository/files"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/unknwon/i18n"
|
||||
@@ -65,7 +67,7 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str
|
||||
|
||||
func TestPullMerge(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
|
||||
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
|
||||
@@ -87,7 +89,7 @@ func TestPullMerge(t *testing.T) {
|
||||
|
||||
func TestPullRebase(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
|
||||
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
|
||||
@@ -109,7 +111,7 @@ func TestPullRebase(t *testing.T) {
|
||||
|
||||
func TestPullRebaseMerge(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
|
||||
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
|
||||
@@ -131,7 +133,7 @@ func TestPullRebaseMerge(t *testing.T) {
|
||||
|
||||
func TestPullSquash(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
|
||||
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
|
||||
assert.NoError(t, err)
|
||||
hookTasksLenBefore := len(hookTasks)
|
||||
|
||||
@@ -335,3 +337,74 @@ func TestCantMergeUnrelated(t *testing.T) {
|
||||
gitRepo.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func TestConflictChecking(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
|
||||
|
||||
// Create new clean repo to test conflict checking.
|
||||
baseRepo, err := repo_service.CreateRepository(user, user, models.CreateRepoOptions{
|
||||
Name: "conflict-checking",
|
||||
Description: "Tempo repo",
|
||||
AutoInit: true,
|
||||
Readme: "Default",
|
||||
DefaultBranch: "main",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, baseRepo)
|
||||
|
||||
// create a commit on new branch.
|
||||
_, err = files_service.CreateOrUpdateRepoFile(baseRepo, user, &files_service.UpdateRepoFileOptions{
|
||||
TreePath: "important_file",
|
||||
Message: "Add a important file",
|
||||
Content: "Just a non-important file",
|
||||
IsNewFile: true,
|
||||
OldBranch: "main",
|
||||
NewBranch: "important-secrets",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// create a commit on main branch.
|
||||
_, err = files_service.CreateOrUpdateRepoFile(baseRepo, user, &files_service.UpdateRepoFileOptions{
|
||||
TreePath: "important_file",
|
||||
Message: "Add a important file",
|
||||
Content: "Not the same content :P",
|
||||
IsNewFile: true,
|
||||
OldBranch: "main",
|
||||
NewBranch: "main",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// create Pull to merge the important-secrets branch into main branch.
|
||||
pullIssue := &models.Issue{
|
||||
RepoID: baseRepo.ID,
|
||||
Title: "PR with conflict!",
|
||||
PosterID: user.ID,
|
||||
Poster: user,
|
||||
IsPull: true,
|
||||
}
|
||||
|
||||
pullRequest := &models.PullRequest{
|
||||
HeadRepoID: baseRepo.ID,
|
||||
BaseRepoID: baseRepo.ID,
|
||||
HeadBranch: "important-secrets",
|
||||
BaseBranch: "main",
|
||||
HeadRepo: baseRepo,
|
||||
BaseRepo: baseRepo,
|
||||
Type: models.PullRequestGitea,
|
||||
}
|
||||
err = pull.NewPullRequest(baseRepo, pullIssue, nil, nil, pullRequest, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{Title: "PR with conflict!"}).(*models.Issue)
|
||||
conflictingPR, err := models.GetPullRequestByIssueID(issue.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Ensure conflictedFiles is populated.
|
||||
assert.Equal(t, 1, len(conflictingPR.ConflictedFiles))
|
||||
// Check if status is correct.
|
||||
assert.Equal(t, models.PullRequestStatusConflict, conflictingPR.Status)
|
||||
// Ensure that mergeable returns false
|
||||
assert.False(t, conflictingPR.Mergeable())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -337,7 +337,7 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
|
||||
|
||||
actions := make([]*Action, 0, setting.UI.FeedPagingNum)
|
||||
|
||||
if err := db.GetEngine(db.DefaultContext).Limit(setting.UI.FeedPagingNum).Desc("created_unix").Where(cond).Find(&actions); err != nil {
|
||||
if err := db.GetEngine(db.DefaultContext).Limit(setting.UI.FeedPagingNum).Desc("`action`.created_unix").Where(cond).Join("INNER", "repository", "`repository`.id = `action`.repo_id").Find(&actions); err != nil {
|
||||
return nil, fmt.Errorf("Find: %v", err)
|
||||
}
|
||||
|
||||
@@ -401,7 +401,7 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
|
||||
cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
|
||||
}
|
||||
if !opts.IncludePrivate {
|
||||
cond = cond.And(builder.Eq{"is_private": false})
|
||||
cond = cond.And(builder.Eq{"`action`.is_private": false})
|
||||
}
|
||||
if !opts.IncludeDeleted {
|
||||
cond = cond.And(builder.Eq{"is_deleted": false})
|
||||
@@ -414,8 +414,8 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
|
||||
} else {
|
||||
dateHigh := dateLow.Add(86399000000000) // 23h59m59s
|
||||
|
||||
cond = cond.And(builder.Gte{"created_unix": dateLow.Unix()})
|
||||
cond = cond.And(builder.Lte{"created_unix": dateHigh.Unix()})
|
||||
cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
|
||||
cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,3 +129,20 @@ func TestNotifyWatchers(t *testing.T) {
|
||||
OpType: action.OpType,
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetFeedsCorrupted(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
|
||||
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||
ID: 8,
|
||||
RepoID: 1700,
|
||||
})
|
||||
|
||||
actions, err := GetFeeds(GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 0)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -94,7 +95,8 @@ func CountOrphanedIssues() (int64, error) {
|
||||
return db.GetEngine(db.DefaultContext).Table("issue").
|
||||
Join("LEFT", "repository", "issue.repo_id=repository.id").
|
||||
Where(builder.IsNull{"repository.id"}).
|
||||
Count("id")
|
||||
Select("COUNT(`issue`.`id`)").
|
||||
Count()
|
||||
}
|
||||
|
||||
// DeleteOrphanedIssues delete issues without a repo
|
||||
@@ -139,8 +141,9 @@ func DeleteOrphanedIssues() error {
|
||||
func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) {
|
||||
return db.GetEngine(db.DefaultContext).Table("`"+subject+"`").
|
||||
Join("LEFT", "`"+refobject+"`", joinCond).
|
||||
Where(builder.IsNull{"`" + refobject + "`.id"}).
|
||||
Count("id")
|
||||
Where(builder.IsNull{"`" + refobject + "`.`id`"}).
|
||||
Select("COUNT(`" + subject + "`.`id`)").
|
||||
Count()
|
||||
}
|
||||
|
||||
// DeleteOrphanedObjects delete subjects with have no existing refobject anymore
|
||||
@@ -240,10 +243,29 @@ func FixIssueLabelWithOutsideLabels() (int64, error) {
|
||||
WHERE
|
||||
(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
|
||||
) AS il_too )`)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
// CountActionCreatedUnixString count actions where created_unix is an empty string
|
||||
func CountActionCreatedUnixString() (int64, error) {
|
||||
if setting.Database.UseSQLite3 {
|
||||
return db.GetEngine(db.DefaultContext).Where(`created_unix = ""`).Count(new(Action))
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// FixActionCreatedUnixString set created_unix to zero if it is an empty string
|
||||
func FixActionCreatedUnixString() (int64, error) {
|
||||
if setting.Database.UseSQLite3 {
|
||||
res, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -33,3 +34,46 @@ func TestDeleteOrphanedObjects(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, countBefore, countAfter)
|
||||
}
|
||||
|
||||
func TestConsistencyUpdateAction(t *testing.T) {
|
||||
if !setting.Database.UseSQLite3 {
|
||||
t.Skip("Test is only for SQLite database.")
|
||||
}
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
id := 8
|
||||
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||
ID: int64(id),
|
||||
})
|
||||
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
|
||||
assert.NoError(t, err)
|
||||
actions := make([]*Action, 0, 1)
|
||||
//
|
||||
// XORM returns an error when created_unix is a string
|
||||
//
|
||||
err = db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "type string to a int64: invalid syntax")
|
||||
}
|
||||
//
|
||||
// Get rid of incorrectly set created_unix
|
||||
//
|
||||
count, err := CountActionCreatedUnixString()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, count)
|
||||
count, err = FixActionCreatedUnixString()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, count)
|
||||
|
||||
count, err = CountActionCreatedUnixString()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, count)
|
||||
count, err = FixActionCreatedUnixString()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, count)
|
||||
|
||||
//
|
||||
// XORM must be happy now
|
||||
//
|
||||
assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
|
||||
unittest.CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
@@ -332,7 +332,6 @@ type ErrInvalidCloneAddr struct {
|
||||
IsProtocolInvalid bool
|
||||
IsPermissionDenied bool
|
||||
LocalPath bool
|
||||
NotResolvedIP bool
|
||||
}
|
||||
|
||||
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
|
||||
@@ -342,9 +341,6 @@ func IsErrInvalidCloneAddr(err error) bool {
|
||||
}
|
||||
|
||||
func (err *ErrInvalidCloneAddr) Error() string {
|
||||
if err.NotResolvedIP {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: unknown hostname", err.Host)
|
||||
}
|
||||
if err.IsInvalidPath {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host)
|
||||
}
|
||||
|
||||
@@ -56,3 +56,11 @@
|
||||
repo_id: 8
|
||||
is_private: false
|
||||
created_unix: 1603011540 # grouped with id:7
|
||||
|
||||
- id: 8
|
||||
user_id: 1
|
||||
op_type: 12 # close issue
|
||||
act_user_id: 1
|
||||
repo_id: 1700 # dangling intentional
|
||||
is_private: false
|
||||
created_unix: 1603011541
|
||||
|
||||
@@ -1165,7 +1165,8 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
|
||||
// IssuesOptions represents options of an issue.
|
||||
type IssuesOptions struct {
|
||||
db.ListOptions
|
||||
RepoIDs []int64 // include all repos if empty
|
||||
RepoID int64 // overwrites RepoCond if not 0
|
||||
RepoCond builder.Cond
|
||||
AssigneeID int64
|
||||
PosterID int64
|
||||
MentionedID int64
|
||||
@@ -1256,15 +1257,15 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
|
||||
sess.In("issue.id", opts.IssueIDs)
|
||||
}
|
||||
|
||||
if len(opts.RepoIDs) > 0 {
|
||||
applyReposCondition(sess, opts.RepoIDs)
|
||||
if opts.RepoID != 0 {
|
||||
opts.RepoCond = builder.Eq{"issue.repo_id": opts.RepoID}
|
||||
}
|
||||
if opts.RepoCond != nil {
|
||||
sess.And(opts.RepoCond)
|
||||
}
|
||||
|
||||
switch opts.IsClosed {
|
||||
case util.OptionalBoolTrue:
|
||||
sess.And("issue.is_closed=?", true)
|
||||
case util.OptionalBoolFalse:
|
||||
sess.And("issue.is_closed=?", false)
|
||||
if !opts.IsClosed.IsNone() {
|
||||
sess.And("issue.is_closed=?", opts.IsClosed.IsTrue())
|
||||
}
|
||||
|
||||
if opts.AssigneeID > 0 {
|
||||
@@ -1345,9 +1346,7 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
|
||||
}
|
||||
|
||||
if opts.User != nil {
|
||||
sess.And(
|
||||
issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue()),
|
||||
)
|
||||
sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1383,10 +1382,6 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *Organizati
|
||||
return cond
|
||||
}
|
||||
|
||||
func applyReposCondition(sess *xorm.Session, repoIDs []int64) *xorm.Session {
|
||||
return sess.In("issue.repo_id", repoIDs)
|
||||
}
|
||||
|
||||
func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session {
|
||||
return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
|
||||
And("issue_assignees.assignee_id = ?", assigneeID)
|
||||
@@ -1416,7 +1411,6 @@ func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
|
||||
e := db.GetEngine(db.DefaultContext)
|
||||
|
||||
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
|
||||
opts.setupSessionNoLimit(sess)
|
||||
|
||||
countsSlice := make([]*struct {
|
||||
@@ -1443,7 +1437,6 @@ func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]i
|
||||
e := db.GetEngine(db.DefaultContext)
|
||||
|
||||
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
|
||||
opts.setupSessionNoLimit(sess)
|
||||
|
||||
accessCond := accessibleRepositoryCondition(user)
|
||||
@@ -1488,6 +1481,7 @@ func CountIssues(opts *IssuesOptions) (int64, error) {
|
||||
sess := e.Select("COUNT(issue.id) AS count").Table("issue")
|
||||
sess.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
opts.setupSessionNoLimit(sess)
|
||||
|
||||
if err := sess.Find(&countsSlice); err != nil {
|
||||
return 0, fmt.Errorf("unable to CountIssues: %w", err)
|
||||
}
|
||||
|
||||
@@ -155,31 +155,26 @@ func toggleUserAssignee(e db.Engine, issue *Issue, assigneeID int64) (removed bo
|
||||
}
|
||||
|
||||
// Check if the submitted user is already assigned, if yes delete him otherwise add him
|
||||
var i int
|
||||
for i = 0; i < len(issue.Assignees); i++ {
|
||||
found := false
|
||||
i := 0
|
||||
for ; i < len(issue.Assignees); i++ {
|
||||
if issue.Assignees[i].ID == assigneeID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assigneeIn := IssueAssignees{AssigneeID: assigneeID, IssueID: issue.ID}
|
||||
|
||||
toBeDeleted := i < len(issue.Assignees)
|
||||
if toBeDeleted {
|
||||
issue.Assignees = append(issue.Assignees[:i], issue.Assignees[i:]...)
|
||||
if found {
|
||||
issue.Assignees = append(issue.Assignees[:i], issue.Assignees[i+1:]...)
|
||||
_, err = e.Delete(assigneeIn)
|
||||
if err != nil {
|
||||
return toBeDeleted, err
|
||||
}
|
||||
} else {
|
||||
issue.Assignees = append(issue.Assignees, assignee)
|
||||
_, err = e.Insert(assigneeIn)
|
||||
if err != nil {
|
||||
return toBeDeleted, err
|
||||
}
|
||||
}
|
||||
|
||||
return toBeDeleted, nil
|
||||
return found, err
|
||||
}
|
||||
|
||||
// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -101,12 +102,10 @@ func (label *Label) CalOpenIssues() {
|
||||
|
||||
// CalOpenOrgIssues calculates the open issues of a label for a specific repo
|
||||
func (label *Label) CalOpenOrgIssues(repoID, labelID int64) {
|
||||
repoIDs := []int64{repoID}
|
||||
labelIDs := []int64{labelID}
|
||||
|
||||
counts, _ := CountIssuesByRepo(&IssuesOptions{
|
||||
RepoIDs: repoIDs,
|
||||
LabelIDs: labelIDs,
|
||||
RepoID: repoID,
|
||||
LabelIDs: []int64{labelID},
|
||||
IsClosed: util.OptionalBoolFalse,
|
||||
})
|
||||
|
||||
for _, count := range counts {
|
||||
|
||||
@@ -116,6 +116,11 @@ func getMilestoneByRepoID(e db.Engine, repoID, id int64) (*Milestone, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// HasMilestoneByRepoID returns if the milestone exists in the repository.
|
||||
func HasMilestoneByRepoID(repoID, id int64) (bool, error) {
|
||||
return db.GetEngine(db.DefaultContext).ID(id).Where("repo_id=?", repoID).Exist(new(Milestone))
|
||||
}
|
||||
|
||||
// GetMilestoneByRepoID returns the milestone in a repository.
|
||||
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
|
||||
return getMilestoneByRepoID(db.GetEngine(db.DefaultContext), repoID, id)
|
||||
@@ -251,6 +256,17 @@ func changeMilestoneStatus(ctx context.Context, m *Milestone, isClosed bool) err
|
||||
}
|
||||
|
||||
func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *Issue, oldMilestoneID int64) error {
|
||||
// Only check if milestone exists if we don't remove it.
|
||||
if issue.MilestoneID > 0 {
|
||||
has, err := HasMilestoneByRepoID(issue.RepoID, issue.MilestoneID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("HasMilestoneByRepoID: %v", err)
|
||||
}
|
||||
if !has {
|
||||
return fmt.Errorf("HasMilestoneByRepoID: issue doesn't exist")
|
||||
}
|
||||
}
|
||||
|
||||
if err := updateIssueCols(ctx, issue, "milestone_id"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func TestIssue_ReplaceLabels(t *testing.T) {
|
||||
@@ -153,7 +154,7 @@ func TestIssues(t *testing.T) {
|
||||
},
|
||||
{
|
||||
IssuesOptions{
|
||||
RepoIDs: []int64{1, 3},
|
||||
RepoCond: builder.In("repo_id", 1, 3),
|
||||
SortType: "oldest",
|
||||
ListOptions: db.ListOptions{
|
||||
Page: 1,
|
||||
@@ -340,7 +341,7 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) {
|
||||
},
|
||||
{
|
||||
IssuesOptions{
|
||||
RepoIDs: []int64{1, 2},
|
||||
RepoCond: builder.In("repo_id", 1, 2),
|
||||
},
|
||||
[]int64{1, 2},
|
||||
},
|
||||
|
||||
@@ -453,7 +453,7 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
|
||||
|
||||
// Downgrading Gitea's database version not supported
|
||||
if int(v-minDBVersion) > len(migrations) {
|
||||
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gita, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations))
|
||||
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gitea, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations))
|
||||
msg += "\nGitea will exit to keep your database safe and unchanged. Please use the correct Gitea release, do not change the migration version manually (incorrect manual operation may lose data)."
|
||||
if !setting.IsProd {
|
||||
msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", minDBVersion+len(migrations))
|
||||
|
||||
@@ -150,6 +150,17 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U
|
||||
e := db.GetEngine(ctx)
|
||||
oldProjectID := issue.projectID(e)
|
||||
|
||||
// Only check if we add a new project and not remove it.
|
||||
if newProjectID > 0 {
|
||||
newProject, err := GetProjectByID(newProjectID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if newProject.RepoID != issue.RepoID {
|
||||
return fmt.Errorf("issue's repository is not the same as project's repository")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := e.Where("project_issue.issue_id=?", issue.ID).Delete(&ProjectIssue{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -622,7 +622,7 @@ func (pr *PullRequest) IsWorkInProgress() bool {
|
||||
// HasWorkInProgressPrefix determines if the given PR title has a Work In Progress prefix
|
||||
func HasWorkInProgressPrefix(title string) bool {
|
||||
for _, prefix := range setting.Repository.PullRequest.WorkInProgressPrefixes {
|
||||
if strings.HasPrefix(strings.ToUpper(title), prefix) {
|
||||
if strings.HasPrefix(strings.ToUpper(title), strings.ToUpper(prefix)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -643,7 +643,7 @@ func (pr *PullRequest) GetWorkInProgressPrefix() string {
|
||||
}
|
||||
|
||||
for _, prefix := range setting.Repository.PullRequest.WorkInProgressPrefixes {
|
||||
if strings.HasPrefix(strings.ToUpper(pr.Issue.Title), prefix) {
|
||||
if strings.HasPrefix(strings.ToUpper(pr.Issue.Title), strings.ToUpper(prefix)) {
|
||||
return pr.Issue.Title[0:len(prefix)]
|
||||
}
|
||||
}
|
||||
@@ -697,3 +697,14 @@ func (pr *PullRequest) GetHeadBranchHTMLURL() string {
|
||||
}
|
||||
return pr.HeadRepo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(pr.HeadBranch)
|
||||
}
|
||||
|
||||
// Mergeable returns if the pullrequest is mergeable.
|
||||
func (pr *PullRequest) Mergeable() bool {
|
||||
// If a pull request isn't mergable if it's:
|
||||
// - Being conflict checked.
|
||||
// - Has a conflict.
|
||||
// - Received a error while being conflict checked.
|
||||
// - Is a work-in-progress pull request.
|
||||
return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict &&
|
||||
pr.Status != PullRequestStatusError && !pr.IsWorkInProgress()
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ func CheckRepoUnitUser(repo *repo_model.Repository, user *user_model.User, unitT
|
||||
}
|
||||
|
||||
func checkRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
|
||||
if user.IsAdmin {
|
||||
if user != nil && user.IsAdmin {
|
||||
return true
|
||||
}
|
||||
perm, err := getUserRepoPermission(ctx, repo, user)
|
||||
|
||||
@@ -233,14 +233,28 @@ func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Typ
|
||||
builder.Select("repo_id").From("team_repo").Where(
|
||||
builder.Eq{
|
||||
"team_id": teamID,
|
||||
}.And(
|
||||
}.And(builder.Or(
|
||||
// Check if the user is member of the team.
|
||||
builder.In(
|
||||
"team_id", builder.Select("team_id").From("team_user").Where(
|
||||
builder.Eq{
|
||||
"uid": userID,
|
||||
},
|
||||
),
|
||||
)).And(
|
||||
),
|
||||
// Check if the user is in the owner team of the organisation.
|
||||
builder.Exists(builder.Select("team_id").From("team_user").
|
||||
Where(builder.Eq{
|
||||
"org_id": orgID,
|
||||
"team_id": builder.Select("id").From("team").Where(
|
||||
builder.Eq{
|
||||
"org_id": orgID,
|
||||
"lower_name": strings.ToLower(ownerTeamName),
|
||||
}),
|
||||
"uid": userID,
|
||||
}),
|
||||
),
|
||||
)).And(
|
||||
builder.In(
|
||||
"team_id", builder.Select("team_id").From("team_unit").Where(
|
||||
builder.Eq{
|
||||
|
||||
@@ -175,8 +175,10 @@ func init() {
|
||||
|
||||
checkForActionConsistency := func(t assert.TestingT, bean interface{}) {
|
||||
action := reflectionWrap(bean)
|
||||
repoRow := AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": action.int("RepoID")})
|
||||
assert.Equal(t, parseBool(repoRow["is_private"]), action.bool("IsPrivate"), "action: %+v", action)
|
||||
if action.int("RepoID") != 1700 { // dangling intentional
|
||||
repoRow := AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": action.int("RepoID")})
|
||||
assert.Equal(t, parseBool(repoRow["is_private"]), action.bool("IsPrivate"), "action: %+v", action)
|
||||
}
|
||||
}
|
||||
|
||||
consistencyCheckMap["user"] = checkForUserConsistency
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
_ "image/jpeg" // Needed for jpeg support
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
@@ -83,6 +84,11 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
|
||||
}
|
||||
// ***** END: Follow *****
|
||||
|
||||
if _, err := db.GetEngine(ctx).In("grant_id", builder.Select("id").From("oauth2_grant").Where(builder.Eq{"oauth2_grant.user_id": u.ID})).
|
||||
Delete(&auth_model.OAuth2AuthorizationCode{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = deleteBeans(e,
|
||||
&AccessToken{UID: u.ID},
|
||||
&Collaboration{UserID: u.ID},
|
||||
@@ -100,6 +106,8 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
|
||||
&Collaboration{UserID: u.ID},
|
||||
&Stopwatch{UserID: u.ID},
|
||||
&user_model.Setting{UserID: u.ID},
|
||||
&auth_model.OAuth2Application{UID: u.ID},
|
||||
&auth_model.OAuth2Grant{UserID: u.ID},
|
||||
); err != nil {
|
||||
return fmt.Errorf("deleteBeans: %v", err)
|
||||
}
|
||||
|
||||
@@ -316,37 +316,45 @@ func (u *User) GenerateEmailActivateCode(email string) string {
|
||||
}
|
||||
|
||||
// GetUserFollowers returns range of user's followers.
|
||||
func GetUserFollowers(u *User, listOptions db.ListOptions) ([]*User, error) {
|
||||
sess := db.GetEngine(db.DefaultContext).
|
||||
func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) {
|
||||
sess := db.GetEngine(ctx).
|
||||
Select("`user`.*").
|
||||
Join("LEFT", "follow", "`user`.id=follow.user_id").
|
||||
Where("follow.follow_id=?", u.ID).
|
||||
Join("LEFT", "follow", "`user`.id=follow.user_id")
|
||||
And(isUserVisibleToViewerCond(viewer))
|
||||
|
||||
if listOptions.Page != 0 {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
|
||||
users := make([]*User, 0, listOptions.PageSize)
|
||||
return users, sess.Find(&users)
|
||||
count, err := sess.FindAndCount(&users)
|
||||
return users, count, err
|
||||
}
|
||||
|
||||
users := make([]*User, 0, 8)
|
||||
return users, sess.Find(&users)
|
||||
count, err := sess.FindAndCount(&users)
|
||||
return users, count, err
|
||||
}
|
||||
|
||||
// GetUserFollowing returns range of user's following.
|
||||
func GetUserFollowing(u *User, listOptions db.ListOptions) ([]*User, error) {
|
||||
func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) {
|
||||
sess := db.GetEngine(db.DefaultContext).
|
||||
Select("`user`.*").
|
||||
Join("LEFT", "follow", "`user`.id=follow.follow_id").
|
||||
Where("follow.user_id=?", u.ID).
|
||||
Join("LEFT", "follow", "`user`.id=follow.follow_id")
|
||||
And(isUserVisibleToViewerCond(viewer))
|
||||
|
||||
if listOptions.Page != 0 {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
|
||||
users := make([]*User, 0, listOptions.PageSize)
|
||||
return users, sess.Find(&users)
|
||||
count, err := sess.FindAndCount(&users)
|
||||
return users, count, err
|
||||
}
|
||||
|
||||
users := make([]*User, 0, 8)
|
||||
return users, sess.Find(&users)
|
||||
count, err := sess.FindAndCount(&users)
|
||||
return users, count, err
|
||||
}
|
||||
|
||||
// NewGitSig generates and returns the signature of given user.
|
||||
@@ -622,7 +630,14 @@ func IsUsableUsername(name string) error {
|
||||
|
||||
// CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation
|
||||
type CreateUserOverwriteOptions struct {
|
||||
Visibility structs.VisibleType
|
||||
KeepEmailPrivate util.OptionalBool
|
||||
Visibility *structs.VisibleType
|
||||
AllowCreateOrganization util.OptionalBool
|
||||
EmailNotificationsPreference *string
|
||||
MaxRepoCreation *int
|
||||
Theme *string
|
||||
IsRestricted util.OptionalBool
|
||||
IsActive util.OptionalBool
|
||||
}
|
||||
|
||||
// CreateUser creates record of a new user.
|
||||
@@ -638,10 +653,36 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e
|
||||
u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
|
||||
u.MaxRepoCreation = -1
|
||||
u.Theme = setting.UI.DefaultTheme
|
||||
u.IsRestricted = setting.Service.DefaultUserIsRestricted
|
||||
u.IsActive = !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm)
|
||||
|
||||
// overwrite defaults if set
|
||||
if len(overwriteDefault) != 0 && overwriteDefault[0] != nil {
|
||||
u.Visibility = overwriteDefault[0].Visibility
|
||||
overwrite := overwriteDefault[0]
|
||||
if !overwrite.KeepEmailPrivate.IsNone() {
|
||||
u.KeepEmailPrivate = overwrite.KeepEmailPrivate.IsTrue()
|
||||
}
|
||||
if overwrite.Visibility != nil {
|
||||
u.Visibility = *overwrite.Visibility
|
||||
}
|
||||
if !overwrite.AllowCreateOrganization.IsNone() {
|
||||
u.AllowCreateOrganization = overwrite.AllowCreateOrganization.IsTrue()
|
||||
}
|
||||
if overwrite.EmailNotificationsPreference != nil {
|
||||
u.EmailNotificationsPreference = *overwrite.EmailNotificationsPreference
|
||||
}
|
||||
if overwrite.MaxRepoCreation != nil {
|
||||
u.MaxRepoCreation = *overwrite.MaxRepoCreation
|
||||
}
|
||||
if overwrite.Theme != nil {
|
||||
u.Theme = *overwrite.Theme
|
||||
}
|
||||
if !overwrite.IsRestricted.IsNone() {
|
||||
u.IsRestricted = overwrite.IsRestricted.IsTrue()
|
||||
}
|
||||
if !overwrite.IsActive.IsNone() {
|
||||
u.IsActive = overwrite.IsActive.IsTrue()
|
||||
}
|
||||
}
|
||||
|
||||
// validate data
|
||||
@@ -1198,3 +1239,36 @@ func GetAdminUser() (*User, error) {
|
||||
|
||||
return &admin, nil
|
||||
}
|
||||
|
||||
func isUserVisibleToViewerCond(viewer *User) builder.Cond {
|
||||
if viewer != nil && viewer.IsAdmin {
|
||||
return builder.NewCond()
|
||||
}
|
||||
|
||||
if viewer == nil || viewer.IsRestricted {
|
||||
return builder.Eq{
|
||||
"`user`.visibility": structs.VisibleTypePublic,
|
||||
}
|
||||
}
|
||||
|
||||
return builder.Neq{
|
||||
"`user`.visibility": structs.VisibleTypePrivate,
|
||||
}.Or(
|
||||
builder.In("`user`.id",
|
||||
builder.
|
||||
Select("`follow`.user_id").
|
||||
From("follow").
|
||||
Where(builder.Eq{"`follow`.follow_id": viewer.ID})),
|
||||
builder.In("`user`.id",
|
||||
builder.
|
||||
Select("`team_user`.uid").
|
||||
From("team_user").
|
||||
Join("INNER", "`team_user` AS t2", "`team_user`.id = `t2`.id").
|
||||
Where(builder.Eq{"`t2`.uid": viewer.ID})),
|
||||
builder.In("`user`.id",
|
||||
builder.
|
||||
Select("`team_user`.uid").
|
||||
From("team_user").
|
||||
Join("INNER", "`team_user` AS t2", "`team_user`.org_id = `t2`.org_id").
|
||||
Where(builder.Eq{"`t2`.uid": viewer.ID})))
|
||||
}
|
||||
|
||||
@@ -131,7 +131,26 @@ func RemoveBOMIfPresent(content []byte) []byte {
|
||||
|
||||
// DetectEncoding detect the encoding of content
|
||||
func DetectEncoding(content []byte) (string, error) {
|
||||
if utf8.Valid(content) {
|
||||
// First we check if the content represents valid utf8 content excepting a truncated character at the end.
|
||||
|
||||
// Now we could decode all the runes in turn but this is not necessarily the cheapest thing to do
|
||||
// instead we walk backwards from the end to trim off a the incomplete character
|
||||
toValidate := content
|
||||
end := len(toValidate) - 1
|
||||
|
||||
if end < 0 {
|
||||
// no-op
|
||||
} else if toValidate[end]>>5 == 0b110 {
|
||||
// Incomplete 1 byte extension e.g. © <c2><a9> which has been truncated to <c2>
|
||||
toValidate = toValidate[:end]
|
||||
} else if end > 0 && toValidate[end]>>6 == 0b10 && toValidate[end-1]>>4 == 0b1110 {
|
||||
// Incomplete 2 byte extension e.g. ⛔ <e2><9b><94> which has been truncated to <e2><9b>
|
||||
toValidate = toValidate[:end-1]
|
||||
} else if end > 1 && toValidate[end]>>6 == 0b10 && toValidate[end-1]>>6 == 0b10 && toValidate[end-2]>>3 == 0b11110 {
|
||||
// Incomplete 3 byte extension e.g. 💩 <f0><9f><92><a9> which has been truncated to <f0><9f><92>
|
||||
toValidate = toValidate[:end-2]
|
||||
}
|
||||
if utf8.Valid(toValidate) {
|
||||
log.Debug("Detected encoding: utf-8 (fast)")
|
||||
return "UTF-8", nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
package charset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -272,3 +274,145 @@ func stringMustEndWith(t *testing.T, expected, value string) {
|
||||
func bytesMustStartWith(t *testing.T, expected, value []byte) {
|
||||
assert.Equal(t, expected, value[:len(expected)])
|
||||
}
|
||||
|
||||
func TestToUTF8WithFallbackReader(t *testing.T) {
|
||||
resetDefaultCharsetsOrder()
|
||||
|
||||
for testLen := 0; testLen < 2048; testLen++ {
|
||||
pattern := " test { () }\n"
|
||||
input := ""
|
||||
for len(input) < testLen {
|
||||
input += pattern
|
||||
}
|
||||
input = input[:testLen]
|
||||
input += "// Выключаем"
|
||||
rd := ToUTF8WithFallbackReader(bytes.NewReader([]byte(input)))
|
||||
r, _ := io.ReadAll(rd)
|
||||
assert.EqualValuesf(t, input, string(r), "testing string len=%d", testLen)
|
||||
}
|
||||
|
||||
truncatedOneByteExtension := failFastBytes
|
||||
encoding, _ := DetectEncoding(truncatedOneByteExtension)
|
||||
assert.Equal(t, "UTF-8", encoding)
|
||||
|
||||
truncatedTwoByteExtension := failFastBytes
|
||||
truncatedTwoByteExtension[len(failFastBytes)-1] = 0x9b
|
||||
truncatedTwoByteExtension[len(failFastBytes)-2] = 0xe2
|
||||
|
||||
encoding, _ = DetectEncoding(truncatedTwoByteExtension)
|
||||
assert.Equal(t, "UTF-8", encoding)
|
||||
|
||||
truncatedThreeByteExtension := failFastBytes
|
||||
truncatedThreeByteExtension[len(failFastBytes)-1] = 0x92
|
||||
truncatedThreeByteExtension[len(failFastBytes)-2] = 0x9f
|
||||
truncatedThreeByteExtension[len(failFastBytes)-3] = 0xf0
|
||||
|
||||
encoding, _ = DetectEncoding(truncatedThreeByteExtension)
|
||||
assert.Equal(t, "UTF-8", encoding)
|
||||
}
|
||||
|
||||
var failFastBytes = []byte{
|
||||
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x72, 0x67, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x74, 0x6f,
|
||||
0x6f, 0x6c, 0x73, 0x2e, 0x61, 0x6e, 0x74, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x64, 0x65, 0x66, 0x73, 0x2e, 0x63, 0x6f, 0x6e,
|
||||
0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4f, 0x73, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x72, 0x67,
|
||||
0x2e, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x2e, 0x67, 0x72, 0x61, 0x64, 0x6c, 0x65, 0x2e, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x2e, 0x72, 0x75, 0x6e, 0x2e, 0x42,
|
||||
0x6f, 0x6f, 0x74, 0x52, 0x75, 0x6e, 0x0a, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x69, 0x64, 0x28, 0x22, 0x6f, 0x72, 0x67, 0x2e, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x66, 0x72, 0x61, 0x6d,
|
||||
0x65, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x22, 0x29, 0x0a, 0x7d, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x65,
|
||||
0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x22, 0x3a,
|
||||
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x61, 0x70, 0x69, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d,
|
||||
0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
|
||||
0x28, 0x22, 0x3a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x61, 0x70, 0x69, 0x2d, 0x64, 0x6f, 0x63, 0x73, 0x22, 0x29,
|
||||
0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x28, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x22, 0x3a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x64, 0x62,
|
||||
0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x28, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x22, 0x3a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a,
|
||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x22, 0x3a,
|
||||
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3a, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x66,
|
||||
0x73, 0x22, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x28, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x22, 0x3a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||
0x3a, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6d, 0x71, 0x22, 0x29, 0x29, 0x0a, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22,
|
||||
0x6a, 0x66, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x65, 0x3a, 0x70, 0x65, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x2d, 0x61, 0x75, 0x74, 0x68, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74,
|
||||
0x65, 0x72, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x28, 0x22, 0x6a, 0x66, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x65, 0x3a, 0x70, 0x65, 0x2d, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2d, 0x68, 0x61, 0x6c, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c,
|
||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22, 0x6a, 0x66, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x2e,
|
||||
0x70, 0x65, 0x3a, 0x70, 0x65, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x29, 0x0a,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28,
|
||||
0x22, 0x6f, 0x72, 0x67, 0x2e, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b,
|
||||
0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x3a, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x74,
|
||||
0x61, 0x72, 0x74, 0x65, 0x72, 0x2d, 0x77, 0x65, 0x62, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c,
|
||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22, 0x6f, 0x72, 0x67, 0x2e, 0x73, 0x70, 0x72, 0x69,
|
||||
0x6e, 0x67, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x3a, 0x73, 0x70, 0x72,
|
||||
0x69, 0x6e, 0x67, 0x2d, 0x62, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x72, 0x2d, 0x61, 0x6f, 0x70,
|
||||
0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x28, 0x22, 0x6f, 0x72, 0x67, 0x2e, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x3a, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x62, 0x6f, 0x6f, 0x74, 0x2d,
|
||||
0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x72, 0x2d, 0x61, 0x63, 0x74, 0x75, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x29, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22, 0x6f,
|
||||
0x72, 0x67, 0x2e, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63,
|
||||
0x6c, 0x6f, 0x75, 0x64, 0x3a, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x73, 0x74,
|
||||
0x61, 0x72, 0x74, 0x65, 0x72, 0x2d, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x22, 0x29, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22, 0x6f, 0x72,
|
||||
0x67, 0x2e, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6c,
|
||||
0x6f, 0x75, 0x64, 0x3a, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x73, 0x74, 0x61,
|
||||
0x72, 0x74, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2d, 0x61, 0x6c, 0x6c, 0x22, 0x29, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22, 0x6f, 0x72,
|
||||
0x67, 0x2e, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x63, 0x6c,
|
||||
0x6f, 0x75, 0x64, 0x3a, 0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x73, 0x74, 0x61,
|
||||
0x72, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x6c, 0x65, 0x75, 0x74, 0x68, 0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d,
|
||||
0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22, 0x6f, 0x72, 0x67, 0x2e, 0x73, 0x70,
|
||||
0x72, 0x69, 0x6e, 0x67, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x72, 0x65, 0x74, 0x72, 0x79, 0x3a,
|
||||
0x73, 0x70, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x72, 0x65, 0x74, 0x72, 0x79, 0x22, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22, 0x63, 0x68, 0x2e, 0x71,
|
||||
0x6f, 0x73, 0x2e, 0x6c, 0x6f, 0x67, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x6c, 0x6f, 0x67, 0x62, 0x61, 0x63, 0x6b, 0x2d, 0x63,
|
||||
0x6c, 0x61, 0x73, 0x73, 0x69, 0x63, 0x22, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22, 0x69, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x6d, 0x65,
|
||||
0x74, 0x65, 0x72, 0x3a, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x2d, 0x72, 0x65, 0x67, 0x69, 0x73,
|
||||
0x74, 0x72, 0x79, 0x2d, 0x70, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x65, 0x75, 0x73, 0x22, 0x29, 0x0a, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6b, 0x6f, 0x74,
|
||||
0x6c, 0x69, 0x6e, 0x28, 0x22, 0x73, 0x74, 0x64, 0x6c, 0x69, 0x62, 0x22, 0x29, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
|
||||
0x2f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64,
|
||||
0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
|
||||
0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74,
|
||||
0x65, 0x73, 0x74, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x22, 0x6a,
|
||||
0x66, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x65, 0x3a, 0x70, 0x65, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2d,
|
||||
0x74, 0x65, 0x73, 0x74, 0x22, 0x29, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x20, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4a,
|
||||
0x61, 0x72, 0x20, 0x62, 0x79, 0x20, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
|
||||
0x69, 0x6e, 0x67, 0x28, 0x4a, 0x61, 0x72, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2e,
|
||||
0x73, 0x65, 0x74, 0x28, 0x22, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x22, 0x29, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x76, 0x61, 0x6c, 0x20, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x70, 0x61, 0x74, 0x68,
|
||||
0x20, 0x62, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x67,
|
||||
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74,
|
||||
0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
|
||||
0x73, 0x28, 0x22, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x2d, 0x50, 0x61, 0x74, 0x68, 0x22, 0x20, 0x74, 0x6f, 0x20, 0x6f, 0x62,
|
||||
0x6a, 0x65, 0x63, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70,
|
||||
0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x20, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x3d,
|
||||
0x20, 0x22, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x2f, 0x2b, 0x22, 0x2e, 0x74, 0x6f, 0x52, 0x65, 0x67, 0x65, 0x78, 0x28, 0x29,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
|
||||
0x65, 0x20, 0x66, 0x75, 0x6e, 0x20, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x3a, 0x20, 0x53, 0x74,
|
||||
0x72, 0x69, 0x6e, 0x67, 0x20, 0x3d, 0x20, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x70,
|
||||
0x61, 0x74, 0x68, 0x2e, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x53, 0x74, 0x72, 0x69,
|
||||
0x6e, 0x67, 0x28, 0x22, 0x20, 0x22, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x2e, 0x74, 0x6f, 0x55, 0x52, 0x49, 0x28, 0x29, 0x2e, 0x74, 0x6f, 0x55,
|
||||
0x52, 0x4c, 0x28, 0x29, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x29, 0x2e, 0x72, 0x65, 0x70, 0x6c,
|
||||
0x61, 0x63, 0x65, 0x46, 0x69, 0x72, 0x73, 0x74, 0x28, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x2c, 0x20, 0x22, 0x2f,
|
||||
0x22, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x74, 0x61, 0x73,
|
||||
0x6b, 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x3c, 0x42, 0x6f, 0x6f, 0x74, 0x52, 0x75, 0x6e, 0x3e, 0x28, 0x22, 0x62,
|
||||
0x6f, 0x6f, 0x74, 0x52, 0x75, 0x6e, 0x22, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x4f,
|
||||
0x73, 0x2e, 0x69, 0x73, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x28, 0x4f, 0x73, 0x2e, 0x46, 0x41, 0x4d, 0x49, 0x4c, 0x59,
|
||||
0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x29, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x70, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x28, 0x73,
|
||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x74, 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x64, 0x28, 0x22, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x22, 0x29, 0x2e, 0x6d, 0x61, 0x70, 0x20, 0x7b, 0x20, 0x69, 0x74, 0x2e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20,
|
||||
0x7d, 0x2c, 0x20, 0x70, 0x61, 0x74, 0x63, 0x68, 0x4a, 0x61, 0x72, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0xd0,
|
||||
}
|
||||
|
||||
@@ -288,6 +288,7 @@ func APIContexter() func(http.Handler) http.Handler {
|
||||
},
|
||||
Org: &APIOrganization{},
|
||||
}
|
||||
defer ctx.Close()
|
||||
|
||||
ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx)
|
||||
ctx.csrf = Csrfer(csrfOpts, ctx.Context)
|
||||
|
||||
@@ -71,6 +71,16 @@ type Context struct {
|
||||
Org *Organization
|
||||
}
|
||||
|
||||
// Close frees all resources hold by Context
|
||||
func (ctx *Context) Close() error {
|
||||
var err error
|
||||
if ctx.Req != nil && ctx.Req.MultipartForm != nil {
|
||||
err = ctx.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
|
||||
}
|
||||
// TODO: close opened repo, and more
|
||||
return err
|
||||
}
|
||||
|
||||
// TrHTMLEscapeArgs runs Tr but pre-escapes all arguments with html.EscapeString.
|
||||
// This is useful if the locale message is intended to only produce HTML content.
|
||||
func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
|
||||
@@ -643,6 +653,8 @@ func Contexter() func(next http.Handler) http.Handler {
|
||||
"RunModeIsProd": setting.IsProd,
|
||||
},
|
||||
}
|
||||
defer ctx.Close()
|
||||
|
||||
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
|
||||
ctx.PageData = map[string]interface{}{}
|
||||
ctx.Data["PageData"] = ctx.PageData
|
||||
|
||||
@@ -70,12 +70,6 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
||||
org := ctx.Org.Organization
|
||||
ctx.Data["Org"] = org
|
||||
|
||||
teams, err := org.LoadTeams()
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadTeams", err)
|
||||
}
|
||||
ctx.Data["OrgTeams"] = teams
|
||||
|
||||
// Admin has super access.
|
||||
if ctx.IsSigned && ctx.User.IsAdmin {
|
||||
ctx.Org.IsOwner = true
|
||||
|
||||
@@ -38,6 +38,8 @@ func PrivateContexter() func(http.Handler) http.Handler {
|
||||
Data: map[string]interface{}{},
|
||||
},
|
||||
}
|
||||
defer ctx.Close()
|
||||
|
||||
ctx.Req = WithPrivateContext(req, ctx)
|
||||
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||
})
|
||||
|
||||
@@ -410,6 +410,12 @@ func RepoIDAssignment() func(ctx *Context) {
|
||||
|
||||
// RepoAssignment returns a middleware to handle repository assignment
|
||||
func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
||||
if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce {
|
||||
log.Trace("RepoAssignment was exec already, skipping second call ...")
|
||||
return
|
||||
}
|
||||
ctx.Data["repoAssignmentExecuted"] = true
|
||||
|
||||
var (
|
||||
owner *user_model.User
|
||||
err error
|
||||
@@ -592,6 +598,9 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
||||
ctx.ServerError("RepoAssignment Invalid repo "+repo_model.RepoPath(userName, repoName), err)
|
||||
return
|
||||
}
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
ctx.Repo.GitRepo.Close()
|
||||
}
|
||||
ctx.Repo.GitRepo = gitRepo
|
||||
|
||||
// We opened it, we should close it
|
||||
|
||||
@@ -67,6 +67,7 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *user_model.User) *api.PullRe
|
||||
PatchURL: pr.Issue.PatchURL(),
|
||||
HasMerged: pr.HasMerged,
|
||||
MergeBase: pr.MergeBase,
|
||||
Mergeable: pr.Mergeable(),
|
||||
Deadline: apiIssue.Deadline,
|
||||
Created: pr.Issue.CreatedUnix.AsTimePtr(),
|
||||
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
|
||||
@@ -190,10 +191,6 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *user_model.User) *api.PullRe
|
||||
}
|
||||
}
|
||||
|
||||
if pr.Status != models.PullRequestStatusChecking {
|
||||
mergeable := !(pr.Status == models.PullRequestStatusConflict || pr.Status == models.PullRequestStatusError) && !pr.IsWorkInProgress()
|
||||
apiPullRequest.Mergeable = mergeable
|
||||
}
|
||||
if pr.HasMerged {
|
||||
apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
|
||||
apiPullRequest.MergedCommitID = &pr.MergedCommitID
|
||||
|
||||
@@ -71,8 +71,8 @@ func checkAuthorizedKeys(logger log.Logger, autofix bool) error {
|
||||
"authorized_keys file %q is out of date.\nRegenerate it with:\n\t\"%s\"\nor\n\t\"%s\"",
|
||||
fPath,
|
||||
"gitea admin regenerate keys",
|
||||
"gitea doctor --run authorized_keys --fix")
|
||||
return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized_keys --fix"`)
|
||||
"gitea doctor --run authorized-keys --fix")
|
||||
return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized-keys --fix"`)
|
||||
}
|
||||
logger.Warn("authorized_keys is out of date. Attempting rewrite...")
|
||||
err = asymkey_model.RewriteAllPublicKeys()
|
||||
|
||||
@@ -142,6 +142,12 @@ func checkDBConsistency(logger log.Logger, autofix bool) error {
|
||||
Fixer: models.FixIssueLabelWithOutsideLabels,
|
||||
FixedMessage: "Removed",
|
||||
},
|
||||
{
|
||||
Name: "Action with created_unix set as an empty string",
|
||||
Counter: models.CountActionCreatedUnixString,
|
||||
Fixer: models.FixActionCreatedUnixString,
|
||||
FixedMessage: "Set to zero",
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: function to recalc all counters
|
||||
@@ -177,6 +183,18 @@ func checkDBConsistency(logger log.Logger, autofix bool) error {
|
||||
// find access without repository
|
||||
genericOrphanCheck("Access entries without existing repository",
|
||||
"access", "repository", "access.repo_id=repository.id"),
|
||||
// find action without repository
|
||||
genericOrphanCheck("Action entries without existing repository",
|
||||
"action", "repository", "action.repo_id=repository.id"),
|
||||
// find OAuth2Grant without existing user
|
||||
genericOrphanCheck("Orphaned OAuth2Grant without existing User",
|
||||
"oauth2_grant", "user", "oauth2_grant.user_id=`user`.id"),
|
||||
// find OAuth2Application without existing user
|
||||
genericOrphanCheck("Orphaned OAuth2Application without existing User",
|
||||
"oauth2_application", "user", "oauth2_application.uid=`user`.id"),
|
||||
// find OAuth2AuthorizationCode without existing OAuth2Grant
|
||||
genericOrphanCheck("Orphaned OAuth2AuthorizationCode without existing OAuth2Grant",
|
||||
"oauth2_authorization_code", "oauth2_grant", "oauth2_authorization_code.grant_id=oauth2_grant.id"),
|
||||
)
|
||||
|
||||
for _, c := range consistencyChecks {
|
||||
|
||||
@@ -54,6 +54,12 @@ func CatFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
|
||||
<-closed
|
||||
}
|
||||
|
||||
// Ensure cancel is called as soon as the provided context is cancelled
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
cancel()
|
||||
}()
|
||||
|
||||
_, filename, line, _ := runtime.Caller(2)
|
||||
filename = strings.TrimPrefix(filename, callerPrefix)
|
||||
|
||||
@@ -93,6 +99,12 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
|
||||
<-closed
|
||||
}
|
||||
|
||||
// Ensure cancel is called as soon as the provided context is cancelled
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
cancel()
|
||||
}()
|
||||
|
||||
_, filename, line, _ := runtime.Caller(2)
|
||||
filename = strings.TrimPrefix(filename, callerPrefix)
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ func createBlameReader(ctx context.Context, dir string, command ...string) (*Bla
|
||||
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
|
||||
cmd.Dir = dir
|
||||
cmd.Stderr = os.Stderr
|
||||
process.SetSysProcAttribute(cmd)
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
|
||||
@@ -188,6 +188,7 @@ func (c *Command) RunWithContext(rc *RunContext) error {
|
||||
if goVersionLessThan115 {
|
||||
cmd.Env = append(cmd.Env, "GODEBUG=asyncpreemptoff=1")
|
||||
}
|
||||
process.SetSysProcAttribute(cmd)
|
||||
cmd.Dir = rc.Dir
|
||||
cmd.Stdout = rc.Stdout
|
||||
cmd.Stderr = rc.Stderr
|
||||
|
||||
@@ -47,7 +47,10 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note)
|
||||
commitID = commitID[2:]
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Unable to find git note corresponding to the commit %q. Error: %v", originalCommitID, err)
|
||||
// Err may have been updated by the SubTree we need to recheck if it's again an ErrNotExist
|
||||
if !IsErrNotExist(err) {
|
||||
log.Error("Unable to find git note corresponding to the commit %q. Error: %v", originalCommitID, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,12 +119,10 @@ type CheckAttributeReader struct {
|
||||
env []string
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
running chan struct{}
|
||||
}
|
||||
|
||||
// Init initializes the cmd
|
||||
func (c *CheckAttributeReader) Init(ctx context.Context) error {
|
||||
c.running = make(chan struct{})
|
||||
cmdArgs := []string{"check-attr", "--stdin", "-z"}
|
||||
|
||||
if len(c.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
|
||||
@@ -183,14 +181,7 @@ func (c *CheckAttributeReader) Run() error {
|
||||
_ = c.stdOut.Close()
|
||||
}()
|
||||
stdErr := new(bytes.Buffer)
|
||||
err := c.cmd.RunInDirTimeoutEnvFullPipelineFunc(c.env, -1, c.Repo.Path, c.stdOut, stdErr, c.stdinReader, func(_ context.Context, _ context.CancelFunc) error {
|
||||
select {
|
||||
case <-c.running:
|
||||
default:
|
||||
close(c.running)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
err := c.cmd.RunInDirTimeoutEnvFullPipeline(c.env, -1, c.Repo.Path, c.stdOut, stdErr, c.stdinReader)
|
||||
if err != nil && // If there is an error we need to return but:
|
||||
c.ctx.Err() != err && // 1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded)
|
||||
err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed
|
||||
@@ -210,7 +201,7 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return nil, c.ctx.Err()
|
||||
case <-c.running:
|
||||
default:
|
||||
}
|
||||
|
||||
if _, err = c.stdinWriter.Write([]byte(path + "\x00")); err != nil {
|
||||
@@ -237,11 +228,6 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
|
||||
func (c *CheckAttributeReader) Close() error {
|
||||
c.cancel()
|
||||
err := c.stdinWriter.Close()
|
||||
select {
|
||||
case <-c.running:
|
||||
default:
|
||||
close(c.running)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -127,13 +127,18 @@ func (hl *HostMatchList) checkIP(ip net.IP) bool {
|
||||
|
||||
// MatchHostName checks if the host matches an allow/deny(block) list
|
||||
func (hl *HostMatchList) MatchHostName(host string) bool {
|
||||
hostname, _, err := net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
hostname = host
|
||||
}
|
||||
|
||||
if hl == nil {
|
||||
return false
|
||||
}
|
||||
if hl.checkPattern(host) {
|
||||
if hl.checkPattern(hostname) {
|
||||
return true
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip := net.ParseIP(hostname); ip != nil {
|
||||
return hl.checkIP(ip)
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -38,6 +38,7 @@ func TestHostOrIPMatchesList(t *testing.T) {
|
||||
|
||||
{"", net.ParseIP("10.0.1.1"), true},
|
||||
{"10.0.1.1", nil, true},
|
||||
{"10.0.1.1:8080", nil, true},
|
||||
{"", net.ParseIP("192.168.1.1"), true},
|
||||
{"192.168.1.1", nil, true},
|
||||
{"", net.ParseIP("fd00::1"), true},
|
||||
@@ -48,6 +49,7 @@ func TestHostOrIPMatchesList(t *testing.T) {
|
||||
|
||||
{"mydomain.com", net.IPv4zero, false},
|
||||
{"sub.mydomain.com", net.IPv4zero, true},
|
||||
{"sub.mydomain.com:8080", net.IPv4zero, true},
|
||||
|
||||
{"", net.ParseIP("169.254.1.1"), true},
|
||||
{"169.254.1.1", nil, true},
|
||||
|
||||
@@ -130,7 +130,7 @@ func Init() {
|
||||
log.Info("PID: %d Repository Indexer closed", os.Getpid())
|
||||
})
|
||||
|
||||
waitChannel := make(chan time.Duration)
|
||||
waitChannel := make(chan time.Duration, 1)
|
||||
|
||||
// Create the Queue
|
||||
switch setting.Indexer.RepoType {
|
||||
|
||||
@@ -98,7 +98,7 @@ var (
|
||||
// InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until
|
||||
// all issue index done.
|
||||
func InitIssueIndexer(syncReindex bool) {
|
||||
waitChannel := make(chan time.Duration)
|
||||
waitChannel := make(chan time.Duration, 1)
|
||||
|
||||
// Create the Queue
|
||||
switch setting.Indexer.IssueType {
|
||||
@@ -272,7 +272,7 @@ func populateIssueIndexer(ctx context.Context) {
|
||||
// UpdateRepoIndexer add/update all issues of the repositories
|
||||
func UpdateRepoIndexer(repo *repo_model.Repository) {
|
||||
is, err := models.Issues(&models.IssuesOptions{
|
||||
RepoIDs: []int64{repo.ID},
|
||||
RepoID: repo.ID,
|
||||
IsClosed: util.OptionalBoolNone,
|
||||
IsPull: util.OptionalBoolNone,
|
||||
})
|
||||
|
||||
2
modules/markup/external/external.go
vendored
2
modules/markup/external/external.go
vendored
@@ -119,6 +119,8 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
|
||||
cmd.Stdin = input
|
||||
}
|
||||
cmd.Stdout = output
|
||||
process.SetSysProcAttribute(cmd)
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("%s render run command %s %v failed: %v", p.Name(), commands[0], args, err)
|
||||
}
|
||||
|
||||
@@ -254,13 +254,13 @@ func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env
|
||||
if stdIn != nil {
|
||||
cmd.Stdin = stdIn
|
||||
}
|
||||
SetSysProcAttribute(cmd)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
err := cmd.Wait()
|
||||
|
||||
if err != nil {
|
||||
err = &Error{
|
||||
PID: GetPID(ctx),
|
||||
|
||||
19
modules/process/manager_unix.go
Normal file
19
modules/process/manager_unix.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// SetSysProcAttribute sets the common SysProcAttrs for commands
|
||||
func SetSysProcAttribute(cmd *exec.Cmd) {
|
||||
// When Gitea runs SubProcessA -> SubProcessB and SubProcessA gets killed by context timeout, use setpgid to make sure the sub processes can be reaped instead of leaving defunct(zombie) processes.
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
}
|
||||
17
modules/process/manager_windows.go
Normal file
17
modules/process/manager_windows.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package process
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// SetSysProcAttribute sets the common SysProcAttrs for commands
|
||||
func SetSysProcAttribute(cmd *exec.Cmd) {
|
||||
// Do nothing
|
||||
}
|
||||
@@ -19,6 +19,10 @@ import (
|
||||
// they use to detect if there is a block and will grow and shrink in
|
||||
// response to demand as per configuration.
|
||||
type WorkerPool struct {
|
||||
// This field requires to be the first one in the struct.
|
||||
// This is to allow 64 bit atomic operations on 32-bit machines.
|
||||
// See: https://pkg.go.dev/sync/atomic#pkg-note-BUG & Gitea issue 19518
|
||||
numInQueue int64
|
||||
lock sync.Mutex
|
||||
baseCtx context.Context
|
||||
baseCtxCancel context.CancelFunc
|
||||
@@ -32,7 +36,6 @@ type WorkerPool struct {
|
||||
blockTimeout time.Duration
|
||||
boostTimeout time.Duration
|
||||
boostWorkers int
|
||||
numInQueue int64
|
||||
}
|
||||
|
||||
// WorkerPoolConfiguration is the basic configuration for a WorkerPool
|
||||
|
||||
@@ -92,7 +92,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
|
||||
}
|
||||
|
||||
if err = git.CloneWithContext(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||
if err := git.CloneWithContext(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
@@ -103,11 +103,12 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
if err := util.RemoveAll(wikiPath); err != nil {
|
||||
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
|
||||
}
|
||||
} else {
|
||||
if err := git.WriteCommitGraph(ctx, wikiPath); err != nil {
|
||||
return repo, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := git.WriteCommitGraph(ctx, wikiPath); err != nil {
|
||||
return repo, err
|
||||
}
|
||||
}
|
||||
|
||||
if repo.OwnerID == u.ID {
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
@@ -100,6 +101,8 @@ func sessionHandler(session ssh.Session) {
|
||||
}
|
||||
defer stdin.Close()
|
||||
|
||||
process.SetSysProcAttribute(cmd)
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
@@ -330,7 +333,7 @@ func GenKeyPair(keyPath string) error {
|
||||
}
|
||||
|
||||
privateKeyPEM := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}
|
||||
f, err := os.OpenFile(keyPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
f, err := os.OpenFile(keyPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -351,7 +354,7 @@ func GenKeyPair(keyPath string) error {
|
||||
}
|
||||
|
||||
public := gossh.MarshalAuthorizedKey(pub)
|
||||
p, err := os.OpenFile(keyPath+".pub", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
p, err := os.OpenFile(keyPath+".pub", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ type CreateUserOption struct {
|
||||
Password string `json:"password" binding:"Required;MaxSize(255)"`
|
||||
MustChangePassword *bool `json:"must_change_password"`
|
||||
SendNotify bool `json:"send_notify"`
|
||||
Restricted *bool `json:"restricted"`
|
||||
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ func MockContext(t *testing.T, path string) *context.Context {
|
||||
Resp: context.NewResponse(resp),
|
||||
Locale: &mockLocale{},
|
||||
}
|
||||
defer ctx.Close()
|
||||
|
||||
requestURL, err := url.Parse(path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -17,8 +17,12 @@ import (
|
||||
// Use at most this many bytes to determine Content Type.
|
||||
const sniffLen = 1024
|
||||
|
||||
// SvgMimeType MIME type of SVG images.
|
||||
const SvgMimeType = "image/svg+xml"
|
||||
const (
|
||||
// SvgMimeType MIME type of SVG images.
|
||||
SvgMimeType = "image/svg+xml"
|
||||
// ApplicationOctetStream MIME type of binary files.
|
||||
ApplicationOctetStream = "application/octet-stream"
|
||||
)
|
||||
|
||||
var svgTagRegex = regexp.MustCompile(`(?si)\A\s*(?:(<!--.*?-->|<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg[\s>\/]`)
|
||||
var svgTagInXMLRegex = regexp.MustCompile(`(?si)\A<\?xml\b.*?\?>\s*(?:(<!--.*?-->|<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg[\s>\/]`)
|
||||
|
||||
@@ -42,11 +42,17 @@ func Wrap(handlers ...interface{}) http.HandlerFunc {
|
||||
handler := handlers[i]
|
||||
switch t := handler.(type) {
|
||||
case http.HandlerFunc:
|
||||
if _, ok := resp.(context.ResponseWriter); !ok {
|
||||
resp = context.NewResponse(resp)
|
||||
}
|
||||
t(resp, req)
|
||||
if r, ok := resp.(context.ResponseWriter); ok && r.Status() > 0 {
|
||||
return
|
||||
}
|
||||
case func(http.ResponseWriter, *http.Request):
|
||||
if _, ok := resp.(context.ResponseWriter); !ok {
|
||||
resp = context.NewResponse(resp)
|
||||
}
|
||||
t(resp, req)
|
||||
if r, ok := resp.(context.ResponseWriter); ok && r.Status() > 0 {
|
||||
return
|
||||
@@ -88,7 +94,7 @@ func Wrap(handlers ...interface{}) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
case func(http.Handler) http.Handler:
|
||||
var next = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
|
||||
next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
|
||||
if len(handlers) > i+1 {
|
||||
next = Wrap(handlers[i+1:]...)
|
||||
}
|
||||
@@ -148,7 +154,7 @@ func MiddleAPI(f func(ctx *context.APIContext)) func(netx http.Handler) http.Han
|
||||
|
||||
// Bind binding an obj to a handler
|
||||
func Bind(obj interface{}) http.HandlerFunc {
|
||||
var tp = reflect.TypeOf(obj)
|
||||
tp := reflect.TypeOf(obj)
|
||||
if tp.Kind() == reflect.Ptr {
|
||||
tp = tp.Elem()
|
||||
}
|
||||
@@ -156,7 +162,7 @@ func Bind(obj interface{}) http.HandlerFunc {
|
||||
panic("Only structs are allowed to bind")
|
||||
}
|
||||
return Wrap(func(ctx *context.Context) {
|
||||
var theObj = reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly
|
||||
theObj := reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly
|
||||
binding.Bind(ctx.Req, theObj)
|
||||
SetForm(ctx, theObj)
|
||||
middleware.AssignForm(theObj, ctx.Data)
|
||||
@@ -214,8 +220,8 @@ func (r *Route) Use(middlewares ...interface{}) {
|
||||
|
||||
// Group mounts a sub-Router along a `pattern` string.
|
||||
func (r *Route) Group(pattern string, fn func(), middlewares ...interface{}) {
|
||||
var previousGroupPrefix = r.curGroupPrefix
|
||||
var previousMiddlewares = r.curMiddlewares
|
||||
previousGroupPrefix := r.curGroupPrefix
|
||||
previousMiddlewares := r.curMiddlewares
|
||||
r.curGroupPrefix += pattern
|
||||
r.curMiddlewares = append(r.curMiddlewares, middlewares...)
|
||||
|
||||
@@ -238,7 +244,7 @@ func (r *Route) getPattern(pattern string) string {
|
||||
|
||||
// Mount attaches another Route along ./pattern/*
|
||||
func (r *Route) Mount(pattern string, subR *Route) {
|
||||
var middlewares = make([]interface{}, len(r.curMiddlewares))
|
||||
middlewares := make([]interface{}, len(r.curMiddlewares))
|
||||
copy(middlewares, r.curMiddlewares)
|
||||
subR.Use(middlewares...)
|
||||
r.R.Mount(r.getPattern(pattern), subR.R)
|
||||
@@ -246,7 +252,7 @@ func (r *Route) Mount(pattern string, subR *Route) {
|
||||
|
||||
// Any delegate requests for all methods
|
||||
func (r *Route) Any(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.HandleFunc(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
@@ -254,7 +260,7 @@ func (r *Route) Any(pattern string, h ...interface{}) {
|
||||
func (r *Route) Route(pattern, methods string, h ...interface{}) {
|
||||
p := r.getPattern(pattern)
|
||||
ms := strings.Split(methods, ",")
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
for _, method := range ms {
|
||||
r.R.MethodFunc(strings.TrimSpace(method), p, Wrap(middlewares...))
|
||||
}
|
||||
@@ -262,12 +268,12 @@ func (r *Route) Route(pattern, methods string, h ...interface{}) {
|
||||
|
||||
// Delete delegate delete method
|
||||
func (r *Route) Delete(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.Delete(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
func (r *Route) getMiddlewares(h []interface{}) []interface{} {
|
||||
var middlewares = make([]interface{}, len(r.curMiddlewares), len(r.curMiddlewares)+len(h))
|
||||
middlewares := make([]interface{}, len(r.curMiddlewares), len(r.curMiddlewares)+len(h))
|
||||
copy(middlewares, r.curMiddlewares)
|
||||
middlewares = append(middlewares, h...)
|
||||
return middlewares
|
||||
@@ -275,51 +281,51 @@ func (r *Route) getMiddlewares(h []interface{}) []interface{} {
|
||||
|
||||
// Get delegate get method
|
||||
func (r *Route) Get(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.Get(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
// Options delegate options method
|
||||
func (r *Route) Options(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.Options(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
// GetOptions delegate get and options method
|
||||
func (r *Route) GetOptions(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.Get(r.getPattern(pattern), Wrap(middlewares...))
|
||||
r.R.Options(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
// PostOptions delegate post and options method
|
||||
func (r *Route) PostOptions(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.Post(r.getPattern(pattern), Wrap(middlewares...))
|
||||
r.R.Options(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
// Head delegate head method
|
||||
func (r *Route) Head(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.Head(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
// Post delegate post method
|
||||
func (r *Route) Post(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.Post(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
// Put delegate put method
|
||||
func (r *Route) Put(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.Put(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
// Patch delegate patch method
|
||||
func (r *Route) Patch(pattern string, h ...interface{}) {
|
||||
var middlewares = r.getMiddlewares(h)
|
||||
middlewares := r.getMiddlewares(h)
|
||||
r.R.Patch(r.getPattern(pattern), Wrap(middlewares...))
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ func TestRoute2(t *testing.T) {
|
||||
tp := chi.URLParam(req, "type")
|
||||
assert.EqualValues(t, "issues", tp)
|
||||
route = 0
|
||||
resp.WriteHeader(200)
|
||||
})
|
||||
|
||||
r.Get("/{type:issues|pulls}/{index}", func(resp http.ResponseWriter, req *http.Request) {
|
||||
@@ -65,9 +66,8 @@ func TestRoute2(t *testing.T) {
|
||||
index := chi.URLParam(req, "index")
|
||||
assert.EqualValues(t, "1", index)
|
||||
route = 1
|
||||
resp.WriteHeader(200)
|
||||
})
|
||||
}, func(resp http.ResponseWriter, req *http.Request) {
|
||||
resp.WriteHeader(200)
|
||||
})
|
||||
|
||||
r.Group("/issues/{index}", func() {
|
||||
@@ -79,6 +79,7 @@ func TestRoute2(t *testing.T) {
|
||||
index := chi.URLParam(req, "index")
|
||||
assert.EqualValues(t, "1", index)
|
||||
route = 2
|
||||
resp.WriteHeader(200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/password"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/api/v1/user"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
@@ -81,7 +82,6 @@ func CreateUser(ctx *context.APIContext) {
|
||||
Email: form.Email,
|
||||
Passwd: form.Password,
|
||||
MustChangePassword: true,
|
||||
IsActive: true,
|
||||
LoginType: auth.Plain,
|
||||
}
|
||||
if form.MustChangePassword != nil {
|
||||
@@ -107,11 +107,17 @@ func CreateUser(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
var overwriteDefault *user_model.CreateUserOverwriteOptions
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
}
|
||||
|
||||
if form.Restricted != nil {
|
||||
overwriteDefault.IsRestricted = util.OptionalBoolOf(*form.Restricted)
|
||||
}
|
||||
|
||||
if form.Visibility != "" {
|
||||
overwriteDefault = &user_model.CreateUserOverwriteOptions{
|
||||
Visibility: api.VisibilityModes[form.Visibility],
|
||||
}
|
||||
visibility := api.VisibilityModes[form.Visibility]
|
||||
overwriteDefault.Visibility = &visibility
|
||||
}
|
||||
|
||||
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
||||
|
||||
@@ -954,7 +954,7 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route {
|
||||
}, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(false))
|
||||
m.Group("/statuses", func() {
|
||||
m.Combo("/{sha}").Get(repo.GetCommitStatuses).
|
||||
Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
|
||||
Post(reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
|
||||
}, reqRepoReader(unit.TypeCode))
|
||||
m.Group("/commits", func() {
|
||||
m.Get("", repo.GetAllCommits)
|
||||
|
||||
@@ -35,6 +35,8 @@ func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecor
|
||||
Render: rnd,
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
return c, resp
|
||||
}
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
|
||||
targetStatus = models.NotificationStatusRead
|
||||
}
|
||||
|
||||
changed := make([]*structs.NotificationThread, len(nl))
|
||||
changed := make([]*structs.NotificationThread, 0, len(nl))
|
||||
|
||||
for _, n := range nl {
|
||||
notif, err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)
|
||||
|
||||
@@ -177,23 +177,18 @@ func CreateBranch(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
err := repo_service.CreateNewBranch(ctx.User, ctx.Repo.Repository, opt.OldBranchName, opt.BranchName)
|
||||
|
||||
if err != nil {
|
||||
if models.IsErrBranchDoesNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
||||
}
|
||||
if models.IsErrTagAlreadyExists(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
||||
|
||||
} else if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
||||
|
||||
} else if models.IsErrBranchNameConflict(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
|
||||
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateRepoBranch", err)
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -263,10 +258,15 @@ func ListBranches(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
apiBranches := make([]*api.Branch, len(branches))
|
||||
apiBranches := make([]*api.Branch, 0, len(branches))
|
||||
for i := range branches {
|
||||
c, err := branches[i].GetCommit()
|
||||
if err != nil {
|
||||
// Skip if this branch doesn't exist anymore.
|
||||
if git.IsErrNotExist(err) {
|
||||
totalNumOfBranches--
|
||||
continue
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||
return
|
||||
}
|
||||
@@ -275,11 +275,12 @@ func ListBranches(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
|
||||
return
|
||||
}
|
||||
apiBranches[i], err = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
|
||||
apiBranch, err := convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||
return
|
||||
}
|
||||
apiBranches = append(apiBranches, apiBranch)
|
||||
}
|
||||
|
||||
ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize)
|
||||
@@ -532,7 +533,6 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToBranchProtection(bp))
|
||||
|
||||
}
|
||||
|
||||
// EditBranchProtection edits a branch protection for a repo
|
||||
|
||||
@@ -173,6 +173,7 @@ func SearchIssues(ctx *context.APIContext) {
|
||||
opts.TeamID = team.ID
|
||||
}
|
||||
|
||||
repoCond := models.SearchRepositoryCondition(opts)
|
||||
repoIDs, _, err := models.SearchRepositoryIDs(opts)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
|
||||
@@ -233,7 +234,7 @@ func SearchIssues(ctx *context.APIContext) {
|
||||
Page: ctx.FormInt("page"),
|
||||
PageSize: limit,
|
||||
},
|
||||
RepoIDs: repoIDs,
|
||||
RepoCond: repoCond,
|
||||
IsClosed: isClosed,
|
||||
IssueIDs: issueIDs,
|
||||
IncludedLabelNames: includedLabelNames,
|
||||
@@ -460,7 +461,7 @@ func ListIssues(ctx *context.APIContext) {
|
||||
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
|
||||
issuesOpt := &models.IssuesOptions{
|
||||
ListOptions: listOptions,
|
||||
RepoIDs: []int64{ctx.Repo.Repository.ID},
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
IsClosed: isClosed,
|
||||
IssueIDs: issueIDs,
|
||||
LabelIDs: labelIDs,
|
||||
|
||||
@@ -883,7 +883,7 @@ func dismissReview(ctx *context.APIContext, msg string, isDismiss bool) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := pull_service.DismissReview(review.ID, msg, ctx.User, isDismiss)
|
||||
_, err := pull_service.DismissReview(review.ID, ctx.Repo.Repository.ID, msg, ctx.User, isDismiss)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err)
|
||||
return
|
||||
|
||||
@@ -344,6 +344,8 @@ func DeleteRelease(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
// "405":
|
||||
// "$ref": "#/responses/empty"
|
||||
|
||||
id := ctx.ParamsInt64(":id")
|
||||
rel, err := models.GetReleaseByID(id)
|
||||
@@ -357,6 +359,10 @@ func DeleteRelease(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
if err := releaseservice.DeleteReleaseByID(id, ctx.User, false); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -92,6 +92,8 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
// "405":
|
||||
// "$ref": "#/responses/empty"
|
||||
|
||||
tag := ctx.Params(":tag")
|
||||
|
||||
@@ -111,7 +113,12 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
if err = releaseservice.DeleteReleaseByID(release.ID, ctx.User, false); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
|
||||
@@ -160,7 +160,7 @@ func Search(ctx *context.APIContext) {
|
||||
opts.Collaborate = util.OptionalBoolFalse
|
||||
}
|
||||
|
||||
var mode = ctx.FormString("mode")
|
||||
mode := ctx.FormString("mode")
|
||||
switch mode {
|
||||
case "source":
|
||||
opts.Fork = util.OptionalBoolFalse
|
||||
@@ -186,9 +186,9 @@ func Search(ctx *context.APIContext) {
|
||||
opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private"))
|
||||
}
|
||||
|
||||
var sortMode = ctx.FormString("sort")
|
||||
sortMode := ctx.FormString("sort")
|
||||
if len(sortMode) > 0 {
|
||||
var sortOrder = ctx.FormString("order")
|
||||
sortOrder := ctx.FormString("order")
|
||||
if len(sortOrder) == 0 {
|
||||
sortOrder = "asc"
|
||||
}
|
||||
@@ -264,7 +264,8 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
|
||||
if repo_model.IsErrRepoAlreadyExist(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
|
||||
} else if db.IsErrNameReserved(err) ||
|
||||
db.IsErrNamePatternNotAllowed(err) {
|
||||
db.IsErrNamePatternNotAllowed(err) ||
|
||||
models.IsErrIssueLabelTemplateLoad(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateRepository", err)
|
||||
|
||||
@@ -176,6 +176,8 @@ func CreateTag(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/Tag"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
// "405":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "409":
|
||||
// "$ref": "#/responses/conflict"
|
||||
form := web.GetForm(ctx).(*api.CreateTagOption)
|
||||
@@ -196,6 +198,11 @@ func CreateTag(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusConflict, "tag exist", err)
|
||||
return
|
||||
}
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "CreateNewTag", "user not allowed to create protected tag")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
@@ -236,6 +243,8 @@ func DeleteTag(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
// "405":
|
||||
// "$ref": "#/responses/empty"
|
||||
// "409":
|
||||
// "$ref": "#/responses/conflict"
|
||||
tagName := ctx.Params("*")
|
||||
@@ -256,7 +265,12 @@ func DeleteTag(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
if err = releaseservice.DeleteReleaseByID(tag.ID, ctx.User, true); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
|
||||
@@ -24,13 +24,13 @@ func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) {
|
||||
}
|
||||
|
||||
func listUserFollowers(ctx *context.APIContext, u *user_model.User) {
|
||||
users, err := user_model.GetUserFollowers(u, utils.GetListOptions(ctx))
|
||||
users, count, err := user_model.GetUserFollowers(ctx, u, ctx.User, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(int64(u.NumFollowers))
|
||||
ctx.SetTotalCountHeader(count)
|
||||
responseAPIUsers(ctx, users)
|
||||
}
|
||||
|
||||
@@ -90,13 +90,13 @@ func ListFollowers(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func listUserFollowing(ctx *context.APIContext, u *user_model.User) {
|
||||
users, err := user_model.GetUserFollowing(u, utils.GetListOptions(ctx))
|
||||
users, count, err := user_model.GetUserFollowing(ctx, u, ctx.User, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserFollowing", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(int64(u.NumFollowing))
|
||||
ctx.SetTotalCountHeader(count)
|
||||
responseAPIUsers(ctx, users)
|
||||
}
|
||||
|
||||
|
||||
@@ -87,10 +87,14 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader)
|
||||
}
|
||||
if (st.IsImage() || st.IsPDF()) && (setting.UI.SVG.Enabled || !st.IsSvgImage()) {
|
||||
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name))
|
||||
if st.IsSvgImage() {
|
||||
if st.IsSvgImage() || st.IsPDF() {
|
||||
ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
|
||||
ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType)
|
||||
if st.IsSvgImage() {
|
||||
ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType)
|
||||
} else {
|
||||
ctx.Resp.Header().Set("Content-Type", typesniffer.ApplicationOctetStream)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name))
|
||||
|
||||
@@ -84,6 +84,8 @@ func Init(next http.Handler) http.Handler {
|
||||
"PasswordHashAlgorithms": user_model.AvailableHashAlgorithms,
|
||||
},
|
||||
}
|
||||
defer ctx.Close()
|
||||
|
||||
for _, lang := range translation.AllLangs() {
|
||||
if lang.Lang == locale.Language() {
|
||||
ctx.Data["LangName"] = lang.Name
|
||||
@@ -508,13 +510,17 @@ func SubmitInstall(ctx *context.Context) {
|
||||
// Create admin account
|
||||
if len(form.AdminName) > 0 {
|
||||
u := &user_model.User{
|
||||
Name: form.AdminName,
|
||||
Email: form.AdminEmail,
|
||||
Passwd: form.AdminPasswd,
|
||||
IsAdmin: true,
|
||||
IsActive: true,
|
||||
Name: form.AdminName,
|
||||
Email: form.AdminEmail,
|
||||
Passwd: form.AdminPasswd,
|
||||
IsAdmin: true,
|
||||
}
|
||||
if err = user_model.CreateUser(u); err != nil {
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsRestricted: util.OptionalBoolFalse,
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
}
|
||||
|
||||
if err = user_model.CreateUser(u, overwriteDefault); err != nil {
|
||||
if !user_model.IsErrUserAlreadyExist(err) {
|
||||
setting.InstallLock = false
|
||||
ctx.Data["Err_AdminName"] = true
|
||||
|
||||
@@ -112,7 +112,7 @@ func NewAuthSource(ctx *context.Context) {
|
||||
ctx.Data["SSPIDefaultLanguage"] = ""
|
||||
|
||||
// only the first as default
|
||||
ctx.Data["oauth2_provider"] = oauth2providers[0].Name
|
||||
ctx.Data["oauth2_provider"] = oauth2providers[0].Name()
|
||||
|
||||
ctx.HTML(http.StatusOK, tplAuthNew)
|
||||
}
|
||||
|
||||
@@ -125,10 +125,14 @@ func NewUserPost(ctx *context.Context) {
|
||||
Name: form.UserName,
|
||||
Email: form.Email,
|
||||
Passwd: form.Password,
|
||||
IsActive: true,
|
||||
LoginType: auth.Plain,
|
||||
}
|
||||
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
Visibility: &form.Visibility,
|
||||
}
|
||||
|
||||
if len(form.LoginType) > 0 {
|
||||
fields := strings.Split(form.LoginType, "-")
|
||||
if len(fields) == 2 {
|
||||
@@ -163,7 +167,7 @@ func NewUserPost(ctx *context.Context) {
|
||||
u.MustChangePassword = form.MustChangePassword
|
||||
}
|
||||
|
||||
if err := user_model.CreateUser(u, &user_model.CreateUserOverwriteOptions{Visibility: form.Visibility}); err != nil {
|
||||
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
||||
switch {
|
||||
case user_model.IsErrUserAlreadyExist(err):
|
||||
ctx.Data["Err_UserName"] = true
|
||||
|
||||
@@ -417,7 +417,7 @@ func SignUp(ctx *context.Context) {
|
||||
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
|
||||
ctx.Data["PageIsSignUp"] = true
|
||||
|
||||
//Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true
|
||||
// Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true
|
||||
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration
|
||||
|
||||
ctx.HTML(http.StatusOK, tplSignUp)
|
||||
@@ -438,7 +438,7 @@ func SignUpPost(ctx *context.Context) {
|
||||
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
|
||||
ctx.Data["PageIsSignUp"] = true
|
||||
|
||||
//Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
|
||||
// Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
|
||||
if setting.Service.DisableRegistration || setting.Service.AllowOnlyExternalRegistration {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
return
|
||||
@@ -507,14 +507,12 @@ func SignUpPost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
u := &user_model.User{
|
||||
Name: form.UserName,
|
||||
Email: form.Email,
|
||||
Passwd: form.Password,
|
||||
IsActive: !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm),
|
||||
IsRestricted: setting.Service.DefaultUserIsRestricted,
|
||||
Name: form.UserName,
|
||||
Email: form.Email,
|
||||
Passwd: form.Password,
|
||||
}
|
||||
|
||||
if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, false) {
|
||||
if !createAndHandleCreatedUser(ctx, tplSignUp, form, u, nil, nil, false) {
|
||||
// error already handled
|
||||
return
|
||||
}
|
||||
@@ -525,8 +523,8 @@ func SignUpPost(ctx *context.Context) {
|
||||
|
||||
// createAndHandleCreatedUser calls createUserInContext and
|
||||
// then handleUserCreated.
|
||||
func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, gothUser *goth.User, allowLink bool) bool {
|
||||
if !createUserInContext(ctx, tpl, form, u, gothUser, allowLink) {
|
||||
func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) bool {
|
||||
if !createUserInContext(ctx, tpl, form, u, overwrites, gothUser, allowLink) {
|
||||
return false
|
||||
}
|
||||
return handleUserCreated(ctx, u, gothUser)
|
||||
@@ -534,8 +532,8 @@ func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form int
|
||||
|
||||
// createUserInContext creates a user and handles errors within a given context.
|
||||
// Optionally a template can be specified.
|
||||
func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, gothUser *goth.User, allowLink bool) (ok bool) {
|
||||
if err := user_model.CreateUser(u); err != nil {
|
||||
func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) {
|
||||
if err := user_model.CreateUser(u, overwrites); err != nil {
|
||||
if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) {
|
||||
if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto {
|
||||
var user *user_model.User
|
||||
@@ -634,8 +632,10 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
|
||||
ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language())
|
||||
ctx.HTML(http.StatusOK, TplActivate)
|
||||
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
if setting.CacheService.Enabled {
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -655,14 +655,15 @@ func Activate(ctx *context.Context) {
|
||||
}
|
||||
// Resend confirmation email.
|
||||
if setting.Service.RegisterEmailConfirm {
|
||||
if ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) {
|
||||
if setting.CacheService.Enabled && ctx.Cache.IsExist("MailResendLimit_"+ctx.User.LowerName) {
|
||||
ctx.Data["ResendLimited"] = true
|
||||
} else {
|
||||
ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language())
|
||||
mailer.SendActivateAccountMail(ctx.Locale, ctx.User)
|
||||
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
if setting.CacheService.Enabled {
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -791,7 +792,7 @@ func ActivateEmail(ctx *context.Context) {
|
||||
|
||||
if u, err := user_model.GetUserByID(email.UID); err != nil {
|
||||
log.Warn("GetUserByID: %d", email.UID)
|
||||
} else {
|
||||
} else if setting.CacheService.Enabled {
|
||||
// Allow user to validate more emails
|
||||
_ = ctx.Cache.Delete("MailResendLimit_" + u.LowerName)
|
||||
}
|
||||
|
||||
@@ -285,13 +285,12 @@ func LinkAccountPostRegister(ctx *context.Context) {
|
||||
Name: form.UserName,
|
||||
Email: form.Email,
|
||||
Passwd: form.Password,
|
||||
IsActive: !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm),
|
||||
LoginType: auth.OAuth2,
|
||||
LoginSource: authSource.ID,
|
||||
LoginName: gothUser.UserID,
|
||||
}
|
||||
|
||||
if !createAndHandleCreatedUser(ctx, tplLinkAccount, form, u, &gothUser, false) {
|
||||
if !createAndHandleCreatedUser(ctx, tplLinkAccount, form, u, nil, &gothUser, false) {
|
||||
// error already handled
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/session"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
auth_service "code.gitea.io/gitea/services/auth"
|
||||
@@ -872,19 +873,21 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
u = &user_model.User{
|
||||
Name: getUserName(&gothUser),
|
||||
FullName: gothUser.Name,
|
||||
Email: gothUser.Email,
|
||||
IsActive: !setting.OAuth2Client.RegisterEmailConfirm,
|
||||
LoginType: auth.OAuth2,
|
||||
LoginSource: authSource.ID,
|
||||
LoginName: gothUser.UserID,
|
||||
IsRestricted: setting.Service.DefaultUserIsRestricted,
|
||||
Name: getUserName(&gothUser),
|
||||
FullName: gothUser.Name,
|
||||
Email: gothUser.Email,
|
||||
LoginType: auth.OAuth2,
|
||||
LoginSource: authSource.ID,
|
||||
LoginName: gothUser.UserID,
|
||||
}
|
||||
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm),
|
||||
}
|
||||
|
||||
setUserGroupClaims(authSource, u, &gothUser)
|
||||
|
||||
if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) {
|
||||
if !createAndHandleCreatedUser(ctx, base.TplName(""), nil, u, overwriteDefault, &gothUser, setting.OAuth2Client.AccountLinking != setting.OAuth2AccountLinkingDisabled) {
|
||||
// error already handled
|
||||
return
|
||||
}
|
||||
|
||||
@@ -425,12 +425,11 @@ func RegisterOpenIDPost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
u := &user_model.User{
|
||||
Name: form.UserName,
|
||||
Email: form.Email,
|
||||
Passwd: password,
|
||||
IsActive: !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm),
|
||||
Name: form.UserName,
|
||||
Email: form.Email,
|
||||
Passwd: password,
|
||||
}
|
||||
if !createUserInContext(ctx, tplSignUpOID, form, u, nil, false) {
|
||||
if !createUserInContext(ctx, tplSignUpOID, form, u, nil, nil, false) {
|
||||
// error already handled
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func ForgotPasswdPost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Cache.IsExist("MailResendLimit_" + u.LowerName) {
|
||||
if setting.CacheService.Enabled && ctx.Cache.IsExist("MailResendLimit_"+u.LowerName) {
|
||||
ctx.Data["ResendLimited"] = true
|
||||
ctx.HTML(http.StatusOK, tplForgotPassword)
|
||||
return
|
||||
@@ -88,8 +88,10 @@ func ForgotPasswdPost(ctx *context.Context) {
|
||||
|
||||
mailer.SendResetPasswordMail(u)
|
||||
|
||||
if err = ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
if setting.CacheService.Enabled {
|
||||
if err = ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["ResetPwdCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, ctx.Locale.Language())
|
||||
|
||||
@@ -34,6 +34,9 @@ func RetrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) []*mode
|
||||
}
|
||||
|
||||
for _, act := range actions {
|
||||
if act.Repo == nil {
|
||||
continue
|
||||
}
|
||||
repoOwner, ok := userCache[act.Repo.OwnerID]
|
||||
if !ok {
|
||||
repoOwner, err = user_model.GetUserByID(act.Repo.OwnerID)
|
||||
|
||||
@@ -370,6 +370,12 @@ func CreateBranch(ctx *context.Context) {
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx.User, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName)
|
||||
}
|
||||
if err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL())
|
||||
return
|
||||
}
|
||||
|
||||
if models.IsErrTagAlreadyExists(err) {
|
||||
e := err.(models.ErrTagAlreadyExists)
|
||||
ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
|
||||
|
||||
@@ -57,17 +57,15 @@ const (
|
||||
issueTemplateTitleKey = "IssueTemplateTitle"
|
||||
)
|
||||
|
||||
var (
|
||||
// IssueTemplateCandidates issue templates
|
||||
IssueTemplateCandidates = []string{
|
||||
"ISSUE_TEMPLATE.md",
|
||||
"issue_template.md",
|
||||
".gitea/ISSUE_TEMPLATE.md",
|
||||
".gitea/issue_template.md",
|
||||
".github/ISSUE_TEMPLATE.md",
|
||||
".github/issue_template.md",
|
||||
}
|
||||
)
|
||||
// IssueTemplateCandidates issue templates
|
||||
var IssueTemplateCandidates = []string{
|
||||
"ISSUE_TEMPLATE.md",
|
||||
"issue_template.md",
|
||||
".gitea/ISSUE_TEMPLATE.md",
|
||||
".gitea/issue_template.md",
|
||||
".github/ISSUE_TEMPLATE.md",
|
||||
".github/issue_template.md",
|
||||
}
|
||||
|
||||
// MustAllowUserComment checks to make sure if an issue is locked.
|
||||
// If locked and user has permissions to write to the repository,
|
||||
@@ -226,7 +224,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
|
||||
Page: pager.Paginater.Current(),
|
||||
PageSize: setting.UI.IssuePagingNum,
|
||||
},
|
||||
RepoIDs: []int64{repo.ID},
|
||||
RepoID: repo.ID,
|
||||
AssigneeID: assigneeID,
|
||||
PosterID: posterID,
|
||||
MentionedID: mentionedID,
|
||||
@@ -245,7 +243,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
|
||||
}
|
||||
}
|
||||
|
||||
var issueList = models.IssueList(issues)
|
||||
issueList := models.IssueList(issues)
|
||||
approvalCounts, err := issueList.GetApprovalCounts()
|
||||
if err != nil {
|
||||
ctx.ServerError("ApprovalCounts", err)
|
||||
@@ -311,8 +309,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
|
||||
assigneeID = 0 // Reset ID to prevent unexpected selection of assignee.
|
||||
}
|
||||
|
||||
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] =
|
||||
issue_service.GetRefEndNamesAndURLs(issues, ctx.Repo.RepoLink)
|
||||
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, ctx.Repo.RepoLink)
|
||||
|
||||
ctx.Data["ApprovalCounts"] = func(issueID int64, typ string) int64 {
|
||||
counts, ok := approvalCounts[issueID]
|
||||
@@ -442,7 +439,6 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R
|
||||
}
|
||||
|
||||
func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
|
||||
|
||||
var err error
|
||||
|
||||
ctx.Data["OpenProjects"], _, err = models.GetProjects(models.ProjectSearchOptions{
|
||||
@@ -796,7 +792,8 @@ func NewIssue(ctx *context.Context) {
|
||||
body := ctx.FormString("body")
|
||||
ctx.Data["BodyQuery"] = body
|
||||
|
||||
ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects)
|
||||
isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects)
|
||||
ctx.Data["IsProjectsEnabled"] = isProjectsEnabled
|
||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||
upload.AddUploadContext(ctx, "comment")
|
||||
|
||||
@@ -812,7 +809,7 @@ func NewIssue(ctx *context.Context) {
|
||||
}
|
||||
|
||||
projectID := ctx.FormInt64("project")
|
||||
if projectID > 0 {
|
||||
if projectID > 0 && isProjectsEnabled {
|
||||
project, err := models.GetProjectByID(projectID)
|
||||
if err != nil {
|
||||
log.Error("GetProjectByID: %d: %v", projectID, err)
|
||||
@@ -1017,6 +1014,12 @@ func NewIssuePost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
if projectID > 0 {
|
||||
if !ctx.Repo.CanRead(unit.TypeProjects) {
|
||||
// User must also be able to see the project.
|
||||
ctx.Error(http.StatusBadRequest, "user hasn't permissions to read projects")
|
||||
return
|
||||
}
|
||||
|
||||
if err := models.ChangeProjectAssign(issue, ctx.User, projectID); err != nil {
|
||||
ctx.ServerError("ChangeProjectAssign", err)
|
||||
return
|
||||
@@ -1713,6 +1716,11 @@ func getActionIssues(ctx *context.Context) []*models.Issue {
|
||||
issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues)
|
||||
prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests)
|
||||
for _, issue := range issues {
|
||||
if issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("some issue's RepoID is incorrect", errors.New("some issue's RepoID is incorrect"))
|
||||
return nil
|
||||
}
|
||||
|
||||
if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled {
|
||||
ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil)
|
||||
return nil
|
||||
@@ -2515,7 +2523,7 @@ func filterXRefComments(ctx *context.Context, issue *models.Issue) error {
|
||||
// GetIssueAttachments returns attachments for the issue
|
||||
func GetIssueAttachments(ctx *context.Context) {
|
||||
issue := GetActionIssue(ctx)
|
||||
var attachments = make([]*api.Attachment, len(issue.Attachments))
|
||||
attachments := make([]*api.Attachment, len(issue.Attachments))
|
||||
for i := 0; i < len(issue.Attachments); i++ {
|
||||
attachments[i] = convert.ToReleaseAttachment(issue.Attachments[i])
|
||||
}
|
||||
@@ -2529,7 +2537,7 @@ func GetCommentAttachments(ctx *context.Context) {
|
||||
ctx.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err)
|
||||
return
|
||||
}
|
||||
var attachments = make([]*api.Attachment, 0)
|
||||
attachments := make([]*api.Attachment, 0)
|
||||
if comment.Type == models.CommentTypeComment {
|
||||
if err := comment.LoadAttachments(); err != nil {
|
||||
ctx.ServerError("LoadAttachments", err)
|
||||
@@ -2674,7 +2682,7 @@ func handleTeamMentions(ctx *context.Context) {
|
||||
var isAdmin bool
|
||||
var err error
|
||||
var teams []*models.Team
|
||||
var org = models.OrgFromUser(ctx.Repo.Owner)
|
||||
org := models.OrgFromUser(ctx.Repo.Owner)
|
||||
// Admin has super access.
|
||||
if ctx.User.IsAdmin {
|
||||
isAdmin = true
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -531,7 +532,6 @@ func EditProjectBoard(ctx *context.Context) {
|
||||
|
||||
// SetDefaultProjectBoard set default board for uncategorized issues/pulls
|
||||
func SetDefaultProjectBoard(ctx *context.Context) {
|
||||
|
||||
project, board := checkProjectBoardChangePermissions(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
@@ -631,10 +631,17 @@ func MoveIssues(ctx *context.Context) {
|
||||
}
|
||||
|
||||
if len(movedIssues) != len(form.Issues) {
|
||||
ctx.ServerError("IssuesNotFound", err)
|
||||
ctx.ServerError("some issues do not exist", errors.New("some issues do not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
for _, issue := range movedIssues {
|
||||
if issue.RepoID != project.RepoID {
|
||||
ctx.ServerError("Some issue's repoID is not equal to project's repoID", errors.New("Some issue's repoID is not equal to project's repoID"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = models.MoveIssuesOnProjectBoard(board, sortedIssueIDs); err != nil {
|
||||
ctx.ServerError("MoveIssuesOnProjectBoard", err)
|
||||
return
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -116,6 +117,11 @@ func UpdateResolveConversation(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("comment's repoID is incorrect", errors.New("comment's repoID is incorrect"))
|
||||
return
|
||||
}
|
||||
|
||||
var permResult bool
|
||||
if permResult, err = models.CanMarkConversation(comment.Issue, ctx.User); err != nil {
|
||||
ctx.ServerError("CanMarkConversation", err)
|
||||
@@ -234,7 +240,7 @@ func SubmitReview(ctx *context.Context) {
|
||||
// DismissReview dismissing stale review by repo admin
|
||||
func DismissReview(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.DismissReviewForm)
|
||||
comm, err := pull_service.DismissReview(form.ReviewID, form.Message, ctx.User, true)
|
||||
comm, err := pull_service.DismissReview(form.ReviewID, ctx.Repo.Repository.ID, form.Message, ctx.User, true)
|
||||
if err != nil {
|
||||
ctx.ServerError("pull_service.DismissReview", err)
|
||||
return
|
||||
|
||||
@@ -98,7 +98,14 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
|
||||
listOptions.PageSize = setting.API.MaxResponseItems
|
||||
}
|
||||
|
||||
tags, err := ctx.Repo.GitRepo.GetTags(listOptions.GetStartEnd())
|
||||
// TODO(20073) tags are used for compare feature witch needs all tags
|
||||
// filtering is doen at the client side atm
|
||||
tagListStart, tagListEnd := 0, 0
|
||||
if isTagList {
|
||||
tagListStart, tagListEnd = listOptions.GetStartEnd()
|
||||
}
|
||||
|
||||
tags, err := ctx.Repo.GitRepo.GetTags(tagListStart, tagListEnd)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetTags", err)
|
||||
return
|
||||
@@ -279,7 +286,9 @@ func NewRelease(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Data["tag_name"] = rel.TagName
|
||||
ctx.Data["tag_target"] = rel.Target
|
||||
if rel.Target != "" {
|
||||
ctx.Data["tag_target"] = rel.Target
|
||||
}
|
||||
ctx.Data["title"] = rel.Title
|
||||
ctx.Data["content"] = rel.Note
|
||||
ctx.Data["attachments"] = rel.Attachments
|
||||
@@ -517,7 +526,11 @@ func DeleteTag(ctx *context.Context) {
|
||||
|
||||
func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) {
|
||||
if err := releaseservice.DeleteReleaseByID(ctx.FormInt64("id"), ctx.User, isDelTag); err != nil {
|
||||
ctx.Flash.Error("DeleteReleaseByID: " + err.Error())
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
||||
} else {
|
||||
ctx.Flash.Error("DeleteReleaseByID: " + err.Error())
|
||||
}
|
||||
} else {
|
||||
if isDelTag {
|
||||
ctx.Flash.Success(ctx.Tr("repo.release.deletion_tag_success"))
|
||||
|
||||
@@ -856,10 +856,14 @@ func renderCode(ctx *context.Context) {
|
||||
ctx.Data["PageIsViewCode"] = true
|
||||
|
||||
if ctx.Repo.Repository.IsEmpty {
|
||||
reallyEmpty, err := ctx.Repo.GitRepo.IsEmpty()
|
||||
if err != nil {
|
||||
ctx.ServerError("GitRepo.IsEmpty", err)
|
||||
return
|
||||
reallyEmpty := true
|
||||
var err error
|
||||
if ctx.Repo.GitRepo != nil {
|
||||
reallyEmpty, err = ctx.Repo.GitRepo.IsEmpty()
|
||||
if err != nil {
|
||||
ctx.ServerError("GitRepo.IsEmpty", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if reallyEmpty {
|
||||
ctx.HTML(http.StatusOK, tplRepoEMPTY)
|
||||
|
||||
@@ -191,7 +191,10 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||
ctx.Data["title"] = pageName
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
|
||||
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
isSideBar := pageName == "_Sidebar"
|
||||
isFooter := pageName == "_Footer"
|
||||
|
||||
// lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
|
||||
if noEntry {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
|
||||
@@ -203,23 +206,33 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar")
|
||||
if ctx.Written() {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
var sidebarContent []byte
|
||||
if !isSideBar {
|
||||
sidebarContent, _, _, _ = wikiContentsByName(ctx, commit, "_Sidebar")
|
||||
if ctx.Written() {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
return nil, nil
|
||||
} else {
|
||||
sidebarContent = data
|
||||
}
|
||||
|
||||
footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer")
|
||||
if ctx.Written() {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
var footerContent []byte
|
||||
if !isFooter {
|
||||
footerContent, _, _, _ = wikiContentsByName(ctx, commit, "_Footer")
|
||||
if ctx.Written() {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
return nil, nil
|
||||
} else {
|
||||
footerContent = data
|
||||
}
|
||||
|
||||
var rctx = &markup.RenderContext{
|
||||
rctx := &markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
|
||||
IsWiki: true,
|
||||
@@ -236,27 +249,35 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||
|
||||
ctx.Data["EscapeStatus"], ctx.Data["content"] = charset.EscapeControlString(buf.String())
|
||||
|
||||
buf.Reset()
|
||||
if err := markdown.Render(rctx, bytes.NewReader(sidebarContent), &buf); err != nil {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
if !isSideBar {
|
||||
buf.Reset()
|
||||
if err := markdown.Render(rctx, bytes.NewReader(sidebarContent), &buf); err != nil {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
}
|
||||
ctx.ServerError("Render", err)
|
||||
return nil, nil
|
||||
}
|
||||
ctx.ServerError("Render", err)
|
||||
return nil, nil
|
||||
ctx.Data["sidebarPresent"] = sidebarContent != nil
|
||||
ctx.Data["sidebarEscapeStatus"], ctx.Data["sidebarContent"] = charset.EscapeControlString(buf.String())
|
||||
} else {
|
||||
ctx.Data["sidebarPresent"] = false
|
||||
}
|
||||
ctx.Data["sidebarPresent"] = sidebarContent != nil
|
||||
ctx.Data["sidebarEscapeStatus"], ctx.Data["sidebarContent"] = charset.EscapeControlString(buf.String())
|
||||
|
||||
buf.Reset()
|
||||
if err := markdown.Render(rctx, bytes.NewReader(footerContent), &buf); err != nil {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
if !isFooter {
|
||||
buf.Reset()
|
||||
if err := markdown.Render(rctx, bytes.NewReader(footerContent), &buf); err != nil {
|
||||
if wikiRepo != nil {
|
||||
wikiRepo.Close()
|
||||
}
|
||||
ctx.ServerError("Render", err)
|
||||
return nil, nil
|
||||
}
|
||||
ctx.ServerError("Render", err)
|
||||
return nil, nil
|
||||
ctx.Data["footerPresent"] = footerContent != nil
|
||||
ctx.Data["footerEscapeStatus"], ctx.Data["footerContent"] = charset.EscapeControlString(buf.String())
|
||||
} else {
|
||||
ctx.Data["footerPresent"] = false
|
||||
}
|
||||
ctx.Data["footerPresent"] = footerContent != nil
|
||||
ctx.Data["footerEscapeStatus"], ctx.Data["footerContent"] = charset.EscapeControlString(buf.String())
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
|
||||
@@ -290,7 +311,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
|
||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||
|
||||
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
// lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
|
||||
if noEntry {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
|
||||
@@ -364,7 +385,7 @@ func renderEditPage(ctx *context.Context) {
|
||||
ctx.Data["title"] = pageName
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
|
||||
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
// lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
|
||||
if noEntry {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
|
||||
|
||||
@@ -442,12 +442,13 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
AllLimited: false,
|
||||
}
|
||||
|
||||
if ctxUser.IsOrganization() && ctx.Org.Team != nil {
|
||||
repoOpts.TeamID = ctx.Org.Team.ID
|
||||
if team != nil {
|
||||
repoOpts.TeamID = team.ID
|
||||
}
|
||||
|
||||
switch filterMode {
|
||||
case models.FilterModeAll:
|
||||
case models.FilterModeYourRepositories:
|
||||
case models.FilterModeAssign:
|
||||
opts.AssigneeID = ctx.User.ID
|
||||
case models.FilterModeCreate:
|
||||
@@ -456,19 +457,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
opts.MentionedID = ctx.User.ID
|
||||
case models.FilterModeReviewRequested:
|
||||
opts.ReviewRequestedID = ctx.User.ID
|
||||
case models.FilterModeYourRepositories:
|
||||
if ctxUser.IsOrganization() && ctx.Org.Team != nil {
|
||||
// Fixes a issue whereby the user's ID would be used
|
||||
// to check if it's in the team(which possible isn't the case).
|
||||
opts.User = nil
|
||||
}
|
||||
userRepoIDs, _, err := models.SearchRepositoryIDs(repoOpts)
|
||||
if err != nil {
|
||||
ctx.ServerError("models.SearchRepositoryIDs: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
opts.RepoIDs = userRepoIDs
|
||||
}
|
||||
|
||||
// keyword holds the search term entered into the search field.
|
||||
@@ -532,7 +520,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
// Gets set when clicking filters on the issues overview page.
|
||||
repoIDs := getRepoIDs(ctx.FormString("repos"))
|
||||
if len(repoIDs) > 0 {
|
||||
opts.RepoIDs = repoIDs
|
||||
opts.RepoCond = builder.In("issue.repo_id", repoIDs)
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
@@ -600,13 +588,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
Org: org,
|
||||
Team: team,
|
||||
}
|
||||
if filterMode == models.FilterModeYourRepositories {
|
||||
statsOpts.RepoCond = models.SearchRepositoryCondition(repoOpts)
|
||||
}
|
||||
// Detect when we only should search by team.
|
||||
if opts.User == nil {
|
||||
statsOpts.UserID = 0
|
||||
}
|
||||
issueStats, err = models.GetUserIssueStats(statsOpts)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserIssueStats Shown", err)
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -35,7 +36,7 @@ func GetNotificationCount(c *context.Context) {
|
||||
c.Data["NotificationUnreadCount"] = func() int64 {
|
||||
count, err := models.GetNotificationCount(c.User, models.NotificationStatusUnread)
|
||||
if err != nil {
|
||||
c.ServerError("GetNotificationCount", err)
|
||||
log.Error("Unable to GetNotificationCount for user:%-v: %v", c.User, err)
|
||||
return -1
|
||||
}
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ func Profile(ctx *context.Context) {
|
||||
ctx.Data["Keyword"] = keyword
|
||||
switch tab {
|
||||
case "followers":
|
||||
items, err := user_model.GetUserFollowers(ctxUser, db.ListOptions{
|
||||
items, count, err := user_model.GetUserFollowers(ctx, ctxUser, ctx.User, db.ListOptions{
|
||||
PageSize: setting.UI.User.RepoPagingNum,
|
||||
Page: page,
|
||||
})
|
||||
@@ -244,9 +244,9 @@ func Profile(ctx *context.Context) {
|
||||
}
|
||||
ctx.Data["Cards"] = items
|
||||
|
||||
total = ctxUser.NumFollowers
|
||||
total = int(count)
|
||||
case "following":
|
||||
items, err := user_model.GetUserFollowing(ctxUser, db.ListOptions{
|
||||
items, count, err := user_model.GetUserFollowing(ctx, ctxUser, ctx.User, db.ListOptions{
|
||||
PageSize: setting.UI.User.RepoPagingNum,
|
||||
Page: page,
|
||||
})
|
||||
@@ -256,9 +256,10 @@ func Profile(ctx *context.Context) {
|
||||
}
|
||||
ctx.Data["Cards"] = items
|
||||
|
||||
total = ctxUser.NumFollowing
|
||||
total = int(count)
|
||||
case "activity":
|
||||
ctx.Data["Feeds"] = feed.RetrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser,
|
||||
ctx.Data["Feeds"] = feed.RetrieveFeeds(ctx, models.GetFeedsOptions{
|
||||
RequestedUser: ctxUser,
|
||||
Actor: ctx.User,
|
||||
IncludePrivate: showPrivate,
|
||||
OnlyPerformedBy: true,
|
||||
|
||||
@@ -35,6 +35,7 @@ func Account(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings")
|
||||
ctx.Data["PageIsSettingsAccount"] = true
|
||||
ctx.Data["Email"] = ctx.User.Email
|
||||
ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail
|
||||
|
||||
loadAccountData(ctx)
|
||||
|
||||
@@ -106,7 +107,7 @@ func EmailPost(ctx *context.Context) {
|
||||
// Send activation Email
|
||||
if ctx.FormString("_method") == "SENDACTIVATION" {
|
||||
var address string
|
||||
if ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) {
|
||||
if setting.CacheService.Enabled && ctx.Cache.IsExist("MailResendLimit_"+ctx.User.LowerName) {
|
||||
log.Error("Send activation: activation still pending")
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||
return
|
||||
@@ -142,8 +143,10 @@ func EmailPost(ctx *context.Context) {
|
||||
}
|
||||
address = email.Email
|
||||
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
if setting.CacheService.Enabled {
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
}
|
||||
}
|
||||
ctx.Flash.Info(ctx.Tr("settings.add_email_confirmation_sent", address, timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language())))
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||
@@ -202,8 +205,10 @@ func EmailPost(ctx *context.Context) {
|
||||
// Send confirmation email
|
||||
if setting.Service.RegisterEmailConfirm {
|
||||
mailer.SendActivateEmailMail(ctx.User, email)
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
if setting.CacheService.Enabled {
|
||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
|
||||
log.Error("Set cache(MailResendLimit) fail: %v", err)
|
||||
}
|
||||
}
|
||||
ctx.Flash.Info(ctx.Tr("settings.add_email_confirmation_sent", email.Email, timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language())))
|
||||
} else {
|
||||
@@ -271,7 +276,7 @@ func loadAccountData(ctx *context.Context) {
|
||||
user_model.EmailAddress
|
||||
CanBePrimary bool
|
||||
}
|
||||
pendingActivation := ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName)
|
||||
pendingActivation := setting.CacheService.Enabled && ctx.Cache.IsExist("MailResendLimit_"+ctx.User.LowerName)
|
||||
emails := make([]*UserEmail, len(emlist))
|
||||
for i, em := range emlist {
|
||||
var email UserEmail
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
"code.gitea.io/gitea/services/mailer"
|
||||
|
||||
@@ -106,11 +107,15 @@ func (r *ReverseProxy) newUser(req *http.Request) *user_model.User {
|
||||
}
|
||||
|
||||
user := &user_model.User{
|
||||
Name: username,
|
||||
Email: email,
|
||||
IsActive: true,
|
||||
Name: username,
|
||||
Email: email,
|
||||
}
|
||||
if err := user_model.CreateUser(user); err != nil {
|
||||
|
||||
overwriteDefault := user_model.CreateUserOverwriteOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
}
|
||||
|
||||
if err := user_model.CreateUser(user, &overwriteDefault); err != nil {
|
||||
// FIXME: should I create a system notice?
|
||||
log.Error("CreateUser: %v", err)
|
||||
return nil
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/mailer"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
)
|
||||
@@ -80,19 +81,21 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
|
||||
}
|
||||
|
||||
user = &user_model.User{
|
||||
LowerName: strings.ToLower(sr.Username),
|
||||
Name: sr.Username,
|
||||
FullName: composeFullName(sr.Name, sr.Surname, sr.Username),
|
||||
Email: sr.Mail,
|
||||
LoginType: source.authSource.Type,
|
||||
LoginSource: source.authSource.ID,
|
||||
LoginName: userName,
|
||||
IsActive: true,
|
||||
IsAdmin: sr.IsAdmin,
|
||||
IsRestricted: sr.IsRestricted,
|
||||
LowerName: strings.ToLower(sr.Username),
|
||||
Name: sr.Username,
|
||||
FullName: composeFullName(sr.Name, sr.Surname, sr.Username),
|
||||
Email: sr.Mail,
|
||||
LoginType: source.authSource.Type,
|
||||
LoginSource: source.authSource.ID,
|
||||
LoginName: userName,
|
||||
IsAdmin: sr.IsAdmin,
|
||||
}
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsRestricted: util.OptionalBoolOf(sr.IsRestricted),
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
}
|
||||
|
||||
err := user_model.CreateUser(user)
|
||||
err := user_model.CreateUser(user, overwriteDefault)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user