Compare commits

..

28 Commits

Author SHA1 Message Date
Lunny Xiao
4d876ab1c8 Changelog for v1.10.6 (#10699)
* Changelog for v1.10.6

* Add warnning

* Apply suggestions from code review

Co-Authored-By: John Olheiser <john.olheiser@gmail.com>

Co-authored-by: John Olheiser <john.olheiser@gmail.com>
2020-03-10 17:09:54 +00:00
Lunny Xiao
f58715d903 cross compile using go 1.13.x (#10684) (#10696)
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2020-03-10 16:25:59 +01:00
Lunny Xiao
81072af8ce add changelog for v1.10.5 (#10628) 2020-03-06 06:34:28 +00:00
guillep2k
1d7a855d66 Fix migration bug on v96.go (#10572) (#10574)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-03-02 10:32:38 -06:00
zeripath
3c8842d58c v1.10.4 Changelog (#10294)
* v1.10.4 Changelog

* Add backport identifier for #10261

* Update CHANGELOG.md entry for #9884

* Workaround docker-library/postgres#658

* Update .drone.yml
2020-02-16 22:45:38 +02:00
Lunny Xiao
e786f098d5 Fix reply on code review (#10261)
* Fix branch page pull request title and link error (#10092)

* Fix branch page pull request title and link error

* Fix ui

* Fix reply on code review (#10227)

Co-authored-by: zeripath <art27@cantab.net>

* Revert unrelated change

* Fix lint

Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: Lauris BH <lauris@nix.lv>
2020-02-14 13:53:36 +02:00
Lunny Xiao
22dec1cea6 Fix branch page pull request title and link error (#10092) (#10098)
* Fix branch page pull request title and link error (#10092)
* Fix tmpl
2020-02-01 17:37:52 +00:00
Lunny Xiao
0c5e2e2e4c Fix milestone API state parameter unhandled (#10049) (#10053)
* Fix milestone API state parameter unhandled (#10049)

* Fix milestone API state parameter unhandled

* Fix test

* Fix test
2020-01-30 11:49:02 +08:00
Lunny Xiao
158b716387 Fix wiki raw view on sub path (#10002) (#10041)
* Fix wiki raw view on sub path

* Add test for subpath wiki raw file

* Fix bug

Co-authored-by: zeripath <art27@cantab.net>
2020-01-28 15:10:40 +00:00
John Olheiser
e611dbbe86 Fix RocketChat (#9925)
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2020-01-22 10:07:11 +02:00
dioss-Machiel
68bca621cd Prevent empty LDAP search from deactivating all users (#9879) (#9890)
* Backport of #9879 (Add option to prevent LDAP from deactivating everything on empty search)

* go fmtted

Co-authored-by: Antoine GIRARD <sapk@users.noreply.github.com>
Co-authored-by: zeripath <art27@cantab.net>
2020-01-20 15:02:35 -05:00
Lunny Xiao
c4e0f717e7 fix bug about wrong dependencies permissions check and other wr… (#9884)
* fix bug about wrong dependencies permissions check and other wrong permissions check

* improve code
2020-01-20 16:45:42 +01:00
zeripath
e3e024876e Ensure that 2fa is checked on reset-password (#9857) (#9877)
* Ensure that 2fa is checked on reset-password

* Apply suggestions from code review

Co-Authored-By: Lauris BH <lauris@nix.lv>

* Properly manage scratch_code regeneration

Co-authored-by: Lauris BH <lauris@nix.lv>

Co-authored-by: Lauris BH <lauris@nix.lv>
2020-01-19 18:37:28 -05:00
6543
cebc125f7f Changelog 1.10.3 (#9832) 2020-01-17 16:18:34 -05:00
6543
c975287149 fix (#9838) 2020-01-17 23:25:46 +08:00
Lunny Xiao
b31068652a Fix download file wrong content-type (#9825) (#9835)
* Fix download file wrong content-type

* change the error text to be more precise

* fix test

Co-authored-by: Lauris BH <lauris@nix.lv>
2020-01-17 14:56:25 +01:00
Lunny Xiao
918e640590 Fix wrong identify poster on a migrated pull request when submi… (#9827) (#9831) 2020-01-17 13:38:53 +01:00
Lunny Xiao
9809fe27c4 fix dump non-exist log directory (#9818) (#9820)
Co-authored-by: Lauris BH <lauris@nix.lv>
2020-01-17 08:51:21 +01:00
Lunny Xiao
ed6a2f2e2e Fix compare (#9808) (#9815)
Co-authored-by: techknowlogick <matti@mdranta.net>

Co-authored-by: techknowlogick <matti@mdranta.net>
2020-01-17 10:08:07 +08:00
Lunny Xiao
a0435fcd63 Fix missing msteam webhook on organization (#9781) (#9795) 2020-01-16 07:36:05 +02:00
David Svantesson
a2e2045a96 Fix #9662 (#9783) 2020-01-16 01:26:55 +01:00
Lauris BH
b67a023bec Send tag create and push webhook when release created on UI (#8671) (#9702)
* 'update'

* Send push tag event when release created

* send tag create event while release created in UI

* update to go v1.13

* fix gofmt error

* update #8671 move release tag created hook to modules/notification/webhook due to #8802 refactoring

* use NotifyCreateRef and NotifyPushCommits instead of NotifyNewReleaseTag

* move tag notification to correct place

Co-authored-by: Benno <blueworrybear@gmail.com>
2020-01-13 07:09:13 +00:00
Lauris BH
b1a90f7286 Hide credentials when submitting migration (#9102) (#9704)
through API.
Same fix, using form.CloneAddr instead of opts.CloneAddr.

Co-authored-by: Jordan <eatsleepgame@gmail.com>
2020-01-11 17:25:16 +02:00
Lauris BH
45c8a3aeeb Fix cache problem on dashboard (#9358) (#9703)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-01-11 16:23:40 +02:00
zeripath
0e5126da22 Never allow an empty password to validate (#9682) (#9684)
* Restore IsPasswordSet previous value

* Update models/user.go
2020-01-11 13:14:03 +02:00
zeripath
cd3e52d91b Prevent redirect to Host (#9678) (#9680) 2020-01-09 16:38:12 -05:00
zeripath
319c92163f Branches not at ref commit ID should not be listed as Merged (#9614) (#9639)
Once a branch has been merged if the commit ID no longer equals that of
the pulls ref commit id don't offer to delete the branch on the pull screen
and don't list it as merged on branches.

Fix #9201

When looking at the pull page we should also get the commits from the refs/pulls/x/head

Fix #9158
2020-01-08 03:06:53 +02:00
6543
a15a644c05 [BugFix] Hide public repos owned by private orgs (#9616) 2020-01-06 10:12:55 +08:00
58 changed files with 621 additions and 212 deletions

View File

@@ -1,44 +1,44 @@
repo: go-gitea/gitea repo: go-gitea/gitea
groups: groups:
- -
name: BREAKING name: BREAKING
labels: labels:
- kind/breaking - kind/breaking
- -
name: FEATURE name: FEATURE
labels: labels:
- kind/feature - kind/feature
-
name: SECURITY
labels:
- kind/security
- -
name: BUGFIXES name: BUGFIXES
labels: labels:
- kind/bug - kind/bug
- -
name: ENHANCEMENT name: ENHANCEMENT
labels: labels:
- kind/enhancement - kind/enhancement
- kind/refactor - kind/refactor
- kind/ui - kind/ui
- -
name: SECURITY
labels:
- kind/security
-
name: TESTING name: TESTING
labels: labels:
- kind/testing - kind/testing
- -
name: TRANSLATION name: TRANSLATION
labels: labels:
- kind/translation - kind/translation
- -
name: BUILD name: BUILD
labels: labels:
- kind/build - kind/build
- kind/lint - kind/lint
- -
name: DOCS name: DOCS
labels: labels:
- kind/docs - kind/docs
- -
name: MISC name: MISC
default: true default: true

View File

@@ -30,6 +30,7 @@ services:
image: postgres:9.5 image: postgres:9.5
environment: environment:
POSTGRES_DB: test POSTGRES_DB: test
POSTGRES_PASSWORD: postgres
- name: mssql - name: mssql
pull: default pull: default
@@ -388,7 +389,7 @@ steps:
- name: static - name: static
pull: always pull: always
image: techknowlogick/xgo:latest image: techknowlogick/xgo:go-1.13.x
commands: commands:
- export PATH=$PATH:$GOPATH/bin - export PATH=$PATH:$GOPATH/bin
- make generate - make generate
@@ -490,7 +491,7 @@ steps:
- name: static - name: static
pull: always pull: always
image: techknowlogick/xgo:latest image: techknowlogick/xgo:go-1.13.x
commands: commands:
- export PATH=$PATH:$GOPATH/bin - export PATH=$PATH:$GOPATH/bin
- make generate - make generate

View File

@@ -4,6 +4,48 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io). been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.10.6](https://github.com/go-gitea/gitea/releases/tag/v1.10.6) - 2020-03-10
This is a re-tag version of v1.10.5 and also explicitly built with Go 1.13.
WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be used.
## [1.10.5](https://github.com/go-gitea/gitea/releases/tag/v1.10.5) - 2020-03-06
* BUGFIXES
* Fix release attachments being deleted while upgrading (#10572) (#10574)
## [1.10.4](https://github.com/go-gitea/gitea/releases/tag/v1.10.4) - 2020-02-16
* FEATURE
* Prevent empty LDAP search from deactivating all users (#9879) (#9890)
* BUGFIXES
* Fix reply on code review (#10261) (#10227)
* Fix branch page pull request title and link error (#10092) (#10098)
* Fix milestone API state parameter unhandled (#10049) (#10053)
* Fix wiki raw view on sub path (#10002) (#10041)
* Fix RocketChat Webhook (#9908) (#9921) (#9925)
* Fix bug about wrong dependencies permissions check and other wrong permissions check (#9884) (Partial backport #9842)
* Ensure that 2fa is checked on reset-password (#9857) (#9877)
## [1.10.3](https://github.com/go-gitea/gitea/releases/tag/v1.10.3) - 2020-01-17
* SECURITY
* Hide credentials when submitting migration (#9102) (#9704)
* Never allow an empty password to validate (#9682) (#9684)
* Prevent redirect to Host (#9678) (#9680)
* Hide public repos owned by private orgs (#9609) (#9616)
* BUGFIXES
* Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9838)
* Fix download file wrong content-type (#9825) (#9835)
* Fix wrong identify poster on a migrated pull request when submit review (#9827) (#9831)
* Fix dump non-exist log directory (#9818) (#9820)
* Fix compare (#9808) (#9815)
* Fix missing msteam webhook on organization (#9781) (#9795)
* Fix add team on collaborator page when same name as organization (#9783)
* Fix cache problem on dashboard (#9358) (#9703)
* Send tag create and push webhook when release created on UI (#8671) (#9702)
* Branches not at ref commit ID should not be listed as Merged (#9614) (#9639)
## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02 ## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
* BUGFIXES * BUGFIXES
* Allow only specific Columns to be updated on Issue via API (#9539) (#9580) * Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
@@ -68,7 +110,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Add option to initialize repository with labels (#6061) * Add option to initialize repository with labels (#6061)
* Add additional password hash algorithms (#6023) * Add additional password hash algorithms (#6023)
* BUGFIXES * BUGFIXES
* Allow to merge if file path contains " or \ (#8629) (#8771) * Allow to merge if file path contains " or \ (#8629) (#8771)
* On windows set core.longpaths true (#8776) (#8786) * On windows set core.longpaths true (#8776) (#8786)
* Fix 500 when edit hook (#8782) (#8789) * Fix 500 when edit hook (#8782) (#8789)
* Fix Checkbox at RepoSettings Protected Branch (#8799) (#8801) * Fix Checkbox at RepoSettings Protected Branch (#8799) (#8801)
@@ -80,7 +122,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Fix require external registration password (#8885) (#8890) * Fix require external registration password (#8885) (#8890)
* Fix password complexity check on registration (#8887) (#8888) * Fix password complexity check on registration (#8887) (#8888)
* Update Github Migration Tests (#8896) (#8938) (#8945) * Update Github Migration Tests (#8896) (#8938) (#8945)
* Enable punctuations ending mentions (#8889) (#8894) * Enable punctuations ending mentions (#8889) (#8894)
* Add Close() method to gogitRepository (#8901) (#8956) * Add Close() method to gogitRepository (#8901) (#8956)
* Hotfix for review actions and notifications (#8965) * Hotfix for review actions and notifications (#8965)
* Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) (#8618) * Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) (#8618)

View File

@@ -61,6 +61,10 @@ var (
Name: "admin-filter", Name: "admin-filter",
Usage: "An LDAP filter specifying if a user should be given administrator privileges.", Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
}, },
cli.BoolFlag{
Name: "allow-deactivate-all",
Usage: "Allow empty search results to deactivate all users.",
},
cli.StringFlag{ cli.StringFlag{
Name: "username-attribute", Name: "username-attribute",
Usage: "The attribute of the users LDAP record containing the user name.", Usage: "The attribute of the users LDAP record containing the user name.",
@@ -231,6 +235,9 @@ func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
if c.IsSet("admin-filter") { if c.IsSet("admin-filter") {
config.Source.AdminFilter = c.String("admin-filter") config.Source.AdminFilter = c.String("admin-filter")
} }
if c.IsSet("allow-deactivate-all") {
config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all")
}
return nil return nil
} }

View File

@@ -145,8 +145,10 @@ func runDump(ctx *cli.Context) error {
} }
} }
if err := z.AddDir("log", setting.LogRootPath); err != nil { if com.IsExist(setting.LogRootPath) {
log.Fatalf("Failed to include log: %v", err) if err := z.AddDir("log", setting.LogRootPath); err != nil {
log.Fatalf("Failed to include log: %v", err)
}
} }
if err = z.Close(); err != nil { if err = z.Close(); err != nil {

View File

@@ -0,0 +1,47 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package integrations
import (
"fmt"
"net/http"
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
)
func TestAPIIssuesMilestone(t *testing.T) {
prepareTestEnv(t)
milestone := models.AssertExistsAndLoadBean(t, &models.Milestone{ID: 1}).(*models.Milestone)
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: milestone.RepoID}).(*models.Repository)
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
assert.Equal(t, int64(1), int64(milestone.NumIssues))
assert.Equal(t, structs.StateOpen, milestone.State())
session := loginUser(t, owner.Name)
token := getTokenForLoggedInUser(t, session)
// update values of issue
milestoneState := "closed"
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/milestones/%d?token=%s", owner.Name, repo.Name, milestone.ID, token)
req := NewRequestWithJSON(t, "PATCH", urlStr, structs.EditMilestoneOption{
State: &milestoneState,
})
resp := session.MakeRequest(t, req, http.StatusOK)
var apiMilestone structs.Milestone
DecodeJSON(t, resp, &apiMilestone)
assert.EqualValues(t, "closed", apiMilestone.State)
req = NewRequest(t, "GET", urlStr)
resp = session.MakeRequest(t, req, http.StatusOK)
var apiMilestone2 structs.Milestone
DecodeJSON(t, resp, &apiMilestone2)
assert.EqualValues(t, "closed", apiMilestone2.State)
}

View File

@@ -0,0 +1 @@
4a357436d925b5c974181ff12a994538ddc5a269

View File

@@ -1 +1 @@
0cf15c3f66ec8384480ed9c3cf87c9e97fbb0ec3 423313fbd38093bb10d0c8387db9105409c6f196

View File

@@ -429,7 +429,7 @@ func (issue *Issue) HashTag() string {
// IsPoster returns true if given user by ID is the poster. // IsPoster returns true if given user by ID is the poster.
func (issue *Issue) IsPoster(uid int64) bool { func (issue *Issue) IsPoster(uid int64) bool {
return issue.PosterID == uid return issue.OriginalAuthorID == 0 && issue.PosterID == uid
} }
func (issue *Issue) hasLabel(e Engine, labelID int64) bool { func (issue *Issue) hasLabel(e Engine, labelID int64) bool {

View File

@@ -253,12 +253,33 @@ func updateMilestone(e Engine, m *Milestone) error {
} }
// UpdateMilestone updates information of given milestone. // UpdateMilestone updates information of given milestone.
func UpdateMilestone(m *Milestone) error { func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
if err := updateMilestone(x, m); err != nil { sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err return err
} }
return updateMilestoneCompleteness(x, m.ID) if m.IsClosed && !oldIsClosed {
m.ClosedDateUnix = timeutil.TimeStampNow()
}
if err := updateMilestone(sess, m); err != nil {
return err
}
if err := updateMilestoneCompleteness(sess, m.ID); err != nil {
return err
}
// if IsClosed changed, update milestone numbers of repository
if oldIsClosed != m.IsClosed {
if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
return err
}
}
return sess.Commit()
} }
func updateMilestoneCompleteness(e Engine, milestoneID int64) error { func updateMilestoneCompleteness(e Engine, milestoneID int64) error {

View File

@@ -160,8 +160,9 @@ func TestUpdateMilestone(t *testing.T) {
milestone := AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone) milestone := AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
milestone.Name = "newMilestoneName" milestone.Name = "newMilestoneName"
milestone.Content = "newMilestoneContent" milestone.Content = "newMilestoneContent"
assert.NoError(t, UpdateMilestone(milestone)) assert.NoError(t, UpdateMilestone(milestone, milestone.IsClosed))
AssertExistsAndLoadBean(t, milestone) milestone = AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
assert.EqualValues(t, "newMilestoneName", milestone.Name)
CheckConsistencyFor(t, &Milestone{}) CheckConsistencyFor(t, &Milestone{})
} }

View File

@@ -26,23 +26,38 @@ func deleteOrphanedAttachments(x *xorm.Engine) error {
sess := x.NewSession() sess := x.NewSession()
defer sess.Close() defer sess.Close()
err := sess.BufferSize(setting.Database.IterateBufferSize). var limit = setting.Database.IterateBufferSize
Where("`issue_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))").Cols("uuid"). if limit <= 0 {
Iterate(new(Attachment), limit = 50
func(idx int, bean interface{}) error {
attachment := bean.(*Attachment)
if err := os.RemoveAll(models.AttachmentLocalPath(attachment.UUID)); err != nil {
return err
}
_, err := sess.ID(attachment.ID).NoAutoCondition().Delete(attachment)
return err
})
if err != nil {
return err
} }
return sess.Commit() for {
attachements := make([]Attachment, 0, limit)
if err := sess.Where("`issue_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))").
Cols("id, uuid").Limit(limit).
Asc("id").
Find(&attachements); err != nil {
return err
}
if len(attachements) == 0 {
return nil
}
var ids = make([]int64, 0, limit)
for _, attachment := range attachements {
ids = append(ids, attachment.ID)
}
if _, err := sess.In("id", ids).Delete(new(Attachment)); err != nil {
return err
}
for _, attachment := range attachements {
if err := os.RemoveAll(models.AttachmentLocalPath(attachment.UUID)); err != nil {
return err
}
}
if len(attachements) < limit {
return nil
}
}
} }

View File

@@ -136,7 +136,7 @@ func (pr *PullRequest) LoadHeadRepo() error {
if has, err := x.ID(pr.HeadRepoID).Get(&repo); err != nil { if has, err := x.ID(pr.HeadRepoID).Get(&repo); err != nil {
return err return err
} else if !has { } else if !has {
return ErrRepoNotExist{ID: pr.BaseRepoID} return ErrRepoNotExist{ID: pr.HeadRepoID}
} }
pr.HeadRepo = &repo pr.HeadRepo = &repo
} }

View File

@@ -120,7 +120,8 @@ type SearchRepoOptions struct {
StarredByID int64 StarredByID int64
Page int Page int
IsProfile bool IsProfile bool
AllPublic bool // Include also all public repositories AllPublic bool // Include also all public repositories of users and public organisations
AllLimited bool // Include also all public repositories of limited organisations
PageSize int // Can be smaller than or equal to setting.ExplorePagingNum PageSize int // Can be smaller than or equal to setting.ExplorePagingNum
// None -> include collaborative AND non-collaborative // None -> include collaborative AND non-collaborative
// True -> include just collaborative // True -> include just collaborative
@@ -240,7 +241,11 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
} }
if opts.AllPublic { if opts.AllPublic {
accessCond = accessCond.Or(builder.Eq{"is_private": false}) accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}))))
}
if opts.AllLimited {
accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited}))))
} }
cond = cond.And(accessCond) cond = cond.And(accessCond)

View File

@@ -177,8 +177,8 @@ func TestSearchRepository(t *testing.T) {
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true}, opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true},
count: 22}, count: 22},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true},
count: 28}, count: 27},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true},
count: 15}, count: 15},

View File

@@ -502,7 +502,7 @@ func (u *User) ValidatePassword(passwd string) bool {
// IsPasswordSet checks if the password is set or left empty // IsPasswordSet checks if the password is set or left empty
func (u *User) IsPasswordSet() bool { func (u *User) IsPasswordSet() bool {
return len(u.Passwd) > 0 return !u.ValidatePassword("")
} }
// UploadAvatar saves custom avatar for user. // UploadAvatar saves custom avatar for user.
@@ -1715,6 +1715,15 @@ func SyncExternalUsers() {
continue continue
} }
if len(sr) == 0 {
if !s.LDAP().AllowDeactivateAll {
log.Error("LDAP search found no entries but did not report an error. Refusing to deactivate all users")
continue
} else {
log.Warn("LDAP search found no entries but did not report an error. All users will be deactivated as per settings")
}
}
for _, su := range sr { for _, su := range sr {
if len(su.Username) == 0 { if len(su.Username) == 0 {
continue continue

View File

@@ -283,8 +283,10 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e
Username: slack.Username, Username: slack.Username,
IconURL: slack.IconURL, IconURL: slack.IconURL,
Attachments: []SlackAttachment{{ Attachments: []SlackAttachment{{
Color: slack.Color, Color: slack.Color,
Text: attachmentText, Title: p.Repo.HTMLURL,
TitleLink: p.Repo.HTMLURL,
Text: attachmentText,
}}, }},
}, nil }, nil
} }
@@ -380,12 +382,11 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM
func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) { func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
var text, title, attachmentText string var text string
switch p.Action { switch p.Action {
case api.HookRepoCreated: case api.HookRepoCreated:
text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink) text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink)
title = p.Repository.HTMLURL
case api.HookRepoDeleted: case api.HookRepoDeleted:
text = fmt.Sprintf("[%s] Repository deleted by %s", p.Repository.FullName, senderLink) text = fmt.Sprintf("[%s] Repository deleted by %s", p.Repository.FullName, senderLink)
} }
@@ -395,12 +396,6 @@ func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*Sla
Text: text, Text: text,
Username: slack.Username, Username: slack.Username,
IconURL: slack.IconURL, IconURL: slack.IconURL,
Attachments: []SlackAttachment{{
Color: slack.Color,
Title: title,
TitleLink: title,
Text: attachmentText,
}},
}, nil }, nil
} }

View File

@@ -30,6 +30,7 @@ type AuthenticationForm struct {
SearchPageSize int SearchPageSize int
Filter string Filter string
AdminFilter string AdminFilter string
AllowDeactivateAll bool
IsActive bool IsActive bool
IsSyncEnabled bool IsSyncEnabled bool
SMTPAuth string SMTPAuth string

View File

@@ -47,6 +47,7 @@ type Source struct {
Filter string // Query filter to validate entry Filter string // Query filter to validate entry
AdminFilter string // Query filter to check if user is admin AdminFilter string // Query filter to check if user is admin
Enabled bool // if this source is disabled Enabled bool // if this source is disabled
AllowDeactivateAll bool // Allow an empty search response to deactivate all users from this source
} }
// SearchResult : user data // SearchResult : user data

View File

@@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@@ -122,7 +123,7 @@ func (ctx *Context) RedirectToFirst(location ...string) {
} }
u, err := url.Parse(loc) u, err := url.Parse(loc)
if err != nil || (u.Scheme != "" && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) { if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
continue continue
} }

View File

@@ -91,12 +91,12 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this? // 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
isAssigned, _ := models.IsUserAssignedToIssue(issue, user) isAssigned, _ := models.IsUserAssignedToIssue(issue, user)
return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() || return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() ||
r.Permission.CanWrite(models.UnitTypeIssues) || issue.IsPoster(user.ID) || isAssigned) r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned)
} }
// CanCreateIssueDependencies returns whether or not a user can create dependencies. // CanCreateIssueDependencies returns whether or not a user can create dependencies.
func (r *Repository) CanCreateIssueDependencies(user *models.User) bool { func (r *Repository) CanCreateIssueDependencies(user *models.User, isPull bool) bool {
return r.Permission.CanWrite(models.UnitTypeIssues) && r.Repository.IsDependenciesEnabled() return r.Repository.IsDependenciesEnabled() && r.Permission.CanWriteIssuesOrPulls(isPull)
} }
// GetCommitsCount returns cached commit count for current view // GetCommitsCount returns cached commit count for current view

View File

@@ -1700,6 +1700,7 @@ auths.attribute_surname = Surname Attribute
auths.attribute_mail = Email Attribute auths.attribute_mail = Email Attribute
auths.attribute_ssh_public_key = Public SSH Key Attribute auths.attribute_ssh_public_key = Public SSH Key Attribute
auths.attributes_in_bind = Fetch Attributes in Bind DN Context auths.attributes_in_bind = Fetch Attributes in Bind DN Context
auths.allow_deactivate_all = Allow an empty search result to deactivate all users
auths.use_paged_search = Use Paged Search auths.use_paged_search = Use Paged Search
auths.search_page_size = Page Size auths.search_page_size = Page Size
auths.filter = User Filter auths.filter = User Filter

View File

@@ -1,6 +1,6 @@
/* globals wipPrefixes, issuesTribute, emojiTribute */ /* globals wipPrefixes, issuesTribute, emojiTribute */
/* exported timeAddManual, toggleStopwatch, cancelStopwatch, initHeatmap */ /* exported timeAddManual, toggleStopwatch, cancelStopwatch, initHeatmap */
/* exported toggleDeadlineForm, setDeadline, deleteDependencyModal, cancelCodeComment, onOAuthLoginClick */ /* exported toggleDeadlineForm, setDeadline, deleteDependencyModal, submitReply, cancelCodeComment, onOAuthLoginClick */
'use strict'; 'use strict';
function htmlEncode(text) { function htmlEncode(text) {
@@ -3134,10 +3134,11 @@ function deleteDependencyModal(id, type) {
function initIssueList() { function initIssueList() {
const repolink = $('#repolink').val(); const repolink = $('#repolink').val();
const tp = $('#type').val();
$('#new-dependency-drop-list') $('#new-dependency-drop-list')
.dropdown({ .dropdown({
apiSettings: { apiSettings: {
url: suburl + '/api/v1/repos/' + repolink + '/issues?q={query}', url: suburl + '/api/v1/repos/' + repolink + '/issues?q={query}&type='+tp,
onResponse: function(response) { onResponse: function(response) {
const filteredResponse = {'success': true, 'results': []}; const filteredResponse = {'success': true, 'results': []};
const currIssueId = $('#new-dependency-drop-list').data('issue-id'); const currIssueId = $('#new-dependency-drop-list').data('issue-id');
@@ -3170,6 +3171,14 @@ function cancelCodeComment(btn) {
form.closest('.comment-code-cloud').remove() form.closest('.comment-code-cloud').remove()
} }
} }
function submitReply(btn) {
const form = $(btn).closest('form');
if (form.length > 0 && form.hasClass('comment-form')) {
form.submit();
}
}
function onOAuthLoginClick() { function onOAuthLoginClick() {
const oauthLoader = $('#oauth2-login-loader'); const oauthLoader = $('#oauth2-login-loader');
const oauthNav = $('#oauth2-login-navigator'); const oauthNav = $('#oauth2-login-navigator');

View File

@@ -115,6 +115,7 @@ func parseLDAPConfig(form auth.AuthenticationForm) *models.LDAPConfig {
SearchPageSize: pageSize, SearchPageSize: pageSize,
Filter: form.Filter, Filter: form.Filter,
AdminFilter: form.AdminFilter, AdminFilter: form.AdminFilter,
AllowDeactivateAll: form.AllowDeactivateAll,
Enabled: true, Enabled: true,
}, },
} }

View File

@@ -57,6 +57,10 @@ func ListIssues(ctx *context.APIContext) {
// in: query // in: query
// description: search string // description: search string
// type: string // type: string
// - name: type
// in: query
// description: filter by type (issues / pulls) if set
// type: string
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/IssueList" // "$ref": "#/responses/IssueList"
@@ -91,6 +95,16 @@ func ListIssues(ctx *context.APIContext) {
} }
} }
var isPull util.OptionalBool
switch ctx.Query("type") {
case "pulls":
isPull = util.OptionalBoolTrue
case "issues":
isPull = util.OptionalBoolFalse
default:
isPull = util.OptionalBoolNone
}
// Only fetch the issues if we either don't have a keyword or the search returned issues // Only fetch the issues if we either don't have a keyword or the search returned issues
// This would otherwise return all issues if no issues were found by the search. // This would otherwise return all issues if no issues were found by the search.
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
@@ -101,6 +115,7 @@ func ListIssues(ctx *context.APIContext) {
IsClosed: isClosed, IsClosed: isClosed,
IssueIDs: issueIDs, IssueIDs: issueIDs,
LabelIDs: labelIDs, LabelIDs: labelIDs,
IsPull: isPull,
}) })
} }
@@ -292,6 +307,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
return return
} }
issue.Repo = ctx.Repo.Repository issue.Repo = ctx.Repo.Repository
canWrite := ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
err = issue.LoadAttributes() err = issue.LoadAttributes()
if err != nil { if err != nil {
@@ -299,7 +315,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
return return
} }
if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypeIssues) { if !issue.IsPoster(ctx.User.ID) && !canWrite {
ctx.Status(403) ctx.Status(403)
return return
} }
@@ -312,7 +328,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
} }
// Update the deadline // Update the deadline
if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) { if form.Deadline != nil && canWrite {
deadlineUnix := timeutil.TimeStamp(form.Deadline.Unix()) deadlineUnix := timeutil.TimeStamp(form.Deadline.Unix())
if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil { if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
ctx.Error(500, "UpdateIssueDeadline", err) ctx.Error(500, "UpdateIssueDeadline", err)
@@ -329,7 +345,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
// Pass one or more user logins to replace the set of assignees on this Issue. // Pass one or more user logins to replace the set of assignees on this Issue.
// Send an empty array ([]) to clear all assignees from the Issue. // Send an empty array ([]) to clear all assignees from the Issue.
if ctx.Repo.CanWrite(models.UnitTypeIssues) && (form.Assignees != nil || form.Assignee != nil) { if canWrite && (form.Assignees != nil || form.Assignee != nil) {
oneAssignee := "" oneAssignee := ""
if form.Assignee != nil { if form.Assignee != nil {
oneAssignee = *form.Assignee oneAssignee = *form.Assignee
@@ -342,7 +358,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
} }
} }
if ctx.Repo.CanWrite(models.UnitTypeIssues) && form.Milestone != nil && if canWrite && form.Milestone != nil &&
issue.MilestoneID != *form.Milestone { issue.MilestoneID != *form.Milestone {
oldMilestoneID := issue.MilestoneID oldMilestoneID := issue.MilestoneID
issue.MilestoneID = *form.Milestone issue.MilestoneID = *form.Milestone
@@ -430,7 +446,7 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
return return
} }
if !ctx.Repo.CanWrite(models.UnitTypeIssues) { if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
ctx.Status(403) ctx.Status(403)
return return
} }
@@ -497,7 +513,7 @@ func StartIssueStopwatch(ctx *context.APIContext) {
return return
} }
if !ctx.Repo.CanWrite(models.UnitTypeIssues) { if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
ctx.Status(403) ctx.Status(403)
return return
} }
@@ -566,7 +582,7 @@ func StopIssueStopwatch(ctx *context.APIContext) {
return return
} }
if !ctx.Repo.CanWrite(models.UnitTypeIssues) { if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
ctx.Status(403) ctx.Status(403)
return return
} }

View File

@@ -185,7 +185,7 @@ func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOpti
return return
} }
if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin { if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.User.IsAdmin {
ctx.Error(403, "CreateIssueComment", errors.New(ctx.Tr("repo.issues.comment_on_locked"))) ctx.Error(403, "CreateIssueComment", errors.New(ctx.Tr("repo.issues.comment_on_locked")))
return return
} }

View File

@@ -189,7 +189,12 @@ func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) {
milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix()) milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
} }
if err := models.UpdateMilestone(milestone); err != nil { var oldIsClosed = milestone.IsClosed
if form.State != nil {
milestone.IsClosed = *form.State == string(api.StateClosed)
}
if err := models.UpdateMilestone(milestone, oldIsClosed); err != nil {
ctx.ServerError("UpdateMilestone", err) ctx.ServerError("UpdateMilestone", err)
return return
} }

View File

@@ -436,7 +436,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{ repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
Name: opts.RepoName, Name: opts.RepoName,
Description: opts.Description, Description: opts.Description,
OriginalURL: opts.CloneAddr, OriginalURL: form.CloneAddr,
IsPrivate: opts.Private, IsPrivate: opts.Private,
IsMirror: opts.Mirror, IsMirror: opts.Mirror,
Status: models.RepositoryBeingMigrated, Status: models.RepositoryBeingMigrated,

View File

@@ -141,6 +141,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
Keyword: keyword, Keyword: keyword,
OwnerID: opts.OwnerID, OwnerID: opts.OwnerID,
AllPublic: true, AllPublic: true,
AllLimited: true,
TopicOnly: topicOnly, TopicOnly: topicOnly,
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,
}) })

View File

@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repofiles" "code.gitea.io/gitea/modules/repofiles"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"gopkg.in/src-d/go-git.v4/plumbing"
) )
const ( const (
@@ -32,6 +33,7 @@ type Branch struct {
CommitsAhead int CommitsAhead int
CommitsBehind int CommitsBehind int
LatestPullRequest *models.PullRequest LatestPullRequest *models.PullRequest
MergeMovedOn bool
} }
// Branches render repository branch page // Branches render repository branch page
@@ -168,6 +170,12 @@ func loadBranches(ctx *context.Context) []*Branch {
return nil return nil
} }
repoIDToRepo := map[int64]*models.Repository{}
repoIDToRepo[ctx.Repo.Repository.ID] = ctx.Repo.Repository
repoIDToGitRepo := map[int64]*git.Repository{}
repoIDToGitRepo[ctx.Repo.Repository.ID] = ctx.Repo.GitRepo
branches := make([]*Branch, len(rawBranches)) branches := make([]*Branch, len(rawBranches))
for i := range rawBranches { for i := range rawBranches {
commit, err := rawBranches[i].GetCommit() commit, err := rawBranches[i].GetCommit()
@@ -196,11 +204,46 @@ func loadBranches(ctx *context.Context) []*Branch {
ctx.ServerError("GetLatestPullRequestByHeadInfo", err) ctx.ServerError("GetLatestPullRequestByHeadInfo", err)
return nil return nil
} }
headCommit := commit.ID.String()
mergeMovedOn := false
if pr != nil { if pr != nil {
pr.HeadRepo = ctx.Repo.Repository
if err := pr.LoadIssue(); err != nil { if err := pr.LoadIssue(); err != nil {
ctx.ServerError("pr.LoadIssue", err) ctx.ServerError("pr.LoadIssue", err)
return nil return nil
} }
if repo, ok := repoIDToRepo[pr.BaseRepoID]; ok {
pr.BaseRepo = repo
} else if err := pr.LoadBaseRepo(); err != nil {
ctx.ServerError("pr.LoadBaseRepo", err)
return nil
} else {
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
}
pr.Issue.Repo = pr.BaseRepo
if pr.HasMerged {
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
if !ok {
baseGitRepo, err = git.OpenRepository(pr.BaseRepo.RepoPath())
if err != nil {
ctx.ServerError("OpenRepository", err)
return nil
}
defer baseGitRepo.Close()
repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo
}
pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil && err != plumbing.ErrReferenceNotFound {
ctx.ServerError("GetBranchCommitID", err)
return nil
}
if err == nil && headCommit != pullCommit {
// the head has moved on from the merge - we shouldn't delete
mergeMovedOn = true
}
}
} }
branches[i] = &Branch{ branches[i] = &Branch{
@@ -210,6 +253,7 @@ func loadBranches(ctx *context.Context) []*Branch {
CommitsAhead: divergence.Ahead, CommitsAhead: divergence.Ahead,
CommitsBehind: divergence.Behind, CommitsBehind: divergence.Behind,
LatestPullRequest: pr, LatestPullRequest: pr,
MergeMovedOn: mergeMovedOn,
} }
} }

View File

@@ -152,12 +152,12 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
ctx.ServerError("OpenRepository", err) ctx.ServerError("OpenRepository", err)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
defer headGitRepo.Close()
} }
// user should have permission to read baseRepo's codes and pulls, NOT headRepo's // user should have permission to read baseRepo's codes and pulls, NOT headRepo's
permBase, err := models.GetUserRepoPermission(baseRepo, ctx.User) permBase, err := models.GetUserRepoPermission(baseRepo, ctx.User)
if err != nil { if err != nil {
headGitRepo.Close()
ctx.ServerError("GetUserRepoPermission", err) ctx.ServerError("GetUserRepoPermission", err)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
@@ -168,42 +168,40 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
baseRepo, baseRepo,
permBase) permBase)
} }
headGitRepo.Close()
ctx.NotFound("ParseCompareInfo", nil) ctx.NotFound("ParseCompareInfo", nil)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
// user should have permission to read headrepo's codes if !isSameRepo {
permHead, err := models.GetUserRepoPermission(headRepo, ctx.User) // user should have permission to read headrepo's codes
if err != nil { permHead, err := models.GetUserRepoPermission(headRepo, ctx.User)
headGitRepo.Close() if err != nil {
ctx.ServerError("GetUserRepoPermission", err) ctx.ServerError("GetUserRepoPermission", err)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
if !permHead.CanRead(models.UnitTypeCode) { if !permHead.CanRead(models.UnitTypeCode) {
if log.IsTrace() { if log.IsTrace() {
log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v", log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v",
ctx.User, ctx.User,
headRepo, headRepo,
permHead) permHead)
}
ctx.NotFound("ParseCompareInfo", nil)
return nil, nil, nil, nil, "", ""
} }
headGitRepo.Close()
ctx.NotFound("ParseCompareInfo", nil)
return nil, nil, nil, nil, "", ""
} }
// Check if head branch is valid. // Check if head branch is valid.
headIsCommit := ctx.Repo.GitRepo.IsCommitExist(headBranch) headIsCommit := headGitRepo.IsCommitExist(headBranch)
headIsBranch := headGitRepo.IsBranchExist(headBranch) headIsBranch := headGitRepo.IsBranchExist(headBranch)
headIsTag := headGitRepo.IsTagExist(headBranch) headIsTag := headGitRepo.IsTagExist(headBranch)
if !headIsCommit && !headIsBranch && !headIsTag { if !headIsCommit && !headIsBranch && !headIsTag {
// Check if headBranch is short sha commit hash // Check if headBranch is short sha commit hash
if headCommit, _ := ctx.Repo.GitRepo.GetCommit(headBranch); headCommit != nil { if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil {
headBranch = headCommit.ID.String() headBranch = headCommit.ID.String()
ctx.Data["HeadBranch"] = headBranch ctx.Data["HeadBranch"] = headBranch
headIsCommit = true headIsCommit = true
} else { } else {
headGitRepo.Close()
ctx.NotFound("IsRefExist", nil) ctx.NotFound("IsRefExist", nil)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
@@ -224,14 +222,12 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
baseRepo, baseRepo,
permBase) permBase)
} }
headGitRepo.Close()
ctx.NotFound("ParseCompareInfo", nil) ctx.NotFound("ParseCompareInfo", nil)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch) compareInfo, err := headGitRepo.GetCompareInfo(baseRepo.RepoPath(), baseBranch, headBranch)
if err != nil { if err != nil {
headGitRepo.Close()
ctx.ServerError("GetCompareInfo", err) ctx.ServerError("GetCompareInfo", err)
return nil, nil, nil, nil, "", "" return nil, nil, nil, nil, "", ""
} }
@@ -377,7 +373,8 @@ func CompareDiff(ctx *context.Context) {
return return
} }
defer headGitRepo.Close() defer headGitRepo.Close()
if err := parseBaseRepoInfo(ctx, headRepo); err != nil { var err error
if err = parseBaseRepoInfo(ctx, headRepo); err != nil {
ctx.ServerError("parseBaseRepoInfo", err) ctx.ServerError("parseBaseRepoInfo", err)
return return
} }
@@ -410,7 +407,7 @@ func CompareDiff(ctx *context.Context) {
if !nothingToCompare { if !nothingToCompare {
// Setup information for new form. // Setup information for new form.
RetrieveRepoMetas(ctx, ctx.Repo.Repository) RetrieveRepoMetas(ctx, ctx.Repo.Repository, true)
if ctx.Written() { if ctx.Written() {
return return
} }
@@ -419,6 +416,11 @@ func CompareDiff(ctx *context.Context) {
beforeCommitID := ctx.Data["BeforeCommitID"].(string) beforeCommitID := ctx.Data["BeforeCommitID"].(string)
afterCommitID := ctx.Data["AfterCommitID"].(string) afterCommitID := ctx.Data["AfterCommitID"].(string)
if ctx.Data["Assignees"], err = ctx.Repo.Repository.GetAssignees(); err != nil {
ctx.ServerError("GetAssignees", err)
return
}
ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + "..." + base.ShortSha(afterCommitID) ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + "..." + base.ShortSha(afterCommitID)
ctx.Data["IsRepoToolbarCommits"] = true ctx.Data["IsRepoToolbarCommits"] = true

View File

@@ -12,6 +12,7 @@ import (
"strings" "strings"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/lfs"
@@ -33,7 +34,12 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error {
name = strings.Replace(name, ",", " ", -1) name = strings.Replace(name, ",", " ", -1)
if base.IsTextFile(buf) || ctx.QueryBool("render") { if base.IsTextFile(buf) || ctx.QueryBool("render") {
ctx.Resp.Header().Set("Content-Type", "text/plain; charset=utf-8") cs, err := charset.DetectEncoding(buf)
if err != nil {
log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err)
cs = "utf-8"
}
ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs))
} else if base.IsImageFile(buf) || base.IsPDFFile(buf) { } else if base.IsImageFile(buf) || base.IsPDFFile(buf) {
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name))
} else { } else {

View File

@@ -67,7 +67,7 @@ func MustAllowUserComment(ctx *context.Context) {
return return
} }
if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin { if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.User.IsAdmin {
ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked")) ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked"))
ctx.Redirect(issue.HTMLURL()) ctx.Redirect(issue.HTMLURL())
return return
@@ -346,8 +346,8 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repos
} }
// RetrieveRepoMetas find all the meta information of a repository // RetrieveRepoMetas find all the meta information of a repository
func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository) []*models.Label { func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository, isPull bool) []*models.Label {
if !ctx.Repo.CanWrite(models.UnitTypeIssues) { if !ctx.Repo.CanWriteIssuesOrPulls(isPull) {
return nil return nil
} }
@@ -371,7 +371,7 @@ func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository) []*models.
ctx.Data["Branches"] = brs ctx.Data["Branches"] = brs
// Contains true if the user can create issue dependencies // Contains true if the user can create issue dependencies
ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User) ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User, isPull)
return labels return labels
} }
@@ -441,7 +441,7 @@ func NewIssue(ctx *context.Context) {
setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates) setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates)
renderAttachmentSettings(ctx) renderAttachmentSettings(ctx)
RetrieveRepoMetas(ctx, ctx.Repo.Repository) RetrieveRepoMetas(ctx, ctx.Repo.Repository, false)
if ctx.Written() { if ctx.Written() {
return return
} }
@@ -456,7 +456,7 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull b
err error err error
) )
labels := RetrieveRepoMetas(ctx, ctx.Repo.Repository) labels := RetrieveRepoMetas(ctx, ctx.Repo.Repository, isPull)
if ctx.Written() { if ctx.Written() {
return nil, nil, 0 return nil, nil, 0
} }
@@ -776,8 +776,16 @@ func ViewIssue(ctx *context.Context) {
} }
} }
if issue.IsPull && !ctx.Repo.CanRead(models.UnitTypeIssues) {
ctx.Data["IssueType"] = "pulls"
} else if !issue.IsPull && !ctx.Repo.CanRead(models.UnitTypePullRequests) {
ctx.Data["IssueType"] = "issues"
} else {
ctx.Data["IssueType"] = "all"
}
// Check if the user can use the dependencies // Check if the user can use the dependencies
ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User) ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User, issue.IsPull)
// Render comments and and fetch participants. // Render comments and and fetch participants.
participants[0] = issue.Poster participants[0] = issue.Poster
@@ -931,7 +939,10 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["IsBlockedByApprovals"] = pull.ProtectedBranch.RequiredApprovals > 0 && cnt < pull.ProtectedBranch.RequiredApprovals ctx.Data["IsBlockedByApprovals"] = pull.ProtectedBranch.RequiredApprovals > 0 && cnt < pull.ProtectedBranch.RequiredApprovals
ctx.Data["GrantedApprovals"] = cnt ctx.Data["GrantedApprovals"] = cnt
} }
ctx.Data["IsPullBranchDeletable"] = canDelete && pull.HeadRepo != nil && git.IsBranchExist(pull.HeadRepo.RepoPath(), pull.HeadBranch) ctx.Data["IsPullBranchDeletable"] = canDelete &&
pull.HeadRepo != nil &&
git.IsBranchExist(pull.HeadRepo.RepoPath(), pull.HeadBranch) &&
(!pull.HasMerged || ctx.Data["HeadBranchCommitID"] == ctx.Data["PullHeadCommitID"])
ctx.Data["PullReviewersWithType"], err = models.GetReviewersByPullID(issue.ID) ctx.Data["PullReviewersWithType"], err = models.GetReviewersByPullID(issue.ID)
if err != nil { if err != nil {
@@ -960,7 +971,6 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID) ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID)
ctx.Data["IsIssueWriter"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) ctx.Data["IsIssueWriter"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
ctx.Data["IsRepoAdmin"] = ctx.IsSigned && (ctx.Repo.IsAdmin() || ctx.User.IsAdmin) ctx.Data["IsRepoAdmin"] = ctx.IsSigned && (ctx.Repo.IsAdmin() || ctx.User.IsAdmin)
ctx.Data["IsRepoIssuesWriter"] = ctx.IsSigned && (ctx.Repo.CanWrite(models.UnitTypeIssues) || ctx.User.IsAdmin)
ctx.Data["LockReasons"] = setting.Repository.Issue.LockReasons ctx.Data["LockReasons"] = setting.Repository.Issue.LockReasons
ctx.HTML(200, tplIssueView) ctx.HTML(200, tplIssueView)
} }
@@ -1205,7 +1215,7 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) {
ctx.Error(403) ctx.Error(403)
} }
if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin { if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.User.IsAdmin {
ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked")) ctx.Flash.Error(ctx.Tr("repo.issues.comment_on_locked"))
ctx.Redirect(issue.HTMLURL(), http.StatusSeeOther) ctx.Redirect(issue.HTMLURL(), http.StatusSeeOther)
return return

View File

@@ -14,14 +14,6 @@ import (
// AddDependency adds new dependencies // AddDependency adds new dependencies
func AddDependency(ctx *context.Context) { func AddDependency(ctx *context.Context) {
// Check if the Repo is allowed to have dependencies
if !ctx.Repo.CanCreateIssueDependencies(ctx.User) {
ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
return
}
depID := ctx.QueryInt64("newDependency")
issueIndex := ctx.ParamsInt64("index") issueIndex := ctx.ParamsInt64("index")
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex)
if err != nil { if err != nil {
@@ -29,6 +21,14 @@ func AddDependency(ctx *context.Context) {
return return
} }
// Check if the Repo is allowed to have dependencies
if !ctx.Repo.CanCreateIssueDependencies(ctx.User, issue.IsPull) {
ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
return
}
depID := ctx.QueryInt64("newDependency")
// Redirect // Redirect
defer ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issueIndex), http.StatusSeeOther) defer ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issueIndex), http.StatusSeeOther)
@@ -68,14 +68,6 @@ func AddDependency(ctx *context.Context) {
// RemoveDependency removes the dependency // RemoveDependency removes the dependency
func RemoveDependency(ctx *context.Context) { func RemoveDependency(ctx *context.Context) {
// Check if the Repo is allowed to have dependencies
if !ctx.Repo.CanCreateIssueDependencies(ctx.User) {
ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
return
}
depID := ctx.QueryInt64("removeDependencyID")
issueIndex := ctx.ParamsInt64("index") issueIndex := ctx.ParamsInt64("index")
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex)
if err != nil { if err != nil {
@@ -83,8 +75,13 @@ func RemoveDependency(ctx *context.Context) {
return return
} }
// Redirect // Check if the Repo is allowed to have dependencies
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issueIndex), http.StatusSeeOther) if !ctx.Repo.CanCreateIssueDependencies(ctx.User, issue.IsPull) {
ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
return
}
depID := ctx.QueryInt64("removeDependencyID")
// Dependency Type // Dependency Type
depTypeStr := ctx.Req.PostForm.Get("dependencyType") depTypeStr := ctx.Req.PostForm.Get("dependencyType")
@@ -116,4 +113,7 @@ func RemoveDependency(ctx *context.Context) {
ctx.ServerError("RemoveIssueDependency", err) ctx.ServerError("RemoveIssueDependency", err)
return return
} }
// Redirect
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issueIndex), http.StatusSeeOther)
} }

