mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-08 05:02:38 +09:00
Compare commits
24 Commits
v1.11.0-rc
...
v1.11.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9169b39458 | ||
|
|
80eb50655a | ||
|
|
b16c555541 | ||
|
|
b5b44364e3 | ||
|
|
6af58022c8 | ||
|
|
e48b460a0a | ||
|
|
2cd2614eaa | ||
|
|
0129e76ef5 | ||
|
|
6896dad675 | ||
|
|
1ed4323005 | ||
|
|
049af0d3d0 | ||
|
|
f5727d83dd | ||
|
|
912ce27421 | ||
|
|
b3549bb5ec | ||
|
|
491cbeca67 | ||
|
|
895d92ffe5 | ||
|
|
4b11f967bd | ||
|
|
1e73dd2446 | ||
|
|
315026c2c5 | ||
|
|
16dfd9ffbe | ||
|
|
16f7b43903 | ||
|
|
043febdbc9 | ||
|
|
60f91d56f0 | ||
|
|
16fc15ae6a |
@@ -1,4 +1,14 @@
|
|||||||
|
# The full repository name
|
||||||
repo: go-gitea/gitea
|
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:
|
groups:
|
||||||
-
|
-
|
||||||
name: BREAKING
|
name: BREAKING
|
||||||
@@ -42,3 +52,6 @@ groups:
|
|||||||
-
|
-
|
||||||
name: MISC
|
name: MISC
|
||||||
default: true
|
default: true
|
||||||
|
|
||||||
|
# regex indicating which labels to skip for the changelog
|
||||||
|
skip-labels: skip-changelog|backport\/.+
|
||||||
|
|||||||
139
CHANGELOG.md
139
CHANGELOG.md
@@ -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
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||||
|
|
||||||
## [1.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
|
* BREAKING
|
||||||
|
* Fix followers and following tabs in profile (#10202) (#10203)
|
||||||
* Make CertFile and KeyFile relative to CustomPath (#9868) (#9874)
|
* 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)
|
* Remove unused endpoints (#9538)
|
||||||
* Prefix all user-generated IDs in markup (#9477)
|
* Prefix all user-generated IDs in markup (#9477)
|
||||||
* Enforce Gitea environment for pushes (#8982)
|
* 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)
|
* 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
|
* FEATURES
|
||||||
* Webhooks should only show sender if it makes sense (#9601)
|
* Webhooks should only show sender if it makes sense (#9601)
|
||||||
* Provide Default messages for merges (#9393)
|
* 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)
|
* Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631)
|
||||||
* Add basic repository lfs management (#7199)
|
* Add basic repository lfs management (#7199)
|
||||||
* BUGFIXES
|
* 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)
|
* Disable remove button on repository teams when have access to all (#9640)
|
||||||
* Clean up old references on branch delete (#9614)
|
* Clean up old references on branch delete (#9614)
|
||||||
* Hide public repos owned by private orgs (#9609)
|
* 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 migrate mirror 500 bug (#8526)
|
||||||
* Fix password complexity regex for special characters (on master) (#8525)
|
* Fix password complexity regex for special characters (on master) (#8525)
|
||||||
* ENHANCEMENTS
|
* 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)
|
* Add a /user/login landing page option (#9622)
|
||||||
* Some more e-mail notification fixes (#9596)
|
* Some more e-mail notification fixes (#9596)
|
||||||
* Add branch protection option to block merge on requested changes. (#9592)
|
* 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)
|
* wiki - add 'write' 'preview' buttons to wiki edit like in issues (#7241)
|
||||||
* Change target branch for pull request (#6488)
|
* Change target branch for pull request (#6488)
|
||||||
* Display PR commits and diffs using base repo rather than forked (#3648)
|
* 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
|
* TESTING
|
||||||
* Add debug option to serv to help debug problems (#9492)
|
* Add debug option to serv to help debug problems (#9492)
|
||||||
* Fix the intermittent TestGPGGit failures (#9360)
|
* 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 Github Migration Tests (#8893) (#8938)
|
||||||
* Update heatmap fixtures to restore tests (#8615)
|
* Update heatmap fixtures to restore tests (#8615)
|
||||||
* TRANSLATION
|
* TRANSLATION
|
||||||
|
* Fix Korean locales (#9761) (#9780)
|
||||||
* Fix placeholders in the error message (#9060)
|
* Fix placeholders in the error message (#9060)
|
||||||
* Fix spelling of admin.users.max_repo_creation (#8934)
|
* Fix spelling of admin.users.max_repo_creation (#8934)
|
||||||
* Improve german translation of homepage (#8549)
|
* Improve german translation of homepage (#8549)
|
||||||
* BUILD
|
* BUILD
|
||||||
|
* Fix webpack polyfills (#9735) (#9738)
|
||||||
* Update gitea.com/macaron to 1.4.0 (#9608)
|
* Update gitea.com/macaron to 1.4.0 (#9608)
|
||||||
* Upgrade lato fonts to v16. (#9498)
|
* Upgrade lato fonts to v16. (#9498)
|
||||||
* Update alpine to 3.11 (#9440)
|
* 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)
|
* Update the provided gitea.service to mention socket activation (#8531)
|
||||||
* Doc added how to setup email (#8520)
|
* Doc added how to setup email (#8520)
|
||||||
* MISC
|
* MISC
|
||||||
|
* Backport Locales [2020-01-14] (#9773)
|
||||||
* Add translatable Powered by Gitea text in footer (#9600)
|
* Add translatable Powered by Gitea text in footer (#9600)
|
||||||
* Add contrib/environment-to-ini (#9519)
|
* Add contrib/environment-to-ini (#9519)
|
||||||
* Remove unnecessary loading of settings in update hook (#9496)
|
* Remove unnecessary loading of settings in update hook (#9496)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -72,6 +73,7 @@ var (
|
|||||||
"git-receive-pack": models.AccessModeWrite,
|
"git-receive-pack": models.AccessModeWrite,
|
||||||
lfsAuthenticateVerb: models.AccessModeNone,
|
lfsAuthenticateVerb: models.AccessModeNone,
|
||||||
}
|
}
|
||||||
|
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func fail(userMessage, logMessage string, args ...interface{}) {
|
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||||
@@ -147,6 +149,10 @@ func runServ(c *cli.Context) error {
|
|||||||
username := strings.ToLower(rr[0])
|
username := strings.ToLower(rr[0])
|
||||||
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
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 setting.EnablePprof || c.Bool("enable-pprof") {
|
||||||
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
|
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)
|
fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ params:
|
|||||||
description: Git with a cup of tea
|
description: Git with a cup of tea
|
||||||
author: The Gitea Authors
|
author: The Gitea Authors
|
||||||
website: https://docs.gitea.io
|
website: https://docs.gitea.io
|
||||||
version: 1.10.2
|
version: 1.11.0
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
home:
|
home:
|
||||||
|
|||||||
47
integrations/api_issue_milestone_test.go
Normal file
47
integrations/api_issue_milestone_test.go
Normal 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)
|
||||||
|
}
|
||||||
@@ -422,6 +422,9 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
|
|||||||
tmpDir, err := ioutil.TempDir("", ctx.Reponame)
|
tmpDir, err := ioutil.TempDir("", ctx.Reponame)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = git.NewCommand("clone", u.String()).RunInDir(tmpDir)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
err = git.InitRepository(tmpDir, false)
|
err = git.InitRepository(tmpDir, false)
|
||||||
assert.NoError(t, err)
|
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)
|
_, err = git.NewCommand("remote", "add", "origin", u.String()).RunInDir(tmpDir)
|
||||||
assert.NoError(t, err)
|
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
|
// Push to create disabled
|
||||||
setting.Repository.EnablePushCreateUser = false
|
setting.Repository.EnablePushCreateUser = false
|
||||||
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
|
_, 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
|
// Push to create enabled
|
||||||
setting.Repository.EnablePushCreateUser = true
|
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)
|
_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
|||||||
0cf15c3f66ec8384480ed9c3cf87c9e97fbb0ec3
|
423313fbd38093bb10d0c8387db9105409c6f196
|
||||||
|
|||||||
@@ -106,3 +106,57 @@ func TestPullCreate_TitleEscape(t *testing.T) {
|
|||||||
assert.Equal(t, "<u>XSS PR</u>", titleHTML)
|
assert.Equal(t, "<u>XSS PR</u>", 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -432,6 +432,8 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cond = cond.And(builder.In("repo_id", repoIDs))
|
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})
|
cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
@@ -95,6 +96,8 @@ func NewMilestone(m *Milestone) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.Name = strings.TrimSpace(m.Name)
|
||||||
|
|
||||||
if _, err = sess.Insert(m); err != nil {
|
if _, err = sess.Insert(m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -268,6 +271,7 @@ func GetMilestones(repoID int64, page int, isClosed bool, sortType string) (Mile
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateMilestone(e Engine, m *Milestone) error {
|
func updateMilestone(e Engine, m *Milestone) error {
|
||||||
|
m.Name = strings.TrimSpace(m.Name)
|
||||||
_, err := e.ID(m.ID).AllCols().
|
_, err := e.ID(m.ID).AllCols().
|
||||||
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
||||||
builder.Eq{"milestone_id": m.ID},
|
builder.Eq{"milestone_id": m.ID},
|
||||||
@@ -283,12 +287,33 @@ func updateMilestone(e Engine, m *Milestone) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMilestone updates information of given milestone.
|
// UpdateMilestone updates information of given milestone.
|
||||||
func UpdateMilestone(m *Milestone) error {
|
func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
|
||||||
if err := updateMilestone(x, m); err != nil {
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return updateMilestoneCompleteness(x, m.ID)
|
if m.IsClosed && !oldIsClosed {
|
||||||
|
m.ClosedDateUnix = timeutil.TimeStampNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateMilestone(sess, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updateMilestoneCompleteness(sess, m.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if IsClosed changed, update milestone numbers of repository
|
||||||
|
if oldIsClosed != m.IsClosed {
|
||||||
|
if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
|
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
|
||||||
|
|||||||
@@ -158,10 +158,11 @@ func TestUpdateMilestone(t *testing.T) {
|
|||||||
assert.NoError(t, PrepareTestDatabase())
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
milestone := AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
|
milestone := AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
|
||||||
milestone.Name = "newMilestoneName"
|
milestone.Name = " newMilestoneName "
|
||||||
milestone.Content = "newMilestoneContent"
|
milestone.Content = "newMilestoneContent"
|
||||||
assert.NoError(t, UpdateMilestone(milestone))
|
assert.NoError(t, UpdateMilestone(milestone, milestone.IsClosed))
|
||||||
AssertExistsAndLoadBean(t, milestone)
|
milestone = AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
|
||||||
|
assert.EqualValues(t, "newMilestoneName", milestone.Name)
|
||||||
CheckConsistencyFor(t, &Milestone{})
|
CheckConsistencyFor(t, &Milestone{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,11 @@ type PullRequest struct {
|
|||||||
// MustHeadUserName returns the HeadRepo's username if failed return blank
|
// MustHeadUserName returns the HeadRepo's username if failed return blank
|
||||||
func (pr *PullRequest) MustHeadUserName() string {
|
func (pr *PullRequest) MustHeadUserName() string {
|
||||||
if err := pr.LoadHeadRepo(); err != nil {
|
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 ""
|
||||||
}
|
}
|
||||||
return pr.HeadRepo.MustOwnerName()
|
return pr.HeadRepo.MustOwnerName()
|
||||||
@@ -416,7 +420,7 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if err = pr.Issue.loadRepo(e); err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
apiIssue := pr.Issue.apiFormat(e)
|
apiIssue := pr.Issue.apiFormat(e)
|
||||||
@@ -427,19 +431,14 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pr.HeadRepo == nil {
|
if pr.HeadRepoID != 0 && pr.HeadRepo == nil {
|
||||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
||||||
if err != nil {
|
if err != nil && !IsErrRepoNotExist(err) {
|
||||||
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
|
log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
|
||||||
return nil
|
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{
|
apiPullRequest := &api.PullRequest{
|
||||||
ID: pr.ID,
|
ID: pr.ID,
|
||||||
URL: pr.Issue.HTMLURL(),
|
URL: pr.Issue.HTMLURL(),
|
||||||
@@ -491,33 +490,41 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
|
|||||||
apiPullRequest.Base = apiBaseBranchInfo
|
apiPullRequest.Base = apiBaseBranchInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
|
if pr.HeadRepo != nil {
|
||||||
if err != nil {
|
headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
|
||||||
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 err != nil {
|
if err != nil {
|
||||||
if git.IsErrNotExist(err) {
|
if git.IsErrBranchNotExist(err) {
|
||||||
apiHeadBranchInfo.Sha = ""
|
apiPullRequest.Head = nil
|
||||||
} else {
|
} else {
|
||||||
log.Error("GetCommit[%s]: %v", headBranch.Name, err)
|
log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
if pr.Status != PullRequestStatusChecking {
|
||||||
|
|||||||
@@ -204,6 +204,14 @@ type Repository struct {
|
|||||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
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
|
// ColorFormat returns a colored string to represent this repo
|
||||||
func (repo *Repository) ColorFormat(s fmt.State) {
|
func (repo *Repository) ColorFormat(s fmt.State) {
|
||||||
var ownerName interface{}
|
var ownerName interface{}
|
||||||
@@ -1902,6 +1910,12 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
|
|||||||
return err
|
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.
|
// FIXME: Remove repository files should be executed after transaction succeed.
|
||||||
repoPath := repo.repoPath(sess)
|
repoPath := repo.repoPath(sess)
|
||||||
removeAllWithNotice(sess, "Delete repository files", repoPath)
|
removeAllWithNotice(sess, "Delete repository files", repoPath)
|
||||||
|
|||||||
@@ -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
|
// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
|
||||||
func accessibleRepositoryCondition(userID int64) builder.Cond {
|
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(
|
return builder.Or(
|
||||||
// 1. Be able to see all non-private repositories that either:
|
// 1. Be able to see all non-private repositories that either:
|
||||||
builder.And(
|
builder.And(
|
||||||
@@ -349,6 +360,12 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
|
|||||||
return SearchRepository(opts)
|
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
|
// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
|
||||||
func FindUserAccessibleRepoIDs(userID int64) ([]int64, error) {
|
func FindUserAccessibleRepoIDs(userID int64) ([]int64, error) {
|
||||||
var accessCond builder.Cond = builder.Eq{"is_private": false}
|
var accessCond builder.Cond = builder.Eq{"is_private": false}
|
||||||
|
|||||||
@@ -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
|
// 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--
|
topic.RepoCount--
|
||||||
if _, err := e.ID(topic.ID).Cols("repo_count").Update(topic); err != nil {
|
if _, err := e.ID(topic.ID).Cols("repo_count").Update(topic); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -145,6 +145,24 @@ func removeTopicFromRepo(repoID int64, topic *Topic, e Engine) error {
|
|||||||
return nil
|
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
|
// FindTopicOptions represents the options when fdin topics
|
||||||
type FindTopicOptions struct {
|
type FindTopicOptions struct {
|
||||||
RepoID int64
|
RepoID int64
|
||||||
@@ -217,7 +235,7 @@ func DeleteTopic(repoID int64, topicName string) (*Topic, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = removeTopicFromRepo(repoID, topic, x)
|
err = removeTopicFromRepo(x, repoID, topic)
|
||||||
|
|
||||||
return topic, err
|
return topic, err
|
||||||
}
|
}
|
||||||
@@ -278,7 +296,7 @@ func SaveTopics(repoID int64, topicNames ...string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, topic := range removeTopics {
|
for _, topic := range removeTopics {
|
||||||
err := removeTopicFromRepo(repoID, topic, sess)
|
err := removeTopicFromRepo(sess, repoID, topic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -215,9 +216,12 @@ func (wl *wrappedListener) Accept() (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closed := int32(0)
|
||||||
|
|
||||||
c = wrappedConn{
|
c = wrappedConn{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
server: wl.server,
|
server: wl.server,
|
||||||
|
closed: &closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
wl.server.wg.Add(1)
|
wl.server.wg.Add(1)
|
||||||
@@ -241,12 +245,12 @@ func (wl *wrappedListener) File() (*os.File, error) {
|
|||||||
type wrappedConn struct {
|
type wrappedConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
server *Server
|
server *Server
|
||||||
|
closed *int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w wrappedConn) Close() error {
|
func (w wrappedConn) Close() error {
|
||||||
err := w.Conn.Close()
|
if atomic.CompareAndSwapInt32(w.closed, 0, 1) {
|
||||||
if err == nil {
|
|
||||||
w.server.wg.Done()
|
w.server.wg.Done()
|
||||||
}
|
}
|
||||||
return err
|
return w.Conn.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,12 @@ func nonGenesisChanges(repo *models.Repository, revision string) (*repoChanges,
|
|||||||
if len(line) == 0 {
|
if len(line) == 0 {
|
||||||
continue
|
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 {
|
if len(filename) == 0 {
|
||||||
continue
|
continue
|
||||||
} else if filename[0] == '"' {
|
} 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':
|
case 'M', 'A':
|
||||||
updatedFilenames = append(updatedFilenames, filename)
|
updatedFilenames = append(updatedFilenames, filename)
|
||||||
case 'D':
|
case 'D':
|
||||||
changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
|
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:
|
default:
|
||||||
log.Warn("Unrecognized status: %c (line=%s)", status, line)
|
log.Warn("Unrecognized status: %c (line=%s)", status, line)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package indexer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
||||||
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"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) {
|
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)
|
code_indexer.UpdateRepoIndexer(repo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -475,9 +475,18 @@ func PushUpdate(repo *models.Repository, branch string, opts PushUpdateOptions)
|
|||||||
return err
|
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 {
|
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)
|
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 {
|
if err = models.RemoveDeletedBranch(repo.ID, opts.Branch); err != nil {
|
||||||
log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, opts.Branch, err)
|
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 {
|
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)
|
log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ package util
|
|||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// urlSafeError wraps an error whose message may contain a sensitive URL
|
// 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 {
|
func SanitizeURLCredentials(unsanitizedURL string, usePlaceholder bool) string {
|
||||||
u, err := url.Parse(unsanitizedURL)
|
u, err := url.Parse(unsanitizedURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Error("parse url %s failed: %v", unsanitizedURL, err)
|
||||||
// don't log the error, since it might contain unsanitized URL.
|
// don't log the error, since it might contain unsanitized URL.
|
||||||
return "(unparsable url)"
|
return "(unparsable url)"
|
||||||
}
|
}
|
||||||
|
|||||||
25
modules/util/sanitize_test.go
Normal file
25
modules/util/sanitize_test.go
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ package admin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
@@ -226,6 +227,11 @@ func DeleteUser(ctx *context.APIContext) {
|
|||||||
return
|
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 err := models.DeleteUser(u); err != nil {
|
||||||
if models.IsErrUserOwnRepos(err) ||
|
if models.IsErrUserOwnRepos(err) ||
|
||||||
models.IsErrUserHasOrgs(err) {
|
models.IsErrUserHasOrgs(err) {
|
||||||
|
|||||||
@@ -194,7 +194,12 @@ func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) {
|
|||||||
milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
|
milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.UpdateMilestone(milestone); err != nil {
|
var oldIsClosed = milestone.IsClosed
|
||||||
|
if form.State != nil {
|
||||||
|
milestone.IsClosed = *form.State == string(api.StateClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := models.UpdateMilestone(milestone, oldIsClosed); err != nil {
|
||||||
ctx.ServerError("UpdateMilestone", err)
|
ctx.ServerError("UpdateMilestone", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ func SettingsDeleteAvatar(ctx *context.Context) {
|
|||||||
ctx.Redirect(ctx.Org.OrgLink + "/settings")
|
ctx.Redirect(ctx.Org.OrgLink + "/settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SettingsDelete response for delete repository
|
// SettingsDelete response for deleting an organization
|
||||||
func SettingsDelete(ctx *context.Context) {
|
func SettingsDelete(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("org.settings")
|
ctx.Data["Title"] = ctx.Tr("org.settings")
|
||||||
ctx.Data["PageIsSettingsDelete"] = true
|
ctx.Data["PageIsSettingsDelete"] = true
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ func ServNoCommand(ctx *macaron.Context) {
|
|||||||
|
|
||||||
// ServCommand returns information about the provided keyid
|
// ServCommand returns information about the provided keyid
|
||||||
func ServCommand(ctx *macaron.Context) {
|
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")
|
keyID := ctx.ParamsInt64(":keyid")
|
||||||
ownerName := ctx.Params(":owner")
|
ownerName := ctx.Params(":owner")
|
||||||
repoName := ctx.Params(":repo")
|
repoName := ctx.Params(":repo")
|
||||||
@@ -105,6 +104,17 @@ func ServCommand(ctx *macaron.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrRepoNotExist(err) {
|
if models.IsErrRepoNotExist(err) {
|
||||||
repoExist = false
|
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 {
|
} else {
|
||||||
log.Error("Unable to get repository: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
|
log.Error("Unable to get repository: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
|
||||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ func loadBranches(ctx *context.Context) []*Branch {
|
|||||||
} else {
|
} else {
|
||||||
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
|
repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo
|
||||||
}
|
}
|
||||||
|
pr.Issue.Repo = pr.BaseRepo
|
||||||
|
|
||||||
if pr.HasMerged {
|
if pr.HasMerged {
|
||||||
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
|
baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID]
|
||||||
@@ -260,7 +261,6 @@ func loadBranches(ctx *context.Context) []*Branch {
|
|||||||
mergeMovedOn = true
|
mergeMovedOn = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName
|
isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ func EditMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) {
|
|||||||
m.Name = form.Title
|
m.Name = form.Title
|
||||||
m.Content = form.Content
|
m.Content = form.Content
|
||||||
m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix())
|
m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix())
|
||||||
if err = models.UpdateMilestone(m); err != nil {
|
if err = models.UpdateMilestone(m, m.IsClosed); err != nil {
|
||||||
ctx.ServerError("UpdateMilestone", err)
|
ctx.ServerError("UpdateMilestone", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,27 +66,20 @@ type PageMeta struct {
|
|||||||
|
|
||||||
// findEntryForFile finds the tree entry for a target filepath.
|
// findEntryForFile finds the tree entry for a target filepath.
|
||||||
func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
|
func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) {
|
||||||
entries, err := commit.ListEntries()
|
entry, err := commit.GetTreeEntryByPath(target)
|
||||||
if err != nil {
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// The longest name should be checked first
|
if entry != nil {
|
||||||
for _, entry := range entries {
|
return entry, nil
|
||||||
if entry.IsRegular() && entry.Name() == target {
|
|
||||||
return entry, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then the unescaped, shortest alternative
|
// Then the unescaped, shortest alternative
|
||||||
var unescapedTarget string
|
var unescapedTarget string
|
||||||
if unescapedTarget, err = url.QueryUnescape(target); err != nil {
|
if unescapedTarget, err = url.QueryUnescape(target); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
return commit.GetTreeEntryByPath(unescapedTarget)
|
||||||
if entry.IsRegular() && entry.Name() == unescapedTarget {
|
|
||||||
return entry, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
|
func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
|
||||||
@@ -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
|
// wikiContentsByName returns the contents of a wiki page, along with a boolean
|
||||||
// indicating whether the page exists. Writes to ctx if an error occurs.
|
// indicating whether the page exists. Writes to ctx if an error occurs.
|
||||||
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
|
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
|
||||||
var entry *git.TreeEntry
|
|
||||||
var err error
|
|
||||||
pageFilename := wiki_service.NameToFilename(wikiName)
|
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)
|
ctx.ServerError("findEntryForFile", err)
|
||||||
return nil, nil, "", false
|
return nil, nil, "", false
|
||||||
} else if entry == nil {
|
} else if entry == nil {
|
||||||
@@ -518,7 +510,7 @@ func WikiRaw(ctx *context.Context) {
|
|||||||
if commit != nil {
|
if commit != nil {
|
||||||
// Try to find a file with that name
|
// Try to find a file with that name
|
||||||
entry, err = findEntryForFile(commit, providedPath)
|
entry, err = findEntryForFile(commit, providedPath)
|
||||||
if err != nil {
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
ctx.ServerError("findFile", err)
|
ctx.ServerError("findFile", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -531,7 +523,7 @@ func WikiRaw(ctx *context.Context) {
|
|||||||
|
|
||||||
wikiPath := wiki_service.NameToFilename(providedPath)
|
wikiPath := wiki_service.NameToFilename(providedPath)
|
||||||
entry, err = findEntryForFile(commit, wikiPath)
|
entry, err = findEntryForFile(commit, wikiPath)
|
||||||
if err != nil {
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
ctx.ServerError("findFile", err)
|
ctx.ServerError("findFile", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ func TestDeleteWikiPagePost(t *testing.T) {
|
|||||||
func TestWikiRaw(t *testing.T) {
|
func TestWikiRaw(t *testing.T) {
|
||||||
for filepath, filetype := range map[string]string{
|
for filepath, filetype := range map[string]string{
|
||||||
"jpeg.jpg": "image/jpeg",
|
"jpeg.jpg": "image/jpeg",
|
||||||
|
"images/jpeg.jpg": "image/jpeg",
|
||||||
"Page With Spaced Name": "text/plain; charset=utf-8",
|
"Page With Spaced Name": "text/plain; charset=utf-8",
|
||||||
"Page-With-Spaced-Name": "text/plain; charset=utf-8",
|
"Page-With-Spaced-Name": "text/plain; charset=utf-8",
|
||||||
"Page With Spaced Name.md": "text/plain; charset=utf-8",
|
"Page With Spaced Name.md": "text/plain; charset=utf-8",
|
||||||
|
|||||||
@@ -475,12 +475,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||||||
// ***** END: Admin *****
|
// ***** END: Admin *****
|
||||||
|
|
||||||
m.Group("", func() {
|
m.Group("", func() {
|
||||||
m.Group("/:username", func() {
|
m.Get("/:username", user.Profile)
|
||||||
m.Get("", user.Profile)
|
|
||||||
m.Get("/followers", user.Followers)
|
|
||||||
m.Get("/following", user.Following)
|
|
||||||
})
|
|
||||||
|
|
||||||
m.Get("/attachments/:uuid", repo.GetAttachment)
|
m.Get("/attachments/:uuid", repo.GetAttachment)
|
||||||
}, ignSignIn)
|
}, ignSignIn)
|
||||||
|
|
||||||
|
|||||||
@@ -142,11 +142,17 @@ func Dashboard(ctx *context.Context) {
|
|||||||
ctx.Data["MirrorCount"] = len(mirrors)
|
ctx.Data["MirrorCount"] = len(mirrors)
|
||||||
ctx.Data["Mirrors"] = mirrors
|
ctx.Data["Mirrors"] = mirrors
|
||||||
|
|
||||||
|
requestingUserID := int64(0)
|
||||||
|
if ctx.User != nil {
|
||||||
|
requestingUserID = ctx.User.ID
|
||||||
|
}
|
||||||
|
|
||||||
retrieveFeeds(ctx, models.GetFeedsOptions{
|
retrieveFeeds(ctx, models.GetFeedsOptions{
|
||||||
RequestedUser: ctxUser,
|
RequestedUser: ctxUser,
|
||||||
IncludePrivate: true,
|
RequestingUserID: requestingUserID,
|
||||||
OnlyPerformedBy: false,
|
IncludePrivate: true,
|
||||||
IncludeDeleted: false,
|
OnlyPerformedBy: false,
|
||||||
|
IncludeDeleted: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
@@ -519,13 +525,17 @@ func Issues(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
issueStats, err := models.GetUserIssueStats(models.UserIssueStatsOptions{
|
issueStatsOpts := models.UserIssueStatsOptions{
|
||||||
UserID: ctxUser.ID,
|
UserID: ctxUser.ID,
|
||||||
UserRepoIDs: userRepoIDs,
|
UserRepoIDs: userRepoIDs,
|
||||||
FilterMode: filterMode,
|
FilterMode: filterMode,
|
||||||
IsPull: isPullList,
|
IsPull: isPullList,
|
||||||
IsClosed: isShowClosed,
|
IsClosed: isShowClosed,
|
||||||
})
|
}
|
||||||
|
if len(repoIDs) > 0 {
|
||||||
|
issueStatsOpts.UserRepoIDs = repoIDs
|
||||||
|
}
|
||||||
|
issueStats, err := models.GetUserIssueStats(issueStatsOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetUserIssueStats", err)
|
ctx.ServerError("GetUserIssueStats", err)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -11,16 +11,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/base"
|
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"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/org"
|
"code.gitea.io/gitea/routers/org"
|
||||||
"code.gitea.io/gitea/routers/repo"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
tplFollowers base.TplName = "user/meta/followers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetUserByName get user by name
|
// GetUserByName get user by name
|
||||||
@@ -156,14 +150,38 @@ func Profile(ctx *context.Context) {
|
|||||||
orderBy = models.SearchOrderByRecentUpdated
|
orderBy = models.SearchOrderByRecentUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestingUserID := int64(0)
|
||||||
|
if ctx.User != nil {
|
||||||
|
requestingUserID = ctx.User.ID
|
||||||
|
}
|
||||||
|
|
||||||
keyword := strings.Trim(ctx.Query("q"), " ")
|
keyword := strings.Trim(ctx.Query("q"), " ")
|
||||||
ctx.Data["Keyword"] = keyword
|
ctx.Data["Keyword"] = keyword
|
||||||
switch tab {
|
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":
|
case "activity":
|
||||||
retrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser,
|
retrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser,
|
||||||
IncludePrivate: showPrivate,
|
RequestingUserID: requestingUserID,
|
||||||
OnlyPerformedBy: true,
|
IncludePrivate: showPrivate,
|
||||||
IncludeDeleted: false,
|
OnlyPerformedBy: true,
|
||||||
|
IncludeDeleted: false,
|
||||||
})
|
})
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
@@ -223,32 +241,6 @@ func Profile(ctx *context.Context) {
|
|||||||
ctx.HTML(200, tplProfile)
|
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
|
// Action response for follow/unfollow user request
|
||||||
func Action(ctx *context.Context) {
|
func Action(ctx *context.Context) {
|
||||||
u := GetUserByParams(ctx)
|
u := GetUserByParams(ctx)
|
||||||
|
|||||||
@@ -213,7 +213,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)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"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) {
|
func PushToBaseRepo(pr *models.PullRequest) (err error) {
|
||||||
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo '%s'", pr.BaseRepoID, pr.GetGitRefName())
|
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()
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("OpenRepository: %v", err)
|
return fmt.Errorf("OpenRepository: %v", err)
|
||||||
}
|
}
|
||||||
defer headGitRepo.Close()
|
|
||||||
|
|
||||||
tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID)
|
if err := gitRepo.AddRemote("base", pr.BaseRepo.RepoPath(), false); err != nil {
|
||||||
if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil {
|
return fmt.Errorf("tmpGitRepo.AddRemote: %v", err)
|
||||||
return fmt.Errorf("headGitRepo.AddRemote: %v", err)
|
|
||||||
}
|
}
|
||||||
// Make sure to remove the remote even if the push fails
|
defer gitRepo.Close()
|
||||||
defer func() {
|
|
||||||
if err := headGitRepo.RemoveRemote(tmpRemoteName); err != nil {
|
|
||||||
log.Error("PushToBaseRepo: RemoveRemote: %s", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
headFile := pr.GetGitRefName()
|
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)
|
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{
|
if err = git.Push(tmpBasePath, git.PushOptions{
|
||||||
Remote: tmpRemoteName,
|
Remote: "base",
|
||||||
Branch: fmt.Sprintf("%s:%s", pr.HeadBranch, headFile),
|
Branch: fmt.Sprintf("%s:%s", pr.HeadBranch, headFile),
|
||||||
Force: true,
|
Force: true,
|
||||||
// Use InternalPushingEnvironment here because we know that pre-receive and post-receive do not run on a refs/pulls/...
|
// 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),
|
Env: models.InternalPushingEnvironment(pr.Issue.Poster, pr.BaseRepo),
|
||||||
}); err != nil {
|
}); 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
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/notification"
|
"code.gitea.io/gitea/modules/notification"
|
||||||
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateRepository creates a repository for the user/organization.
|
// 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.
|
// DeleteRepository deletes a repository for a user or organization.
|
||||||
func DeleteRepository(doer *models.User, repo *models.Repository) error {
|
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 {
|
if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td class="two wide right aligned">
|
<td class="three wide right aligned">
|
||||||
{{if not .LatestPullRequest}}
|
{{if not .LatestPullRequest}}
|
||||||
{{if .IsIncluded}}
|
{{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">
|
<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>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}">#{{.LatestPullRequest.Issue.Index}}</a>
|
<a href="{{.LatestPullRequest.Issue.HTMLURL}}">{{if ne .LatestPullRequest.BaseRepoID .LatestPullRequest.HeadRepoID}}{{.LatestPullRequest.BaseRepo.FullName}}{{end}}#{{.LatestPullRequest.Issue.Index}}</a>
|
||||||
{{if .LatestPullRequest.HasMerged}}
|
{{if .LatestPullRequest.HasMerged}}
|
||||||
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui purple small label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a>
|
<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui purple mini label"><i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.pulls.merged"}}</a>
|
||||||
{{else if .LatestPullRequest.Issue.IsClosed}}
|
{{else if .LatestPullRequest.Issue.IsClosed}}
|
||||||
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui red small label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a>
|
<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui red mini label"><i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.closed_title"}}</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a href="{{$.RepoLink}}/pulls/{{.LatestPullRequest.Issue.Index}}" class="ui green small label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a>
|
<a href="{{.LatestPullRequest.Issue.HTMLURL}}" class="ui green mini label"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_title"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
{{if and .RelAvatarLink .IsPrivate}}<i class="mega-octicon octicon-lock"></i>{{end}}
|
{{if and .RelAvatarLink .IsPrivate}}<i class="mega-octicon octicon-lock"></i>{{end}}
|
||||||
{{if .IsTemplate}}<i class="icon fa-copy"></i>{{end}}
|
{{if .IsTemplate}}<i class="icon fa-copy"></i>{{end}}
|
||||||
{{if .IsArchived}}<i class="archive icon archived-icon"></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 .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}}
|
{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -336,7 +336,7 @@
|
|||||||
{{if .Content}}
|
{{if .Content}}
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<span class="octicon octicon-quote"></span>
|
<span class="octicon octicon-quote"></span>
|
||||||
<span class="text grey">{{.Content}}</span>
|
<span class="text grey has-emoji">{{.Content}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{ range $filename, $lines := .Review.CodeComments}}
|
{{ range $filename, $lines := .Review.CodeComments}}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
<div class="ui purple large label"><i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls.merged"}}</div>
|
<div class="ui purple large label"><i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls.merged"}}</div>
|
||||||
{{else if .Issue.IsClosed}}
|
{{else if .Issue.IsClosed}}
|
||||||
<div class="ui red large label"><i class="octicon octicon-issue-closed"></i> {{.i18n.Tr "repo.issues.closed_title"}}</div>
|
<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}}
|
{{else}}
|
||||||
<div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
|
<div class="ui green large label"><i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues.open_title"}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
<div class="ui container user-cards">
|
<div class="ui container user-cards">
|
||||||
|
{{if .CardsTitle}}
|
||||||
<h2 class="ui dividing header">
|
<h2 class="ui dividing header">
|
||||||
{{.CardsTitle}}
|
{{.CardsTitle}}
|
||||||
</h2>
|
</h2>
|
||||||
|
{{end}}
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
{{range .Cards}}
|
{{range .Cards}}
|
||||||
<li class="item ui segment">
|
<li class="item ui segment">
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
{{if .Repo}}
|
{{if .Repo}}
|
||||||
<li class="item">
|
<li class="item">
|
||||||
<div class="ui label">{{.Repo.FullName}}#{{.Index}}</div>
|
<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 .IsPull }}
|
||||||
{{if (index $.CommitStatus .PullRequest.ID)}}
|
{{if (index $.CommitStatus .PullRequest.ID)}}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
{{template "base/head" .}}
|
|
||||||
<div class="user followers">
|
|
||||||
{{template "user/meta/header" .}}
|
|
||||||
{{template "repo/user_cards" .}}
|
|
||||||
</div>
|
|
||||||
{{template "base/footer" .}}
|
|
||||||
@@ -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>
|
|
||||||
@@ -49,24 +49,6 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<li><i class="octicon octicon-clock"></i> {{.i18n.Tr "user.join_on"}} {{.Owner.CreatedUnix.FormatShort}}</li>
|
<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}}
|
{{if and .Orgs .HasOrgsVisible}}
|
||||||
<li>
|
<li>
|
||||||
<ul class="user-orgs">
|
<ul class="user-orgs">
|
||||||
@@ -95,7 +77,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ui eleven wide column">
|
<div class="ui eleven wide column">
|
||||||
<div class="ui secondary stackable pointing menu">
|
<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"}}
|
<i class="octicon octicon-repo"></i> {{.i18n.Tr "user.repositories"}}
|
||||||
</a>
|
</a>
|
||||||
<a class='{{if eq .TabName "activity"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=activity">
|
<a class='{{if eq .TabName "activity"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=activity">
|
||||||
@@ -103,6 +85,15 @@
|
|||||||
</a>
|
</a>
|
||||||
<a class='{{if eq .TabName "stars"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=stars">
|
<a class='{{if eq .TabName "stars"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=stars">
|
||||||
<i class="octicon octicon-star"></i> {{.i18n.Tr "user.starred"}}
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -126,6 +117,10 @@
|
|||||||
{{template "explore/repo_list" .}}
|
{{template "explore/repo_list" .}}
|
||||||
{{template "base/paginate" .}}
|
{{template "base/paginate" .}}
|
||||||
</div>
|
</div>
|
||||||
|
{{else if eq .TabName "following"}}
|
||||||
|
{{template "repo/user_cards" .}}
|
||||||
|
{{else if eq .TabName "followers"}}
|
||||||
|
{{template "repo/user_cards" .}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{template "explore/repo_search" .}}
|
{{template "explore/repo_search" .}}
|
||||||
{{template "explore/repo_list" .}}
|
{{template "explore/repo_list" .}}
|
||||||
|
|||||||
@@ -1362,7 +1362,16 @@ function initWikiForm() {
|
|||||||
}, '|',
|
}, '|',
|
||||||
'unordered-list', 'ordered-list', '|',
|
'unordered-list', 'ordered-list', '|',
|
||||||
'link', 'image', 'table', 'horizontal-rule', '|',
|
'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');
|
$(simplemde.codemirror.getInputField()).addClass('js-quick-submit');
|
||||||
|
|
||||||
@@ -1466,7 +1475,16 @@ function setSimpleMDE($editArea) {
|
|||||||
'code', 'quote', '|',
|
'code', 'quote', '|',
|
||||||
'unordered-list', 'ordered-list', '|',
|
'unordered-list', 'ordered-list', '|',
|
||||||
'link', 'image', 'table', 'horizontal-rule', '|',
|
'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;
|
return true;
|
||||||
@@ -1488,7 +1506,16 @@ function setCommentSimpleMDE($editArea) {
|
|||||||
'code', 'quote', '|',
|
'code', 'quote', '|',
|
||||||
'unordered-list', 'ordered-list', '|',
|
'unordered-list', 'ordered-list', '|',
|
||||||
'link', 'image', 'table', 'horizontal-rule', '|',
|
'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', {
|
simplemde.codemirror.setOption('extraKeys', {
|
||||||
Enter: () => {
|
Enter: () => {
|
||||||
|
|||||||
@@ -1111,6 +1111,9 @@ input {
|
|||||||
.lines-code .hljs {
|
.lines-code .hljs {
|
||||||
background-color: #2a2e3a !important;
|
background-color: #2a2e3a !important;
|
||||||
}
|
}
|
||||||
|
td.blob-excerpt {
|
||||||
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
.code-view .active {
|
.code-view .active {
|
||||||
background: #554a00;
|
background: #554a00;
|
||||||
|
|||||||
Reference in New Issue
Block a user