Compare commits

...

23 Commits

Author SHA1 Message Date
John Olheiser
e766f11bd3 Changelog 1.10.0-rc2 (#8750)
* 1.10.0-rc2

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* Wording

Signed-off-by: jolheiser <john.olheiser@gmail.com>

* Update CHANGELOG.md

Co-Authored-By: jaqra <48099350+jaqra@users.noreply.github.com>
2019-10-30 19:27:52 +02:00
6543
432f9dd1a3 [Fix] milestone close timestamp (#8728) (#8730)
* BugFix: Update closed_date_unix colum on milestone table on close

* go fmt
2019-10-29 03:11:24 +00:00
David Svantesson
8caf05989f Fix deadline on update issue or PR via API (#8698) 2019-10-28 01:36:59 +02:00
Monty Taylor
9bde52ffc1 Fix 500 when getting user as unauthenticated user (#8653) (#8663)
Backport #8653

When doing GET /api/v1/users/{user} as an unauthenticated user,
gitea throws a 500 because it's trying to dereference elements
from the context user. It wants to do this to see whether to
show the primary email and will do that if the logged in user
is admin or the user in question. However, if ctx.User is nil there is a panic
2019-10-25 13:09:15 +01:00
jaqra
fa03af8456 make call createMilestoneComment on newIssue func (#8678) (#8681)
* make call createMilestoneComment on newIssue func

* make OldMilestoneID 0 instead of -1
2019-10-25 11:09:19 +01:00
Lunny Xiao
14ebda6fd5 Hide some user information via API if user have no enough permission (#8655) (#8657)
* Hide some user information via API if user have no enough permission

* fix test
2019-10-24 08:59:53 +03:00
zeripath
1d10747514 Use AppSubUrl for more redirections (#8647) (#8651)
Partial backport without changes to locale files.

Fix #8461 - fix misspelling of {{AppSubUrl}} and other misspelling in template
Fixes /explore and organisation redirection
2019-10-23 18:27:10 -04:00
John Olheiser
83c90e9ba0 Add SubURL to redirect path (#8632) (#8634)
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2019-10-22 22:16:20 +01:00
John Olheiser
2fbd5ae2e5 Fix template error on account page (#8562) (#8622) 2019-10-22 10:08:59 +01:00
guillep2k
0032278a46 Allow externalID to be UUID (#8551) (#8624)
Signed-off-by: Wenxuan Zhao <viz@linux.com>
2019-10-22 09:12:10 +01:00
guillep2k
ccf5298a2c Prevent .code-view from overriding font on icon fonts (#8614) (#8627) 2019-10-22 14:39:40 +08:00
zeripath
ece768ab6e Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) (#8618)
* Expose db.SetMaxOpenConns and allow other dbs to set their connection params
* Add note about port exhaustion

Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com>
2019-10-22 07:00:37 +01:00
guillep2k
bac9424a62 fix emoji panel be removed bug in issue page, when the sub issue summit the duplicate emoji (#8609) (#8623) 2019-10-22 02:17:25 +03:00
zeripath
afeab941b3 Update heatmap fixtures to restore tests (#8615) (#8616)
* Update heatmap fixtures to restore tests
* Add hint to check the fixture age on fail
2019-10-21 22:15:55 +01:00
6543
cf35355db8 Ensure that diff stats can scroll independently of the diff (#8581) (#8611)
This PR ensures that once opened the diff stats detail box can be scrolled independently of the diff on the compare page.

Fixes #5532 

Details:

* make diff-detail-box the main container
* move file diff at the same level as diff-stats
* make diff-view options sticy again
* make diff-stats scroll if to mouch
* rm useless css info
* less: mv diff-stats to own class
* use new css class
* cleanup less file
* diff-counter: margin-right: 15px;
* make CI work
* make numbers colorful
* add sign (-/+) to numbers
2019-10-21 16:53:34 +08:00
Viktor Szakats
8e9265c402 webhook: set Content-Type for application/x-www-form-urlencoded (#8600)
This header is missing since switching http client from GiteaServer (`code.gitea.io/gitea/modules/httplib`) to Go-http-client/1.1 (`net.http`). The header [was added by default](https://github.com/go-gitea/gitea/blob/release/v1.8/modules/httplib/httplib.go#L301) by the former, but this is no longer true with `net.http`, so it needs to be done explicitly.

Closes: #7700
2019-10-20 18:18:05 +01:00
6543
435ce92935 Fix #8582 by handling empty repos (#8587) (#8594)
* Fix #8582 by handling empty repos

Signed-off-by: Jonas Franz <info@jonasfranz.software>

* Fix tests

Signed-off-by: Jonas Franz <info@jonasfranz.software>
2019-10-19 21:35:22 +01:00
Lunny Xiao
22cea96c18 Fix bug on pull requests when transfer head repository (#8564) (#8569)
* fix bug on pull requests when transfer head repository

* add migration and fix lint

* fix tests and add a cache check on LoadBaseRepo
2019-10-19 08:29:35 +01:00
6543
7565ac02c2 Allow more than 255 characters for tokens in external_login_user tabl… (#8585)
* Allow more than 255 characters for tokens in external_login_user table (#8554)

Signed-off-by: Wenxuan Zhao <viz@linux.com>

* use old xorm repo
2019-10-19 12:54:09 +08:00
zeripath
4e85c8e0d8 Add missed close in ServeBlobLFS (#8527) (#8542) 2019-10-16 20:32:15 +01:00
zeripath
34b8becef0 Ensure that GitRepo is set on Empty repositories (#8539) (#8541)
Both issues/new and settings/hooks/git expect `ctx.Repo.GitRepo` to be set.
This PR changes the context code to open the GitRepo.

Fixes #8538
2019-10-17 00:03:25 +08:00
6543
0752a3895a Fix migrate mirror 500 bug (#8526) (#8530)
* fix migrate mirror 500 bug

* update backport
2019-10-16 10:48:45 +01:00
guillep2k
595033f78e Fix password complexity regex for special characters (backport for v1.10.0) (#8524)
* Fix extra space

* Fix regular expression

* Fix error template name

* Simplify check code, fix default values, add test

* Fix router tests

* Fix fmt

* Fix setting and lint

* Move cleaning up code to test, improve comments

* Tidy up variable declaration
2019-10-16 11:09:27 +08:00
51 changed files with 591 additions and 411 deletions

View File

@@ -4,6 +4,32 @@ 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.0-RC2](https://github.com/go-gitea/gitea/releases/tag/v1.10.0-rc2) - 2019-10-30
* BREAKING
* Fix deadline on update issue or PR via API (#8698)
* Hide some user information via API if user doesn't have enough permission (#8655) (#8657)
* BUGFIXES
* Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) (#8618)
* Fix milestone close timestamp (#8728) (#8730)
* Fix 500 when getting user as unauthenticated user (#8653) (#8663)
* Fix 'New Issue Missing Milestone Comment' (#8678) (#8681)
* Use AppSubUrl for more redirections (#8647) (#8651)
* Add SubURL to redirect path (#8632) (#8634)
* Fix template error on account page (#8562) (#8622)
* Allow externalID to be UUID (#8551) (#8624)
* Prevent removal of non-empty emoji panel following selection of duplicate (#8609) (#8623)
* Update heatmap fixtures to restore tests (#8615) (#8616)
* Ensure that diff stats can scroll independently of the diff (#8581) (#8621)
* Webhook: set Content-Type for application/x-www-form-urlencoded (#8600)
* Fix #8582 by handling empty repos (#8587) (#8594)
* Fix bug on pull requests when transfer head repository (#8564) (#8569)
* Add missed close in ServeBlobLFS (#8527) (#8542)
* Ensure that GitRepo is set on Empty repositories (#8539) (#8541)
* Fix migrate mirror 500 bug (#8526) (#8530)
* Fix password complexity regex for special characters (#8524)
* Prevent .code-view from overriding font on icon fonts (#8614) (#8627)
* Allow more than 255 characters for tokens in external_login_user table (#8554)
## [1.10.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.10.0-rc1) - 2019-10-14 ## [1.10.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.10.0-rc1) - 2019-10-14
* BREAKING * BREAKING
* Remove legacy handling of drone token (#8191) * Remove legacy handling of drone token (#8191)

View File

@@ -277,10 +277,12 @@ LOG_SQL = true
DB_RETRIES = 10 DB_RETRIES = 10
; Backoff time per DB retry (time.Duration) ; Backoff time per DB retry (time.Duration)
DB_RETRY_BACKOFF = 3s DB_RETRY_BACKOFF = 3s
; Max idle database connections on connnection pool, default is 0 ; Max idle database connections on connnection pool, default is 2
MAX_IDLE_CONNS = 0 MAX_IDLE_CONNS = 2
; Database connection max life time, default is 3s ; Database connection max life time, default is 0 or 3s mysql (See #6804 & #7071 for reasoning)
CONN_MAX_LIFETIME = 3s CONN_MAX_LIFETIME = 3s
; Database maximum number of open connections, default is 0 meaning no maximum
MAX_OPEN_CONNS = 0
[indexer] [indexer]
; Issue indexer type, currently support: bleve or db, default is bleve ; Issue indexer type, currently support: bleve or db, default is bleve
@@ -333,7 +335,8 @@ IMPORT_LOCAL_PATHS = false
; Set to true to prevent all users (including admin) from creating custom git hooks ; Set to true to prevent all users (including admin) from creating custom git hooks
DISABLE_GIT_HOOKS = false DISABLE_GIT_HOOKS = false
;Comma separated list of character classes required to pass minimum complexity. ;Comma separated list of character classes required to pass minimum complexity.
;If left empty or no valid values are specified, the default values (`lower,upper,digit,spec`) will be used. ;If left empty or no valid values are specified, the default values ("lower,upper,digit,spec") will be used.
;Use "off" to disable checking.
PASSWORD_COMPLEXITY = lower,upper,digit,spec PASSWORD_COMPLEXITY = lower,upper,digit,spec
; Password Hash algorithm, either "pbkdf2", "argon2", "scrypt" or "bcrypt" ; Password Hash algorithm, either "pbkdf2", "argon2", "scrypt" or "bcrypt"
PASSWORD_HASH_ALGO = pbkdf2 PASSWORD_HASH_ALGO = pbkdf2
@@ -822,6 +825,6 @@ TOKEN =
QUEUE_TYPE = channel QUEUE_TYPE = channel
; Task queue length, available only when `QUEUE_TYPE` is `channel`. ; Task queue length, available only when `QUEUE_TYPE` is `channel`.
QUEUE_LENGTH = 1000 QUEUE_LENGTH = 1000
; Task queue connction string, available only when `QUEUE_TYPE` is `redis`. ; Task queue connection string, available only when `QUEUE_TYPE` is `redis`.
; If there is a password of redis, use `addrs=127.0.0.1:6379 password=123 db=0`. ; If there is a password of redis, use `addrs=127.0.0.1:6379 password=123 db=0`.
QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0" QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0"

View File

@@ -167,8 +167,12 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `LOG_SQL`: **true**: Log the executed SQL. - `LOG_SQL`: **true**: Log the executed SQL.
- `DB_RETRIES`: **10**: How many ORM init / DB connect attempts allowed. - `DB_RETRIES`: **10**: How many ORM init / DB connect attempts allowed.
- `DB_RETRY_BACKOFF`: **3s**: time.Duration to wait before trying another ORM init / DB connect attempt, if failure occured. - `DB_RETRY_BACKOFF`: **3s**: time.Duration to wait before trying another ORM init / DB connect attempt, if failure occured.
- `MAX_IDLE_CONNS` **0**: Max idle database connections on connnection pool, default is 0 - `MAX_OPEN_CONNS` **0**: Database maximum open connections - default is 0, meaning there is no limit.
- `CONN_MAX_LIFETIME` **3s**: Database connection max lifetime - `MAX_IDLE_CONNS` **2**: Max idle database connections on connnection pool, default is 2 - this will be capped to `MAX_OPEN_CONNS`.
- `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071).
Please see #8540 & #8273 for further discussion of the appropriate values for `MAX_OPEN_CONNS`, `MAX_IDLE_CONNS` & `CONN_MAX_LIFETIME` and their
relation to port exhaustion.
## Indexer (`indexer`) ## Indexer (`indexer`)
@@ -212,7 +216,8 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- lower - use one or more lower latin characters - lower - use one or more lower latin characters
- upper - use one or more upper latin characters - upper - use one or more upper latin characters
- digit - use one or more digits - digit - use one or more digits
- spec - use one or more special characters as ``][!"#$%&'()*+,./:;<=>?@\^_{|}~`-`` and space symbol. - spec - use one or more special characters as ``!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~``
- off - do not check password complexity
## OpenID (`openid`) ## OpenID (`openid`)

View File

@@ -29,7 +29,6 @@ func TestAPITeamUser(t *testing.T) {
var user2 *api.User var user2 *api.User
DecodeJSON(t, resp, &user2) DecodeJSON(t, resp, &user2)
user2.Created = user2.Created.In(time.Local) user2.Created = user2.Created.In(time.Local)
user2.LastLogin = user2.LastLogin.In(time.Local)
user := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User) user := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
assert.Equal(t, convert.ToUser(user, true, false), user2) assert.Equal(t, convert.ToUser(user, true, false), user2)

View File

@@ -26,7 +26,7 @@ func TestUserHeatmap(t *testing.T) {
var heatmap []*models.UserHeatmapData var heatmap []*models.UserHeatmapData
DecodeJSON(t, resp, &heatmap) DecodeJSON(t, resp, &heatmap)
var dummyheatmap []*models.UserHeatmapData var dummyheatmap []*models.UserHeatmapData
dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1540080000, Contributions: 1}) dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1571616000, Contributions: 1})
assert.Equal(t, dummyheatmap, heatmap) assert.Equal(t, dummyheatmap, heatmap)
} }

View File

@@ -28,9 +28,9 @@ type ExternalLoginUser struct {
Description string Description string
AvatarURL string AvatarURL string
Location string Location string
AccessToken string AccessToken string `xorm:"TEXT"`
AccessTokenSecret string AccessTokenSecret string `xorm:"TEXT"`
RefreshToken string RefreshToken string `xorm:"TEXT"`
ExpiresAt time.Time ExpiresAt time.Time
} }
@@ -168,7 +168,7 @@ func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginU
} }
// UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID // UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID
func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID, userID int64) error { func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, userID int64) error {
if err := UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil { if err := UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil {
return err return err
} }

View File

@@ -5,7 +5,7 @@
act_user_id: 2 act_user_id: 2
repo_id: 2 repo_id: 2
is_private: true is_private: true
created_unix: 1540139562 created_unix: 1571686356
- -
id: 2 id: 2

View File

@@ -6,7 +6,6 @@
index: 2 index: 2
head_repo_id: 1 head_repo_id: 1
base_repo_id: 1 base_repo_id: 1
head_user_name: user1
head_branch: branch1 head_branch: branch1
base_branch: master base_branch: master
merge_base: 1234567890abcdef merge_base: 1234567890abcdef
@@ -21,7 +20,6 @@
index: 3 index: 3
head_repo_id: 1 head_repo_id: 1
base_repo_id: 1 base_repo_id: 1
head_user_name: user1
head_branch: branch2 head_branch: branch2
base_branch: master base_branch: master
merge_base: fedcba9876543210 merge_base: fedcba9876543210
@@ -35,7 +33,6 @@
index: 1 index: 1
head_repo_id: 11 head_repo_id: 11
base_repo_id: 10 base_repo_id: 10
head_user_name: user13
head_branch: branch2 head_branch: branch2
base_branch: master base_branch: master
merge_base: 0abcb056019adb83 merge_base: 0abcb056019adb83

View File

@@ -1072,6 +1072,10 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil { if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil {
return err return err
} }
if _, err = createMilestoneComment(e, doer, opts.Repo, opts.Issue, 0, opts.Issue.MilestoneID); err != nil {
return err
}
} }
// Insert the assignees // Insert the assignees
@@ -1950,7 +1954,7 @@ func (issue *Issue) ResolveMentionsByVisibility(ctx DBContext, doer *User, menti
} }
// UpdateIssuesMigrationsByType updates all migrated repositories' issues from gitServiceType to replace originalAuthorID to posterID // UpdateIssuesMigrationsByType updates all migrated repositories' issues from gitServiceType to replace originalAuthorID to posterID
func UpdateIssuesMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID, posterID int64) error { func UpdateIssuesMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID string, posterID int64) error {
_, err := x.Table("issue"). _, err := x.Table("issue").
Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType). Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
And("original_author_id = ?", originalAuthorID). And("original_author_id = ?", originalAuthorID).

View File

@@ -1025,7 +1025,7 @@ func FetchCodeComments(issue *Issue, currentUser *User) (CodeComments, error) {
} }
// UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id // UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id
func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID, posterID int64) error { func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID string, posterID int64) error {
_, err := x.Table("comment"). _, err := x.Table("comment").
Where(builder.In("issue_id", Where(builder.In("issue_id",
builder.Select("issue.id"). builder.Select("issue.id").

View File

@@ -306,7 +306,11 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
} }
m.IsClosed = isClosed m.IsClosed = isClosed
if _, err := sess.ID(m.ID).Cols("is_closed").Update(m); err != nil { if isClosed {
m.ClosedDateUnix = timeutil.TimeStampNow()
}
if _, err := sess.ID(m.ID).Cols("is_closed", "closed_date_unix").Update(m); err != nil {
return err return err
} }

View File

@@ -256,6 +256,10 @@ var migrations = []Migration{
NewMigration("add task table and status column for repository table", addTaskTable), NewMigration("add task table and status column for repository table", addTaskTable),
// v100 -> v101 // v100 -> v101
NewMigration("update migration repositories' service type", updateMigrationServiceTypes), NewMigration("update migration repositories' service type", updateMigrationServiceTypes),
// v101 -> v102
NewMigration("change length of some external login users columns", changeSomeColumnsLengthOfExternalLoginUser),
// v102 -> v103
NewMigration("update migration repositories' service type", dropColumnHeadUserNameOnPullRequest),
} }
// Migrate database to current version // Migrate database to current version

19
models/migrations/v101.go Normal file
View File

@@ -0,0 +1,19 @@
// Copyright 2019 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 migrations
import (
"github.com/go-xorm/xorm"
)
func changeSomeColumnsLengthOfExternalLoginUser(x *xorm.Engine) error {
type ExternalLoginUser struct {
AccessToken string `xorm:"TEXT"`
AccessTokenSecret string `xorm:"TEXT"`
RefreshToken string `xorm:"TEXT"`
}
return x.Sync2(new(ExternalLoginUser))
}

15
models/migrations/v102.go Normal file
View File

@@ -0,0 +1,15 @@
// Copyright 2019 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 migrations
import (
"github.com/go-xorm/xorm"
)
func dropColumnHeadUserNameOnPullRequest(x *xorm.Engine) error {
sess := x.NewSession()
defer sess.Close()
return dropTableColumns(sess, "pull_request", "head_user_name")
}

View File

@@ -157,11 +157,9 @@ func SetEngine() (err error) {
// so use log file to instead print to stdout. // so use log file to instead print to stdout.
x.SetLogger(NewXORMLogger(setting.Database.LogSQL)) x.SetLogger(NewXORMLogger(setting.Database.LogSQL))
x.ShowSQL(setting.Database.LogSQL) x.ShowSQL(setting.Database.LogSQL)
if setting.Database.UseMySQL { x.SetMaxOpenConns(setting.Database.MaxOpenConns)
x.SetMaxIdleConns(setting.Database.MaxIdleConns) x.SetMaxIdleConns(setting.Database.MaxIdleConns)
x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
}
return nil return nil
} }

View File

@@ -66,7 +66,6 @@ type PullRequest struct {
HeadRepo *Repository `xorm:"-"` HeadRepo *Repository `xorm:"-"`
BaseRepoID int64 `xorm:"INDEX"` BaseRepoID int64 `xorm:"INDEX"`
BaseRepo *Repository `xorm:"-"` BaseRepo *Repository `xorm:"-"`
HeadUserName string
HeadBranch string HeadBranch string
BaseBranch string BaseBranch string
ProtectedBranch *ProtectedBranch `xorm:"-"` ProtectedBranch *ProtectedBranch `xorm:"-"`
@@ -79,6 +78,15 @@ type PullRequest struct {
MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"` MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"`
} }
// MustHeadUserName returns the HeadRepo's username if failed return blank
func (pr *PullRequest) MustHeadUserName() string {
if err := pr.LoadHeadRepo(); err != nil {
log.Error("LoadHeadRepo: %v", err)
return ""
}
return pr.HeadRepo.MustOwnerName()
}
// Note: don't try to get Issue because will end up recursive querying. // Note: don't try to get Issue because will end up recursive querying.
func (pr *PullRequest) loadAttributes(e Engine) (err error) { func (pr *PullRequest) loadAttributes(e Engine) (err error) {
if pr.HasMerged && pr.Merger == nil { if pr.HasMerged && pr.Merger == nil {
@@ -102,6 +110,10 @@ func (pr *PullRequest) LoadAttributes() error {
// LoadBaseRepo loads pull request base repository from database // LoadBaseRepo loads pull request base repository from database
func (pr *PullRequest) LoadBaseRepo() error { func (pr *PullRequest) LoadBaseRepo() error {
if pr.BaseRepo == nil { if pr.BaseRepo == nil {
if pr.HeadRepoID == pr.BaseRepoID && pr.HeadRepo != nil {
pr.BaseRepo = pr.HeadRepo
return nil
}
var repo Repository var repo Repository
if has, err := x.ID(pr.BaseRepoID).Get(&repo); err != nil { if has, err := x.ID(pr.BaseRepoID).Get(&repo); err != nil {
return err return err
@@ -113,6 +125,24 @@ func (pr *PullRequest) LoadBaseRepo() error {
return nil return nil
} }
// LoadHeadRepo loads pull request head repository from database
func (pr *PullRequest) LoadHeadRepo() error {
if pr.HeadRepo == nil {
if pr.HeadRepoID == pr.BaseRepoID && pr.BaseRepo != nil {
pr.HeadRepo = pr.BaseRepo
return nil
}
var repo Repository
if has, err := x.ID(pr.HeadRepoID).Get(&repo); err != nil {
return err
} else if !has {
return ErrRepoNotExist{ID: pr.BaseRepoID}
}
pr.HeadRepo = &repo
}
return nil
}
// LoadIssue loads issue information from database // LoadIssue loads issue information from database
func (pr *PullRequest) LoadIssue() (err error) { func (pr *PullRequest) LoadIssue() (err error) {
return pr.loadIssue(x) return pr.loadIssue(x)
@@ -152,7 +182,7 @@ func (pr *PullRequest) GetDefaultMergeMessage() string {
return "" return ""
} }
} }
return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch) return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.MustHeadUserName(), pr.HeadRepo.Name, pr.BaseBranch)
} }
// GetDefaultSquashMessage returns default message used when squash and merging pull request // GetDefaultSquashMessage returns default message used when squash and merging pull request
@@ -1159,18 +1189,6 @@ func checkForInvalidation(requests PullRequestList, repoID int64, doer *User, br
return nil return nil
} }
// ChangeUsernameInPullRequests changes the name of head_user_name
func ChangeUsernameInPullRequests(oldUserName, newUserName string) error {
pr := PullRequest{
HeadUserName: strings.ToLower(newUserName),
}
_, err := x.
Cols("head_user_name").
Where("head_user_name = ?", strings.ToLower(oldUserName)).
Update(pr)
return err
}
// checkAndUpdateStatus checks if pull request is possible to leaving checking status, // checkAndUpdateStatus checks if pull request is possible to leaving checking status,
// and set to be either conflict or mergeable. // and set to be either conflict or mergeable.
func (pr *PullRequest) checkAndUpdateStatus() { func (pr *PullRequest) checkAndUpdateStatus() {

View File

@@ -232,20 +232,6 @@ func TestPullRequestList_LoadAttributes(t *testing.T) {
// TODO TestAddTestPullRequestTask // TODO TestAddTestPullRequestTask
func TestChangeUsernameInPullRequests(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
const newUsername = "newusername"
assert.NoError(t, ChangeUsernameInPullRequests("user1", newUsername))
prs := make([]*PullRequest, 0, 10)
assert.NoError(t, x.Where("head_user_name = ?", newUsername).Find(&prs))
assert.Len(t, prs, 2)
for _, pr := range prs {
assert.Equal(t, newUsername, pr.HeadUserName)
}
CheckConsistencyFor(t, &PullRequest{})
}
func TestPullRequest_IsWorkInProgress(t *testing.T) { func TestPullRequest_IsWorkInProgress(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())

View File

@@ -369,7 +369,7 @@ func SyncReleasesWithTags(repo *Repository, gitRepo *git.Repository) error {
} }
// UpdateReleasesMigrationsByType updates all migrated repositories' releases from gitServiceType to replace originalAuthorID to posterID // UpdateReleasesMigrationsByType updates all migrated repositories' releases from gitServiceType to replace originalAuthorID to posterID
func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID, posterID int64) error { func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID string, posterID int64) error {
_, err := x.Table("release"). _, err := x.Table("release").
Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType). Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
And("original_author_id = ?", originalAuthorID). And("original_author_id = ?", originalAuthorID).

View File

@@ -1430,6 +1430,7 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err
IsFsckEnabled: !opts.IsMirror, IsFsckEnabled: !opts.IsMirror,
CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch,
Status: opts.Status, Status: opts.Status,
IsEmpty: !opts.AutoInit,
} }
sess := x.NewSession() sess := x.NewSession()

View File

@@ -994,10 +994,6 @@ func ChangeUserName(u *User, newUserName string) (err error) {
return ErrUserAlreadyExist{newUserName} return ErrUserAlreadyExist{newUserName}
} }
if err = ChangeUsernameInPullRequests(u.Name, newUserName); err != nil {
return fmt.Errorf("ChangeUsernameInPullRequests: %v", err)
}
// Do not fail if directory does not exist // Do not fail if directory does not exist
if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) { if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("Rename user directory: %v", err) return fmt.Errorf("Rename user directory: %v", err)

View File

@@ -17,7 +17,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
CountResult int CountResult int
JSONResult string JSONResult string
}{ }{
{2, 1, `[{"timestamp":1540080000,"contributions":1}]`}, {2, 1, `[{"timestamp":1571616000,"contributions":1}]`},
{3, 0, `[]`}, {3, 0, `[]`},
} }
// Prepare // Prepare
@@ -41,7 +41,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
// Get the heatmap and compare // Get the heatmap and compare
heatmap, err := GetUserHeatmapDataByUser(user) heatmap, err := GetUserHeatmapDataByUser(user)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, len(actions), len(heatmap)) assert.Equal(t, len(actions), len(heatmap), "invalid action count: did the test data became too old?")
assert.Equal(t, tc.CountResult, len(heatmap)) assert.Equal(t, tc.CountResult, len(heatmap))
//Test JSON rendering //Test JSON rendering

View File

@@ -833,6 +833,8 @@ func (t *HookTask) deliver() error {
return err return err
} }
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
} }
case http.MethodGet: case http.MethodGet:
u, err := url.Parse(t.URL) u, err := url.Parse(t.URL)

View File

@@ -63,7 +63,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
// Force redirection when username is actually a user. // Force redirection when username is actually a user.
if !org.IsOrganization() { if !org.IsOrganization() {
ctx.Redirect("/" + org.Name) ctx.Redirect(setting.AppSubURL + "/" + org.Name)
return return
} }

View File

@@ -236,7 +236,7 @@ func RedirectToRepo(ctx *Context, redirectRepoID int64) {
if ctx.Req.URL.RawQuery != "" { if ctx.Req.URL.RawQuery != "" {
redirectPath += "?" + ctx.Req.URL.RawQuery redirectPath += "?" + ctx.Req.URL.RawQuery
} }
ctx.Redirect(redirectPath) ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
} }
func repoAssignment(ctx *Context, repo *models.Repository) { func repoAssignment(ctx *Context, repo *models.Repository) {
@@ -414,8 +414,8 @@ func RepoAssignment() macaron.Handler {
} }
} }
// repo is empty and display enable // Disable everything when the repo is being created
if ctx.Repo.Repository.IsEmpty || ctx.Repo.Repository.IsBeingCreated() { if ctx.Repo.Repository.IsBeingCreated() {
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
return return
} }
@@ -427,6 +427,12 @@ func RepoAssignment() macaron.Handler {
} }
ctx.Repo.GitRepo = gitRepo ctx.Repo.GitRepo = gitRepo
// Stop at this point when the repo is empty.
if ctx.Repo.Repository.IsEmpty {
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
return
}
tags, err := ctx.Repo.GitRepo.GetTags() tags, err := ctx.Repo.GitRepo.GetTags()
if err != nil { if err != nil {
ctx.ServerError("GetTags", err) ctx.ServerError("GetTags", err)

View File

@@ -581,7 +581,6 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR
var pullRequest = models.PullRequest{ var pullRequest = models.PullRequest{
HeadRepoID: g.repo.ID, HeadRepoID: g.repo.ID,
HeadBranch: head, HeadBranch: head,
HeadUserName: g.repoOwner,
BaseRepoID: g.repo.ID, BaseRepoID: g.repo.ID,
BaseBranch: pr.Base.Ref, BaseBranch: pr.Base.Ref,
MergeBase: pr.Base.SHA, MergeBase: pr.Base.SHA,

View File

@@ -5,8 +5,6 @@
package migrations package migrations
import ( import (
"strconv"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
@@ -40,11 +38,7 @@ func updateMigrationPosterIDByGitService(tp structs.GitServiceType) error {
} }
for _, user := range users { for _, user := range users {
externalUserID, err := strconv.ParseInt(user.ExternalID, 10, 64) externalUserID := user.ExternalID
if err != nil {
log.Warn("Parse externalUser %#v 's userID failed: %v", user, err)
continue
}
if err := models.UpdateMigrationsByType(tp, externalUserID, user.UserID); err != nil { if err := models.UpdateMigrationsByType(tp, externalUserID, user.UserID); err != nil {
log.Error("UpdateMigrationsByType type %s external user id %v to local user id %v failed: %v", tp.Name(), user.ExternalID, user.UserID, err) log.Error("UpdateMigrationsByType type %s external user id %v to local user id %v failed: %v", tp.Name(), user.ExternalID, user.UserID, err)
} }

View File

@@ -7,45 +7,60 @@ package password
import ( import (
"crypto/rand" "crypto/rand"
"math/big" "math/big"
"regexp" "strings"
"sync" "sync"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
var matchComplexities = map[string]regexp.Regexp{} var (
var matchComplexityOnce sync.Once matchComplexityOnce sync.Once
var validChars string validChars string
var validComplexities = map[string]string{ requiredChars []string
"lower": "abcdefghijklmnopqrstuvwxyz",
"upper": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", charComplexities = map[string]string{
"digit": "0123456789", "lower": `abcdefghijklmnopqrstuvwxyz`,
"spec": `][ !"#$%&'()*+,./:;<=>?@\^_{|}~` + "`-", "upper": `ABCDEFGHIJKLMNOPQRSTUVWXYZ`,
} "digit": `0123456789`,
"spec": ` !"#$%&'()*+,-./:;<=>?@[\]^_{|}~` + "`",
}
)
// NewComplexity for preparation // NewComplexity for preparation
func NewComplexity() { func NewComplexity() {
matchComplexityOnce.Do(func() { matchComplexityOnce.Do(func() {
if len(setting.PasswordComplexity) > 0 { setupComplexity(setting.PasswordComplexity)
for key, val := range setting.PasswordComplexity {
matchComplexity := regexp.MustCompile(val)
matchComplexities[key] = *matchComplexity
validChars += validComplexities[key]
}
} else {
for _, val := range validComplexities {
validChars += val
}
}
}) })
} }
// IsComplexEnough return True if password is Complexity func setupComplexity(values []string) {
if len(values) != 1 || values[0] != "off" {
for _, val := range values {
if chars, ok := charComplexities[val]; ok {
validChars += chars
requiredChars = append(requiredChars, chars)
}
}
if len(requiredChars) == 0 {
// No valid character classes found; use all classes as default
for _, chars := range charComplexities {
validChars += chars
requiredChars = append(requiredChars, chars)
}
}
}
if validChars == "" {
// No complexities to check; provide a sensible default for password generation
validChars = charComplexities["lower"] + charComplexities["upper"] + charComplexities["digit"]
}
}
// IsComplexEnough return True if password meets complexity settings
func IsComplexEnough(pwd string) bool { func IsComplexEnough(pwd string) bool {
if len(setting.PasswordComplexity) > 0 {
NewComplexity() NewComplexity()
for _, val := range matchComplexities { if len(validChars) > 0 {
if !val.MatchString(pwd) { for _, req := range requiredChars {
if !strings.ContainsAny(req, pwd) {
return false return false
} }
} }

View File

@@ -0,0 +1,75 @@
// Copyright 2019 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 password
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestComplexity_IsComplexEnough(t *testing.T) {
matchComplexityOnce.Do(func() {})
testlist := []struct {
complexity []string
truevalues []string
falsevalues []string
}{
{[]string{"lower"}, []string{"abc", "abc!"}, []string{"ABC", "123", "=!$", ""}},
{[]string{"upper"}, []string{"ABC"}, []string{"abc", "123", "=!$", "abc!", ""}},
{[]string{"digit"}, []string{"123"}, []string{"abc", "ABC", "=!$", "abc!", ""}},
{[]string{"spec"}, []string{"=!$", "abc!"}, []string{"abc", "ABC", "123", ""}},
{[]string{"off"}, []string{"abc", "ABC", "123", "=!$", "abc!", ""}, nil},
{[]string{"lower", "spec"}, []string{"abc!"}, []string{"abc", "ABC", "123", "=!$", "abcABC123", ""}},
{[]string{"lower", "upper", "digit"}, []string{"abcABC123"}, []string{"abc", "ABC", "123", "=!$", "abc!", ""}},
}
for _, test := range testlist {
testComplextity(test.complexity)
for _, val := range test.truevalues {
assert.True(t, IsComplexEnough(val))
}
for _, val := range test.falsevalues {
assert.False(t, IsComplexEnough(val))
}
}
// Remove settings for other tests
testComplextity([]string{"off"})
}
func TestComplexity_Generate(t *testing.T) {
matchComplexityOnce.Do(func() {})
const maxCount = 50
const pwdLen = 50
test := func(t *testing.T, modes []string) {
testComplextity(modes)
for i := 0; i < maxCount; i++ {
pwd, err := Generate(pwdLen)
assert.NoError(t, err)
assert.Equal(t, pwdLen, len(pwd))
assert.True(t, IsComplexEnough(pwd), "Failed complexities with modes %+v for generated: %s", modes, pwd)
}
}
test(t, []string{"lower"})
test(t, []string{"upper"})
test(t, []string{"lower", "upper", "spec"})
test(t, []string{"off"})
test(t, []string{""})
// Remove settings for other tests
testComplextity([]string{"off"})
}
func testComplextity(values []string) {
// Cleanup previous values
validChars = ""
requiredChars = make([]string, 0, len(values))
setupComplexity(values)
}

View File

@@ -38,6 +38,9 @@ func (ct *ContentType) String() string {
// GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree // GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree
// directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag // directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
func GetContentsOrList(repo *models.Repository, treePath, ref string) (interface{}, error) { func GetContentsOrList(repo *models.Repository, treePath, ref string) (interface{}, error) {
if repo.IsEmpty {
return make([]interface{}, 0), nil
}
if ref == "" { if ref == "" {
ref = repo.DefaultBranch ref = repo.DefaultBranch
} }

View File

@@ -190,3 +190,19 @@ func TestGetContentsOrListErrors(t *testing.T) {
assert.Nil(t, fileContentResponse) assert.Nil(t, fileContentResponse)
}) })
} }
func TestGetContentsOrListOfEmptyRepos(t *testing.T) {
models.PrepareTestEnv(t)
ctx := test.MockContext(t, "user2/repo15")
ctx.SetParams(":id", "15")
test.LoadRepo(t, ctx, 15)
test.LoadUser(t, ctx, 2)
test.LoadGitRepo(t, ctx)
repo := ctx.Repo.Repository
t.Run("empty repo", func(t *testing.T) {
contents, err := GetContentsOrList(repo, "", "")
assert.NoError(t, err)
assert.Empty(t, contents)
})
}

View File

@@ -42,12 +42,11 @@ var (
DBConnectRetries int DBConnectRetries int
DBConnectBackoff time.Duration DBConnectBackoff time.Duration
MaxIdleConns int MaxIdleConns int
MaxOpenConns int
ConnMaxLifetime time.Duration ConnMaxLifetime time.Duration
IterateBufferSize int IterateBufferSize int
}{ }{
Timeout: 500, Timeout: 500,
MaxIdleConns: 0,
ConnMaxLifetime: 3 * time.Second,
} }
) )
@@ -80,8 +79,13 @@ func InitDBConfig() {
Database.Charset = sec.Key("CHARSET").In("utf8", []string{"utf8", "utf8mb4"}) Database.Charset = sec.Key("CHARSET").In("utf8", []string{"utf8", "utf8mb4"})
Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db")) Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db"))
Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500) Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500)
Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(0) Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2)
if Database.UseMySQL {
Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(3 * time.Second) Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(3 * time.Second)
} else {
Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(0)
}
Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0)
Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50) Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50)
Database.LogSQL = sec.Key("LOG_SQL").MustBool(true) Database.LogSQL = sec.Key("LOG_SQL").MustBool(true)

View File

@@ -146,7 +146,7 @@ var (
MinPasswordLength int MinPasswordLength int
ImportLocalPaths bool ImportLocalPaths bool
DisableGitHooks bool DisableGitHooks bool
PasswordComplexity map[string]string PasswordComplexity []string
PasswordHashAlgo string PasswordHashAlgo string
// UI settings // UI settings
@@ -775,26 +775,14 @@ func NewContext() {
InternalToken = loadInternalToken(sec) InternalToken = loadInternalToken(sec)
var dictPC = map[string]string{
"lower": "[a-z]+",
"upper": "[A-Z]+",
"digit": "[0-9]+",
"spec": `][ !"#$%&'()*+,./:;<=>?@\\^_{|}~` + "`-",
}
PasswordComplexity = make(map[string]string)
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",") cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
for _, y := range cfgdata { PasswordComplexity = make([]string, 0, len(cfgdata))
ts := strings.TrimSpace(y) for _, name := range cfgdata {
for a := range dictPC { name := strings.ToLower(strings.Trim(name, `"`))
if strings.ToLower(ts) == a { if name != "" {
PasswordComplexity[ts] = dictPC[ts] PasswordComplexity = append(PasswordComplexity, name)
break
} }
} }
}
if len(PasswordComplexity) == 0 {
PasswordComplexity = dictPC
}
sec = Cfg.Section("attachment") sec = Cfg.Section("attachment")
AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments")) AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))

View File

@@ -239,7 +239,7 @@ footer .ui.left,footer .ui.right{line-height:40px}
.lines-commit .ui.avatar.image{height:18px;width:18px} .lines-commit .ui.avatar.image{height:18px;width:18px}
.lines-code .bottom-line,.lines-commit .bottom-line,.lines-num .bottom-line{border-bottom:1px solid #eaecef} .lines-code .bottom-line,.lines-commit .bottom-line,.lines-num .bottom-line{border-bottom:1px solid #eaecef}
.code-view{overflow:auto;overflow-x:auto;overflow-y:hidden} .code-view{overflow:auto;overflow-x:auto;overflow-y:hidden}
.code-view *{font-size:12px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;line-height:20px} .code-view :not(.fa):not(.octicon):not(.icon){font-size:12px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;line-height:20px}
.code-view table{width:100%} .code-view table{width:100%}
.code-view .active{background:#fff866} .code-view .active{background:#fff866}
.markdown:not(code){overflow:hidden;font-size:16px;line-height:1.6!important;word-wrap:break-word} .markdown:not(code){overflow:hidden;font-size:16px;line-height:1.6!important;word-wrap:break-word}
@@ -659,8 +659,6 @@ footer .ui.left,footer .ui.right{line-height:40px}
.repository #commits-table td.sha .sha.label.isSigned.isVerified:hover,.repository #repo-files-table .sha.label.isSigned.isVerified:hover{background:rgba(33,186,69,.3)!important} .repository #commits-table td.sha .sha.label.isSigned.isVerified:hover,.repository #repo-files-table .sha.label.isSigned.isVerified:hover{background:rgba(33,186,69,.3)!important}
.repository .diff-detail-box{padding:7px 0;background:#fff;line-height:30px} .repository .diff-detail-box{padding:7px 0;background:#fff;line-height:30px}
.repository .diff-detail-box>div:after{clear:both;content:"";display:block} .repository .diff-detail-box>div:after{clear:both;content:"";display:block}
.repository .diff-detail-box ol{clear:both;padding-left:0;margin-top:5px;margin-bottom:28px}
.repository .diff-detail-box ol li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #ddd;padding-left:6px}
.repository .diff-detail-box span.status{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:middle} .repository .diff-detail-box span.status{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:middle}
.repository .diff-detail-box span.status.modify{background-color:#f0db88} .repository .diff-detail-box span.status.modify{background-color:#f0db88}
.repository .diff-detail-box span.status.add{background-color:#b4e2b4} .repository .diff-detail-box span.status.add{background-color:#b4e2b4}
@@ -697,6 +695,11 @@ footer .ui.left,footer .ui.right{line-height:40px}
.repository .diff-file-box.file-content{clear:right} .repository .diff-file-box.file-content{clear:right}
.repository .diff-file-box.file-content img{max-width:100%;padding:5px 5px 0 5px} .repository .diff-file-box.file-content img{max-width:100%;padding:5px 5px 0 5px}
.repository .diff-file-box.file-content img.emoji{padding:0} .repository .diff-file-box.file-content img.emoji{padding:0}
.repository .diff-stats{clear:both;margin-bottom:5px;max-height:400px;overflow:auto;padding-left:0}
.repository .diff-stats li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #ddd;padding-left:6px}
.repository .diff-stats .diff-counter{margin-right:15px}
.repository .diff-stats .diff-counter .del{color:red}
.repository .diff-stats .diff-counter .add{color:green}
.repository .repo-search-result{padding-top:10px;padding-bottom:10px} .repository .repo-search-result{padding-top:10px;padding-bottom:10px}
.repository .repo-search-result .lines-num a{color:inherit} .repository .repo-search-result .lines-num a{color:inherit}
.repository.quickstart .guide .item{padding:1em} .repository.quickstart .guide .item{padding:1em}

View File

@@ -306,7 +306,7 @@ function initReactionSelector(parent) {
if (resp && (resp.html || resp.empty)) { if (resp && (resp.html || resp.empty)) {
const content = $(vm).closest('.content'); const content = $(vm).closest('.content');
let react = content.find('.segment.reactions'); let react = content.find('.segment.reactions');
if (react.length > 0) { if (!resp.empty && react.length > 0) {
react.remove(); react.remove();
} }
if (!resp.empty) { if (!resp.empty) {

View File

@@ -1047,7 +1047,7 @@ footer {
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
* { *:not(.fa):not(.octicon):not(.icon) {
font-size: 12px; font-size: 12px;
font-family: @monospaced-fonts, monospace; font-family: @monospaced-fonts, monospace;
line-height: 20px; line-height: 20px;

View File

@@ -1237,21 +1237,6 @@
display: block; display: block;
} }
ol {
clear: both;
padding-left: 0;
margin-top: 5px;
margin-bottom: 28px;
li {
list-style: none;
padding-bottom: 4px;
margin-bottom: 4px;
border-bottom: 1px dashed #dddddd;
padding-left: 6px;
}
}
span.status { span.status {
display: inline-block; display: inline-block;
width: 12px; width: 12px;
@@ -1466,6 +1451,34 @@
} }
} }
.diff-stats {
clear: both;
margin-bottom: 5px;
max-height: 400px;
overflow: auto;
padding-left: 0;
li {
list-style: none;
padding-bottom: 4px;
margin-bottom: 4px;
border-bottom: 1px dashed #dddddd;
padding-left: 6px;
}
.diff-counter {
margin-right: 15px;
.del {
color: red;
}
.add {
color: green;
}
}
}
.repo-search-result { .repo-search-result {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;

View File

@@ -34,7 +34,7 @@ func TestNewUserPost_MustChangePassword(t *testing.T) {
LoginName: "local", LoginName: "local",
UserName: username, UserName: username,
Email: email, Email: email,
Password: "xxxxxxxx", Password: "abc123ABC!=$",
SendNotify: false, SendNotify: false,
MustChangePassword: true, MustChangePassword: true,
} }
@@ -71,7 +71,7 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) {
LoginName: "local", LoginName: "local",
UserName: username, UserName: username,
Email: email, Email: email,
Password: "xxxxxxxx", Password: "abc123ABC!=$",
SendNotify: false, SendNotify: false,
MustChangePassword: false, MustChangePassword: false,
} }

View File

@@ -232,12 +232,9 @@ func ToTeam(team *models.Team) *api.Team {
// ToUser convert models.User to api.User // ToUser convert models.User to api.User
func ToUser(user *models.User, signed, authed bool) *api.User { func ToUser(user *models.User, signed, authed bool) *api.User {
result := &api.User{ result := &api.User{
ID: user.ID,
UserName: user.Name, UserName: user.Name,
AvatarURL: user.AvatarLink(), AvatarURL: user.AvatarLink(),
FullName: markup.Sanitize(user.FullName), FullName: markup.Sanitize(user.FullName),
IsAdmin: user.IsAdmin,
LastLogin: user.LastLoginUnix.AsTime(),
Created: user.CreatedUnix.AsTime(), Created: user.CreatedUnix.AsTime(),
} }
// hide primary email if API caller isn't user itself or an admin // hide primary email if API caller isn't user itself or an admin
@@ -245,8 +242,11 @@ func ToUser(user *models.User, signed, authed bool) *api.User {
result.Email = "" result.Email = ""
} else if user.KeepEmailPrivate && !authed { } else if user.KeepEmailPrivate && !authed {
result.Email = user.GetEmail() result.Email = user.GetEmail()
} else { } else { // only user himself and admin could visit these information
result.ID = user.ID
result.Email = user.Email result.Email = user.Email
result.IsAdmin = user.IsAdmin
result.LastLogin = user.LastLoginUnix.AsTime()
} }
return result return result
} }

View File

@@ -312,15 +312,14 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
} }
// Update the deadline // Update the deadline
var deadlineUnix timeutil.TimeStamp if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) {
if form.Deadline != nil && !form.Deadline.IsZero() && ctx.Repo.CanWrite(models.UnitTypeIssues) { 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)
return return
} }
issue.DeadlineUnix = deadlineUnix
}
// Add/delete assignees // Add/delete assignees

View File

@@ -190,7 +190,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
) )
// Get repo/branch information // Get repo/branch information
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form) _, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
if ctx.Written() { if ctx.Written() {
return return
} }
@@ -267,7 +267,6 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
pr := &models.PullRequest{ pr := &models.PullRequest{
HeadRepoID: headRepo.ID, HeadRepoID: headRepo.ID,
BaseRepoID: repo.ID, BaseRepoID: repo.ID,
HeadUserName: headUser.Name,
HeadBranch: headBranch, HeadBranch: headBranch,
BaseBranch: baseBranch, BaseBranch: baseBranch,
HeadRepo: headRepo, HeadRepo: headRepo,
@@ -369,15 +368,14 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
} }
// Update Deadline // Update Deadline
var deadlineUnix timeutil.TimeStamp if form.Deadline != nil {
if form.Deadline != nil && !form.Deadline.IsZero() { 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)
return return
} }
issue.DeadlineUnix = deadlineUnix
}
// Add/delete assignees // Add/delete assignees

View File

@@ -104,7 +104,7 @@ func GetInfo(ctx *context.APIContext) {
return return
} }
ctx.JSON(200, convert.ToUser(u, ctx.IsSigned, ctx.User.ID == u.ID || ctx.User.IsAdmin)) ctx.JSON(200, convert.ToUser(u, ctx.IsSigned, ctx.User != nil && (ctx.User.ID == u.ID || ctx.User.IsAdmin)))
} }
// GetAuthenticatedUser get current user's information // GetAuthenticatedUser get current user's information

View File

@@ -273,7 +273,7 @@ func ExploreOrganizations(ctx *context.Context) {
// ExploreCode render explore code page // ExploreCode render explore code page
func ExploreCode(ctx *context.Context) { func ExploreCode(ctx *context.Context) {
if !setting.Indexer.RepoIndexerEnabled { if !setting.Indexer.RepoIndexerEnabled {
ctx.Redirect("/explore", 302) ctx.Redirect(setting.AppSubURL+"/explore", 302)
return return
} }

View File

@@ -84,6 +84,11 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error {
if err != nil { if err != nil {
return err return err
} }
defer func() {
if err = lfsDataRc.Close(); err != nil {
log.Error("ServeBlobOrLFS: Close: %v", err)
}
}()
return ServeData(ctx, ctx.Repo.TreePath, lfsDataRc) return ServeData(ctx, ctx.Repo.TreePath, lfsDataRc)
} }

View File

@@ -272,12 +272,12 @@ func checkPullInfo(ctx *context.Context) *models.Issue {
} }
func setMergeTarget(ctx *context.Context, pull *models.PullRequest) { func setMergeTarget(ctx *context.Context, pull *models.PullRequest) {
if ctx.Repo.Owner.Name == pull.HeadUserName { if ctx.Repo.Owner.Name == pull.MustHeadUserName() {
ctx.Data["HeadTarget"] = pull.HeadBranch ctx.Data["HeadTarget"] = pull.HeadBranch
} else if pull.HeadRepo == nil { } else if pull.HeadRepo == nil {
ctx.Data["HeadTarget"] = pull.HeadUserName + ":" + pull.HeadBranch ctx.Data["HeadTarget"] = pull.MustHeadUserName() + ":" + pull.HeadBranch
} else { } else {
ctx.Data["HeadTarget"] = pull.HeadUserName + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch ctx.Data["HeadTarget"] = pull.MustHeadUserName() + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch
} }
ctx.Data["BaseTarget"] = pull.BaseBranch ctx.Data["BaseTarget"] = pull.BaseBranch
} }
@@ -440,7 +440,7 @@ func ViewPullCommits(ctx *context.Context) {
ctx.NotFound("ViewPullCommits", nil) ctx.NotFound("ViewPullCommits", nil)
return return
} }
ctx.Data["Username"] = pull.HeadUserName ctx.Data["Username"] = pull.MustHeadUserName()
ctx.Data["Reponame"] = pull.HeadRepo.Name ctx.Data["Reponame"] = pull.HeadRepo.Name
commits = prInfo.Commits commits = prInfo.Commits
} }
@@ -512,7 +512,7 @@ func ViewPullFiles(ctx *context.Context) {
return return
} }
headRepoPath := models.RepoPath(pull.HeadUserName, pull.HeadRepo.Name) headRepoPath := pull.HeadRepo.RepoPath()
headGitRepo, err := git.OpenRepository(headRepoPath) headGitRepo, err := git.OpenRepository(headRepoPath)
if err != nil { if err != nil {
@@ -531,8 +531,8 @@ func ViewPullFiles(ctx *context.Context) {
endCommitID = headCommitID endCommitID = headCommitID
gitRepo = headGitRepo gitRepo = headGitRepo
headTarget = path.Join(pull.HeadUserName, pull.HeadRepo.Name) headTarget = path.Join(pull.MustHeadUserName(), pull.HeadRepo.Name)
ctx.Data["Username"] = pull.HeadUserName ctx.Data["Username"] = pull.MustHeadUserName()
ctx.Data["Reponame"] = pull.HeadRepo.Name ctx.Data["Reponame"] = pull.HeadRepo.Name
} }
@@ -756,7 +756,6 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
pullRequest := &models.PullRequest{ pullRequest := &models.PullRequest{
HeadRepoID: headRepo.ID, HeadRepoID: headRepo.ID,
BaseRepoID: repo.ID, BaseRepoID: repo.ID,
HeadUserName: headUser.Name,
HeadBranch: headBranch, HeadBranch: headBranch,
BaseBranch: baseBranch, BaseBranch: baseBranch,
HeadRepo: headRepo, HeadRepo: headRepo,

View File

@@ -28,7 +28,6 @@ func Account(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsAccount"] = true ctx.Data["PageIsSettingsAccount"] = true
ctx.Data["Email"] = ctx.User.Email ctx.Data["Email"] = ctx.User.Email
ctx.Data["EmailNotificationsPreference"] = ctx.User.EmailNotifications()
loadAccountData(ctx) loadAccountData(ctx)
@@ -54,7 +53,7 @@ func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) {
} else if form.Password != form.Retype { } else if form.Password != form.Retype {
ctx.Flash.Error(ctx.Tr("form.password_not_match")) ctx.Flash.Error(ctx.Tr("form.password_not_match"))
} else if !password.IsComplexEnough(form.Password) { } else if !password.IsComplexEnough(form.Password) {
ctx.Flash.Error(ctx.Tr("settings.password_complexity")) ctx.Flash.Error(ctx.Tr("form.password_complexity"))
} else { } else {
var err error var err error
if ctx.User.Salt, err = models.GetUserSalt(); err != nil { if ctx.User.Salt, err = models.GetUserSalt(); err != nil {
@@ -230,4 +229,5 @@ func loadAccountData(ctx *context.Context) {
return return
} }
ctx.Data["Emails"] = emails ctx.Data["Emails"] = emails
ctx.Data["EmailNotificationsPreference"] = ctx.User.EmailNotifications()
} }

View File

@@ -19,76 +19,64 @@ import (
func TestChangePassword(t *testing.T) { func TestChangePassword(t *testing.T) {
oldPassword := "password" oldPassword := "password"
setting.MinPasswordLength = 6 setting.MinPasswordLength = 6
setting.PasswordComplexity = map[string]string{ var pcALL = []string{"lower", "upper", "digit", "spec"}
"lower": "[a-z]+", var pcLUN = []string{"lower", "upper", "digit"}
"upper": "[A-Z]+", var pcLU = []string{"lower", "upper"}
"digit": "[0-9]+",
"spec": "[-_]+",
}
var pcLUN = map[string]string{
"lower": "[a-z]+",
"upper": "[A-Z]+",
"digit": "[0-9]+",
}
var pcLU = map[string]string{
"lower": "[a-z]+",
"upper": "[A-Z]+",
}
for _, req := range []struct { for _, req := range []struct {
OldPassword string OldPassword string
NewPassword string NewPassword string
Retype string Retype string
Message string Message string
PasswordComplexity map[string]string PasswordComplexity []string
}{ }{
{ {
OldPassword: oldPassword, OldPassword: oldPassword,
NewPassword: "Qwerty123456-", NewPassword: "Qwerty123456-",
Retype: "Qwerty123456-", Retype: "Qwerty123456-",
Message: "", Message: "",
PasswordComplexity: setting.PasswordComplexity, PasswordComplexity: pcALL,
}, },
{ {
OldPassword: oldPassword, OldPassword: oldPassword,
NewPassword: "12345", NewPassword: "12345",
Retype: "12345", Retype: "12345",
Message: "auth.password_too_short", Message: "auth.password_too_short",
PasswordComplexity: setting.PasswordComplexity, PasswordComplexity: pcALL,
}, },
{ {
OldPassword: "12334", OldPassword: "12334",
NewPassword: "123456", NewPassword: "123456",
Retype: "123456", Retype: "123456",
Message: "settings.password_incorrect", Message: "settings.password_incorrect",
PasswordComplexity: setting.PasswordComplexity, PasswordComplexity: pcALL,
}, },
{ {
OldPassword: oldPassword, OldPassword: oldPassword,
NewPassword: "123456", NewPassword: "123456",
Retype: "12345", Retype: "12345",
Message: "form.password_not_match", Message: "form.password_not_match",
PasswordComplexity: setting.PasswordComplexity, PasswordComplexity: pcALL,
}, },
{ {
OldPassword: oldPassword, OldPassword: oldPassword,
NewPassword: "Qwerty", NewPassword: "Qwerty",
Retype: "Qwerty", Retype: "Qwerty",
Message: "settings.password_complexity", Message: "form.password_complexity",
PasswordComplexity: setting.PasswordComplexity, PasswordComplexity: pcALL,
}, },
{ {
OldPassword: oldPassword, OldPassword: oldPassword,
NewPassword: "Qwerty", NewPassword: "Qwerty",
Retype: "Qwerty", Retype: "Qwerty",
Message: "settings.password_complexity", Message: "form.password_complexity",
PasswordComplexity: pcLUN, PasswordComplexity: pcLUN,
}, },
{ {
OldPassword: oldPassword, OldPassword: oldPassword,
NewPassword: "QWERTY", NewPassword: "QWERTY",
Retype: "QWERTY", Retype: "QWERTY",
Message: "settings.password_complexity", Message: "form.password_complexity",
PasswordComplexity: pcLU, PasswordComplexity: pcLU,
}, },
} { } {

View File

@@ -5,7 +5,6 @@
package externalaccount package externalaccount
import ( import (
"strconv"
"strings" "strings"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@@ -45,10 +44,7 @@ func LinkAccountToUser(user *models.User, gothUser goth.User) error {
return err return err
} }
externalID, err := strconv.ParseInt(externalLoginUser.ExternalID, 10, 64) externalID := externalLoginUser.ExternalID
if err != nil {
return err
}
var tp structs.GitServiceType var tp structs.GitServiceType
for _, s := range structs.SupportedFullGitService { for _, s := range structs.SupportedFullGitService {

View File

@@ -65,7 +65,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
} }
}() }()
headRepoPath := models.RepoPath(pr.HeadUserName, pr.HeadRepo.Name) headRepoPath := pr.HeadRepo.RepoPath()
if err := git.InitRepository(tmpBasePath, false); err != nil { if err := git.InitRepository(tmpBasePath, false); err != nil {
return fmt.Errorf("git init: %v", err) return fmt.Errorf("git init: %v", err)
@@ -260,14 +260,17 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
} }
} }
headUser, err := models.GetUserByName(pr.HeadUserName) var headUser *models.User
err = pr.HeadRepo.GetOwner()
if err != nil { if err != nil {
if !models.IsErrUserNotExist(err) { if !models.IsErrUserNotExist(err) {
log.Error("Can't find user: %s for head repository - %v", pr.HeadUserName, err) log.Error("Can't find user: %d for head repository - %v", pr.HeadRepo.OwnerID, err)
return err return err
} }
log.Error("Can't find user: %s for head repository - defaulting to doer: %s - %v", pr.HeadUserName, doer.Name, err) log.Error("Can't find user: %d for head repository - defaulting to doer: %s - %v", pr.HeadRepo.OwnerID, doer.Name, err)
headUser = doer headUser = doer
} else {
headUser = pr.HeadRepo.Owner
} }
env := models.FullPushingEnvironment( env := models.FullPushingEnvironment(

View File

@@ -16,8 +16,8 @@
</div> </div>
<h4>{{.i18n.Tr "repo.diff.data_not_available"}}</h4> <h4>{{.i18n.Tr "repo.diff.data_not_available"}}</h4>
{{else}} {{else}}
<div class="diff-detail-box diff-box ui sticky">
<div> <div>
<div class="diff-detail-box diff-box ui sticky">
<i class="fa fa-retweet"></i> <i class="fa fa-retweet"></i>
{{.i18n.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion | Str2html}} {{.i18n.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion | Str2html}}
<div class="ui right"> <div class="ui right">
@@ -32,17 +32,17 @@
{{end}} {{end}}
</div> </div>
</div> </div>
<ol class="detail-files hide" id="diff-files"> <ol class="diff-detail-box diff-stats detail-files hide" id="diff-files">
{{range .Diff.Files}} {{range .Diff.Files}}
<li> <li>
<div class="diff-counter count pull-right"> <div class="diff-counter count pull-right">
{{if not .IsBin}} {{if not .IsBin}}
<span class="add" data-line="{{.Addition}}">{{.Addition}}</span> <span class="add" data-line="{{.Addition}}">+{{.Addition}}</span>
<span class="bar"> <span class="bar">
<div class="pull-left add"></div> <div class="pull-left add"></div>
<div class="pull-left del"></div> <div class="pull-left del"></div>
</span> </span>
<span class="del" data-line="{{.Deletion}}">{{.Deletion}}</span> <span class="del" data-line="{{.Deletion}}">-{{.Deletion}}</span>
{{else}} {{else}}
<span>{{$.i18n.Tr "repo.diff.bin"}}</span> <span>{{$.i18n.Tr "repo.diff.bin"}}</span>
{{end}} {{end}}
@@ -53,8 +53,6 @@
</li> </li>
{{end}} {{end}}
</ol> </ol>
</div>
{{range $i, $file := .Diff.Files}} {{range $i, $file := .Diff.Files}}
{{if $file.IsIncomplete}} {{if $file.IsIncomplete}}
<div class="diff-file-box diff-box file-content"> <div class="diff-file-box diff-box file-content">
@@ -237,4 +235,5 @@
}); });
</script> </script>
{{end}} {{end}}
</div>
{{end}} {{end}}

View File

@@ -16,7 +16,7 @@
<p>{{.i18n.Tr "auth.authroize_redirect_notice" .ApplicationRedirectDomainHTML | Str2html}}</p> <p>{{.i18n.Tr "auth.authroize_redirect_notice" .ApplicationRedirectDomainHTML | Str2html}}</p>
</div> </div>
<div class="ui attached segment"> <div class="ui attached segment">
<form method="post" action="{{.AppSubUrl}}/login/oauth/grant"> <form method="post" action="{{AppSubUrl}}/login/oauth/grant">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<input type="hidden" name="client_id" value="{{.Application.ClientID}}"> <input type="hidden" name="client_id" value="{{.Application.ClientID}}">
<input type="hidden" name="state" value="{{.State}}"> <input type="hidden" name="state" value="{{.State}}">