View File

@@ -192,7 +192,7 @@ func EditMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) {
m.Name = form.Title m.Name = form.Title
m.Content = form.Content m.Content = form.Content
m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix()) m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix())
if err = models.UpdateMilestone(m); err != nil { if err = models.UpdateMilestone(m, m.IsClosed); err != nil {
ctx.ServerError("UpdateMilestone", err) ctx.ServerError("UpdateMilestone", err)
return return
} }

View File

@@ -315,25 +315,37 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
repo := ctx.Repo.Repository repo := ctx.Repo.Repository
pull := issue.PullRequest pull := issue.PullRequest
var err error if err := pull.GetHeadRepo(); err != nil {
if err = pull.GetHeadRepo(); err != nil {
ctx.ServerError("GetHeadRepo", err) ctx.ServerError("GetHeadRepo", err)
return nil return nil
} }
if err := pull.GetBaseRepo(); err != nil {
ctx.ServerError("GetBaseRepo", err)
return nil
}
setMergeTarget(ctx, pull) setMergeTarget(ctx, pull)
if err = pull.LoadProtectedBranch(); err != nil { if err := pull.LoadProtectedBranch(); err != nil {
ctx.ServerError("GetLatestCommitStatus", err) ctx.ServerError("GetLatestCommitStatus", err)
return nil return nil
} }
ctx.Data["EnableStatusCheck"] = pull.ProtectedBranch != nil && pull.ProtectedBranch.EnableStatusCheck ctx.Data["EnableStatusCheck"] = pull.ProtectedBranch != nil && pull.ProtectedBranch.EnableStatusCheck
var headGitRepo *git.Repository baseGitRepo, err := git.OpenRepository(pull.BaseRepo.RepoPath())
if err != nil {
ctx.ServerError("OpenRepository", err)
return nil
}
defer baseGitRepo.Close()
var headBranchExist bool var headBranchExist bool
var headBranchSha string
// HeadRepo may be missing // HeadRepo may be missing
if pull.HeadRepo != nil { if pull.HeadRepo != nil {
headGitRepo, err = git.OpenRepository(pull.HeadRepo.RepoPath()) var err error
headGitRepo, err := git.OpenRepository(pull.HeadRepo.RepoPath())
if err != nil { if err != nil {
ctx.ServerError("OpenRepository", err) ctx.ServerError("OpenRepository", err)
return nil return nil
@@ -343,46 +355,53 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch) headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch)
if headBranchExist { if headBranchExist {
sha, err := headGitRepo.GetBranchCommitID(pull.HeadBranch) headBranchSha, err = headGitRepo.GetBranchCommitID(pull.HeadBranch)
if err != nil { if err != nil {
ctx.ServerError("GetBranchCommitID", err) ctx.ServerError("GetBranchCommitID", err)
return nil return nil
} }
commitStatuses, err := models.GetLatestCommitStatus(repo, sha, 0)
if err != nil {
ctx.ServerError("GetLatestCommitStatus", err)
return nil
}
if len(commitStatuses) > 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(commitStatuses)
}
if pull.ProtectedBranch != nil && pull.ProtectedBranch.EnableStatusCheck {
ctx.Data["is_context_required"] = func(context string) bool {
for _, c := range pull.ProtectedBranch.StatusCheckContexts {
if c == context {
return true
}
}
return false
}
ctx.Data["IsRequiredStatusCheckSuccess"] = pull_service.IsCommitStatusContextSuccess(commitStatuses, pull.ProtectedBranch.StatusCheckContexts)
}
} }
} }
if pull.HeadRepo == nil || !headBranchExist { sha, err := baseGitRepo.GetRefCommitID(pull.GetGitRefName())
ctx.Data["IsPullRequestBroken"] = true if err != nil {
ctx.Data["HeadTarget"] = "deleted" ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err)
ctx.Data["NumCommits"] = 0
ctx.Data["NumFiles"] = 0
return nil return nil
} }
compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(repo.Owner.Name, repo.Name), commitStatuses, err := models.GetLatestCommitStatus(repo, sha, 0)
pull.BaseBranch, pull.HeadBranch) if err != nil {
ctx.ServerError("GetLatestCommitStatus", err)
return nil
}
if len(commitStatuses) > 0 {
ctx.Data["LatestCommitStatuses"] = commitStatuses
ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(commitStatuses)
}
if pull.ProtectedBranch != nil && pull.ProtectedBranch.EnableStatusCheck {
ctx.Data["is_context_required"] = func(context string) bool {
for _, c := range pull.ProtectedBranch.StatusCheckContexts {
if c == context {
return true
}
}
return false
}
ctx.Data["IsRequiredStatusCheckSuccess"] = pull_service.IsCommitStatusContextSuccess(commitStatuses, pull.ProtectedBranch.StatusCheckContexts)
}
ctx.Data["HeadBranchMovedOn"] = headBranchSha != sha
ctx.Data["HeadBranchCommitID"] = headBranchSha
ctx.Data["PullHeadCommitID"] = sha
if pull.HeadRepo == nil || !headBranchExist || headBranchSha != sha {
ctx.Data["IsPullRequestBroken"] = true
ctx.Data["HeadTarget"] = "deleted"
}
compareInfo, err := baseGitRepo.GetCompareInfo(pull.BaseRepo.RepoPath(),
pull.BaseBranch, pull.GetGitRefName())
if err != nil { if err != nil {
if strings.Contains(err.Error(), "fatal: Not a valid object name") { if strings.Contains(err.Error(), "fatal: Not a valid object name") {
ctx.Data["IsPullRequestBroken"] = true ctx.Data["IsPullRequestBroken"] = true

View File

@@ -117,9 +117,7 @@ func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) {
// can not approve/reject your own PR // can not approve/reject your own PR
case models.ReviewTypeApprove, models.ReviewTypeReject: case models.ReviewTypeApprove, models.ReviewTypeReject:
if issue.IsPoster(ctx.User.ID) {
if issue.Poster.ID == ctx.User.ID {
var translated string var translated string
if reviewType == models.ReviewTypeApprove { if reviewType == models.ReviewTypeApprove {

View File

@@ -600,7 +600,7 @@ func AddTeamPost(ctx *context.Context) {
} }
name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.Query("team"))) name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.Query("team")))
if len(name) == 0 || ctx.Repo.Owner.LowerName == name { if len(name) == 0 {
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
return return
} }

View File

@@ -65,27 +65,20 @@ type PageMeta struct {
// findEntryForFile finds the tree entry for a target filepath. // findEntryForFile finds the tree entry for a target filepath.
func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) { func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
entries, err := commit.ListEntries() entry, err := commit.GetTreeEntryByPath(target)
if err != nil { if err != nil && !git.IsErrNotExist(err) {
return nil, err return nil, err
} }
// The longest name should be checked first if entry != nil {
for _, entry := range entries { return entry, nil
if entry.IsRegular() && entry.Name() == target {
return entry, nil
}
} }
// Then the unescaped, shortest alternative // Then the unescaped, shortest alternative
var unescapedTarget string var unescapedTarget string
if unescapedTarget, err = url.QueryUnescape(target); err != nil { if unescapedTarget, err = url.QueryUnescape(target); err != nil {
return nil, err return nil, err
} }
for _, entry := range entries { return commit.GetTreeEntryByPath(unescapedTarget)
if entry.IsRegular() && entry.Name() == unescapedTarget {
return entry, nil
}
}
return nil, nil
} }
func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) { func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
@@ -122,10 +115,9 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
// wikiContentsByName returns the contents of a wiki page, along with a boolean // wikiContentsByName returns the contents of a wiki page, along with a boolean
// indicating whether the page exists. Writes to ctx if an error occurs. // indicating whether the page exists. Writes to ctx if an error occurs.
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) { func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
var entry *git.TreeEntry
var err error
pageFilename := models.WikiNameToFilename(wikiName) pageFilename := models.WikiNameToFilename(wikiName)
if entry, err = findEntryForFile(commit, pageFilename); err != nil { entry, err := findEntryForFile(commit, pageFilename)
if err != nil && !git.IsErrNotExist(err) {
ctx.ServerError("findEntryForFile", err) ctx.ServerError("findEntryForFile", err)
return nil, nil, "", false return nil, nil, "", false
} else if entry == nil { } else if entry == nil {
@@ -517,7 +509,7 @@ func WikiRaw(ctx *context.Context) {
if commit != nil { if commit != nil {
// Try to find a file with that name // Try to find a file with that name
entry, err = findEntryForFile(commit, providedPath) entry, err = findEntryForFile(commit, providedPath)
if err != nil { if err != nil && !git.IsErrNotExist(err) {
ctx.ServerError("findFile", err) ctx.ServerError("findFile", err)
return return
} }
@@ -530,7 +522,7 @@ func WikiRaw(ctx *context.Context) {
wikiPath := models.WikiNameToFilename(providedPath) wikiPath := models.WikiNameToFilename(providedPath)
entry, err = findEntryForFile(commit, wikiPath) entry, err = findEntryForFile(commit, wikiPath)
if err != nil { if err != nil && !git.IsErrNotExist(err) {
ctx.ServerError("findFile", err) ctx.ServerError("findFile", err)
return return
} }

View File

@@ -191,6 +191,7 @@ func TestDeleteWikiPagePost(t *testing.T) {
func TestWikiRaw(t *testing.T) { func TestWikiRaw(t *testing.T) {
for filepath, filetype := range map[string]string{ for filepath, filetype := range map[string]string{
"jpeg.jpg": "image/jpeg", "jpeg.jpg": "image/jpeg",
"images/jpeg.jpg": "image/jpeg",
"Page With Spaced Name": "text/plain; charset=utf-8", "Page With Spaced Name": "text/plain; charset=utf-8",
"Page-With-Spaced-Name": "text/plain; charset=utf-8", "Page-With-Spaced-Name": "text/plain; charset=utf-8",
"Page With Spaced Name.md": "text/plain; charset=utf-8", "Page With Spaced Name.md": "text/plain; charset=utf-8",

View File

@@ -457,7 +457,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
}) })
m.Group("/auths", func() { m.Group("/auths", func() {
@@ -532,18 +532,12 @@ func RegisterRoutes(m *macaron.Macaron) {
reqRepoReleaseReader := context.RequireRepoReader(models.UnitTypeReleases) reqRepoReleaseReader := context.RequireRepoReader(models.UnitTypeReleases)
reqRepoWikiWriter := context.RequireRepoWriter(models.UnitTypeWiki) reqRepoWikiWriter := context.RequireRepoWriter(models.UnitTypeWiki)
reqRepoIssueReader := context.RequireRepoReader(models.UnitTypeIssues) reqRepoIssueReader := context.RequireRepoReader(models.UnitTypeIssues)
reqRepoIssueWriter := context.RequireRepoWriter(models.UnitTypeIssues)
reqRepoPullsWriter := context.RequireRepoWriter(models.UnitTypePullRequests) reqRepoPullsWriter := context.RequireRepoWriter(models.UnitTypePullRequests)
reqRepoPullsReader := context.RequireRepoReader(models.UnitTypePullRequests) reqRepoPullsReader := context.RequireRepoReader(models.UnitTypePullRequests)
reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(models.UnitTypeIssues, models.UnitTypePullRequests) reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(models.UnitTypeIssues, models.UnitTypePullRequests)
reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(models.UnitTypeIssues, models.UnitTypePullRequests) reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(models.UnitTypeIssues, models.UnitTypePullRequests)
reqRepoIssueWriter := func(ctx *context.Context) {
if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
ctx.Error(403)
return
}
}
// ***** START: Organization ***** // ***** START: Organization *****
m.Group("/org", func() { m.Group("/org", func() {
m.Group("", func() { m.Group("", func() {
@@ -590,6 +584,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost) m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost)
m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
m.Get("/:id", repo.WebHooksEdit) m.Get("/:id", repo.WebHooksEdit)
m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost)
@@ -597,6 +592,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost)
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
}) })
m.Route("/delete", "GET,POST", org.SettingsDelete) m.Route("/delete", "GET,POST", org.SettingsDelete)

View File

@@ -1282,7 +1282,7 @@ func ForgotPasswdPost(ctx *context.Context) {
ctx.HTML(200, tplForgotPassword) ctx.HTML(200, tplForgotPassword)
} }
func commonResetPassword(ctx *context.Context) *models.User { func commonResetPassword(ctx *context.Context) (*models.User, *models.TwoFactor) {
code := ctx.Query("code") code := ctx.Query("code")
ctx.Data["Title"] = ctx.Tr("auth.reset_password") ctx.Data["Title"] = ctx.Tr("auth.reset_password")
@@ -1294,14 +1294,25 @@ func commonResetPassword(ctx *context.Context) *models.User {
if len(code) == 0 { if len(code) == 0 {
ctx.Flash.Error(ctx.Tr("auth.invalid_code")) ctx.Flash.Error(ctx.Tr("auth.invalid_code"))
return nil return nil, nil
} }
// Fail early, don't frustrate the user // Fail early, don't frustrate the user
u := models.VerifyUserActiveCode(code) u := models.VerifyUserActiveCode(code)
if u == nil { if u == nil {
ctx.Flash.Error(ctx.Tr("auth.invalid_code")) ctx.Flash.Error(ctx.Tr("auth.invalid_code"))
return nil return nil, nil
}
twofa, err := models.GetTwoFactorByUID(u.ID)
if err != nil {
if !models.IsErrTwoFactorNotEnrolled(err) {
ctx.Error(http.StatusInternalServerError, "CommonResetPassword", err.Error())
return nil, nil
}
} else {
ctx.Data["has_two_factor"] = true
ctx.Data["scratch_code"] = ctx.QueryBool("scratch_code")
} }
// Show the user that they are affecting the account that they intended to // Show the user that they are affecting the account that they intended to
@@ -1309,10 +1320,10 @@ func commonResetPassword(ctx *context.Context) *models.User {
if nil != ctx.User && u.ID != ctx.User.ID { if nil != ctx.User && u.ID != ctx.User.ID {
ctx.Flash.Error(ctx.Tr("auth.reset_password_wrong_user", ctx.User.Email, u.Email)) ctx.Flash.Error(ctx.Tr("auth.reset_password_wrong_user", ctx.User.Email, u.Email))
return nil return nil, nil
} }
return u return u, twofa
} }
// ResetPasswd render the account recovery page // ResetPasswd render the account recovery page
@@ -1320,13 +1331,19 @@ func ResetPasswd(ctx *context.Context) {
ctx.Data["IsResetForm"] = true ctx.Data["IsResetForm"] = true
commonResetPassword(ctx) commonResetPassword(ctx)
if ctx.Written() {
return
}
ctx.HTML(200, tplResetPassword) ctx.HTML(200, tplResetPassword)
} }
// ResetPasswdPost response from account recovery request // ResetPasswdPost response from account recovery request
func ResetPasswdPost(ctx *context.Context) { func ResetPasswdPost(ctx *context.Context) {
u := commonResetPassword(ctx) u, twofa := commonResetPassword(ctx)
if ctx.Written() {
return
}
if u == nil { if u == nil {
// Flash error has been set // Flash error has been set
@@ -1348,6 +1365,39 @@ func ResetPasswdPost(ctx *context.Context) {
return return
} }
// Handle two-factor
regenerateScratchToken := false
if twofa != nil {
if ctx.QueryBool("scratch_code") {
if !twofa.VerifyScratchToken(ctx.Query("token")) {
ctx.Data["IsResetForm"] = true
ctx.Data["Err_Token"] = true
ctx.RenderWithErr(ctx.Tr("auth.twofa_scratch_token_incorrect"), tplResetPassword, nil)
return
}
regenerateScratchToken = true
} else {
passcode := ctx.Query("passcode")
ok, err := twofa.ValidateTOTP(passcode)
if err != nil {
ctx.Error(http.StatusInternalServerError, "ValidateTOTP", err.Error())
return
}
if !ok || twofa.LastUsedPasscode == passcode {
ctx.Data["IsResetForm"] = true
ctx.Data["Err_Passcode"] = true
ctx.RenderWithErr(ctx.Tr("auth.twofa_passcode_incorrect"), tplResetPassword, nil)
return
}
twofa.LastUsedPasscode = passcode
if err = models.UpdateTwoFactor(twofa); err != nil {
ctx.ServerError("ResetPasswdPost: UpdateTwoFactor", err)
return
}
}
}
var err error var err error
if u.Rands, err = models.GetUserSalt(); err != nil { if u.Rands, err = models.GetUserSalt(); err != nil {
ctx.ServerError("UpdateUser", err) ctx.ServerError("UpdateUser", err)
@@ -1357,7 +1407,6 @@ func ResetPasswdPost(ctx *context.Context) {
ctx.ServerError("UpdateUser", err) ctx.ServerError("UpdateUser", err)
return return
} }
u.HashPassword(passwd) u.HashPassword(passwd)
u.MustChangePassword = false u.MustChangePassword = false
if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil { if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil {
@@ -1366,9 +1415,27 @@ func ResetPasswdPost(ctx *context.Context) {
} }
log.Trace("User password reset: %s", u.Name) log.Trace("User password reset: %s", u.Name)
ctx.Data["IsResetFailed"] = true ctx.Data["IsResetFailed"] = true
remember := len(ctx.Query("remember")) != 0 remember := len(ctx.Query("remember")) != 0
if regenerateScratchToken {
// Invalidate the scratch token.
_, err = twofa.GenerateScratchToken()
if err != nil {
ctx.ServerError("UserSignIn", err)
return
}
if err = models.UpdateTwoFactor(twofa); err != nil {
ctx.ServerError("UserSignIn", err)
return
}
handleSignInFull(ctx, u, remember, false)
ctx.Flash.Info(ctx.Tr("auth.twofa_scratch_used"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
return
}
handleSignInFull(ctx, u, remember, true) handleSignInFull(ctx, u, remember, true)
} }

View File

@@ -74,7 +74,9 @@ func retrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) {
if act.ActUser != nil { if act.ActUser != nil {
userCache[act.ActUserID] = act.ActUser userCache[act.ActUserID] = act.ActUser
} }
}
for _, act := range actions {
repoOwner, ok := userCache[act.Repo.OwnerID] repoOwner, ok := userCache[act.Repo.OwnerID]
if !ok { if !ok {
repoOwner, err = models.GetUserByID(act.Repo.OwnerID) repoOwner, err = models.GetUserByID(act.Repo.OwnerID)

View File

@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
) )
@@ -37,6 +38,54 @@ func createTag(gitRepo *git.Repository, rel *models.Release) error {
return err return err
} }
rel.LowerTagName = strings.ToLower(rel.TagName) rel.LowerTagName = strings.ToLower(rel.TagName)
// Prepare Notify
if err := rel.LoadAttributes(); err != nil {
log.Error("LoadAttributes: %v", err)
return err
}
refName := git.TagPrefix + rel.TagName
apiPusher := rel.Publisher.APIFormat()
apiRepo := rel.Repo.APIFormat(models.AccessModeNone)
apiCommits, err := models.NewPushCommits().ToAPIPayloadCommits(rel.Repo.RepoPath(), rel.Repo.HTMLURL())
if err != nil {
log.Error("commits.ToAPIPayloadCommits failed: %v", err)
return err
}
if err := models.PrepareWebhooks(rel.Repo, models.HookEventPush, &api.PushPayload{
Ref: refName,
Before: git.EmptySHA,
After: commit.ID.String(),
CompareURL: setting.AppURL + models.NewPushCommits().CompareURL,
Commits: apiCommits,
Repo: apiRepo,
Pusher: apiPusher,
Sender: apiPusher,
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
gitRepo, err := git.OpenRepository(rel.Repo.RepoPath())
if err != nil {
log.Error("OpenRepository[%s]: %v", rel.Repo.RepoPath(), err)
}
shaSum, err := gitRepo.GetTagCommitID(refName)
if err != nil {
gitRepo.Close()
log.Error("GetTagCommitID[%s]: %v", refName, err)
}
gitRepo.Close()
if err = models.PrepareWebhooks(rel.Repo, models.HookEventCreate, &api.CreatePayload{
Ref: git.RefEndName(refName),
Sha: shaSum,
RefType: "tag",
Repo: apiRepo,
Sender: apiPusher,
}); err != nil {
return fmt.Errorf("PrepareWebhooks: %v", err)
}
} }
commit, err := gitRepo.GetTagCommit(rel.TagName) commit, err := gitRepo.GetTagCommit(rel.TagName)
if err != nil { if err != nil {

View File

@@ -112,6 +112,12 @@
</div> </div>
</div> </div>
{{end}} {{end}}
<div class="inline field">
<div class="ui checkbox">
<label for="allow_deactivate_all"><strong>{{.i18n.Tr "admin.auths.allow_deactivate_all"}}</strong></label>
<input id="allow_deactivate_all" name="allow_deactivate_all" type="checkbox" {{if $cfg.AllowDeactivateAll}}checked{{end}}>
</div>
</div>
{{end}} {{end}}
<!-- SMTP --> <!-- SMTP -->

View File

@@ -73,21 +73,27 @@
</div> </div>
{{end}} {{end}}
</td> </td>
<td class="two wide right aligned"> <td class="three wide right aligned">
{{if not .LatestPullRequest}} {{if not .LatestPullRequest}}
{{if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}} {{if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
<a href="{{$.RepoLink}}/compare/{{$.DefaultBranch | EscapePound}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | EscapePound}}"> <a href="{{$.RepoLink}}/compare/{{$.DefaultBranch | EscapePound}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | EscapePound}}">
<button id="new-pull-request" class="ui compact basic button">{{$.i18n.Tr "repo.pulls.compare_changes"}}</button> <button id="new-pull-request" class="ui compact basic button">{{$.i18n.Tr "repo.pulls.compare_changes"}}</button>
</a> </a>
{{end}} {{end}}
{{else if and .LatestPullRequest.HasMerged .MergeMovedOn}}
{{if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
<a href="{{$.RepoLink}}/compare/{{$.DefaultBranch | EscapePound}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | EscapePound}}">
<button id="new-pull-request" class="ui compact basic button">{{$.i18n.Tr "repo.pulls.compare_changes"}}</button>
</a>
{{end}}
{{else}} {{else}}
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}">#{{.LatestPullRequest.Issue.Index}}</a> <a href="{{.LatestPullRequest.Issue.HTMLURL}}">{{if ne .LatestPullRequest.BaseRepoID .LatestPullRequest.HeadRepoID}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
{{if .LatestPullRequest.HasMerged}} {{if .LatestPullRequest.HasMerged}}
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui purple small label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a> <a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui purple mini label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a>
{{else if .LatestPullRequest.Issue.IsClosed}} {{else if .LatestPullRequest.Issue.IsClosed}}
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui red small label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a> <a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui red mini label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a>
{{else}} {{else}}
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui green small label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a> <a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui green mini label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a>
{{end}} {{end}}
{{end}} {{end}}
</td> </td>

View File

@@ -26,7 +26,8 @@
<span class="markdown-info"><i class="octicon octicon-markdown"></i> {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span> <span class="markdown-info"><i class="octicon octicon-markdown"></i> {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span>
<div class="ui right floated"> <div class="ui right floated">
{{if $.reply}} {{if $.reply}}
<button name="reply" value="{{$.reply}}" class="ui submit green tiny button btn-reply">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button> <input type="hidden" name="reply" value="{{$.reply}}">
<button class="ui submit green tiny button btn-reply" onclick="submitReply(this);">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button>
{{else}} {{else}}
{{if $.root.CurrentReview}} {{if $.root.CurrentReview}}
<button name="is_review" value="true" type="submit" <button name="is_review" value="true" type="submit"

View File

@@ -78,7 +78,7 @@
{{ template "repo/issue/view_content/pull". }} {{ template "repo/issue/view_content/pull". }}
{{end}} {{end}}
{{if .IsSigned}} {{if .IsSigned}}
{{ if and (or .IsRepoAdmin .IsRepoIssuesWriter (or (not .Issue.IsLocked))) (not .Repository.IsArchived) }} {{ if and (or .IsRepoAdmin .IsIssuesWriter (or (not .Issue.IsLocked))) (not .Repository.IsArchived) }}
<div class="comment form"> <div class="comment form">
<a class="avatar" href="{{.SignedUser.HomeLink}}"> <a class="avatar" href="{{.SignedUser.HomeLink}}">
<img src="{{.SignedUser.RelAvatarLink}}"> <img src="{{.SignedUser.RelAvatarLink}}">

View File

@@ -71,7 +71,7 @@
{{$.i18n.Tr "repo.pulls.reopen_to_merge"}} {{$.i18n.Tr "repo.pulls.reopen_to_merge"}}
{{end}} {{end}}
</div> </div>
{{if .IsPullBranchDeletable}} {{if and .IsPullBranchDeletable ( not .IsPullRequestBroken )}}
<div class="ui divider"></div> <div class="ui divider"></div>
<div> <div>
<a class="delete-button ui red button" href="" data-url="{{.DeleteBranchLink}}">{{$.i18n.Tr "repo.branch.delete" .HeadTarget}}</a> <a class="delete-button ui red button" href="" data-url="{{.DeleteBranchLink}}">{{$.i18n.Tr "repo.branch.delete" .HeadTarget}}</a>

View File

@@ -426,6 +426,7 @@
<input type="hidden" id="repolink" value="{{$.RepoRelPath}}"> <input type="hidden" id="repolink" value="{{$.RepoRelPath}}">
<!-- I know, there is probably a better way to do this --> <!-- I know, there is probably a better way to do this -->
<input type="hidden" id="issueIndex" value="{{.Issue.Index}}"/> <input type="hidden" id="issueIndex" value="{{.Issue.Index}}"/>
<input type="hidden" id="type" value="{{.IssueType}}">
<div class="ui basic modal remove-dependency"> <div class="ui basic modal remove-dependency">
<div class="ui icon header"> <div class="ui icon header">

View File

@@ -2777,6 +2777,12 @@
"description": "search string", "description": "search string",
"name": "q", "name": "q",
"in": "query" "in": "query"
},
{
"type": "string",
"description": "filter by type (issues / pulls) if set",
"name": "type",
"in": "query"
} }
], ],
"responses": { "responses": {

View File

@@ -18,7 +18,7 @@
{{end}} {{end}}
{{if .IsResetForm}} {{if .IsResetForm}}
<div class="required inline field {{if .Err_Password}}error{{end}}"> <div class="required inline field {{if .Err_Password}}error{{end}}">
<label for="password">{{.i18n.Tr "password"}}</label> <label for="password">{{.i18n.Tr "settings.new_password"}}</label>
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" autofocus required> <input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" autofocus required>
</div> </div>
{{if not .user_signed_in}} {{if not .user_signed_in}}
@@ -30,10 +30,31 @@
</div> </div>
</div> </div>
{{end}} {{end}}
{{if .has_two_factor}}
<h4 class="ui dividing header">
{{.i18n.Tr "twofa"}}
</h4>
<div class="ui warning visible message">{{.i18n.Tr "settings.twofa_is_enrolled" | Str2html }}</div>
{{if .scratch_code}}
<div class="required inline field {{if .Err_Token}}error{{end}}">
<label for="token">{{.i18n.Tr "auth.scratch_code"}}</label>
<input id="token" name="token" type="text" autocomplete="off" autofocus required>
</div>
<input type="hidden" name="scratch_code" value="true">
{{else}}
<div class="required inline field {{if .Err_Passcode}}error{{end}}">
<label for="passcode">{{.i18n.Tr "passcode"}}</label>
<input id="passcode" name="passcode" type="number" autocomplete="off" autofocus required>
</div>
{{end}}
{{end}}
<div class="ui divider"></div> <div class="ui divider"></div>
<div class="inline field"> <div class="inline field">
<label></label> <label></label>
<button class="ui blue button">{{.i18n.Tr "auth.reset_password_helper"}}</button> <button class="ui blue button">{{.i18n.Tr "auth.reset_password_helper"}}</button>
{{if and .has_two_factor (not .scratch_code)}}
<a href="{{.Link}}?code={{.Code}}&amp;scratch_code=true">{{.i18n.Tr "auth.use_scratch_code" | Str2html}}</a>
{{end}}
</div> </div>
{{else}} {{else}}
<p class="center">{{.i18n.Tr "auth.invalid_code"}}</p> <p class="center">{{.i18n.Tr "auth.invalid_code"}}</p>