mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-08 05:02:38 +09:00
Compare commits
29 Commits
v1.3.0-rc2
...
v1.3.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86d61bbb10 | ||
|
|
bfe13a28c2 | ||
|
|
ed27da4b0a | ||
|
|
88c363f933 | ||
|
|
f33cd3c676 | ||
|
|
fecf9390ef | ||
|
|
bdf1856011 | ||
|
|
6037f183cf | ||
|
|
4b187f5167 | ||
|
|
aee45d072e | ||
|
|
e893aced89 | ||
|
|
d63ca66623 | ||
|
|
59afb62ab2 | ||
|
|
8a19c6b9a2 | ||
|
|
81fd8c8fb6 | ||
|
|
fd7686171e | ||
|
|
ec6718ef40 | ||
|
|
8f7054a864 | ||
|
|
84352316a9 | ||
|
|
237df2f339 | ||
|
|
b9abcb3b61 | ||
|
|
9832b9509d | ||
|
|
3d688bd2cc | ||
|
|
ce4a52c22c | ||
|
|
974cffead9 | ||
|
|
8cdd5a33cd | ||
|
|
992ee21a29 | ||
|
|
5ec9c45661 | ||
|
|
b5bdb0474a |
13
.drone.yml
13
.drone.yml
@@ -86,6 +86,19 @@ pipeline:
|
|||||||
event: [ push, pull_request ]
|
event: [ push, pull_request ]
|
||||||
branch: [ master ]
|
branch: [ master ]
|
||||||
|
|
||||||
|
test:
|
||||||
|
image: webhippie/golang:edge
|
||||||
|
pull: true
|
||||||
|
group: test
|
||||||
|
environment:
|
||||||
|
TAGS: bindata sqlite
|
||||||
|
GOPATH: /srv/app
|
||||||
|
commands:
|
||||||
|
- make test
|
||||||
|
when:
|
||||||
|
event: [ push, pull_request ]
|
||||||
|
branch: [ release/* ]
|
||||||
|
|
||||||
test:
|
test:
|
||||||
image: webhippie/golang:edge
|
image: webhippie/golang:edge
|
||||||
pull: true
|
pull: true
|
||||||
|
|||||||
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,15 +1,35 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [1.3.0-rc2](https://github.com/go-gitea/gitea/releases/tag/v1.3.0-rc2) - 2017-11-27
|
## [1.3.3](https://github.com/go-gitea/gitea/releases/tag/v1.3.3) - 2018-02-15
|
||||||
|
* SECURITY
|
||||||
|
* Fix escaping changed title in comments (#3530) (#3535)
|
||||||
|
* Escape search query display (#3486) (#3489)
|
||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
* Fix memcache support when value is returned as string always (#2950)
|
* Fix repo-transfer-and-team-repo-count bug (#3241) (#3244)
|
||||||
* Update golang x/crypto dependencies - Fix SSH transport fail (#2951)
|
* Open external tracker in blank window, consistently with wiki (#3227) (#3228)
|
||||||
* Fix files/commits of merged PRs (#2970)
|
* Change SSL Mode from checkbox to string in admin page (#3208) (#3211)
|
||||||
* Fix error when add user has full name to team (#2975)
|
|
||||||
* Fix go-get, src and raw urls to new scheme (#2986)
|
|
||||||
* Fix over-escaped characters (#2992)
|
|
||||||
|
|
||||||
## [1.3.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.3.0-rc1) - 2017-11-15
|
## [1.3.2](https://github.com/go-gitea/gitea/releases/tag/v1.3.2) - 2017-12-14
|
||||||
|
* BUGFIXES
|
||||||
|
* fix run web with -p push failed (#3154) (#3179)
|
||||||
|
* Fix source download link when no code unit allowed (#3166) (#3169)
|
||||||
|
* Allow adding collaborators with (fullname) (#3103) (#3168)
|
||||||
|
* Fix repo links (#3093) (#3163)
|
||||||
|
* Fix Uninitialized variable in ParsePatch (#3156) (#3162)
|
||||||
|
* Fix migration order v1.3 (#3157)
|
||||||
|
* Fix avatar URLs (#3069) (#3143)
|
||||||
|
|
||||||
|
## [1.3.1](https://github.com/go-gitea/gitea/releases/tag/v1.3.1) - 2017-12-08
|
||||||
|
* BUGFIXES
|
||||||
|
* Sanitize logs for mirror sync (#3057, #3082) (#3078)
|
||||||
|
* Fix missing branch in release bug (#3108) (#3117)
|
||||||
|
* Fix repo indexer and submodule bug (#3107) (#3110)
|
||||||
|
* Fix legacy URL redirects (#3100) (#3106)
|
||||||
|
* Fix redis session failed (#3086) (#3089)
|
||||||
|
* Fix issue list branch link broken (#3061) (#3070)
|
||||||
|
* Fix missing password length check when change password (#3039) (#3071)
|
||||||
|
|
||||||
|
## [1.3.0](https://github.com/go-gitea/gitea/releases/tag/v1.3.0) - 2017-11-29
|
||||||
* BREAKING
|
* BREAKING
|
||||||
* Make URL scheme unambiguous (#2408)
|
* Make URL scheme unambiguous (#2408)
|
||||||
* FEATURE
|
* FEATURE
|
||||||
@@ -30,6 +50,14 @@
|
|||||||
* Disable custom Git Hooks globally via configuration file (#2450)
|
* Disable custom Git Hooks globally via configuration file (#2450)
|
||||||
* Sync releases table with tags on push and for mirrors (#2459)
|
* Sync releases table with tags on push and for mirrors (#2459)
|
||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
|
* Fix label comments for French locale (#3017)
|
||||||
|
* Remove duplicate "Max Diff Lines" from config view (#3001)
|
||||||
|
* Fix over-escaped characters (#2992)
|
||||||
|
* Fix go-get, src and raw urls to new scheme (#2986)
|
||||||
|
* Fix error when add user has full name to team (#2975)
|
||||||
|
* Fix files/commits of merged PRs (#2970)
|
||||||
|
* Update golang x/crypto dependencies - Fix SSH transport fail (#2951)
|
||||||
|
* Fix memcache support when value is returned as string always (#2950)
|
||||||
* Fix issue link rendering in commit messages (#2897)
|
* Fix issue link rendering in commit messages (#2897)
|
||||||
* Fix adding a new authentication source after selecting OAuth (#2889)
|
* Fix adding a new authentication source after selecting OAuth (#2889)
|
||||||
* Fix new branch creation to new url scheme (#2884)
|
* Fix new branch creation to new url scheme (#2884)
|
||||||
@@ -109,6 +137,8 @@
|
|||||||
* Fix duplicated rel attribute (#2549)
|
* Fix duplicated rel attribute (#2549)
|
||||||
* Fix tests code to prevent some runtime errors (#2381)
|
* Fix tests code to prevent some runtime errors (#2381)
|
||||||
* ENHANCEMENT
|
* ENHANCEMENT
|
||||||
|
* Memory usage improvements and lower minimal git requirement to 1.7.2 (#3013) (#3028)
|
||||||
|
* Set OpenID support on by default when installing new instance (#3010) (#3027)
|
||||||
* Use api.TrackedTime in API (#2807)
|
* Use api.TrackedTime in API (#2807)
|
||||||
* Configurable SSH key exchange algorithm and MAC suite (#2806)
|
* Configurable SSH key exchange algorithm and MAC suite (#2806)
|
||||||
* Add Safari pinned tab icon (#2799)
|
* Add Safari pinned tab icon (#2799)
|
||||||
|
|||||||
30
cmd/web.go
30
cmd/web.go
@@ -19,8 +19,10 @@ import (
|
|||||||
"code.gitea.io/gitea/routers"
|
"code.gitea.io/gitea/routers"
|
||||||
"code.gitea.io/gitea/routers/routes"
|
"code.gitea.io/gitea/routers/routes"
|
||||||
|
|
||||||
|
"github.com/Unknwon/com"
|
||||||
context2 "github.com/gorilla/context"
|
context2 "github.com/gorilla/context"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
ini "gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdWeb represents the available web sub-command.
|
// CmdWeb represents the available web sub-command.
|
||||||
@@ -69,6 +71,34 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
if ctx.IsSet("port") {
|
if ctx.IsSet("port") {
|
||||||
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, ctx.String("port"), 1)
|
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, ctx.String("port"), 1)
|
||||||
setting.HTTPPort = ctx.String("port")
|
setting.HTTPPort = ctx.String("port")
|
||||||
|
|
||||||
|
switch setting.Protocol {
|
||||||
|
case setting.UnixSocket:
|
||||||
|
case setting.FCGI:
|
||||||
|
default:
|
||||||
|
// Save LOCAL_ROOT_URL if port changed
|
||||||
|
cfg := ini.Empty()
|
||||||
|
if com.IsFile(setting.CustomConf) {
|
||||||
|
// Keeps custom settings if there is already something.
|
||||||
|
if err := cfg.Append(setting.CustomConf); err != nil {
|
||||||
|
return fmt.Errorf("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultLocalURL := string(setting.Protocol) + "://"
|
||||||
|
if setting.HTTPAddr == "0.0.0.0" {
|
||||||
|
defaultLocalURL += "localhost"
|
||||||
|
} else {
|
||||||
|
defaultLocalURL += setting.HTTPAddr
|
||||||
|
}
|
||||||
|
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||||
|
|
||||||
|
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||||
|
|
||||||
|
if err := cfg.SaveTo(setting.CustomConf); err != nil {
|
||||||
|
return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var listenAddr string
|
var listenAddr string
|
||||||
|
|||||||
@@ -46,8 +46,10 @@ func TestRedirectsNoLogin(t *testing.T) {
|
|||||||
prepareTestEnv(t)
|
prepareTestEnv(t)
|
||||||
|
|
||||||
var redirects = map[string]string{
|
var redirects = map[string]string{
|
||||||
"/user2/repo1/commits/master": "/user2/repo1/commits/branch/master",
|
"/user2/repo1/commits/master": "/user2/repo1/commits/branch/master",
|
||||||
"/user2/repo1/src/master": "/user2/repo1/src/branch/master",
|
"/user2/repo1/src/master": "/user2/repo1/src/branch/master",
|
||||||
|
"/user2/repo1/src/master/file.txt": "/user2/repo1/src/branch/master/file.txt",
|
||||||
|
"/user2/repo1/src/master/directory/file.txt": "/user2/repo1/src/branch/master/directory/file.txt",
|
||||||
}
|
}
|
||||||
for link, redirectLink := range redirects {
|
for link, redirectLink := range redirects {
|
||||||
req := NewRequest(t, "GET", link)
|
req := NewRequest(t, "GET", link)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testPullCreate(t *testing.T, session *TestSession, user, repo, branch string) *TestResponse {
|
func testPullCreate(t *testing.T, session *TestSession, user, repo, branch, title string) *TestResponse {
|
||||||
req := NewRequest(t, "GET", path.Join(user, repo))
|
req := NewRequest(t, "GET", path.Join(user, repo))
|
||||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo, branch strin
|
|||||||
assert.True(t, exists, "The template has changed")
|
assert.True(t, exists, "The template has changed")
|
||||||
req = NewRequestWithValues(t, "POST", link, map[string]string{
|
req = NewRequestWithValues(t, "POST", link, map[string]string{
|
||||||
"_csrf": htmlDoc.GetCSRF(),
|
"_csrf": htmlDoc.GetCSRF(),
|
||||||
"title": "This is a pull title",
|
"title": title,
|
||||||
})
|
})
|
||||||
resp = session.MakeRequest(t, req, http.StatusFound)
|
resp = session.MakeRequest(t, req, http.StatusFound)
|
||||||
|
|
||||||
@@ -48,5 +48,40 @@ func TestPullCreate(t *testing.T) {
|
|||||||
session := loginUser(t, "user1")
|
session := loginUser(t, "user1")
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
testPullCreate(t, session, "user1", "repo1", "master")
|
testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPullCreate_TitleEscape(t *testing.T) {
|
||||||
|
prepareTestEnv(t)
|
||||||
|
session := loginUser(t, "user1")
|
||||||
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
|
resp := testPullCreate(t, session, "user1", "repo1", "master", "<i>XSS PR</i>")
|
||||||
|
|
||||||
|
// check the redirected URL
|
||||||
|
url := RedirectURL(t, resp)
|
||||||
|
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
|
||||||
|
|
||||||
|
// Edit title
|
||||||
|
req := NewRequest(t, "GET", url)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url")
|
||||||
|
assert.True(t, exists, "The template has changed")
|
||||||
|
|
||||||
|
req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{
|
||||||
|
"_csrf": htmlDoc.GetCSRF(),
|
||||||
|
"title": "<u>XSS PR</u>",
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", url)
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||||
|
titleHTML, err := htmlDoc.doc.Find(".comments .event .text b").First().Html()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "<i>XSS PR</i>", titleHTML)
|
||||||
|
titleHTML, err = htmlDoc.doc.Find(".comments .event .text b").Next().Html()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "<u>XSS PR</u>", titleHTML)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func TestPullMerge(t *testing.T) {
|
|||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
|
|
||||||
resp := testPullCreate(t, session, "user1", "repo1", "master")
|
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
|
||||||
|
|
||||||
elem := strings.Split(RedirectURL(t, resp), "/")
|
elem := strings.Split(RedirectURL(t, resp), "/")
|
||||||
assert.EqualValues(t, "pulls", elem[3])
|
assert.EqualValues(t, "pulls", elem[3])
|
||||||
@@ -64,7 +64,7 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
|
|||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
|
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
|
||||||
|
|
||||||
resp := testPullCreate(t, session, "user1", "repo1", "feature/test")
|
resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title")
|
||||||
|
|
||||||
elem := strings.Split(RedirectURL(t, resp), "/")
|
elem := strings.Split(RedirectURL(t, resp), "/")
|
||||||
assert.EqualValues(t, "pulls", elem[3])
|
assert.EqualValues(t, "pulls", elem[3])
|
||||||
|
|||||||
@@ -19,16 +19,16 @@ func TestRepoActivity(t *testing.T) {
|
|||||||
// Create PRs (1 merged & 2 proposed)
|
// Create PRs (1 merged & 2 proposed)
|
||||||
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
|
||||||
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||||
resp := testPullCreate(t, session, "user1", "repo1", "master")
|
resp := testPullCreate(t, session, "user1", "repo1", "master", "This is a pull title")
|
||||||
elem := strings.Split(RedirectURL(t, resp), "/")
|
elem := strings.Split(RedirectURL(t, resp), "/")
|
||||||
assert.EqualValues(t, "pulls", elem[3])
|
assert.EqualValues(t, "pulls", elem[3])
|
||||||
testPullMerge(t, session, elem[1], elem[2], elem[4])
|
testPullMerge(t, session, elem[1], elem[2], elem[4])
|
||||||
|
|
||||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n")
|
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/better_readme", "README.md", "Hello, World (Edited Again)\n")
|
||||||
testPullCreate(t, session, "user1", "repo1", "feat/better_readme")
|
testPullCreate(t, session, "user1", "repo1", "feat/better_readme", "This is a pull title")
|
||||||
|
|
||||||
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/much_better_readme", "README.md", "Hello, World (Edited More)\n")
|
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feat/much_better_readme", "README.md", "Hello, World (Edited More)\n")
|
||||||
testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme")
|
testPullCreate(t, session, "user1", "repo1", "feat/much_better_readme", "This is a pull title")
|
||||||
|
|
||||||
// Create issues (3 new issues)
|
// Create issues (3 new issues)
|
||||||
testNewIssue(t, session, "user2", "repo1", "Issue 1", "Description 1")
|
testNewIssue(t, session, "user2", "repo1", "Issue 1", "Description 1")
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
|
|||||||
var (
|
var (
|
||||||
diff = &Diff{Files: make([]*DiffFile, 0)}
|
diff = &Diff{Files: make([]*DiffFile, 0)}
|
||||||
|
|
||||||
curFile *DiffFile
|
curFile = &DiffFile{}
|
||||||
curSection = &DiffSection{
|
curSection = &DiffSection{
|
||||||
Lines: make([]*DiffLine, 0, 10),
|
Lines: make([]*DiffLine, 0, 10),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ type Version struct {
|
|||||||
Version int64
|
Version int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func emptyMigration(x *xorm.Engine) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// This is a sequence of migrations. Add new migrations to the bottom of the list.
|
// This is a sequence of migrations. Add new migrations to the bottom of the list.
|
||||||
// If you want to "retire" a migration, remove it from the top of the list and
|
// If you want to "retire" a migration, remove it from the top of the list and
|
||||||
// update minDBVersion accordingly
|
// update minDBVersion accordingly
|
||||||
@@ -127,17 +131,17 @@ var migrations = []Migration{
|
|||||||
// v38 -> v39
|
// v38 -> v39
|
||||||
NewMigration("remove commits and settings unit types", removeCommitsUnitType),
|
NewMigration("remove commits and settings unit types", removeCommitsUnitType),
|
||||||
// v39 -> v40
|
// v39 -> v40
|
||||||
NewMigration("adds time tracking and stopwatches", addTimetracking),
|
|
||||||
// v40 -> v41
|
|
||||||
NewMigration("migrate protected branch struct", migrateProtectedBranchStruct),
|
|
||||||
// v41 -> v42
|
|
||||||
NewMigration("add default value to user prohibit_login", addDefaultValueToUserProhibitLogin),
|
|
||||||
// v42 -> v43
|
|
||||||
NewMigration("add tags to releases and sync existing repositories", releaseAddColumnIsTagAndSyncTags),
|
|
||||||
// v43 -> v44
|
|
||||||
NewMigration("fix protected branch can push value to false", fixProtectedBranchCanPushValue),
|
NewMigration("fix protected branch can push value to false", fixProtectedBranchCanPushValue),
|
||||||
// v44 -> v45
|
// v40 -> v41
|
||||||
|
NewMigration("add tags to releases and sync existing repositories", releaseAddColumnIsTagAndSyncTags),
|
||||||
|
// v41 -> v42
|
||||||
NewMigration("remove duplicate unit types", removeDuplicateUnitTypes),
|
NewMigration("remove duplicate unit types", removeDuplicateUnitTypes),
|
||||||
|
// v42 -> v43
|
||||||
|
NewMigration("empty step", emptyMigration),
|
||||||
|
// v43 -> v44
|
||||||
|
NewMigration("empty step", emptyMigration),
|
||||||
|
// v44 -> v45
|
||||||
|
NewMigration("empty step", emptyMigration),
|
||||||
// v45 -> v46
|
// v45 -> v46
|
||||||
NewMigration("remove index column from repo_unit table", removeIndexColumnFromRepoUnitTable),
|
NewMigration("remove index column from repo_unit table", removeIndexColumnFromRepoUnitTable),
|
||||||
// v46 -> v47
|
// v46 -> v47
|
||||||
@@ -146,6 +150,12 @@ var migrations = []Migration{
|
|||||||
NewMigration("add deleted branches", addDeletedBranch),
|
NewMigration("add deleted branches", addDeletedBranch),
|
||||||
// v48 -> v49
|
// v48 -> v49
|
||||||
NewMigration("add repo indexer status", addRepoIndexerStatus),
|
NewMigration("add repo indexer status", addRepoIndexerStatus),
|
||||||
|
// v49 -> v50
|
||||||
|
NewMigration("adds time tracking and stopwatches", addTimetracking),
|
||||||
|
// v50 -> v51
|
||||||
|
NewMigration("migrate protected branch struct", migrateProtectedBranchStruct),
|
||||||
|
// v51 -> v52
|
||||||
|
NewMigration("add default value to user prohibit_login", addDefaultValueToUserProhibitLogin),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate database to current version
|
// Migrate database to current version
|
||||||
|
|||||||
@@ -6,69 +6,21 @@ package migrations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func addTimetracking(x *xorm.Engine) error {
|
func fixProtectedBranchCanPushValue(x *xorm.Engine) error {
|
||||||
// RepoUnit describes all units of a repository
|
type ProtectedBranch struct {
|
||||||
type RepoUnit struct {
|
CanPush bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
ID int64
|
|
||||||
RepoID int64 `xorm:"INDEX(s)"`
|
|
||||||
Type int `xorm:"INDEX(s)"`
|
|
||||||
Index int
|
|
||||||
Config map[string]interface{} `xorm:"JSON"`
|
|
||||||
CreatedUnix int64 `xorm:"INDEX CREATED"`
|
|
||||||
Created time.Time `xorm:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stopwatch see models/issue_stopwatch.go
|
if err := x.Sync2(new(ProtectedBranch)); err != nil {
|
||||||
type Stopwatch struct {
|
|
||||||
ID int64 `xorm:"pk autoincr"`
|
|
||||||
IssueID int64 `xorm:"INDEX"`
|
|
||||||
UserID int64 `xorm:"INDEX"`
|
|
||||||
Created time.Time `xorm:"-"`
|
|
||||||
CreatedUnix int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrackedTime see models/issue_tracked_time.go
|
|
||||||
type TrackedTime struct {
|
|
||||||
ID int64 `xorm:"pk autoincr" json:"id"`
|
|
||||||
IssueID int64 `xorm:"INDEX" json:"issue_id"`
|
|
||||||
UserID int64 `xorm:"INDEX" json:"user_id"`
|
|
||||||
Created time.Time `xorm:"-" json:"created"`
|
|
||||||
CreatedUnix int64 `json:"-"`
|
|
||||||
Time int64 `json:"time"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := x.Sync2(new(Stopwatch)); err != nil {
|
|
||||||
return fmt.Errorf("Sync2: %v", err)
|
return fmt.Errorf("Sync2: %v", err)
|
||||||
}
|
}
|
||||||
if err := x.Sync2(new(TrackedTime)); err != nil {
|
|
||||||
return fmt.Errorf("Sync2: %v", err)
|
_, err := x.Cols("can_push").Update(&ProtectedBranch{
|
||||||
}
|
CanPush: false,
|
||||||
//Updating existing issue units
|
})
|
||||||
units := make([]*RepoUnit, 0, 100)
|
return err
|
||||||
err := x.Where("`type` = ?", V16UnitTypeIssues).Find(&units)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Query repo units: %v", err)
|
|
||||||
}
|
|
||||||
for _, unit := range units {
|
|
||||||
if unit.Config == nil {
|
|
||||||
unit.Config = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
if _, ok := unit.Config["EnableTimetracker"]; !ok {
|
|
||||||
unit.Config["EnableTimetracker"] = setting.Service.DefaultEnableTimetracking
|
|
||||||
}
|
|
||||||
if _, ok := unit.Config["AllowOnlyContributorsToTrackTime"]; !ok {
|
|
||||||
unit.Config["AllowOnlyContributorsToTrackTime"] = setting.Service.DefaultAllowOnlyContributorsToTrackTime
|
|
||||||
}
|
|
||||||
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,50 +6,52 @@ package migrations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
|
"code.gitea.io/git"
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func migrateProtectedBranchStruct(x *xorm.Engine) error {
|
// ReleaseV39 describes the added field for Release
|
||||||
type ProtectedBranch struct {
|
type ReleaseV39 struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
IsTag bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
}
|
||||||
BranchName string `xorm:"UNIQUE(s)"`
|
|
||||||
CanPush bool
|
// TableName will be invoked by XORM to customrize the table name
|
||||||
Created time.Time `xorm:"-"`
|
func (*ReleaseV39) TableName() string {
|
||||||
CreatedUnix int64
|
return "release"
|
||||||
Updated time.Time `xorm:"-"`
|
}
|
||||||
UpdatedUnix int64
|
|
||||||
|
func releaseAddColumnIsTagAndSyncTags(x *xorm.Engine) error {
|
||||||
|
if err := x.Sync2(new(ReleaseV39)); err != nil {
|
||||||
|
return fmt.Errorf("Sync2: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pbs []ProtectedBranch
|
// For the sake of SQLite3, we can't use x.Iterate here.
|
||||||
err := x.Find(&pbs)
|
offset := 0
|
||||||
if err != nil {
|
pageSize := 20
|
||||||
return err
|
for {
|
||||||
}
|
repos := make([]*models.Repository, 0, pageSize)
|
||||||
|
if err := x.Table("repository").Asc("id").Limit(pageSize, offset).Find(&repos); err != nil {
|
||||||
|
return fmt.Errorf("select repos [offset: %d]: %v", offset, err)
|
||||||
|
}
|
||||||
|
for _, repo := range repos {
|
||||||
|
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("OpenRepository: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
for _, pb := range pbs {
|
if err = models.SyncReleasesWithTags(repo, gitRepo); err != nil {
|
||||||
if pb.CanPush {
|
log.Warn("SyncReleasesWithTags: %v", err)
|
||||||
if _, err = x.ID(pb.ID).Delete(new(ProtectedBranch)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if len(repos) < pageSize {
|
||||||
|
break
|
||||||
switch {
|
|
||||||
case setting.UseSQLite3:
|
|
||||||
log.Warn("Unable to drop columns in SQLite")
|
|
||||||
case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB:
|
|
||||||
if _, err := x.Exec("ALTER TABLE protected_branch DROP COLUMN can_push"); err != nil {
|
|
||||||
return fmt.Errorf("DROP COLUMN can_push: %v", err)
|
|
||||||
}
|
}
|
||||||
default:
|
offset += pageSize
|
||||||
log.Fatal(4, "Unrecognized DB")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,36 +7,63 @@ package migrations
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func addDefaultValueToUserProhibitLogin(x *xorm.Engine) (err error) {
|
func removeDuplicateUnitTypes(x *xorm.Engine) error {
|
||||||
user := &models.User{
|
// RepoUnit describes all units of a repository
|
||||||
ProhibitLogin: false,
|
type RepoUnit struct {
|
||||||
|
RepoID int64
|
||||||
|
Type int
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := x.Where("`prohibit_login` IS NULL").Cols("prohibit_login").Update(user); err != nil {
|
// Enumerate all the unit types
|
||||||
|
const (
|
||||||
|
UnitTypeCode = iota + 1 // 1 code
|
||||||
|
UnitTypeIssues // 2 issues
|
||||||
|
UnitTypePullRequests // 3 PRs
|
||||||
|
UnitTypeReleases // 4 Releases
|
||||||
|
UnitTypeWiki // 5 Wiki
|
||||||
|
UnitTypeExternalWiki // 6 ExternalWiki
|
||||||
|
UnitTypeExternalTracker // 7 ExternalTracker
|
||||||
|
)
|
||||||
|
|
||||||
|
var externalIssueRepoUnits []RepoUnit
|
||||||
|
err := x.Where("type = ?", UnitTypeExternalTracker).Find(&externalIssueRepoUnits)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Query repositories: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var externalWikiRepoUnits []RepoUnit
|
||||||
|
err = x.Where("type = ?", UnitTypeExternalWiki).Find(&externalWikiRepoUnits)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Query repositories: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dialect := x.Dialect().DriverName()
|
for _, repoUnit := range externalIssueRepoUnits {
|
||||||
|
if _, err = sess.Delete(&RepoUnit{
|
||||||
switch dialect {
|
RepoID: repoUnit.RepoID,
|
||||||
case "mysql":
|
Type: UnitTypeIssues,
|
||||||
_, err = x.Exec("ALTER TABLE user MODIFY `prohibit_login` tinyint(1) NOT NULL DEFAULT 0")
|
}); err != nil {
|
||||||
case "postgres":
|
return fmt.Errorf("Delete repo unit: %v", err)
|
||||||
_, err = x.Exec("ALTER TABLE \"user\" ALTER COLUMN `prohibit_login` SET NOT NULL, ALTER COLUMN `prohibit_login` SET DEFAULT false")
|
}
|
||||||
case "mssql":
|
|
||||||
// xorm already set DEFAULT 0 for data type BIT in mssql
|
|
||||||
_, err = x.Exec(`ALTER TABLE [user] ALTER COLUMN "prohibit_login" BIT NOT NULL`)
|
|
||||||
case "sqlite3":
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
for _, repoUnit := range externalWikiRepoUnits {
|
||||||
return fmt.Errorf("Error changing user prohibit_login column definition: %v", err)
|
if _, err = sess.Delete(&RepoUnit{
|
||||||
|
RepoID: repoUnit.RepoID,
|
||||||
|
Type: UnitTypeWiki,
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("Delete repo unit: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
// Copyright 2017 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 (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"code.gitea.io/git"
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReleaseV39 describes the added field for Release
|
|
||||||
type ReleaseV39 struct {
|
|
||||||
IsTag bool `xorm:"NOT NULL DEFAULT false"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TableName will be invoked by XORM to customrize the table name
|
|
||||||
func (*ReleaseV39) TableName() string {
|
|
||||||
return "release"
|
|
||||||
}
|
|
||||||
|
|
||||||
func releaseAddColumnIsTagAndSyncTags(x *xorm.Engine) error {
|
|
||||||
if err := x.Sync2(new(ReleaseV39)); err != nil {
|
|
||||||
return fmt.Errorf("Sync2: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// For the sake of SQLite3, we can't use x.Iterate here.
|
|
||||||
offset := 0
|
|
||||||
pageSize := 20
|
|
||||||
for {
|
|
||||||
repos := make([]*models.Repository, 0, pageSize)
|
|
||||||
if err := x.Table("repository").Asc("id").Limit(pageSize, offset).Find(&repos); err != nil {
|
|
||||||
return fmt.Errorf("select repos [offset: %d]: %v", offset, err)
|
|
||||||
}
|
|
||||||
for _, repo := range repos {
|
|
||||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("OpenRepository: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = models.SyncReleasesWithTags(repo, gitRepo); err != nil {
|
|
||||||
log.Warn("SyncReleasesWithTags: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(repos) < pageSize {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
offset += pageSize
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
// Copyright 2017 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 (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func fixProtectedBranchCanPushValue(x *xorm.Engine) error {
|
|
||||||
type ProtectedBranch struct {
|
|
||||||
CanPush bool `xorm:"NOT NULL DEFAULT false"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := x.Sync2(new(ProtectedBranch)); err != nil {
|
|
||||||
return fmt.Errorf("Sync2: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := x.Cols("can_push").Update(&ProtectedBranch{
|
|
||||||
CanPush: false,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
// Copyright 2017 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 (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func removeDuplicateUnitTypes(x *xorm.Engine) error {
|
|
||||||
// RepoUnit describes all units of a repository
|
|
||||||
type RepoUnit struct {
|
|
||||||
RepoID int64
|
|
||||||
Type int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enumerate all the unit types
|
|
||||||
const (
|
|
||||||
UnitTypeCode = iota + 1 // 1 code
|
|
||||||
UnitTypeIssues // 2 issues
|
|
||||||
UnitTypePullRequests // 3 PRs
|
|
||||||
UnitTypeReleases // 4 Releases
|
|
||||||
UnitTypeWiki // 5 Wiki
|
|
||||||
UnitTypeExternalWiki // 6 ExternalWiki
|
|
||||||
UnitTypeExternalTracker // 7 ExternalTracker
|
|
||||||
)
|
|
||||||
|
|
||||||
var externalIssueRepoUnits []RepoUnit
|
|
||||||
err := x.Where("type = ?", UnitTypeExternalTracker).Find(&externalIssueRepoUnits)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Query repositories: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var externalWikiRepoUnits []RepoUnit
|
|
||||||
err = x.Where("type = ?", UnitTypeExternalWiki).Find(&externalWikiRepoUnits)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Query repositories: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sess.Close()
|
|
||||||
|
|
||||||
if err := sess.Begin(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, repoUnit := range externalIssueRepoUnits {
|
|
||||||
if _, err = sess.Delete(&RepoUnit{
|
|
||||||
RepoID: repoUnit.RepoID,
|
|
||||||
Type: UnitTypeIssues,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("Delete repo unit: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, repoUnit := range externalWikiRepoUnits {
|
|
||||||
if _, err = sess.Delete(&RepoUnit{
|
|
||||||
RepoID: repoUnit.RepoID,
|
|
||||||
Type: UnitTypeWiki,
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("Delete repo unit: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
|
||||||
73
models/migrations/v49.go
Normal file
73
models/migrations/v49.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright 2017 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 (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addTimetracking(x *xorm.Engine) error {
|
||||||
|
// RepoUnit describes all units of a repository
|
||||||
|
type RepoUnit struct {
|
||||||
|
ID int64
|
||||||
|
RepoID int64 `xorm:"INDEX(s)"`
|
||||||
|
Type int `xorm:"INDEX(s)"`
|
||||||
|
Config map[string]interface{} `xorm:"JSON"`
|
||||||
|
CreatedUnix int64 `xorm:"INDEX CREATED"`
|
||||||
|
Created time.Time `xorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stopwatch see models/issue_stopwatch.go
|
||||||
|
type Stopwatch struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
IssueID int64 `xorm:"INDEX"`
|
||||||
|
UserID int64 `xorm:"INDEX"`
|
||||||
|
Created time.Time `xorm:"-"`
|
||||||
|
CreatedUnix int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrackedTime see models/issue_tracked_time.go
|
||||||
|
type TrackedTime struct {
|
||||||
|
ID int64 `xorm:"pk autoincr" json:"id"`
|
||||||
|
IssueID int64 `xorm:"INDEX" json:"issue_id"`
|
||||||
|
UserID int64 `xorm:"INDEX" json:"user_id"`
|
||||||
|
Created time.Time `xorm:"-" json:"created"`
|
||||||
|
CreatedUnix int64 `json:"-"`
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := x.Sync2(new(Stopwatch)); err != nil {
|
||||||
|
return fmt.Errorf("Sync2: %v", err)
|
||||||
|
}
|
||||||
|
if err := x.Sync2(new(TrackedTime)); err != nil {
|
||||||
|
return fmt.Errorf("Sync2: %v", err)
|
||||||
|
}
|
||||||
|
//Updating existing issue units
|
||||||
|
units := make([]*RepoUnit, 0, 100)
|
||||||
|
err := x.Where("`type` = ?", V16UnitTypeIssues).Find(&units)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Query repo units: %v", err)
|
||||||
|
}
|
||||||
|
for _, unit := range units {
|
||||||
|
if unit.Config == nil {
|
||||||
|
unit.Config = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
if _, ok := unit.Config["EnableTimetracker"]; !ok {
|
||||||
|
unit.Config["EnableTimetracker"] = setting.Service.DefaultEnableTimetracking
|
||||||
|
}
|
||||||
|
if _, ok := unit.Config["AllowOnlyContributorsToTrackTime"]; !ok {
|
||||||
|
unit.Config["AllowOnlyContributorsToTrackTime"] = setting.Service.DefaultAllowOnlyContributorsToTrackTime
|
||||||
|
}
|
||||||
|
if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
55
models/migrations/v50.go
Normal file
55
models/migrations/v50.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2017 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 (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func migrateProtectedBranchStruct(x *xorm.Engine) error {
|
||||||
|
type ProtectedBranch struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||||
|
BranchName string `xorm:"UNIQUE(s)"`
|
||||||
|
CanPush bool
|
||||||
|
Created time.Time `xorm:"-"`
|
||||||
|
CreatedUnix int64
|
||||||
|
Updated time.Time `xorm:"-"`
|
||||||
|
UpdatedUnix int64
|
||||||
|
}
|
||||||
|
|
||||||
|
var pbs []ProtectedBranch
|
||||||
|
err := x.Find(&pbs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pb := range pbs {
|
||||||
|
if pb.CanPush {
|
||||||
|
if _, err = x.ID(pb.ID).Delete(new(ProtectedBranch)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case setting.UseSQLite3:
|
||||||
|
log.Warn("Unable to drop columns in SQLite")
|
||||||
|
case setting.UseMySQL, setting.UsePostgreSQL, setting.UseMSSQL, setting.UseTiDB:
|
||||||
|
if _, err := x.Exec("ALTER TABLE protected_branch DROP COLUMN can_push"); err != nil {
|
||||||
|
// Ignoring this error in case we run this migration second time (after migration reordering)
|
||||||
|
log.Warn("DROP COLUMN can_push: %v", err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Fatal(4, "Unrecognized DB")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
42
models/migrations/v51.go
Normal file
42
models/migrations/v51.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2017 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 (
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addDefaultValueToUserProhibitLogin(x *xorm.Engine) (err error) {
|
||||||
|
user := &models.User{
|
||||||
|
ProhibitLogin: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := x.Where("`prohibit_login` IS NULL").Cols("prohibit_login").Update(user); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dialect := x.Dialect().DriverName()
|
||||||
|
|
||||||
|
switch dialect {
|
||||||
|
case "mysql":
|
||||||
|
_, err = x.Exec("ALTER TABLE user MODIFY `prohibit_login` tinyint(1) NOT NULL DEFAULT 0")
|
||||||
|
case "postgres":
|
||||||
|
_, err = x.Exec("ALTER TABLE \"user\" ALTER COLUMN `prohibit_login` SET NOT NULL, ALTER COLUMN `prohibit_login` SET DEFAULT false")
|
||||||
|
case "mssql":
|
||||||
|
// xorm already set DEFAULT 0 for data type BIT in mssql
|
||||||
|
_, err = x.Exec(`ALTER TABLE [user] ALTER COLUMN "prohibit_login" BIT NOT NULL`)
|
||||||
|
case "sqlite3":
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Ignoring this error in case we run this migration second time (after migration reordering)
|
||||||
|
log.Warn("Error changing user prohibit_login column definition: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -605,9 +605,14 @@ func (repo *Repository) RepoPath() string {
|
|||||||
return repo.repoPath(x)
|
return repo.repoPath(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GitConfigPath returns the path to a repository's git config/ directory
|
||||||
|
func GitConfigPath(repoPath string) string {
|
||||||
|
return filepath.Join(repoPath, "config")
|
||||||
|
}
|
||||||
|
|
||||||
// GitConfigPath returns the repository git config path
|
// GitConfigPath returns the repository git config path
|
||||||
func (repo *Repository) GitConfigPath() string {
|
func (repo *Repository) GitConfigPath() string {
|
||||||
return filepath.Join(repo.RepoPath(), "config")
|
return GitConfigPath(repo.RepoPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
// RelLink returns the repository relative link
|
// RelLink returns the repository relative link
|
||||||
@@ -1496,20 +1501,6 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
|||||||
|
|
||||||
// Remove old team-repository relations.
|
// Remove old team-repository relations.
|
||||||
if owner.IsOrganization() {
|
if owner.IsOrganization() {
|
||||||
if err = owner.getTeams(sess); err != nil {
|
|
||||||
return fmt.Errorf("getTeams: %v", err)
|
|
||||||
}
|
|
||||||
for _, t := range owner.Teams {
|
|
||||||
if !t.hasRepository(sess, repo.ID) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
t.NumRepos--
|
|
||||||
if _, err := sess.ID(t.ID).Cols("num_repos").Update(t); err != nil {
|
|
||||||
return fmt.Errorf("decrease team repository count '%d': %v", t.ID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = owner.removeOrgRepo(sess, repo.ID); err != nil {
|
if err = owner.removeOrgRepo(sess, repo.ID); err != nil {
|
||||||
return fmt.Errorf("removeOrgRepo: %v", err)
|
return fmt.Errorf("removeOrgRepo: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,10 +100,6 @@ func populateRepoIndexer() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type updateBatch struct {
|
|
||||||
updates []indexer.RepoIndexerUpdate
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateRepoIndexer(repo *Repository) error {
|
func updateRepoIndexer(repo *Repository) error {
|
||||||
changes, err := getRepoChanges(repo)
|
changes, err := getRepoChanges(repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -163,6 +159,10 @@ func addUpdate(filename string, repo *Repository, batch *indexer.Batch) error {
|
|||||||
return err
|
return err
|
||||||
} else if stat.Size() > setting.Indexer.MaxIndexerFileSize {
|
} else if stat.Size() > setting.Indexer.MaxIndexerFileSize {
|
||||||
return nil
|
return nil
|
||||||
|
} else if stat.IsDir() {
|
||||||
|
// file could actually be a directory, if it is the root of a submodule.
|
||||||
|
// We do not index submodule contents, so don't do anything.
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
fileContents, err := ioutil.ReadFile(filepath)
|
fileContents, err := ioutil.ReadFile(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
|
|
||||||
"code.gitea.io/git"
|
"code.gitea.io/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"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/sync"
|
"code.gitea.io/gitea/modules/sync"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
"github.com/Unknwon/com"
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MirrorQueue holds an UniqueQueue object of the mirror
|
// MirrorQueue holds an UniqueQueue object of the mirror
|
||||||
@@ -76,41 +76,41 @@ func (m *Mirror) ScheduleNextUpdate() {
|
|||||||
m.NextUpdate = time.Now().Add(m.Interval)
|
m.NextUpdate = time.Now().Add(m.Interval)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func remoteAddress(repoPath string) (string, error) {
|
||||||
|
cfg, err := ini.Load(GitConfigPath(repoPath))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return cfg.Section("remote \"origin\"").Key("url").Value(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Mirror) readAddress() {
|
func (m *Mirror) readAddress() {
|
||||||
if len(m.address) > 0 {
|
if len(m.address) > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var err error
|
||||||
cfg, err := ini.Load(m.Repo.GitConfigPath())
|
m.address, err = remoteAddress(m.Repo.RepoPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(4, "Load: %v", err)
|
log.Error(4, "remoteAddress: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
m.address = cfg.Section("remote \"origin\"").Key("url").Value()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleCloneUserCredentials replaces user credentials from HTTP/HTTPS URL
|
// sanitizeOutput sanitizes output of a command, replacing occurrences of the
|
||||||
// with placeholder <credentials>.
|
// repository's remote address with a sanitized version.
|
||||||
// It will fail for any other forms of clone addresses.
|
func sanitizeOutput(output, repoPath string) (string, error) {
|
||||||
func HandleCloneUserCredentials(url string, mosaics bool) string {
|
remoteAddr, err := remoteAddress(repoPath)
|
||||||
i := strings.Index(url, "@")
|
if err != nil {
|
||||||
if i == -1 {
|
// if we're unable to load the remote address, then we're unable to
|
||||||
return url
|
// sanitize.
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
start := strings.Index(url, "://")
|
return util.SanitizeMessage(output, remoteAddr), nil
|
||||||
if start == -1 {
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
if mosaics {
|
|
||||||
return url[:start+3] + "<credentials>" + url[i:]
|
|
||||||
}
|
|
||||||
return url[:start+3] + url[i+1:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address returns mirror address from Git repository config without credentials.
|
// Address returns mirror address from Git repository config without credentials.
|
||||||
func (m *Mirror) Address() string {
|
func (m *Mirror) Address() string {
|
||||||
m.readAddress()
|
m.readAddress()
|
||||||
return HandleCloneUserCredentials(m.address, false)
|
return util.SanitizeURLCredentials(m.address, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FullAddress returns mirror address from Git repository config.
|
// FullAddress returns mirror address from Git repository config.
|
||||||
@@ -145,7 +145,14 @@ func (m *Mirror) runSync() bool {
|
|||||||
if _, stderr, err := process.GetManager().ExecDir(
|
if _, stderr, err := process.GetManager().ExecDir(
|
||||||
timeout, repoPath, fmt.Sprintf("Mirror.runSync: %s", repoPath),
|
timeout, repoPath, fmt.Sprintf("Mirror.runSync: %s", repoPath),
|
||||||
"git", gitArgs...); err != nil {
|
"git", gitArgs...); err != nil {
|
||||||
desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderr)
|
// sanitize the output, since it may contain the remote address, which may
|
||||||
|
// contain a password
|
||||||
|
message, err := sanitizeOutput(stderr, repoPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "sanitizeOutput: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, message)
|
||||||
log.Error(4, desc)
|
log.Error(4, desc)
|
||||||
if err = CreateRepositoryNotice(desc); err != nil {
|
if err = CreateRepositoryNotice(desc); err != nil {
|
||||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||||
@@ -170,7 +177,14 @@ func (m *Mirror) runSync() bool {
|
|||||||
if _, stderr, err := process.GetManager().ExecDir(
|
if _, stderr, err := process.GetManager().ExecDir(
|
||||||
timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath),
|
timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath),
|
||||||
"git", "remote", "update", "--prune"); err != nil {
|
"git", "remote", "update", "--prune"); err != nil {
|
||||||
desc := fmt.Sprintf("Failed to update mirror wiki repository '%s': %s", wikiPath, stderr)
|
// sanitize the output, since it may contain the remote address, which may
|
||||||
|
// contain a password
|
||||||
|
message, err := sanitizeOutput(stderr, wikiPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "sanitizeOutput: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
desc := fmt.Sprintf("Failed to update mirror wiki repository '%s': %s", wikiPath, message)
|
||||||
log.Error(4, desc)
|
log.Error(4, desc)
|
||||||
if err = CreateRepositoryNotice(desc); err != nil {
|
if err = CreateRepositoryNotice(desc); err != nil {
|
||||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||||
|
|||||||
@@ -153,3 +153,26 @@ func TestRepoLocalCopyPath(t *testing.T) {
|
|||||||
setting.Repository.Local.LocalCopyPath = tempPath
|
setting.Repository.Local.LocalCopyPath = tempPath
|
||||||
assert.Equal(t, expected, repo.LocalCopyPath())
|
assert.Equal(t, expected, repo.LocalCopyPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTransferOwnership(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
|
doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||||
|
repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
|
||||||
|
repo.Owner = AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User)
|
||||||
|
assert.NoError(t, TransferOwnership(doer, "user2", repo))
|
||||||
|
|
||||||
|
transferredRepo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
|
||||||
|
assert.EqualValues(t, 2, transferredRepo.OwnerID)
|
||||||
|
|
||||||
|
assert.False(t, com.IsExist(RepoPath("user3", "repo3")))
|
||||||
|
assert.True(t, com.IsExist(RepoPath("user2", "repo3")))
|
||||||
|
AssertExistsAndLoadBean(t, &Action{
|
||||||
|
OpType: ActionTransferRepo,
|
||||||
|
ActUserID: 2,
|
||||||
|
RepoID: 3,
|
||||||
|
Content: "user3/repo3",
|
||||||
|
})
|
||||||
|
|
||||||
|
CheckConsistencyFor(t, &Repository{}, &User{}, &Team{})
|
||||||
|
}
|
||||||
|
|||||||
@@ -315,10 +315,9 @@ func (u *User) generateRandomAvatar(e Engine) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RelAvatarLink returns relative avatar link to the site domain,
|
// SizedRelAvatarLink returns a relative link to the user's avatar. When
|
||||||
// which includes app sub-url as prefix. However, it is possible
|
// applicable, the link is for an avatar of the indicated size (in pixels).
|
||||||
// to return full URL if user enables Gravatar-like service.
|
func (u *User) SizedRelAvatarLink(size int) string {
|
||||||
func (u *User) RelAvatarLink() string {
|
|
||||||
if u.ID == -1 {
|
if u.ID == -1 {
|
||||||
return base.DefaultAvatarLink()
|
return base.DefaultAvatarLink()
|
||||||
}
|
}
|
||||||
@@ -338,7 +337,14 @@ func (u *User) RelAvatarLink() string {
|
|||||||
|
|
||||||
return setting.AppSubURL + "/avatars/" + u.Avatar
|
return setting.AppSubURL + "/avatars/" + u.Avatar
|
||||||
}
|
}
|
||||||
return base.AvatarLink(u.AvatarEmail)
|
return base.SizedAvatarLink(u.AvatarEmail, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RelAvatarLink returns a relative link to the user's avatar. The link
|
||||||
|
// may either be a sub-URL to this site, or a full URL to an external avatar
|
||||||
|
// service.
|
||||||
|
func (u *User) RelAvatarLink() string {
|
||||||
|
return u.SizedRelAvatarLink(base.DefaultAvatarSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AvatarLink returns user avatar absolute link.
|
// AvatarLink returns user avatar absolute link.
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -197,24 +199,59 @@ func DefaultAvatarLink() string {
|
|||||||
return setting.AppSubURL + "/img/avatar_default.png"
|
return setting.AppSubURL + "/img/avatar_default.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultAvatarSize is a sentinel value for the default avatar size, as
|
||||||
|
// determined by the avatar-hosting service.
|
||||||
|
const DefaultAvatarSize = -1
|
||||||
|
|
||||||
|
// libravatarURL returns the URL for the given email. This function should only
|
||||||
|
// be called if a federated avatar service is enabled.
|
||||||
|
func libravatarURL(email string) (*url.URL, error) {
|
||||||
|
urlStr, err := setting.LibravatarService.FromEmail(email)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "LibravatarService.FromEmail(email=%s): error %v", email, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u, err := url.Parse(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(4, "Failed to parse libravatar url(%s): error %v", urlStr, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SizedAvatarLink returns a sized link to the avatar for the given email
|
||||||
|
// address.
|
||||||
|
func SizedAvatarLink(email string, size int) string {
|
||||||
|
var avatarURL *url.URL
|
||||||
|
if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
|
||||||
|
var err error
|
||||||
|
avatarURL, err = libravatarURL(email)
|
||||||
|
if err != nil {
|
||||||
|
return DefaultAvatarLink()
|
||||||
|
}
|
||||||
|
} else if !setting.DisableGravatar {
|
||||||
|
// copy GravatarSourceURL, because we will modify its Path.
|
||||||
|
copyOfGravatarSourceURL := *setting.GravatarSourceURL
|
||||||
|
avatarURL = ©OfGravatarSourceURL
|
||||||
|
avatarURL.Path = path.Join(avatarURL.Path, HashEmail(email))
|
||||||
|
} else {
|
||||||
|
return DefaultAvatarLink()
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := avatarURL.Query()
|
||||||
|
vals.Set("d", "identicon")
|
||||||
|
if size != DefaultAvatarSize {
|
||||||
|
vals.Set("s", strconv.Itoa(size))
|
||||||
|
}
|
||||||
|
avatarURL.RawQuery = vals.Encode()
|
||||||
|
return avatarURL.String()
|
||||||
|
}
|
||||||
|
|
||||||
// AvatarLink returns relative avatar link to the site domain by given email,
|
// AvatarLink returns relative avatar link to the site domain by given email,
|
||||||
// which includes app sub-url as prefix. However, it is possible
|
// which includes app sub-url as prefix. However, it is possible
|
||||||
// to return full URL if user enables Gravatar-like service.
|
// to return full URL if user enables Gravatar-like service.
|
||||||
func AvatarLink(email string) string {
|
func AvatarLink(email string) string {
|
||||||
if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
|
return SizedAvatarLink(email, DefaultAvatarSize)
|
||||||
url, err := setting.LibravatarService.FromEmail(email)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(4, "LibravatarService.FromEmail(email=%s): error %v", email, err)
|
|
||||||
return DefaultAvatarLink()
|
|
||||||
}
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
if !setting.DisableGravatar {
|
|
||||||
return setting.GravatarSource + HashEmail(email) + "?d=identicon"
|
|
||||||
}
|
|
||||||
|
|
||||||
return DefaultAvatarLink()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seconds-based time units
|
// Seconds-based time units
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package base
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/Unknwon/i18n"
|
"github.com/Unknwon/i18n"
|
||||||
macaroni18n "github.com/go-macaron/i18n"
|
macaroni18n "github.com/go-macaron/i18n"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -126,16 +128,40 @@ func TestHashEmail(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAvatarLink(t *testing.T) {
|
const gravatarSource = "https://secure.gravatar.com/avatar/"
|
||||||
|
|
||||||
|
func disableGravatar() {
|
||||||
setting.EnableFederatedAvatar = false
|
setting.EnableFederatedAvatar = false
|
||||||
setting.LibravatarService = nil
|
setting.LibravatarService = nil
|
||||||
setting.DisableGravatar = true
|
setting.DisableGravatar = true
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal(t, "/img/avatar_default.png", AvatarLink(""))
|
func enableGravatar(t *testing.T) {
|
||||||
|
|
||||||
setting.DisableGravatar = false
|
setting.DisableGravatar = false
|
||||||
|
var err error
|
||||||
|
setting.GravatarSourceURL, err = url.Parse(gravatarSource)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSizedAvatarLink(t *testing.T) {
|
||||||
|
disableGravatar()
|
||||||
|
assert.Equal(t, "/img/avatar_default.png",
|
||||||
|
SizedAvatarLink("gitea@example.com", 100))
|
||||||
|
|
||||||
|
enableGravatar(t)
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
"353cbad9b58e69c96154ad99f92bedc7?d=identicon",
|
"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100",
|
||||||
|
SizedAvatarLink("gitea@example.com", 100),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAvatarLink(t *testing.T) {
|
||||||
|
disableGravatar()
|
||||||
|
assert.Equal(t, "/img/avatar_default.png", AvatarLink("gitea@example.com"))
|
||||||
|
|
||||||
|
enableGravatar(t)
|
||||||
|
assert.Equal(t,
|
||||||
|
"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon",
|
||||||
AvatarLink("gitea@example.com"),
|
AvatarLink("gitea@example.com"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,9 @@ func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if treeEntry.Blob().Size() >= setting.UI.MaxDisplayFileSize {
|
||||||
|
return nil, git.ErrNotExist{ID: "", RelPath: ".editorconfig"}
|
||||||
|
}
|
||||||
reader, err := treeEntry.Blob().Data()
|
reader, err := treeEntry.Blob().Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -615,7 +618,11 @@ func RepoRefByType(refType RepoRefType) macaron.Handler {
|
|||||||
|
|
||||||
if refType == RepoRefLegacy {
|
if refType == RepoRefLegacy {
|
||||||
// redirect from old URL scheme to new URL scheme
|
// redirect from old URL scheme to new URL scheme
|
||||||
ctx.Redirect(path.Join(setting.AppSubURL, strings.TrimSuffix(ctx.Req.URL.String(), ctx.Params("*")), ctx.Repo.BranchNameSubURL()))
|
ctx.Redirect(path.Join(
|
||||||
|
setting.AppSubURL,
|
||||||
|
strings.TrimSuffix(ctx.Req.URL.String(), ctx.Params("*")),
|
||||||
|
ctx.Repo.BranchNameSubURL(),
|
||||||
|
ctx.Repo.TreePath))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
|
|||||||
var closeError error
|
var closeError error
|
||||||
var waitError error
|
var waitError error
|
||||||
|
|
||||||
args := []string{"-F", from, "-i"}
|
args := []string{"-f", from, "-i"}
|
||||||
args = append(args, setting.MailService.SendmailArgs...)
|
args = append(args, setting.MailService.SendmailArgs...)
|
||||||
args = append(args, to...)
|
args = append(args, to...)
|
||||||
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
|
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)
|
||||||
|
|||||||
@@ -325,6 +325,7 @@ var (
|
|||||||
// Picture settings
|
// Picture settings
|
||||||
AvatarUploadPath string
|
AvatarUploadPath string
|
||||||
GravatarSource string
|
GravatarSource string
|
||||||
|
GravatarSourceURL *url.URL
|
||||||
DisableGravatar bool
|
DisableGravatar bool
|
||||||
EnableFederatedAvatar bool
|
EnableFederatedAvatar bool
|
||||||
LibravatarService *libravatar.Libravatar
|
LibravatarService *libravatar.Libravatar
|
||||||
@@ -1026,18 +1027,22 @@ func NewContext() {
|
|||||||
if DisableGravatar {
|
if DisableGravatar {
|
||||||
EnableFederatedAvatar = false
|
EnableFederatedAvatar = false
|
||||||
}
|
}
|
||||||
|
if EnableFederatedAvatar || !DisableGravatar {
|
||||||
|
GravatarSourceURL, err = url.Parse(GravatarSource)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(4, "Failed to parse Gravatar URL(%s): %v",
|
||||||
|
GravatarSource, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if EnableFederatedAvatar {
|
if EnableFederatedAvatar {
|
||||||
LibravatarService = libravatar.New()
|
LibravatarService = libravatar.New()
|
||||||
parts := strings.Split(GravatarSource, "/")
|
if GravatarSourceURL.Scheme == "https" {
|
||||||
if len(parts) >= 3 {
|
LibravatarService.SetUseHTTPS(true)
|
||||||
if parts[0] == "https:" {
|
LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host)
|
||||||
LibravatarService.SetUseHTTPS(true)
|
} else {
|
||||||
LibravatarService.SetSecureFallbackHost(parts[2])
|
LibravatarService.SetUseHTTPS(false)
|
||||||
} else {
|
LibravatarService.SetFallbackHost(GravatarSourceURL.Host)
|
||||||
LibravatarService.SetUseHTTPS(false)
|
|
||||||
LibravatarService.SetFallbackHost(parts[2])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1166,7 +1171,7 @@ func newService() {
|
|||||||
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply.example.org")
|
Service.NoReplyAddress = sec.Key("NO_REPLY_ADDRESS").MustString("noreply.example.org")
|
||||||
|
|
||||||
sec = Cfg.Section("openid")
|
sec = Cfg.Section("openid")
|
||||||
Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(false)
|
Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock)
|
||||||
Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn)
|
Service.EnableOpenIDSignUp = sec.Key("ENABLE_OPENID_SIGNUP").MustBool(!Service.DisableRegistration && Service.EnableOpenIDSignIn)
|
||||||
pats := sec.Key("WHITELISTED_URIS").Strings(" ")
|
pats := sec.Key("WHITELISTED_URIS").Strings(" ")
|
||||||
if len(pats) != 0 {
|
if len(pats) != 0 {
|
||||||
@@ -1392,7 +1397,7 @@ func newSessionService() {
|
|||||||
SessionConfig.Provider = Cfg.Section("session").Key("PROVIDER").In("memory",
|
SessionConfig.Provider = Cfg.Section("session").Key("PROVIDER").In("memory",
|
||||||
[]string{"memory", "file", "redis", "mysql"})
|
[]string{"memory", "file", "redis", "mysql"})
|
||||||
SessionConfig.ProviderConfig = strings.Trim(Cfg.Section("session").Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ")
|
SessionConfig.ProviderConfig = strings.Trim(Cfg.Section("session").Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ")
|
||||||
if !filepath.IsAbs(SessionConfig.ProviderConfig) {
|
if SessionConfig.Provider == "file" && !filepath.IsAbs(SessionConfig.ProviderConfig) {
|
||||||
SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig)
|
SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig)
|
||||||
}
|
}
|
||||||
SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("i_like_gitea")
|
SessionConfig.CookieName = Cfg.Section("session").Key("COOKIE_NAME").MustString("i_like_gitea")
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"container/list"
|
"container/list"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
"mime"
|
"mime"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -162,6 +163,7 @@ func NewFuncMap() []template.FuncMap {
|
|||||||
"UnescapeLocale": func(str string) string {
|
"UnescapeLocale": func(str string) string {
|
||||||
return strings.NewReplacer("\\;", ";", "\\#", "#").Replace(str)
|
return strings.NewReplacer("\\;", ";", "\\#", "#").Replace(str)
|
||||||
},
|
},
|
||||||
|
"Escape": Escape,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,6 +182,11 @@ func Str2html(raw string) template.HTML {
|
|||||||
return template.HTML(markup.Sanitize(raw))
|
return template.HTML(markup.Sanitize(raw))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Escape escapes a HTML string
|
||||||
|
func Escape(raw string) string {
|
||||||
|
return html.EscapeString(raw)
|
||||||
|
}
|
||||||
|
|
||||||
// List traversings the list
|
// List traversings the list
|
||||||
func List(l *list.List) chan interface{} {
|
func List(l *list.List) chan interface{} {
|
||||||
e := l.Front()
|
e := l.Front()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
|
||||||
|
"github.com/go-macaron/session"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
macaron "gopkg.in/macaron.v1"
|
macaron "gopkg.in/macaron.v1"
|
||||||
)
|
)
|
||||||
@@ -33,6 +34,9 @@ func MockContext(t *testing.T) *context.Context {
|
|||||||
macaronContext.Render = &mockRender{ResponseWriter: macaronContext.Resp}
|
macaronContext.Render = &mockRender{ResponseWriter: macaronContext.Resp}
|
||||||
return &context.Context{
|
return &context.Context{
|
||||||
Context: macaronContext,
|
Context: macaronContext,
|
||||||
|
Flash: &session.Flash{
|
||||||
|
Values: make(url.Values),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
48
modules/util/sanitize.go
Normal file
48
modules/util/sanitize.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2017 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 util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// urlSafeError wraps an error whose message may contain a sensitive URL
|
||||||
|
type urlSafeError struct {
|
||||||
|
err error
|
||||||
|
unsanitizedURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err urlSafeError) Error() string {
|
||||||
|
return SanitizeMessage(err.err.Error(), err.unsanitizedURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLSanitizedError returns the sanitized version an error whose message may
|
||||||
|
// contain a sensitive URL
|
||||||
|
func URLSanitizedError(err error, unsanitizedURL string) error {
|
||||||
|
return urlSafeError{err: err, unsanitizedURL: unsanitizedURL}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanitizeMessage sanitizes a message which may contains a sensitive URL
|
||||||
|
func SanitizeMessage(message, unsanitizedURL string) string {
|
||||||
|
sanitizedURL := SanitizeURLCredentials(unsanitizedURL, true)
|
||||||
|
return strings.Replace(message, unsanitizedURL, sanitizedURL, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanitizeURLCredentials sanitizes a url, either removing user credentials
|
||||||
|
// or replacing them with a placeholder.
|
||||||
|
func SanitizeURLCredentials(unsanitizedURL string, usePlaceholder bool) string {
|
||||||
|
u, err := url.Parse(unsanitizedURL)
|
||||||
|
if err != nil {
|
||||||
|
// don't log the error, since it might contain unsanitized URL.
|
||||||
|
return "(unparsable url)"
|
||||||
|
}
|
||||||
|
if u.User != nil && usePlaceholder {
|
||||||
|
u.User = url.User("<credentials>")
|
||||||
|
} else {
|
||||||
|
u.User = nil
|
||||||
|
}
|
||||||
|
return u.String()
|
||||||
|
}
|
||||||
@@ -627,8 +627,8 @@ issues.label_templates.info=Il n'y a pas encore d'étiquettes. Vous pouvez cliqu
|
|||||||
issues.label_templates.helper=Sélectionnez un ensemble d'étiquettes
|
issues.label_templates.helper=Sélectionnez un ensemble d'étiquettes
|
||||||
issues.label_templates.use=Utiliser ce jeu d'étiquettes
|
issues.label_templates.use=Utiliser ce jeu d'étiquettes
|
||||||
issues.label_templates.fail_to_load_file=Impossible de charger le fichier de modèle étiquette '%s' : %v
|
issues.label_templates.fail_to_load_file=Impossible de charger le fichier de modèle étiquette '%s' : %v
|
||||||
issues.add_label_at=« enlevé la <div class="ui label" style="color: %s\; background-color: %s">%s</div> étiquette %s »
|
issues.add_label_at=`a ajouté l'étiquette <div class="ui label" style="color: %s\; background-color: %s">%s</div> %s`
|
||||||
issues.remove_label_at=« enlevé la <div class="ui label" style="color: %s\; background-color: %s">%s</div> étiquette %s »
|
issues.remove_label_at=`a supprimé l'étiquette <div class="ui label" style="color: %s\; background-color: %s">%s</div> %s`
|
||||||
issues.add_milestone_at=`a ajouté cela au jalon <b>%s</b> %s`
|
issues.add_milestone_at=`a ajouté cela au jalon <b>%s</b> %s`
|
||||||
issues.change_milestone_at=`a modifié le jalon de <b>%s</b> à <b>%s</b> %s`
|
issues.change_milestone_at=`a modifié le jalon de <b>%s</b> à <b>%s</b> %s`
|
||||||
issues.remove_milestone_at=`a supprimé cela du jalon <b>%s</b> %s`
|
issues.remove_milestone_at=`a supprimé cela du jalon <b>%s</b> %s`
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
api "code.gitea.io/sdk/gitea"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
"code.gitea.io/gitea/modules/auth"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
@@ -18,6 +16,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/routers/api/v1/convert"
|
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||||
|
api "code.gitea.io/sdk/gitea"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Search repositories via options
|
// Search repositories via options
|
||||||
@@ -322,12 +321,13 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
|
|||||||
RemoteAddr: remoteAddr,
|
RemoteAddr: remoteAddr,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = util.URLSanitizedError(err, remoteAddr)
|
||||||
if repo != nil {
|
if repo != nil {
|
||||||
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
|
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
|
||||||
log.Error(4, "DeleteRepository: %v", errDelete)
|
log.Error(4, "DeleteRepository: %v", errDelete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.Error(500, "MigrateRepository", models.HandleCloneUserCredentials(err.Error(), true))
|
ctx.Error(500, "MigrateRepository", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,8 +108,8 @@ func Install(ctx *context.Context) {
|
|||||||
form.OfflineMode = setting.OfflineMode
|
form.OfflineMode = setting.OfflineMode
|
||||||
form.DisableGravatar = setting.DisableGravatar
|
form.DisableGravatar = setting.DisableGravatar
|
||||||
form.EnableFederatedAvatar = setting.EnableFederatedAvatar
|
form.EnableFederatedAvatar = setting.EnableFederatedAvatar
|
||||||
form.EnableOpenIDSignIn = true
|
form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn
|
||||||
form.EnableOpenIDSignUp = true
|
form.EnableOpenIDSignUp = setting.Service.EnableOpenIDSignUp
|
||||||
form.DisableRegistration = setting.Service.DisableRegistration
|
form.DisableRegistration = setting.Service.DisableRegistration
|
||||||
form.EnableCaptcha = setting.Service.EnableCaptcha
|
form.EnableCaptcha = setting.Service.EnableCaptcha
|
||||||
form.RequireSignInView = setting.Service.RequireSignInView
|
form.RequireSignInView = setting.Service.RequireSignInView
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/routers/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -76,11 +77,7 @@ func TeamsAction(ctx *context.Context) {
|
|||||||
ctx.Error(404)
|
ctx.Error(404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
uname := ctx.Query("uname")
|
uname := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.Query("uname")))
|
||||||
// uname may be formatted as "username (fullname)"
|
|
||||||
if strings.Contains(uname, "(") && strings.HasSuffix(uname, ")") {
|
|
||||||
uname = strings.TrimSpace(strings.Split(uname, "(")[0])
|
|
||||||
}
|
|
||||||
var u *models.User
|
var u *models.User
|
||||||
u, err = models.GetUserByName(uname)
|
u, err = models.GetUserByName(uname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -45,10 +45,11 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error {
|
|||||||
|
|
||||||
// ServeBlob download a git.Blob
|
// ServeBlob download a git.Blob
|
||||||
func ServeBlob(ctx *context.Context, blob *git.Blob) error {
|
func ServeBlob(ctx *context.Context, blob *git.Blob) error {
|
||||||
dataRc, err := blob.Data()
|
dataRc, err := blob.DataAsync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer dataRc.Close()
|
||||||
|
|
||||||
return ServeData(ctx, ctx.Repo.TreePath, dataRc)
|
return ServeData(ctx, ctx.Repo.TreePath, dataRc)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,11 +73,16 @@ func editFile(ctx *context.Context, isNewFile bool) {
|
|||||||
|
|
||||||
// No way to edit a directory online.
|
// No way to edit a directory online.
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
ctx.Handle(404, "", nil)
|
ctx.Handle(404, "entry.IsDir", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
blob := entry.Blob()
|
blob := entry.Blob()
|
||||||
|
if blob.Size() >= setting.UI.MaxDisplayFileSize {
|
||||||
|
ctx.Handle(404, "blob.Size", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
dataRc, err := blob.Data()
|
dataRc, err := blob.Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(404, "blob.Data", err)
|
ctx.Handle(404, "blob.Data", err)
|
||||||
@@ -93,7 +98,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
|
|||||||
|
|
||||||
// Only text file are editable online.
|
// Only text file are editable online.
|
||||||
if !base.IsTextFile(buf) {
|
if !base.IsTextFile(buf) {
|
||||||
ctx.Handle(404, "", nil)
|
ctx.Handle(404, "base.IsTextFile", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -319,6 +319,9 @@ func getFileContentFromDefaultBranch(ctx *context.Context, filename string) (str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
if entry.Blob().Size() >= setting.UI.MaxDisplayFileSize {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
r, err = entry.Blob().Data()
|
r, err = entry.Blob().Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", false
|
return "", false
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ func NewReleasePost(ctx *context.Context, form auth.NewReleaseForm) {
|
|||||||
|
|
||||||
rel.Title = form.Title
|
rel.Title = form.Title
|
||||||
rel.Note = form.Content
|
rel.Note = form.Content
|
||||||
|
rel.Target = form.Target
|
||||||
rel.IsDraft = len(form.Draft) > 0
|
rel.IsDraft = len(form.Draft) > 0
|
||||||
rel.IsPrerelease = form.Prerelease
|
rel.IsPrerelease = form.Prerelease
|
||||||
rel.PublisherID = ctx.User.ID
|
rel.PublisherID = ctx.User.ID
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -232,6 +233,9 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remoteAddr may contain credentials, so we sanitize it
|
||||||
|
err = util.URLSanitizedError(err, remoteAddr)
|
||||||
|
|
||||||
if repo != nil {
|
if repo != nil {
|
||||||
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
|
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
|
||||||
log.Error(4, "DeleteRepository: %v", errDelete)
|
log.Error(4, "DeleteRepository: %v", errDelete)
|
||||||
@@ -241,11 +245,11 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
|
|||||||
if strings.Contains(err.Error(), "Authentication failed") ||
|
if strings.Contains(err.Error(), "Authentication failed") ||
|
||||||
strings.Contains(err.Error(), "could not read Username") {
|
strings.Contains(err.Error(), "could not read Username") {
|
||||||
ctx.Data["Err_Auth"] = true
|
ctx.Data["Err_Auth"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("form.auth_failed", models.HandleCloneUserCredentials(err.Error(), true)), tplMigrate, &form)
|
ctx.RenderWithErr(ctx.Tr("form.auth_failed", err.Error()), tplMigrate, &form)
|
||||||
return
|
return
|
||||||
} else if strings.Contains(err.Error(), "fatal:") {
|
} else if strings.Contains(err.Error(), "fatal:") {
|
||||||
ctx.Data["Err_CloneAddr"] = true
|
ctx.Data["Err_CloneAddr"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", models.HandleCloneUserCredentials(err.Error(), true)), tplMigrate, &form)
|
ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", err.Error()), tplMigrate, &form)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/routers/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -366,7 +367,7 @@ func Collaboration(ctx *context.Context) {
|
|||||||
|
|
||||||
// CollaborationPost response for actions for a collaboration of a repository
|
// CollaborationPost response for actions for a collaboration of a repository
|
||||||
func CollaborationPost(ctx *context.Context) {
|
func CollaborationPost(ctx *context.Context) {
|
||||||
name := strings.ToLower(ctx.Query("collaborator"))
|
name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.Query("collaborator")))
|
||||||
if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
|
if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
|
||||||
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
|
ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -76,11 +76,12 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
|||||||
ctx.Data["ReadmeInList"] = true
|
ctx.Data["ReadmeInList"] = true
|
||||||
ctx.Data["ReadmeExist"] = true
|
ctx.Data["ReadmeExist"] = true
|
||||||
|
|
||||||
dataRc, err := readmeFile.Data()
|
dataRc, err := readmeFile.DataAsync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "Data", err)
|
ctx.Handle(500, "Data", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer dataRc.Close()
|
||||||
|
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
n, _ := dataRc.Read(buf)
|
n, _ := dataRc.Read(buf)
|
||||||
@@ -91,14 +92,21 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
|||||||
ctx.Data["FileName"] = readmeFile.Name()
|
ctx.Data["FileName"] = readmeFile.Name()
|
||||||
// FIXME: what happens when README file is an image?
|
// FIXME: what happens when README file is an image?
|
||||||
if isTextFile {
|
if isTextFile {
|
||||||
d, _ := ioutil.ReadAll(dataRc)
|
if readmeFile.Size() >= setting.UI.MaxDisplayFileSize {
|
||||||
buf = append(buf, d...)
|
// Pretend that this is a normal text file to display 'This file is too large to be shown'
|
||||||
if markup.Type(readmeFile.Name()) != "" {
|
ctx.Data["IsFileTooLarge"] = true
|
||||||
ctx.Data["IsMarkup"] = true
|
ctx.Data["IsTextFile"] = true
|
||||||
ctx.Data["FileContent"] = string(markup.Render(readmeFile.Name(), buf, treeLink, ctx.Repo.Repository.ComposeMetas()))
|
ctx.Data["FileSize"] = readmeFile.Size()
|
||||||
} else {
|
} else {
|
||||||
ctx.Data["IsRenderedHTML"] = true
|
d, _ := ioutil.ReadAll(dataRc)
|
||||||
ctx.Data["FileContent"] = string(bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1))
|
buf = append(buf, d...)
|
||||||
|
if markup.Type(readmeFile.Name()) != "" {
|
||||||
|
ctx.Data["IsMarkup"] = true
|
||||||
|
ctx.Data["FileContent"] = string(markup.Render(readmeFile.Name(), buf, treeLink, ctx.Repo.Repository.ComposeMetas()))
|
||||||
|
} else {
|
||||||
|
ctx.Data["IsRenderedHTML"] = true
|
||||||
|
ctx.Data["FileContent"] = string(bytes.Replace(buf, []byte("\n"), []byte(`<br>`), -1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,11 +143,12 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
|||||||
ctx.Data["IsViewFile"] = true
|
ctx.Data["IsViewFile"] = true
|
||||||
|
|
||||||
blob := entry.Blob()
|
blob := entry.Blob()
|
||||||
dataRc, err := blob.Data()
|
dataRc, err := blob.DataAsync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Handle(500, "Data", err)
|
ctx.Handle(500, "DataAsync", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer dataRc.Close()
|
||||||
|
|
||||||
ctx.Data["FileSize"] = blob.Size()
|
ctx.Data["FileSize"] = blob.Size()
|
||||||
ctx.Data["FileName"] = blob.Name()
|
ctx.Data["FileName"] = blob.Name()
|
||||||
|
|||||||
@@ -223,7 +223,9 @@ func SettingsSecurityPost(ctx *context.Context, form auth.ChangePasswordForm) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.User.IsPasswordSet() && !ctx.User.ValidatePassword(form.OldPassword) {
|
if len(form.Password) < setting.MinPasswordLength {
|
||||||
|
ctx.Flash.Error(ctx.Tr("auth.password_too_short", setting.MinPasswordLength))
|
||||||
|
} else if ctx.User.IsPasswordSet() && !ctx.User.ValidatePassword(form.OldPassword) {
|
||||||
ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
|
ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
|
||||||
} 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"))
|
||||||
|
|||||||
56
routers/user/setting_test.go
Normal file
56
routers/user/setting_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2017 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 user
|
||||||
|
|
||||||
|
/*func TestChangePassword(t *testing.T) {
|
||||||
|
oldPassword := "password"
|
||||||
|
setting.MinPasswordLength = 6
|
||||||
|
|
||||||
|
for _, req := range []struct {
|
||||||
|
OldPassword string
|
||||||
|
NewPassword string
|
||||||
|
Retype string
|
||||||
|
Message string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
OldPassword: oldPassword,
|
||||||
|
NewPassword: "123456",
|
||||||
|
Retype: "123456",
|
||||||
|
Message: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OldPassword: oldPassword,
|
||||||
|
NewPassword: "12345",
|
||||||
|
Retype: "12345",
|
||||||
|
Message: "auth.password_too_short",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OldPassword: "12334",
|
||||||
|
NewPassword: "123456",
|
||||||
|
Retype: "123456",
|
||||||
|
Message: "settings.password_incorrect",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OldPassword: oldPassword,
|
||||||
|
NewPassword: "123456",
|
||||||
|
Retype: "12345",
|
||||||
|
Message: "form.password_not_match",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
models.PrepareTestEnv(t)
|
||||||
|
ctx := test.MockContext(t, "user/settings/security")
|
||||||
|
test.LoadUser(t, ctx, 2)
|
||||||
|
test.LoadRepo(t, ctx, 1)
|
||||||
|
|
||||||
|
SettingsSecurityPost(ctx, auth.ChangePasswordForm{
|
||||||
|
OldPassword: req.OldPassword,
|
||||||
|
Password: req.NewPassword,
|
||||||
|
Retype: req.Retype,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.EqualValues(t, req.Message, ctx.Flash.ErrorMsg)
|
||||||
|
assert.EqualValues(t, http.StatusFound, ctx.Resp.Status())
|
||||||
|
}
|
||||||
|
}*/
|
||||||
17
routers/utils/utils.go
Normal file
17
routers/utils/utils.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2017 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 utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RemoveUsernameParameterSuffix returns the username parameter without the (fullname) suffix - leaving just the username
|
||||||
|
func RemoveUsernameParameterSuffix(name string) string {
|
||||||
|
if index := strings.Index(name, " ("); index >= 0 {
|
||||||
|
name = name[:index]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
17
routers/utils/utils_test.go
Normal file
17
routers/utils/utils_test.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2017 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 utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRemoveUsernameParameterSuffix(t *testing.T) {
|
||||||
|
assert.Equal(t, "foobar", RemoveUsernameParameterSuffix("foobar (Foo Bar)"))
|
||||||
|
assert.Equal(t, "foobar", RemoveUsernameParameterSuffix("foobar"))
|
||||||
|
assert.Equal(t, "", RemoveUsernameParameterSuffix(""))
|
||||||
|
}
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
<dt>{{.i18n.Tr "admin.config.db_user"}}</dt>
|
<dt>{{.i18n.Tr "admin.config.db_user"}}</dt>
|
||||||
<dd>{{if .DbCfg.User}}{{.DbCfg.User}}{{else}}-{{end}}</dd>
|
<dd>{{if .DbCfg.User}}{{.DbCfg.User}}{{else}}-{{end}}</dd>
|
||||||
<dt>{{.i18n.Tr "admin.config.db_ssl_mode"}}</dt>
|
<dt>{{.i18n.Tr "admin.config.db_ssl_mode"}}</dt>
|
||||||
<dd><i class="fa fa{{if .DbCfg.SSLMode}}-check{{end}}-square-o"></i> {{.i18n.Tr "admin.config.db_ssl_mode_helper"}}</dd>
|
<dd>{{if .DbCfg.SSLMode}}{{.DbCfg.SSLMode}}{{else}}-{{end}} {{.i18n.Tr "admin.config.db_ssl_mode_helper"}}</dd>
|
||||||
<dt>{{.i18n.Tr "admin.config.db_path"}}</dt>
|
<dt>{{.i18n.Tr "admin.config.db_path"}}</dt>
|
||||||
<dd>{{if .DbCfg.Path}}{{.DbCfg.Path}}{{else}}-{{end}} {{.i18n.Tr "admin.config.db_path_helper"}}</dd>
|
<dd>{{if .DbCfg.Path}}{{.DbCfg.Path}}{{else}}-{{end}} {{.i18n.Tr "admin.config.db_path_helper"}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
@@ -264,8 +264,6 @@
|
|||||||
<dd>{{.Git.MaxGitDiffFiles}}</dd>
|
<dd>{{.Git.MaxGitDiffFiles}}</dd>
|
||||||
<dt>{{.i18n.Tr "admin.config.git_gc_args"}}</dt>
|
<dt>{{.i18n.Tr "admin.config.git_gc_args"}}</dt>
|
||||||
<dd><code>{{.Git.GCArgs}}</code></dd>
|
<dd><code>{{.Git.GCArgs}}</code></dd>
|
||||||
<dt>{{.i18n.Tr "admin.config.git_max_diff_lines"}}</dt>
|
|
||||||
<dd>{{.Git.MaxGitDiffLines}}</dd>
|
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<dt>{{.i18n.Tr "admin.config.git_migrate_timeout"}}</dt>
|
<dt>{{.i18n.Tr "admin.config.git_migrate_timeout"}}</dt>
|
||||||
<dd>{{.Git.Timeout.Migrate}} {{.i18n.Tr "tool.raw_seconds"}}</dd>
|
<dd>{{.Git.Timeout.Migrate}} {{.i18n.Tr "tool.raw_seconds"}}</dd>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{{range .Repos}}
|
{{range .Repos}}
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
<a class="name" href="{{AppSubUrl}}/{{if .Owner}}{{.Owner.Name}}{{else if $.Org}}{{$.Org.Name}}{{else}}{{$.Owner.Name}}{{end}}/{{.Name}}">{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}}</a>
|
<a class="name" href="{{.HTMLURL}}">{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}}</a>
|
||||||
{{if .IsPrivate}}
|
{{if .IsPrivate}}
|
||||||
<span class="text gold"><i class="octicon octicon-lock"></i></span>
|
<span class="text gold"><i class="octicon octicon-lock"></i></span>
|
||||||
{{else if .IsFork}}
|
{{else if .IsFork}}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="ui vertically grid head">
|
<div class="ui vertically grid head">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
<img class="ui image" src="{{.RelAvatarLink}}?s=100">
|
<img class="ui image" src="{{.SizedRelAvatarLink 100}}">
|
||||||
<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
|
<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
|
||||||
|
|
||||||
<div class="ui right">
|
<div class="ui right">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui grid">
|
<div class="ui grid">
|
||||||
<div class="ui sixteen wide column">
|
<div class="ui sixteen wide column">
|
||||||
<img class="ui left" id="org-avatar" src="{{.Org.RelAvatarLink}}?s=140"/>
|
<img class="ui left" id="org-avatar" src="{{.Org.SizedRelAvatarLink 140}}"/>
|
||||||
<div id="org-info">
|
<div id="org-info">
|
||||||
<div class="ui header">
|
<div class="ui header">
|
||||||
{{.Org.DisplayName}}
|
{{.Org.DisplayName}}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
{{range .Members}}
|
{{range .Members}}
|
||||||
<div class="item ui grid">
|
<div class="item ui grid">
|
||||||
<div class="ui one wide column">
|
<div class="ui one wide column">
|
||||||
<img class="ui avatar" src="{{.RelAvatarLink}}?s=48">
|
<img class="ui avatar" src="{{.SizedRelAvatarLink 48}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="ui three wide column">
|
<div class="ui three wide column">
|
||||||
<div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div>
|
<div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if .Repository.UnitEnabled $.UnitTypeExternalTracker}}
|
{{if .Repository.UnitEnabled $.UnitTypeExternalTracker}}
|
||||||
<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues">
|
<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues" target="_blank">
|
||||||
<i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} </span>
|
<i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} </span>
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -172,7 +172,7 @@
|
|||||||
<a class="title has-emoji" href="{{$.Link}}/{{.Index}}">{{.Title}}</a>
|
<a class="title has-emoji" href="{{$.Link}}/{{.Index}}">{{.Title}}</a>
|
||||||
|
|
||||||
{{if .Ref}}
|
{{if .Ref}}
|
||||||
<a class="ui label" href="{{$.RepoLink}}/src/commit/{{.Ref}}">{{.Ref}}</a>
|
<a class="ui label" href="{{$.RepoLink}}/src/branch/{{.Ref}}">{{.Ref}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{.Name}}</a>
|
<a class="ui label" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" style="color: {{.ForegroundColor}}; background-color: {{.Color}}">{{.Name}}</a>
|
||||||
|
|||||||
@@ -96,7 +96,7 @@
|
|||||||
<img src="{{.Poster.RelAvatarLink}}">
|
<img src="{{.Poster.RelAvatarLink}}">
|
||||||
</a>
|
</a>
|
||||||
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
|
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
|
||||||
{{if .Content}}{{$.i18n.Tr "repo.issues.add_label_at" .Label.ForegroundColor .Label.Color .Label.Name $createdStr | UnescapeLocale | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_label_at" .Label.ForegroundColor .Label.Color .Label.Name $createdStr | UnescapeLocale | Safe}}{{end}}</span>
|
{{if .Content}}{{$.i18n.Tr "repo.issues.add_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|Escape) $createdStr | UnescapeLocale | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_label_at" .Label.ForegroundColor .Label.Color (.Label.Name|Escape) $createdStr | UnescapeLocale | Safe}}{{end}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else if eq .Type 8}}
|
{{else if eq .Type 8}}
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
<img src="{{.Poster.RelAvatarLink}}">
|
<img src="{{.Poster.RelAvatarLink}}">
|
||||||
</a>
|
</a>
|
||||||
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
|
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
|
||||||
{{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.change_milestone_at" .OldMilestone.Name .Milestone.Name $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_milestone_at" .OldMilestone.Name $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.add_milestone_at" .Milestone.Name $createdStr | Safe}}{{end}}</span>
|
{{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.change_milestone_at" (.OldMilestone.Name|Escape) (.Milestone.Name|Escape) $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_milestone_at" (.OldMilestone.Name|Escape) $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.add_milestone_at" (.Milestone.Name|Escape) $createdStr | Safe}}{{end}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{else if eq .Type 9}}
|
{{else if eq .Type 9}}
|
||||||
<div class="event">
|
<div class="event">
|
||||||
@@ -124,23 +124,23 @@
|
|||||||
{{else if eq .Type 10}}
|
{{else if eq .Type 10}}
|
||||||
<div class="event">
|
<div class="event">
|
||||||
<span class="octicon octicon-primitive-dot"></span>
|
<span class="octicon octicon-primitive-dot"></span>
|
||||||
|
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
|
||||||
|
<img src="{{.Poster.RelAvatarLink}}">
|
||||||
|
</a>
|
||||||
|
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
|
||||||
|
{{$.i18n.Tr "repo.issues.change_title_at" (.OldTitle|Escape) (.NewTitle|Escape) $createdStr | Safe}}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
|
|
||||||
<img src="{{.Poster.RelAvatarLink}}">
|
|
||||||
</a>
|
|
||||||
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
|
|
||||||
{{$.i18n.Tr "repo.issues.change_title_at" .OldTitle .NewTitle $createdStr | Safe}}
|
|
||||||
</span>
|
|
||||||
{{else if eq .Type 11}}
|
{{else if eq .Type 11}}
|
||||||
<div class="event">
|
<div class="event">
|
||||||
<span class="octicon octicon-primitive-dot"></span>
|
<span class="octicon octicon-primitive-dot"></span>
|
||||||
|
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
|
||||||
|
<img src="{{.Poster.RelAvatarLink}}">
|
||||||
|
</a>
|
||||||
|
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
|
||||||
|
{{$.i18n.Tr "repo.issues.delete_branch_at" .CommitSHA $createdStr | Safe}}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
|
|
||||||
<img src="{{.Poster.RelAvatarLink}}">
|
|
||||||
</a>
|
|
||||||
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
|
|
||||||
{{$.i18n.Tr "repo.issues.delete_branch_at" .CommitSHA $createdStr | Safe}}
|
|
||||||
</span>
|
|
||||||
{{else if eq .Type 12}}
|
{{else if eq .Type 12}}
|
||||||
<div class="event">
|
<div class="event">
|
||||||
<span class="octicon octicon-primitive-dot"></span>
|
<span class="octicon octicon-primitive-dot"></span>
|
||||||
|
|||||||
@@ -41,9 +41,11 @@
|
|||||||
<a href="{{$.RepoLink}}/src/tag/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a>
|
<a href="{{$.RepoLink}}/src/tag/{{.TagName}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a>
|
||||||
</h4>
|
</h4>
|
||||||
<div class="download">
|
<div class="download">
|
||||||
|
{{if $.Repository.UnitEnabled $.UnitTypeCode}}
|
||||||
<a href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a>
|
<a href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a>
|
||||||
<a href="{{$.RepoLink}}/archive/{{.TagName}}.zip" rel="nofollow"><i class="octicon octicon-file-zip"></i> ZIP</a>
|
<a href="{{$.RepoLink}}/archive/{{.TagName}}.zip" rel="nofollow"><i class="octicon octicon-file-zip"></i> ZIP</a>
|
||||||
<a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> TAR.GZ</a>
|
<a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> TAR.GZ</a>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<h3>
|
<h3>
|
||||||
@@ -64,12 +66,14 @@
|
|||||||
<div class="download">
|
<div class="download">
|
||||||
<h2>{{$.i18n.Tr "repo.release.downloads"}}</h2>
|
<h2>{{$.i18n.Tr "repo.release.downloads"}}</h2>
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
|
{{if $.Repository.UnitEnabled $.UnitTypeCode}}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{$.RepoLink}}/archive/{{.TagName}}.zip" rel="nofollow"><i class="octicon octicon-file-zip"></i> {{$.i18n.Tr "repo.release.source_code"}} (ZIP)</a>
|
<a href="{{$.RepoLink}}/archive/{{.TagName}}.zip" rel="nofollow"><i class="octicon octicon-file-zip"></i> {{$.i18n.Tr "repo.release.source_code"}} (ZIP)</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> {{$.i18n.Tr "repo.release.source_code"}} (TAR.GZ)</a>
|
<a href="{{$.RepoLink}}/archive/{{.TagName}}.tar.gz"><i class="octicon octicon-file-zip"></i> {{$.i18n.Tr "repo.release.source_code"}} (TAR.GZ)</a>
|
||||||
</li>
|
</li>
|
||||||
|
{{end}}
|
||||||
{{if .Attachments}}
|
{{if .Attachments}}
|
||||||
{{range .Attachments}}
|
{{range .Attachments}}
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{if .Keyword}}
|
{{if .Keyword}}
|
||||||
<h3>
|
<h3>
|
||||||
{{.i18n.Tr "repo.search.results" .Keyword .RepoLink .RepoName | Str2html}}
|
{{.i18n.Tr "repo.search.results" (.Keyword|Escape) .RepoLink .RepoName | Str2html }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="repository search">
|
<div class="repository search">
|
||||||
{{range $result := .SearchResults}}
|
{{range $result := .SearchResults}}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
{{.i18n.Tr "repo.wiki.delete_page_button"}}
|
{{.i18n.Tr "repo.wiki.delete_page_button"}}
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p>{{.i18n.Tr "repo.wiki.delete_page_notice_1" $title | Safe}}</p>
|
<p>{{.i18n.Tr "repo.wiki.delete_page_notice_1" ($title|Escape) | Safe}}</p>
|
||||||
</div>
|
</div>
|
||||||
{{template "base/delete_modal_actions" .}}
|
{{template "base/delete_modal_actions" .}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
<div class="ui card">
|
<div class="ui card">
|
||||||
{{if eq .SignedUserName .Owner.Name}}
|
{{if eq .SignedUserName .Owner.Name}}
|
||||||
<a class="image poping up" href="{{AppSubUrl}}/user/settings/avatar" id="profile-avatar" data-content="{{.i18n.Tr "user.change_avatar"}}" data-variation="inverted tiny" data-position="bottom center">
|
<a class="image poping up" href="{{AppSubUrl}}/user/settings/avatar" id="profile-avatar" data-content="{{.i18n.Tr "user.change_avatar"}}" data-variation="inverted tiny" data-position="bottom center">
|
||||||
<img src="{{.Owner.RelAvatarLink}}?s=290" title="{{.Owner.Name}}"/>
|
<img src="{{.Owner.SizedRelAvatarLink 290}}" title="{{.Owner.Name}}"/>
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="image">
|
<span class="image">
|
||||||
<img src="{{.Owner.RelAvatarLink}}?s=290" title="{{.Owner.Name}}"/>
|
<img src="{{.Owner.SizedRelAvatarLink 290}}" title="{{.Owner.Name}}"/>
|
||||||
</span>
|
</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|||||||
50
vendor/code.gitea.io/git/blob.go
generated
vendored
50
vendor/code.gitea.io/git/blob.go
generated
vendored
@@ -6,7 +6,11 @@ package git
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Blob represents a Git object.
|
// Blob represents a Git object.
|
||||||
@@ -18,14 +22,52 @@ type Blob struct {
|
|||||||
// Data gets content of blob all at once and wrap it as io.Reader.
|
// Data gets content of blob all at once and wrap it as io.Reader.
|
||||||
// This can be very slow and memory consuming for huge content.
|
// This can be very slow and memory consuming for huge content.
|
||||||
func (b *Blob) Data() (io.Reader, error) {
|
func (b *Blob) Data() (io.Reader, error) {
|
||||||
stdout, err := NewCommand("show", b.ID.String()).RunInDirBytes(b.repo.Path)
|
stdout := new(bytes.Buffer)
|
||||||
if err != nil {
|
stderr := new(bytes.Buffer)
|
||||||
return nil, err
|
|
||||||
|
// Preallocate memory to save ~50% memory usage on big files.
|
||||||
|
stdout.Grow(int(b.Size() + 2048))
|
||||||
|
|
||||||
|
if err := b.DataPipeline(stdout, stderr); err != nil {
|
||||||
|
return nil, concatenateError(err, stderr.String())
|
||||||
}
|
}
|
||||||
return bytes.NewBuffer(stdout), nil
|
return stdout, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DataPipeline gets content of blob and write the result or error to stdout or stderr
|
// DataPipeline gets content of blob and write the result or error to stdout or stderr
|
||||||
func (b *Blob) DataPipeline(stdout, stderr io.Writer) error {
|
func (b *Blob) DataPipeline(stdout, stderr io.Writer) error {
|
||||||
return NewCommand("show", b.ID.String()).RunInDirPipeline(b.repo.Path, stdout, stderr)
|
return NewCommand("show", b.ID.String()).RunInDirPipeline(b.repo.Path, stdout, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type cmdReadCloser struct {
|
||||||
|
cmd *exec.Cmd
|
||||||
|
stdout io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c cmdReadCloser) Read(p []byte) (int, error) {
|
||||||
|
return c.stdout.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c cmdReadCloser) Close() error {
|
||||||
|
io.Copy(ioutil.Discard, c.stdout)
|
||||||
|
return c.cmd.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataAsync gets a ReadCloser for the contents of a blob without reading it all.
|
||||||
|
// Calling the Close function on the result will discard all unread output.
|
||||||
|
func (b *Blob) DataAsync() (io.ReadCloser, error) {
|
||||||
|
cmd := exec.Command("git", "show", b.ID.String())
|
||||||
|
cmd.Dir = b.repo.Path
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
stdout, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("StdoutPipe: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cmd.Start(); err != nil {
|
||||||
|
return nil, fmt.Errorf("Start: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdReadCloser{stdout: stdout, cmd: cmd}, nil
|
||||||
|
}
|
||||||
|
|||||||
3
vendor/code.gitea.io/git/commit.go
generated
vendored
3
vendor/code.gitea.io/git/commit.go
generated
vendored
@@ -98,10 +98,11 @@ func (c *Commit) IsImageFile(name string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
dataRc, err := blob.Data()
|
dataRc, err := blob.DataAsync()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
defer dataRc.Close()
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
n, _ := dataRc.Read(buf)
|
n, _ := dataRc.Read(buf)
|
||||||
buf = buf[:n]
|
buf = buf[:n]
|
||||||
|
|||||||
2
vendor/code.gitea.io/git/git.go
generated
vendored
2
vendor/code.gitea.io/git/git.go
generated
vendored
@@ -25,7 +25,7 @@ var (
|
|||||||
// Prefix the log prefix
|
// Prefix the log prefix
|
||||||
Prefix = "[git-module] "
|
Prefix = "[git-module] "
|
||||||
// GitVersionRequired is the minimum Git version required
|
// GitVersionRequired is the minimum Git version required
|
||||||
GitVersionRequired = "1.8.1.6"
|
GitVersionRequired = "1.7.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func log(format string, args ...interface{}) {
|
func log(format string, args ...interface{}) {
|
||||||
|
|||||||
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
@@ -3,10 +3,10 @@
|
|||||||
"ignore": "test appengine",
|
"ignore": "test appengine",
|
||||||
"package": [
|
"package": [
|
||||||
{
|
{
|
||||||
"checksumSHA1": "JN/re4+x/hCzMLGHmieUcykVDAg=",
|
"checksumSHA1": "vAVjAz7Wpjnu7GGba4JLIDTpQEw=",
|
||||||
"path": "code.gitea.io/git",
|
"path": "code.gitea.io/git",
|
||||||
"revision": "d47b98c44c9a6472e44ab80efe65235e11c6da2a",
|
"revision": "f9dd6826bbb51c92c6964ce18176c304ea286e54",
|
||||||
"revisionTime": "2017-10-23T00:52:09Z"
|
"revisionTime": "2017-11-28T15:25:05Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "OICEgmUefW4L4l/FK/NVFnl/aOM=",
|
"checksumSHA1": "OICEgmUefW4L4l/FK/NVFnl/aOM=",
|
||||||
|
|||||||
Reference in New Issue
Block a user