Compare commits

..

24 Commits

Author SHA1 Message Date
John Olheiser
9169b39458 Changelog 1.11.0 (#10204)
* Changelog 1.11.0

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

* Backport snackport

Co-Authored-By: techknowlogick <matti@mdranta.net>

* Update CHANGELOG.md

Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com>

Co-authored-by: techknowlogick <matti@mdranta.net>
Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
2020-02-10 13:34:30 +02:00
Lauris BH
80eb50655a Fix followers and following tabs in profile (#10202) (#10203) 2020-02-09 21:00:24 +00:00
6543
b16c555541 Fix code-expansion arc-green theme bug (#10180) (#10185)
* fix code-expansion theme bug

* working solution without important

* no new color

* the midle
2020-02-08 00:04:03 +01:00
guillep2k
b5b44364e3 Allow emoji on review head comments (#10159) (#10174)
Co-authored-by: techknowlogick <matti@mdranta.net>

Co-authored-by: techknowlogick <matti@mdranta.net>
Co-authored-by: Antoine GIRARD <sapk@users.noreply.github.com>
2020-02-07 21:39:24 +01:00
guillep2k
6af58022c8 Prevent double waitgroup decrement (#10170) (#10175)
* Prevent double waitgroup decrement

Co-authored-by: zeripath <art27@cantab.net>
2020-02-07 17:12:49 +00:00
guillep2k
e48b460a0a Fix issue/pull link (#10158) (#10173) 2020-02-07 11:51:23 +08:00
John Olheiser
2cd2614eaa Fix push-create SSH bugs (#10145) (#10151)
* Attempt to fix push-create SSH bugs

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

* Fix binding

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

* Invalid ctx

Signed-off-by: jolheiser <john.olheiser@gmail.com>
2020-02-05 15:50:06 +01:00
6543
0129e76ef5 Prevent DeleteUser API abuse (#10125) (#10128)
* fix & co

* word suggestions from @jolheiser
2020-02-03 19:44:06 +02:00
6543
6896dad675 working part of #9998 (#10114) (#10115)
Co-authored-by: zeripath <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2020-02-03 11:19:04 +08:00
zeripath
1ed4323005 Add button to revert SimpleMDE to plain textarea (#10099) (#10102) 2020-02-01 22:24:58 +02:00
Lunny Xiao
049af0d3d0 Fix branch page pull request title and link error (#10092) (#10097)
* Fix branch page pull request title and link error (#10092)
2020-02-01 15:14:56 +00:00
6543
f5727d83dd [BugFix] [API] Pull.API.Convert: Only try to get HeadBranch if HeadRepo exist (#10029) (#10088) 2020-01-31 20:49:45 -05:00
Lauris BH
912ce27421 Update topics repo count when deleting repository (#10081) 2020-01-31 08:04:49 +00:00
6543
b3549bb5ec [UI] Show pull icon on pull (#10061) (#10062) 2020-01-29 20:40:43 +00:00
Lunny Xiao
491cbeca67 Fix milestone API state parameter unhandled (#10049) (#10052)
* Fix milestone API state parameter unhandled

* Fix test
2020-01-29 18:01:44 +02:00
zeripath
895d92ffe5 Ensure that feeds are appropriately restricted (#10018) (#10019)
* Ensure that feeds are appropriately restricted

* Placate golangci-lint
2020-01-28 23:54:09 +02:00
zeripath
4b11f967bd PRs: Move to use a temporary repo for pushing new prs (#10009) (#10042)
* Move to use a temporary repo for pushing new prs
2020-01-28 18:46:59 +00:00
Lunny Xiao
1e73dd2446 Fix wiki raw view on sub path (#10002) (#10040)
* Fix wiki raw view on sub path

* Add test for subpath wiki raw file

* Fix bug
2020-01-28 15:10:25 +00:00
6543
315026c2c5 trim whitespace of MilestoneName at creation/rename (#10017) (#10034) 2020-01-28 16:52:24 +08:00
Lunny Xiao
16dfd9ffbe Sanitize credentials in mirror form (#9975) (#9991) 2020-01-26 08:16:53 +00:00
Lunny Xiao
16f7b43903 Fix pull view when head repository or head branch missed and close related pull requests when delete head repository or head branch (#9927) (#9974)
* fix pull view when head repository or head branch missed and close related pull requests when delete branch

* fix pull view broken when head repository deleted

* close pull requests when head repositories deleted

* Add tests for broken pull request head repository or branch

* fix typo

* ignore special error when close pull request

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

Co-authored-by: Lauris BH <lauris@nix.lv>
2020-01-25 17:39:53 +08:00
techknowlogick
043febdbc9 Switch to use -f instead of -F for sendmail (#9961) (#9970)
Fix #9385

Co-authored-by: zeripath <art27@cantab.net>
2020-01-24 20:45:53 -05:00
guillep2k
60f91d56f0 Fix check for push on default branch (#9963)
Co-authored-by: techknowlogick <matti@mdranta.net>
2020-01-24 19:56:30 -05:00
guillep2k
16fc15ae6a Fix file rename/copy not supported by indexer (#9967) 2020-01-24 18:25:00 -05:00
49 changed files with 672 additions and 261 deletions

View File

@@ -1,4 +1,14 @@
# The full repository name
repo: go-gitea/gitea
# Service type (gitea or github)
service: github
# Base URL for Gitea instance if using gitea service type (optional)
# Default: https://gitea.com
base-url:
# Changelog groups and which labeled PRs to add to each group
groups:
-
name: BREAKING
@@ -42,3 +52,6 @@ groups:
-
name: MISC
default: true
# regex indicating which labels to skip for the changelog
skip-labels: skip-changelog|backport\/.+

View File

@@ -4,69 +4,22 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.11.0-RC2](https://github.com/go-gitea/gitea/releases/tag/v1.11.0-rc2) - 2020-01-18
## [1.11.0](https://github.com/go-gitea/gitea/releases/tag/v1.11.0) - 2020-02-10
* BREAKING
* Fix followers and following tabs in profile (#10202) (#10203)
* Make CertFile and KeyFile relative to CustomPath (#9868) (#9874)
* SECURITY
* Never allow an empty password to validate (#9682) (#9683)
* Prevent redirect to Host (#9678) (#9679)
* BUGFIXES
* Don't convert ellipsis in markdown (#9905) (#9937)
* Fixed repo link in generated comment for cross repository dependency (#9863) (#9935)
* Check if diff actually contains sections when rendering (#9926) (#9933)
* Fix wrong hint when status checking is running on pull request view (#9886) (#9928)
* Fix RocketChat (#9908) (#9921)
* Do not try to recreate ldap user if they are already created (#9900) (#9919)
* Create terminated channel in queue_redis (#9910) (#9911)
* Prevent empty LDAP search result from deactivating all users (#9879) (#9896)
* Fix wrong permissions check when issues/prs shared operations (#9885) (#9889)
* Check user != nil before checking values (#9881) (#9883)
* Allow hyphen in language name (#9873) (#9880)
* Ensure that 2fa is checked on reset-password (#9857) (#9876)
* Fix issues/pulls dependencies problems (#9842) (#9864)
* Explicitly refer to PR in squash-merge commit message in case of external tracker (#9844) (#9855)
* Fix markdown anchor links (#9673) (#9840)
* Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9837)
* Fix download file wrong content-type (#9825) (#9834)
* Fix wrong poster identity on a migrated pull request when submit review (#9827) (#9830)
* Fix database dump when log directory is missing (#9818) (#9819)
* Fix compare (#9808) (#9814)
* Fix push-to-create (#9772) (#9797)
* Fix missing msteam webhook on organization (#9781) (#9794)
* Fix missing unlock in uniquequeue (#9790) (#9791)
* Fix add team on collaborator page when same name as organization (#9778)
* DeleteRepoFile incorrectly handles Delete to new branch (#9769) (#9775)
* Fix milestones page (#9771)
* Fix SimpleMDE quote reply (#9757) (#9768)
* Fix missing updated time on migrated issues and comments (#9744) (#9764)
* Move Errored PRs out of StatusChecking (#9675) (#9726)
* Make hook status printing configurable with delay (#9641) (#9725)
* Fix /repos/issues/search (#9698) (#9724)
* Silence fomantic error regarding tabs (#9713) (#9718)
* Remove unused lock (#9709) (#9710)
* Remove q.lock.Unlock() in setInternal to prevent panic (#9705) (#9706)
* Load milestone in API PR list (#9671) (#9700)
* Don't attempt to close issue if already closed (#9696) (#9699)
* Remove google font call (#9668) (#9681)
* Eliminate horizontal scroll caused by footer (#9674)
* Fix nil reference in repo generation (#9660) (#9666)
* Add HTML URL to API Issues (#9654) (#9661)
* Add PR review webhook to Telegram (#9653) (#9655)
* Use filepath.IsAbs instead of path.IsAbs (#9651) (#9652)
* TRANSLATION
* Fix Korean locales (#9761) (#9780)
* BUILD
* Fix webpack polyfills (#9735) (#9738)
* MISC
* Backport Locales [2020-01-14] (#9773)
## [1.11.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.11.0-rc1) - 2020-01-07
* BREAKING
* Remove unused endpoints (#9538)
* Prefix all user-generated IDs in markup (#9477)
* Enforce Gitea environment for pushes (#8982)
* Hide some user information via API if user have no enough permission (#8655)
* Hide some user information via API if user have not enough permissions (#8655)
* Move startpage/homepage translation to crowdin (#8596)
* SECURITY
* Never allow an empty password to validate (#9682) (#9683)
* Prevent redirect to Host (#9678) (#9679)
* Swagger hide search field (#9554)
* Add "search" to reserved usernames (#9063)
* Switch to fomantic-ui (#9374)
* Only serve attachments when linked to issue/release and if accessible by user (#9340)
* FEATURES
* Webhooks should only show sender if it makes sense (#9601)
* Provide Default messages for merges (#9393)
@@ -100,6 +53,68 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631)
* Add basic repository lfs management (#7199)
* BUGFIXES
* Fix code-expansion arc-green theme bug (#10180) (#10185)
* Prevent double wait-group decrement (#10170) (#10175)
* Allow emoji on review head comments (#10159) (#10174)
* Fix issue/pull link (#10158) (#10173)
* Fix push-create SSH bugs (#10145) (#10151)
* Prevent DeleteUser API abuse (#10125) (#10128)
* Fix issues/pulls dashboard paging error (#10114) (#10115)
* Add button to revert SimpleMDE to plain textarea (#10099) (#10102)
* Fix branch page pull request title and link error (#10092) (#10097)
* Fix PR API: Only try to get HeadBranch if HeadRepo exist (#10029) (#10088)
* Update topics repo count when deleting repository (#10051) (#10081)
* Show pull icon on pull requests (#10061) (#10062)
* Fix milestone API state parameter unhandled (#10049) (#10052)
* Move to using a temporary repo for pushing new PRs (#10009) (#10042)
* Fix wiki raw view on sub path (#10002) (#10040)
* Ensure that feeds are appropriately restricted (#10018) (#10019)
* Sanitize credentials in mirror form (#9975) (#9991)
* Close related pull requests when deleting head repository or head branch (#9927) (#9974)
* Switch to use -f instead of -F for sendmail (#9961) (#9970)
* Fix file rename/copy not supported by indexer (#9965) (#9967)
* Fix repo indexer not updating upon push (#9957) (#9963)
* Don't convert ellipsis in markdown (#9905) (#9937)
* Fixed repo link in generated comment for cross repository dependency (#9863) (#9935)
* Check if diff actually contains sections when rendering (#9926) (#9933)
* Fix wrong hint when status checking is running on pull request view (#9886) (#9928)
* Fix RocketChat (#9908) (#9921)
* Do not try to recreate ldap user if they are already created (#9900) (#9919)
* Create terminated channel in queue_redis (#9910) (#9911)
* Prevent empty LDAP search result from deactivating all users (#9879) (#9896)
* Fix wrong permissions check when issues/prs shared operations (#9885) (#9889)
* Check user != nil before checking values (#9881) (#9883)
* Allow hyphen in language name (#9873) (#9880)
* Ensure that 2fa is checked on reset-password (#9857) (#9876)
* Fix issues/pulls dependencies problems (#9842) (#9864)
* Fix markdown anchor links (#9673) (#9840)
* Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9837)
* Fix download file wrong content-type (#9825) (#9834)
* Fix wrong poster identity on a migrated pull request when submit review (#9827) (#9830)
* Fix database dump when log directory is missing (#9818) (#9819)
* Fix compare (#9808) (#9814)
* Fix push-to-create (#9772) (#9797)
* Fix missing msteam webhook on organization (#9781) (#9794)
* Fix missing unlock in uniquequeue (#9790) (#9791)
* Fix add team on collaborator page when same name as organization (#9778)
* DeleteRepoFile incorrectly handles Delete to new branch (#9769) (#9775)
* Fix milestones page (#9771)
* Fix SimpleMDE quote reply (#9757) (#9768)
* Fix missing updated time on migrated issues and comments (#9744) (#9764)
* Move Errored PRs out of StatusChecking (#9675) (#9726)
* Make hook status printing configurable with delay (#9641) (#9725)
* Fix /repos/issues/search (#9698) (#9724)
* Silence fomantic error regarding tabs (#9713) (#9718)
* Remove unused lock (#9709) (#9710)
* Remove q.lock.Unlock() in setInternal to prevent panic (#9705) (#9706)
* Load milestone in API PR list (#9671) (#9700)
* Don't attempt to close issue if already closed (#9696) (#9699)
* Remove google font call (#9668) (#9681)
* Eliminate horizontal scroll caused by footer (#9674)
* Fix nil reference in repo generation (#9660) (#9666)
* Add HTML URL to API Issues (#9654) (#9661)
* Add PR review webhook to Telegram (#9653) (#9655)
* Use filepath.IsAbs instead of path.IsAbs (#9651) (#9652)
* Disable remove button on repository teams when have access to all (#9640)
* Clean up old references on branch delete (#9614)
* Hide public repos owned by private orgs (#9609)
@@ -231,6 +246,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Fix migrate mirror 500 bug (#8526)
* Fix password complexity regex for special characters (on master) (#8525)
* ENHANCEMENTS
* Explicitly refer to PR in squash-merge commit message in case of external tracker (#9844) (#9855)
* Add a /user/login landing page option (#9622)
* Some more e-mail notification fixes (#9596)
* Add branch protection option to block merge on requested changes. (#9592)
@@ -347,12 +363,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* wiki - add 'write' 'preview' buttons to wiki edit like in issues (#7241)
* Change target branch for pull request (#6488)
* Display PR commits and diffs using base repo rather than forked (#3648)
* SECURITY
* Swagger hide search field (#9554)
* Add "search" to reserved usernames (#9063)
* Switch to fomantic-ui (#9374)
* Only serve attachments when linked to issue/release and if accessible by user (#9340)
* Hide credentials when submitting migration through API (#9102)
* TESTING
* Add debug option to serv to help debug problems (#9492)
* Fix the intermittent TestGPGGit failures (#9360)
@@ -366,10 +376,12 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Update Github Migration Tests (#8893) (#8938)
* Update heatmap fixtures to restore tests (#8615)
* TRANSLATION
* Fix Korean locales (#9761) (#9780)
* Fix placeholders in the error message (#9060)
* Fix spelling of admin.users.max_repo_creation (#8934)
* Improve german translation of homepage (#8549)
* BUILD
* Fix webpack polyfills (#9735) (#9738)
* Update gitea.com/macaron to 1.4.0 (#9608)
* Upgrade lato fonts to v16. (#9498)
* Update alpine to 3.11 (#9440)
@@ -400,6 +412,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Update the provided gitea.service to mention socket activation (#8531)
* Doc added how to setup email (#8520)
* MISC
* Backport Locales [2020-01-14] (#9773)
* Add translatable Powered by Gitea text in footer (#9600)
* Add contrib/environment-to-ini (#9519)
* Remove unnecessary loading of settings in update hook (#9496)

View File

@@ -12,6 +12,7 @@ import (
"net/url"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"time"
@@ -72,6 +73,7 @@ var (
"git-receive-pack": models.AccessModeWrite,
lfsAuthenticateVerb: models.AccessModeNone,
}
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
)
func fail(userMessage, logMessage string, args ...interface{}) {
@@ -147,6 +149,10 @@ func runServ(c *cli.Context) error {
username := strings.ToLower(rr[0])
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
if alphaDashDotPattern.MatchString(reponame) {
fail("Invalid repo name", "Invalid repo name: %s", reponame)
}
if setting.EnablePprof || c.Bool("enable-pprof") {
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)

View File

@@ -18,7 +18,7 @@ params:
description: Git with a cup of tea
author: The Gitea Authors
website: https://docs.gitea.io
version: 1.10.2
version: 1.11.0
outputs:
home:

View File

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

View File

@@ -422,6 +422,9 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
tmpDir, err := ioutil.TempDir("", ctx.Reponame)
assert.NoError(t, err)
_, err = git.NewCommand("clone", u.String()).RunInDir(tmpDir)
assert.Error(t, err)
err = git.InitRepository(tmpDir, false)
assert.NoError(t, err)
@@ -449,6 +452,13 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
_, err = git.NewCommand("remote", "add", "origin", u.String()).RunInDir(tmpDir)
assert.NoError(t, err)
invalidCtx := ctx
invalidCtx.Reponame = fmt.Sprintf("invalid/repo-tmp-push-create-%s", u.Scheme)
u.Path = invalidCtx.GitPath()
_, err = git.NewCommand("remote", "add", "invalid", u.String()).RunInDir(tmpDir)
assert.NoError(t, err)
// Push to create disabled
setting.Repository.EnablePushCreateUser = false
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
@@ -456,6 +466,12 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
// Push to create enabled
setting.Repository.EnablePushCreateUser = true
// Invalid repo
_, err = git.NewCommand("push", "invalid", "master").RunInDir(tmpDir)
assert.Error(t, err)
// Valid repo
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
assert.NoError(t, err)

View File

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

View File

@@ -106,3 +106,57 @@ func TestPullCreate_TitleEscape(t *testing.T) {
assert.Equal(t, "&lt;u&gt;XSS PR&lt;/u&gt;", titleHTML)
})
}
func testUIDeleteBranch(t *testing.T, session *TestSession, ownerName, repoName, branchName string) {
relURL := "/" + path.Join(ownerName, repoName, "branches")
req := NewRequest(t, "GET", relURL)
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
req = NewRequestWithValues(t, "POST", relURL+"/delete", map[string]string{
"_csrf": getCsrf(t, htmlDoc.doc),
"name": branchName,
})
session.MakeRequest(t, req, http.StatusOK)
}
func testDeleteRepository(t *testing.T, session *TestSession, ownerName, repoName string) {
relURL := "/" + path.Join(ownerName, repoName, "settings")
req := NewRequest(t, "GET", relURL)
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
req = NewRequestWithValues(t, "POST", relURL+"?action=delete", map[string]string{
"_csrf": getCsrf(t, htmlDoc.doc),
"repo_name": repoName,
})
session.MakeRequest(t, req, http.StatusFound)
}
func TestPullBranchDelete(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
defer prepareTestEnv(t)()
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusFound)
testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", "master1", "This is a pull title")
// check the redirected URL
url := resp.HeaderMap.Get("Location")
assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
req := NewRequest(t, "GET", url)
session.MakeRequest(t, req, http.StatusOK)
// delete head branch and confirm pull page is ok
testUIDeleteBranch(t, session, "user1", "repo1", "master1")
req = NewRequest(t, "GET", url)
session.MakeRequest(t, req, http.StatusOK)
// delete head repository and confirm pull page is ok
testDeleteRepository(t, session, "user1", "repo1")
req = NewRequest(t, "GET", url)
session.MakeRequest(t, req, http.StatusOK)
})
}

View File

@@ -432,6 +432,8 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
}
cond = cond.And(builder.In("repo_id", repoIDs))
} else {
cond = cond.And(builder.In("repo_id", AccessibleRepoIDsQuery(opts.RequestingUserID)))
}
cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})

View File

@@ -6,6 +6,7 @@ package models
import (
"fmt"
"strings"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
@@ -95,6 +96,8 @@ func NewMilestone(m *Milestone) (err error) {
return err
}
m.Name = strings.TrimSpace(m.Name)
if _, err = sess.Insert(m); err != nil {
return err
}
@@ -268,6 +271,7 @@ func GetMilestones(repoID int64, page int, isClosed bool, sortType string) (Mile
}
func updateMilestone(e Engine, m *Milestone) error {
m.Name = strings.TrimSpace(m.Name)
_, err := e.ID(m.ID).AllCols().
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
builder.Eq{"milestone_id": m.ID},
@@ -283,12 +287,33 @@ func updateMilestone(e Engine, m *Milestone) error {
}
// UpdateMilestone updates information of given milestone.
func UpdateMilestone(m *Milestone) error {
if err := updateMilestone(x, m); err != nil {
func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
return updateMilestoneCompleteness(x, m.ID)
if m.IsClosed && !oldIsClosed {
m.ClosedDateUnix = timeutil.TimeStampNow()
}
if err := updateMilestone(sess, m); err != nil {
return err
}
if err := updateMilestoneCompleteness(sess, m.ID); err != nil {
return err
}
// if IsClosed changed, update milestone numbers of repository
if oldIsClosed != m.IsClosed {
if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
return err
}
}
return sess.Commit()
}
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {

View File

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

View File

@@ -68,7 +68,11 @@ type PullRequest struct {
// MustHeadUserName returns the HeadRepo's username if failed return blank
func (pr *PullRequest) MustHeadUserName() string {
if err := pr.LoadHeadRepo(); err != nil {
log.Error("LoadHeadRepo: %v", err)
if !IsErrRepoNotExist(err) {
log.Error("LoadHeadRepo: %v", err)
} else {
log.Warn("LoadHeadRepo %d but repository does not exist: %v", pr.HeadRepoID, err)
}
return ""
}
return pr.HeadRepo.MustOwnerName()
@@ -416,7 +420,7 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
err error
)
if err = pr.Issue.loadRepo(e); err != nil {
log.Error("loadRepo[%d]: %v", pr.ID, err)
log.Error("pr.Issue.loadRepo[%d]: %v", pr.ID, err)
return nil
}
apiIssue := pr.Issue.apiFormat(e)
@@ -427,19 +431,14 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
return nil
}
}
if pr.HeadRepo == nil {
if pr.HeadRepoID != 0 && pr.HeadRepo == nil {
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
if err != nil {
if err != nil && !IsErrRepoNotExist(err) {
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
return nil
}
}
if err = pr.Issue.loadRepo(e); err != nil {
log.Error("pr.Issue.loadRepo[%d]: %v", pr.ID, err)
return nil
}
apiPullRequest := &api.PullRequest{
ID: pr.ID,
URL: pr.Issue.HTMLURL(),
@@ -491,33 +490,41 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
apiPullRequest.Base = apiBaseBranchInfo
}
headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
if err != nil {
if git.IsErrBranchNotExist(err) {
apiPullRequest.Head = nil
} else {
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
return nil
}
} else {
apiHeadBranchInfo := &api.PRBranchInfo{
Name: pr.HeadBranch,
Ref: pr.HeadBranch,
RepoID: pr.HeadRepoID,
Repository: pr.HeadRepo.innerAPIFormat(e, AccessModeNone, false),
}
headCommit, err = headBranch.GetCommit()
if pr.HeadRepo != nil {
headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
if err != nil {
if git.IsErrNotExist(err) {
apiHeadBranchInfo.Sha = ""
if git.IsErrBranchNotExist(err) {
apiPullRequest.Head = nil
} else {
log.Error("GetCommit[%s]: %v", headBranch.Name, err)
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
return nil
}
} else {
apiHeadBranchInfo.Sha = headCommit.ID.String()
apiHeadBranchInfo := &api.PRBranchInfo{
Name: pr.HeadBranch,
Ref: pr.HeadBranch,
RepoID: pr.HeadRepoID,
Repository: pr.HeadRepo.innerAPIFormat(e, AccessModeNone, false),
}
headCommit, err = headBranch.GetCommit()
if err != nil {
if git.IsErrNotExist(err) {
apiHeadBranchInfo.Sha = ""
} else {
log.Error("GetCommit[%s]: %v", headBranch.Name, err)
return nil
}
} else {
apiHeadBranchInfo.Sha = headCommit.ID.String()
}
apiPullRequest.Head = apiHeadBranchInfo
}
} else {
apiPullRequest.Head = &api.PRBranchInfo{
Name: pr.HeadBranch,
Ref: fmt.Sprintf("refs/pull/%d/head", pr.Index),
RepoID: -1,
}
apiPullRequest.Head = apiHeadBranchInfo
}
if pr.Status != PullRequestStatusChecking {

View File

@@ -204,6 +204,14 @@ type Repository struct {
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
// SanitizedOriginalURL returns a sanitized OriginalURL
func (repo *Repository) SanitizedOriginalURL() string {
if repo.OriginalURL == "" {
return ""
}
return util.SanitizeURLCredentials(repo.OriginalURL, false)
}
// ColorFormat returns a colored string to represent this repo
func (repo *Repository) ColorFormat(s fmt.State) {
var ownerName interface{}
@@ -1902,6 +1910,12 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return err
}
if len(repo.Topics) > 0 {
if err = removeTopicsFromRepo(sess, repo.ID); err != nil {
return err
}
}
// FIXME: Remove repository files should be executed after transaction succeed.
repoPath := repo.repoPath(sess)
removeAllWithNotice(sess, "Delete repository files", repoPath)

View File

@@ -315,6 +315,17 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
func accessibleRepositoryCondition(userID int64) builder.Cond {
if userID <= 0 {
return builder.And(
builder.Eq{"`repository`.is_private": false},
builder.Or(
// A. Aren't in organisations __OR__
builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})),
// B. Is a public organisation.
builder.In("`repository`.owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"visibility": structs.VisibleTypePublic}))),
)
}
return builder.Or(
// 1. Be able to see all non-private repositories that either:
builder.And(
@@ -349,6 +360,12 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
return SearchRepository(opts)
}
// AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered.
func AccessibleRepoIDsQuery(userID int64) *builder.Builder {
// NB: Please note this code needs to still work if user is nil
return builder.Select("id").From("repository").Where(accessibleRepositoryCondition(userID))
}
// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
func FindUserAccessibleRepoIDs(userID int64) ([]int64, error) {
var accessCond builder.Cond = builder.Eq{"is_private": false}

View File

@@ -129,7 +129,7 @@ func addTopicByNameToRepo(e Engine, repoID int64, topicName string) (*Topic, err
}
// removeTopicFromRepo remove a topic from a repo and decrements the topic repo count
func removeTopicFromRepo(repoID int64, topic *Topic, e Engine) error {
func removeTopicFromRepo(e Engine, repoID int64, topic *Topic) error {
topic.RepoCount--
if _, err := e.ID(topic.ID).Cols("repo_count").Update(topic); err != nil {
return err
@@ -145,6 +145,24 @@ func removeTopicFromRepo(repoID int64, topic *Topic, e Engine) error {
return nil
}
// removeTopicsFromRepo remove all topics from the repo and decrements respective topics repo count
func removeTopicsFromRepo(e Engine, repoID int64) error {
_, err := e.Where(
builder.In("id",
builder.Select("topic_id").From("repo_topic").Where(builder.Eq{"repo_id": repoID}),
),
).Cols("repo_count").SetExpr("repo_count", "repo_count-1").Update(&Topic{})
if err != nil {
return err
}
if _, err = e.Delete(&RepoTopic{RepoID: repoID}); err != nil {
return err
}
return nil
}
// FindTopicOptions represents the options when fdin topics
type FindTopicOptions struct {
RepoID int64
@@ -217,7 +235,7 @@ func DeleteTopic(repoID int64, topicName string) (*Topic, error) {
return nil, nil
}
err = removeTopicFromRepo(repoID, topic, x)
err = removeTopicFromRepo(x, repoID, topic)
return topic, err
}
@@ -278,7 +296,7 @@ func SaveTopics(repoID int64, topicNames ...string) error {
}
for _, topic := range removeTopics {
err := removeTopicFromRepo(repoID, topic, sess)
err := removeTopicFromRepo(sess, repoID, topic)
if err != nil {
return err
}

View File

@@ -12,6 +12,7 @@ import (
"os"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
@@ -215,9 +216,12 @@ func (wl *wrappedListener) Accept() (net.Conn, error) {
}
}
closed := int32(0)
c = wrappedConn{
Conn: c,
server: wl.server,
closed: &closed,
}
wl.server.wg.Add(1)
@@ -241,12 +245,12 @@ func (wl *wrappedListener) File() (*os.File, error) {
type wrappedConn struct {
net.Conn
server *Server
closed *int32
}
func (w wrappedConn) Close() error {
err := w.Conn.Close()
if err == nil {
if atomic.CompareAndSwapInt32(w.closed, 0, 1) {
w.server.wg.Done()
}
return err
return w.Conn.Close()
}

View File

@@ -116,7 +116,12 @@ func nonGenesisChanges(repo *models.Repository, revision string) (*repoChanges,
if len(line) == 0 {
continue
}
filename := strings.TrimSpace(line[1:])
fields := strings.Split(line, "\t")
if len(fields) < 2 {
log.Warn("Unparseable output for diff --name-status: `%s`)", line)
continue
}
filename := fields[1]
if len(filename) == 0 {
continue
} else if filename[0] == '"' {
@@ -126,11 +131,31 @@ func nonGenesisChanges(repo *models.Repository, revision string) (*repoChanges,
}
}
switch status := line[0]; status {
switch status := fields[0][0]; status {
case 'M', 'A':
updatedFilenames = append(updatedFilenames, filename)
case 'D':
changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
case 'R', 'C':
if len(fields) < 3 {
log.Warn("Unparseable output for diff --name-status: `%s`)", line)
continue
}
dest := fields[2]
if len(dest) == 0 {
log.Warn("Unparseable output for diff --name-status: `%s`)", line)
continue
}
if dest[0] == '"' {
dest, err = strconv.Unquote(dest)
if err != nil {
return nil, err
}
}
if status == 'R' {
changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
}
updatedFilenames = append(updatedFilenames, dest)
default:
log.Warn("Unrecognized status: %c (line=%s)", status, line)
}

View File

@@ -6,6 +6,7 @@ package indexer
import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/log"
@@ -118,7 +119,7 @@ func (r *indexerNotifier) NotifyMigrateRepository(doer *models.User, u *models.U
}
func (r *indexerNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *models.PushCommits) {
if setting.Indexer.RepoIndexerEnabled && refName == repo.DefaultBranch {
if setting.Indexer.RepoIndexerEnabled && refName == git.BranchPrefix+repo.DefaultBranch {
code_indexer.UpdateRepoIndexer(repo)
}
}

View File

@@ -475,9 +475,18 @@ func PushUpdate(repo *models.Repository, branch string, opts PushUpdateOptions)
return err
}
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
if !isDelRef {
if err = models.RemoveDeletedBranch(repo.ID, opts.Branch); err != nil {
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, opts.Branch, err)
}
go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true)
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true)
// close all related pulls
} else if err = pull_service.CloseBranchPulls(pusher, repo.ID, branch); err != nil {
log.Error("close related pull request failed: %v", err)
}
if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil {
log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
@@ -524,12 +533,15 @@ func PushUpdates(repo *models.Repository, optsList []*PushUpdateOptions) error {
if err = models.RemoveDeletedBranch(repo.ID, opts.Branch); err != nil {
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, opts.Branch, err)
}
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, opts.Branch, pusher.Name)
go pull_service.AddTestPullRequestTask(pusher, repo.ID, opts.Branch, true)
// close all related pulls
} else if err = pull_service.CloseBranchPulls(pusher, repo.ID, opts.Branch); err != nil {
log.Error("close related pull request failed: %v", err)
}
log.Trace("TriggerTask '%s/%s' by %s", repo.Name, opts.Branch, pusher.Name)
go pull_service.AddTestPullRequestTask(pusher, repo.ID, opts.Branch, true)
if err = models.WatchIfAuto(opts.PusherID, repo.ID, true); err != nil {
log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
}

View File

@@ -7,6 +7,8 @@ package util
import (
"net/url"
"strings"
"code.gitea.io/gitea/modules/log"
)
// urlSafeError wraps an error whose message may contain a sensitive URL
@@ -36,6 +38,7 @@ func SanitizeMessage(message, unsanitizedURL string) string {
func SanitizeURLCredentials(unsanitizedURL string, usePlaceholder bool) string {
u, err := url.Parse(unsanitizedURL)
if err != nil {
log.Error("parse url %s failed: %v", unsanitizedURL, err)
// don't log the error, since it might contain unsanitized URL.
return "(unparsable url)"
}

View File

@@ -0,0 +1,25 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package util
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSanitizeURLCredentials(t *testing.T) {
var kases = map[string]string{
"https://github.com/go-gitea/test_repo.git": "https://github.com/go-gitea/test_repo.git",
"https://mytoken@github.com/go-gitea/test_repo.git": "https://github.com/go-gitea/test_repo.git",
"http://github.com/go-gitea/test_repo.git": "http://github.com/go-gitea/test_repo.git",
"/test/repos/repo1": "/test/repos/repo1",
"git@github.com:go-gitea/test_repo.git": "(unparsable url)",
}
for source, value := range kases {
assert.EqualValues(t, value, SanitizeURLCredentials(source, false))
}
}

View File

@@ -7,6 +7,7 @@ package admin
import (
"errors"
"fmt"
"net/http"
"code.gitea.io/gitea/models"
@@ -226,6 +227,11 @@ func DeleteUser(ctx *context.APIContext) {
return
}
if u.IsOrganization() {
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", u.Name))
return
}
if err := models.DeleteUser(u); err != nil {
if models.IsErrUserOwnRepos(err) ||
models.IsErrUserHasOrgs(err) {

View File

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

View File

@@ -115,7 +115,7 @@ func SettingsDeleteAvatar(ctx *context.Context) {
ctx.Redirect(ctx.Org.OrgLink + "/settings")
}
// SettingsDelete response for delete repository
// SettingsDelete response for deleting an organization
func SettingsDelete(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("org.settings")
ctx.Data["PageIsSettingsDelete"] = true

View File

@@ -68,7 +68,6 @@ func ServNoCommand(ctx *macaron.Context) {
// ServCommand returns information about the provided keyid
func ServCommand(ctx *macaron.Context) {
// Although we provide the verbs we don't need them at present they're just for logging purposes
keyID := ctx.ParamsInt64(":keyid")
ownerName := ctx.Params(":owner")
repoName := ctx.Params(":repo")
@@ -105,6 +104,17 @@ func ServCommand(ctx *macaron.Context) {
if err != nil {
if models.IsErrRepoNotExist(err) {
repoExist = false
for _, verb := range ctx.QueryStrings("verb") {
if "git-upload-pack" == verb {
// User is fetching/cloning a non-existent repository
ctx.JSON(http.StatusNotFound, map[string]interface{}{
"results": results,
"type": "ErrRepoNotExist",
"err": fmt.Sprintf("Cannot find repository: %s/%s", results.OwnerName, results.RepoName),
})
return
}
}
} else {
log.Error("Unable to get repository: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{

View File

@@ -238,6 +238,7 @@ func loadBranches(ctx *context.Context) []*Branch {
} else {
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
}
pr.Issue.Repo = pr.BaseRepo
if pr.HasMerged {
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
@@ -260,7 +261,6 @@ func loadBranches(ctx *context.Context) []*Branch {
mergeMovedOn = true
}
}
}
isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName

View File

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

View File

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

View File

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

View File

@@ -475,12 +475,7 @@ func RegisterRoutes(m *macaron.Macaron) {
// ***** END: Admin *****
m.Group("", func() {
m.Group("/:username", func() {
m.Get("", user.Profile)
m.Get("/followers", user.Followers)
m.Get("/following", user.Following)
})
m.Get("/:username", user.Profile)
m.Get("/attachments/:uuid", repo.GetAttachment)
}, ignSignIn)

View File

@@ -142,11 +142,17 @@ func Dashboard(ctx *context.Context) {
ctx.Data["MirrorCount"] = len(mirrors)
ctx.Data["Mirrors"] = mirrors
requestingUserID := int64(0)
if ctx.User != nil {
requestingUserID = ctx.User.ID
}
retrieveFeeds(ctx, models.GetFeedsOptions{
RequestedUser: ctxUser,
IncludePrivate: true,
OnlyPerformedBy: false,
IncludeDeleted: false,
RequestedUser: ctxUser,
RequestingUserID: requestingUserID,
IncludePrivate: true,
OnlyPerformedBy: false,
IncludeDeleted: false,
})
if ctx.Written() {
@@ -519,13 +525,17 @@ func Issues(ctx *context.Context) {
}
}
issueStats, err := models.GetUserIssueStats(models.UserIssueStatsOptions{
issueStatsOpts := models.UserIssueStatsOptions{
UserID: ctxUser.ID,
UserRepoIDs: userRepoIDs,
FilterMode: filterMode,
IsPull: isPullList,
IsClosed: isShowClosed,
})
}
if len(repoIDs) > 0 {
issueStatsOpts.UserRepoIDs = repoIDs
}
issueStats, err := models.GetUserIssueStats(issueStatsOpts)
if err != nil {
ctx.ServerError("GetUserIssueStats", err)
return

View File

@@ -11,16 +11,10 @@ import (
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/org"
"code.gitea.io/gitea/routers/repo"
)
const (
tplFollowers base.TplName = "user/meta/followers"
)
// GetUserByName get user by name
@@ -156,14 +150,38 @@ func Profile(ctx *context.Context) {
orderBy = models.SearchOrderByRecentUpdated
}
requestingUserID := int64(0)
if ctx.User != nil {
requestingUserID = ctx.User.ID
}
keyword := strings.Trim(ctx.Query("q"), " ")
ctx.Data["Keyword"] = keyword
switch tab {
case "followers":
items, err := ctxUser.GetFollowers(page)
if err != nil {
ctx.ServerError("GetFollowers", err)
return
}
ctx.Data["Cards"] = items
total = ctxUser.NumFollowers
case "following":
items, err := ctxUser.GetFollowing(page)
if err != nil {
ctx.ServerError("GetFollowing", err)
return
}
ctx.Data["Cards"] = items
total = ctxUser.NumFollowing
case "activity":
retrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser,
IncludePrivate: showPrivate,
OnlyPerformedBy: true,
IncludeDeleted: false,
RequestingUserID: requestingUserID,
IncludePrivate: showPrivate,
OnlyPerformedBy: true,
IncludeDeleted: false,
})
if ctx.Written() {
return
@@ -223,32 +241,6 @@ func Profile(ctx *context.Context) {
ctx.HTML(200, tplProfile)
}
// Followers render user's followers page
func Followers(ctx *context.Context) {
u := GetUserByParams(ctx)
if ctx.Written() {
return
}
ctx.Data["Title"] = u.DisplayName()
ctx.Data["CardsTitle"] = ctx.Tr("user.followers")
ctx.Data["PageIsFollowers"] = true
ctx.Data["Owner"] = u
repo.RenderUserCards(ctx, u.NumFollowers, u.GetFollowers, tplFollowers)
}
// Following render user's followering page
func Following(ctx *context.Context) {
u := GetUserByParams(ctx)
if ctx.Written() {
return
}
ctx.Data["Title"] = u.DisplayName()
ctx.Data["CardsTitle"] = ctx.Tr("user.following")
ctx.Data["PageIsFollowing"] = true
ctx.Data["Owner"] = u
repo.RenderUserCards(ctx, u.NumFollowing, u.GetFollowing, tplFollowers)
}
// Action response for follow/unfollow user request
func Action(ctx *context.Context) {
u := GetUserByParams(ctx)

View File

@@ -213,7 +213,7 @@ func (s *sendmailSender) Send(from string, to []string, msg io.WriterTo) error {
var closeError error
var waitError error
args := []string{"-F", from, "-i"}
args := []string{"-f", from, "-i"}
args = append(args, setting.MailService.SendmailArgs...)
args = append(args, to...)
log.Trace("Sending with: %s %v", setting.MailService.SendmailPath, args)

View File

@@ -9,6 +9,7 @@ import (
"fmt"
"os"
"path"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
@@ -217,23 +218,39 @@ func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSy
func PushToBaseRepo(pr *models.PullRequest) (err error) {
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName())
// Clone base repo.
tmpBasePath, err := models.CreateTemporaryPath("pull")
if err != nil {
log.Error("CreateTemporaryPath: %v", err)
return err
}
defer func() {
err := models.RemoveTemporaryPath(tmpBasePath)
if err != nil {
log.Error("Error whilst removing temporary path: %s Error: %v", tmpBasePath, err)
}
}()
headRepoPath := pr.HeadRepo.RepoPath()
headGitRepo, err := git.OpenRepository(headRepoPath)
if err := git.Clone(headRepoPath, tmpBasePath, git.CloneRepoOptions{
Bare: true,
Shared: true,
Branch: pr.HeadBranch,
Quiet: true,
}); err != nil {
log.Error("git clone tmpBasePath: %v", err)
return err
}
gitRepo, err := git.OpenRepository(tmpBasePath)
if err != nil {
return fmt.Errorf("OpenRepository: %v", err)
}
defer headGitRepo.Close()
tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID)
if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil {
return fmt.Errorf("headGitRepo.AddRemote: %v", err)
if err := gitRepo.AddRemote("base", pr.BaseRepo.RepoPath(), false); err != nil {
return fmt.Errorf("tmpGitRepo.AddRemote: %v", err)
}
// Make sure to remove the remote even if the push fails
defer func() {
if err := headGitRepo.RemoveRemote(tmpRemoteName); err != nil {
log.Error("PushToBaseRepo: RemoveRemote: %s", err)
}
}()
defer gitRepo.Close()
headFile := pr.GetGitRefName()
@@ -249,15 +266,91 @@ func PushToBaseRepo(pr *models.PullRequest) (err error) {
return fmt.Errorf("unable to load poster %d for pr %d: %v", pr.Issue.PosterID, pr.ID, err)
}
if err = git.Push(headRepoPath, git.PushOptions{
Remote: tmpRemoteName,
if err = git.Push(tmpBasePath, git.PushOptions{
Remote: "base",
Branch: fmt.Sprintf("%s:%s", pr.HeadBranch, headFile),
Force: true,
// Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/...
Env: models.InternalPushingEnvironment(pr.Issue.Poster, pr.BaseRepo),
}); err != nil {
return fmt.Errorf("Push: %v", err)
return fmt.Errorf("Push: %s:%s %s:%s %v", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), headFile, err)
}
return nil
}
type errlist []error
func (errs errlist) Error() string {
if len(errs) > 0 {
var buf strings.Builder
for i, err := range errs {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(err.Error())
}
return buf.String()
}
return ""
}
// CloseBranchPulls close all the pull requests who's head branch is the branch
func CloseBranchPulls(doer *models.User, repoID int64, branch string) error {
prs, err := models.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
if err != nil {
return err
}
prs2, err := models.GetUnmergedPullRequestsByBaseInfo(repoID, branch)
if err != nil {
return err
}
prs = append(prs, prs2...)
if err := models.PullRequestList(prs).LoadAttributes(); err != nil {
return err
}
var errs errlist
for _, pr := range prs {
if err = issue_service.ChangeStatus(pr.Issue, doer, true); err != nil && !models.IsErrIssueWasClosed(err) {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return errs
}
return nil
}
// CloseRepoBranchesPulls close all pull requests which head branches are in the given repository
func CloseRepoBranchesPulls(doer *models.User, repo *models.Repository) error {
branches, err := git.GetBranchesByPath(repo.RepoPath())
if err != nil {
return err
}
var errs errlist
for _, branch := range branches {
prs, err := models.GetUnmergedPullRequestsByHeadInfo(repo.ID, branch.Name)
if err != nil {
return err
}
if err = models.PullRequestList(prs).LoadAttributes(); err != nil {
return err
}
for _, pr := range prs {
if err = issue_service.ChangeStatus(pr.Issue, doer, true); err != nil && !models.IsErrIssueWasClosed(err) {
errs = append(errs, err)
}
}
}
if len(errs) > 0 {
return errs
}
return nil
}

View File

@@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
pull_service "code.gitea.io/gitea/services/pull"
)
// CreateRepository creates a repository for the user/organization.
@@ -48,6 +49,10 @@ func ForkRepository(doer, u *models.User, oldRepo *models.Repository, name, desc
// DeleteRepository deletes a repository for a user or organization.
func DeleteRepository(doer *models.User, repo *models.Repository) error {
if err := pull_service.CloseRepoBranchesPulls(doer, repo); err != nil {
log.Error("CloseRepoBranchesPulls failed: %v", err)
}
if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
return err
}

View File

@@ -73,7 +73,7 @@
</div>
{{end}}
</td>
<td class="two wide right aligned">
<td class="three wide right aligned">
{{if not .LatestPullRequest}}
{{if .IsIncluded}}
<a class="ui poping up orange small label" data-content="{{$.i18n.Tr "repo.branch.included_desc"}}" data-variation="tiny inverted" data-position="top right">
@@ -91,13 +91,13 @@
</a>
{{end}}
{{else}}
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}">#{{.LatestPullRequest.Issue.Index}}</a>
<a href="{{.LatestPullRequest.Issue.HTMLURL}}">{{if ne .LatestPullRequest.BaseRepoID .LatestPullRequest.HeadRepoID}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
{{if .LatestPullRequest.HasMerged}}
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui purple small label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a>
<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui purple mini label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a>
{{else if .LatestPullRequest.Issue.IsClosed}}
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui red small label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a>
<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui red mini label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a>
{{else}}
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui green small label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a>
<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui green mini label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a>
{{end}}
{{end}}
</td>

View File

@@ -14,7 +14,7 @@
{{if and .RelAvatarLink .IsPrivate}}<i class="mega-octicon octicon-lock"></i>{{end}}
{{if .IsTemplate}}<i class="icon fa-copy"></i>{{end}}
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}}
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{MirrorAddress $.Mirror}}">{{MirrorAddress $.Mirror}}</a></div>{{end}}
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}">{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}</a></div>{{end}}
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{SubStr .BaseRepo.RelLink 1 -1}}</a></div>{{end}}
{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}}
</div>

View File

@@ -336,7 +336,7 @@
{{if .Content}}
<div class="detail">
<span class="octicon octicon-quote"></span>
<span class="text grey">{{.Content}}</span>
<span class="text grey has-emoji">{{.Content}}</span>
</div>
{{end}}
{{ range $filename, $lines := .Review.CodeComments}}

View File

@@ -20,6 +20,8 @@
<div class="ui purple large label"><i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls.merged"}}</div>
{{else if .Issue.IsClosed}}
<div class="ui red large label"><i class="octicon octicon-issue-closed"></i> {{.i18n.Tr "repo.issues.closed_title"}}</div>
{{else if .Issue.IsPull}}
<div class="ui green large label"><i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
{{else}}
<div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
{{end}}

View File

@@ -1,7 +1,9 @@
<div class="ui container user-cards">
{{if .CardsTitle}}
<h2 class="ui dividing header">
{{.CardsTitle}}
</h2>
{{end}}
<ul class="list">
{{range .Cards}}
<li class="item ui segment">

View File

@@ -89,7 +89,7 @@
{{if .Repo}}
<li class="item">
<div class="ui label">{{.Repo.FullName}}#{{.Index}}</div>
<a class="title has-emoji" href="{{AppSubUrl}}/{{.Repo.Owner.Name}}/{{.Repo.Name}}/issues/{{.Index}}">{{.Title}}</a>
<a class="title has-emoji" href="{{.HTMLURL}}">{{.Title}}</a>
{{if .IsPull }}
{{if (index $.CommitStatus .PullRequest.ID)}}

View File

@@ -1,6 +0,0 @@
{{template "base/head" .}}
<div class="user followers">
{{template "user/meta/header" .}}
{{template "repo/user_cards" .}}
</div>
{{template "base/footer" .}}

View File

@@ -1,24 +0,0 @@
{{with .Owner}}
<div class="ui container">
<img class="ui avatar image" src="{{.RelAvatarLink}}">
<span class="header name">
<a href="{{.HomeLink}}">{{.Name}}</a>
{{with .FullName}}({{.}}){{end}}
</span>
<div class="ui right">
{{if or $.PageIsFollowers $.PageIsFollowing}}
{{if and $.IsSigned (ne $.SignedUserName .Name)}}
<div class="follow">
{{if $.SignedUser.IsFollowing .ID}}
<a class="ui small basic red button" href="{{.HomeLink}}/action/unfollow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{$.i18n.Tr "user.unfollow"}}</a>
{{else}}
<a class="ui small basic green button" href="{{.HomeLink}}/action/follow?redirect_to={{$.Link}}"><i class="octicon octicon-person"></i> {{$.i18n.Tr "user.follow"}}</a>
{{end}}
</div>
{{end}}
{{end}}
</div>
</div>
{{end}}
<div class="ui divider"></div>

View File

@@ -49,24 +49,6 @@
{{end}}
{{end}}
<li><i class="octicon octicon-clock"></i> {{.i18n.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}}</li>
<li>
<i class="octicon octicon-person"></i>
<a href="{{.Owner.HomeLink}}/followers">
{{.Owner.NumFollowers}} {{.i18n.Tr "user.followers"}}
</a>
-
<a href="{{.Owner.HomeLink}}/following">
{{.Owner.NumFollowing}} {{.i18n.Tr "user.following"}}
</a>
</li>
{{/*
<li>
<i class="octicon octicon-star"></i>
<a href="{{.Owner.HomeLink}}/stars">
{{.Owner.NumStars}} {{.i18n.Tr "user.starred"}}
</a>
</li>
*/}}
{{if and .Orgs .HasOrgsVisible}}
<li>
<ul class="user-orgs">
@@ -95,7 +77,7 @@
</div>
<div class="ui eleven wide column">
<div class="ui secondary stackable pointing menu">
<a class='{{if and (ne .TabName "activity") (ne .TabName "stars")}}active{{end}} item' href="{{.Owner.HomeLink}}">
<a class='{{if and (ne .TabName "activity") (ne .TabName "following") (ne .TabName "followers") (ne .TabName "stars")}}active{{end}} item' href="{{.Owner.HomeLink}}">
<i class="octicon octicon-repo"></i> {{.i18n.Tr "user.repositories"}}
</a>
<a class='{{if eq .TabName "activity"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=activity">
@@ -103,6 +85,15 @@
</a>
<a class='{{if eq .TabName "stars"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=stars">
<i class="octicon octicon-star"></i> {{.i18n.Tr "user.starred"}}
<div class="ui label">{{.Owner.NumStars}}</div>
</a>
<a class='{{if eq .TabName "following"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=following">
<i class="octicon octicon-person"></i> {{.i18n.Tr "user.following"}}
<div class="ui label">{{.Owner.NumFollowing}}</div>
</a>
<a class='{{if eq .TabName "followers"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=followers">
<i class="octicon octicon-person"></i> {{.i18n.Tr "user.followers"}}
<div class="ui label">{{.Owner.NumFollowers}}</div>
</a>
</div>
@@ -126,6 +117,10 @@
{{template "explore/repo_list" .}}
{{template "base/paginate" .}}
</div>
{{else if eq .TabName "following"}}
{{template "repo/user_cards" .}}
{{else if eq .TabName "followers"}}
{{template "repo/user_cards" .}}
{{else}}
{{template "explore/repo_search" .}}
{{template "explore/repo_list" .}}

View File

@@ -1362,7 +1362,16 @@ function initWikiForm() {
}, '|',
'unordered-list', 'ordered-list', '|',
'link', 'image', 'table', 'horizontal-rule', '|',
'clean-block', 'preview', 'fullscreen', 'side-by-side']
'clean-block', 'preview', 'fullscreen', 'side-by-side', '|',
{
name: 'revert-to-textarea',
action(e) {
e.toTextArea();
},
className: 'fa fa-file',
title: 'Revert to simple textarea',
},
]
});
$(simplemde.codemirror.getInputField()).addClass('js-quick-submit');
@@ -1466,7 +1475,16 @@ function setSimpleMDE($editArea) {
'code', 'quote', '|',
'unordered-list', 'ordered-list', '|',
'link', 'image', 'table', 'horizontal-rule', '|',
'clean-block', 'preview', 'fullscreen', 'side-by-side']
'clean-block', 'preview', 'fullscreen', 'side-by-side', '|',
{
name: 'revert-to-textarea',
action(e) {
e.toTextArea();
},
className: 'fa fa-file',
title: 'Revert to simple textarea',
},
]
});
return true;
@@ -1488,7 +1506,16 @@ function setCommentSimpleMDE($editArea) {
'code', 'quote', '|',
'unordered-list', 'ordered-list', '|',
'link', 'image', 'table', 'horizontal-rule', '|',
'clean-block']
'clean-block', '|',
{
name: 'revert-to-textarea',
action(e) {
e.toTextArea();
},
className: 'fa fa-file',
title: 'Revert to simple textarea',
},
]
});
simplemde.codemirror.setOption('extraKeys', {
Enter: () => {

View File

@@ -1111,6 +1111,9 @@ input {
.lines-code .hljs {
background-color: #2a2e3a !important;
}
td.blob-excerpt {
background-color: rgba(0, 0, 0, 0.15);
}
.code-view .active {
background: #554a00;