mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-09-07 15:33:26 +00:00
Merge branch 'master' into refactor_issues-subscription
This commit is contained in:
commit
29023568d3
64 changed files with 4573 additions and 242 deletions
1
.eslintignore
Normal file
1
.eslintignore
Normal file
|
@ -0,0 +1 @@
|
|||
/public/js/semantic.dropdown.custom.js
|
|
@ -511,6 +511,10 @@ DELIVER_TIMEOUT = 5
|
|||
SKIP_TLS_VERIFY = false
|
||||
; Number of history information in each page
|
||||
PAGING_NUM = 10
|
||||
; Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
|
||||
PROXY_URL =
|
||||
; Comma separated list of host names requiring proxy. Glob patterns (*) are accepted; use ** to match all hosts.
|
||||
PROXY_HOSTS =
|
||||
|
||||
[mailer]
|
||||
ENABLED = false
|
||||
|
|
|
@ -312,6 +312,8 @@ relation to port exhaustion.
|
|||
- `DELIVER_TIMEOUT`: **5**: Delivery timeout (sec) for shooting webhooks.
|
||||
- `SKIP_TLS_VERIFY`: **false**: Allow insecure certification.
|
||||
- `PAGING_NUM`: **10**: Number of webhook history events that are shown in one page.
|
||||
- `PROXY_URL`: ****: Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
|
||||
- `PROXY_HOSTS`: ****: Comma separated list of host names requiring proxy. Glob patterns (*) are accepted; use ** to match all hosts.
|
||||
|
||||
## Mailer (`mailer`)
|
||||
|
||||
|
|
|
@ -129,6 +129,8 @@ menu:
|
|||
- `DELIVER_TIMEOUT`: 请求webhooks的超时时间,单位秒。
|
||||
- `SKIP_TLS_VERIFY`: 是否允许不安全的证书。
|
||||
- `PAGING_NUM`: 每页显示的Webhook 历史数量。
|
||||
- `PROXY_URL`: ****: 代理服务器网址,支持 http://, https//, socks://, 为空将使用环境变量中的 http_proxy/https_proxy 设置。
|
||||
- `PROXY_HOSTS`: ****: 逗号分隔的需要代理的域名或IP地址。支持 * 号匹配符,使用 ** 匹配所有域名和IP地址。
|
||||
|
||||
## Mailer (`mailer`)
|
||||
|
||||
|
|
36
docs/content/doc/advanced/third-party-tools.zh-cn.md
Normal file
36
docs/content/doc/advanced/third-party-tools.zh-cn.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
date: "2019-03-11T21:45:00+00:00"
|
||||
title: "高级: 第三方工具"
|
||||
slug: "third-party-tools"
|
||||
weight: 50
|
||||
toc: true
|
||||
draft: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "advanced"
|
||||
name: "第三方工具"
|
||||
weight: 50
|
||||
identifier: "third-party-tools"
|
||||
---
|
||||
|
||||
# 第三方工具列表
|
||||
**注意:** 这些工具并没有经过Gitea的检验,在这里列出它们只是为了便捷.
|
||||
|
||||
*此列表并不是完整的列表,可以随时咨询如何添加!*
|
||||
|
||||
### 持续集成
|
||||
[BuildKite 连接器](https://github.com/techknowlogick/gitea-buildkite-connector)
|
||||
[Jenkins 插件](https://github.com/jenkinsci/gitea-plugin)
|
||||
[Gitea搭配Drone](https://docs.drone.io/installation/gitea)
|
||||
|
||||
|
||||
### 迁移
|
||||
[Gitea安装脚本](https://git.coolaj86.com/coolaj86/gitea-installer.sh)
|
||||
[GitHub迁移](https://gitea.com/gitea/migrator)
|
||||
|
||||
|
||||
### 移动端
|
||||
[安卓客户端GitNex](https://gitlab.com/mmarif4u/gitnex)
|
||||
|
||||
### 编辑器扩展
|
||||
- [Gitea的Visual Studio扩展](https://github.com/maikebing/Gitea.VisualStudio) 从 [Visual Studio 扩展市场](https://marketplace.visualstudio.com/items?itemName=MysticBoy.GiteaExtensionforVisualStudio) 下载
|
|
@ -43,6 +43,8 @@ Also see [Support Options]({{< relref "doc/help/seek-help.en-us.md" >}})
|
|||
* [Missing releases after migration repository with tags](#missing-releases-after-migrating-repository-with-tags)
|
||||
* [LFS Issues](#lfs-issues)
|
||||
* [How can I create users before starting Gitea](#how-can-i-create-users-before-starting-gitea)
|
||||
* [How can I enable password reset](#how-can-i-enable-password-reset)
|
||||
* [How can a user's password be changed](#how-can-a-user-s-password-be-changed)
|
||||
|
||||
|
||||
## Difference between 1.x and 1.x.x downloads
|
||||
|
@ -275,4 +277,17 @@ By default, your LFS token will expire after 20 minutes. If you have a slow conn
|
|||
You may want to set this value to `60m` or `120m`.
|
||||
|
||||
## How can I create users before starting Gitea
|
||||
Gitea provides a sub-command `gitea migrate` to initialize the database, after which you can use the [admin CLI commands]({{< relref "doc/usage/command-line.en-us.md" >}}) to add users like normal.
|
||||
Gitea provides a sub-command `gitea migrate` to initialize the database, after which you can use the [admin CLI commands]({{< relref "doc/usage/command-line.en-us.md#admin" >}}) to add users like normal.
|
||||
|
||||
## How can I enable password reset
|
||||
There is no setting for password resets. It is enabled when a [mail service]({{< relref "doc/usage/email-setup.en-us.md" >}}) is configured, and disabled otherwise.
|
||||
|
||||
## How can a user's password be changed
|
||||
- As an **admin**, you can change any user's password (and optionally force them to change it on next login)...
|
||||
- By navigating to your `Site Administration -> User Accounts` page and editing a user.
|
||||
- By using the [admin CLI commands]({{< relref "doc/usage/command-line.en-us.md#admin" >}}).
|
||||
Keep in mind most commands will also need a [global flag]({{< relref "doc/usage/command-line.en-us.md#global-options" >}}) to point the CLI at the correct configuration.
|
||||
- As a **user** you can change it...
|
||||
- In your account `Settings -> Account` page (this method **requires** you to know your current password).
|
||||
- By using the `Forgot Password` link.
|
||||
If the `Forgot Password/Account Recovery` page is disabled, please contact your administrator to configure a [mail service]({{< relref "doc/usage/email-setup.en-us.md" >}}).
|
||||
|
|
|
@ -334,7 +334,7 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) {
|
|||
resp := httpContext.Session.MakeRequest(t, req, http.StatusConflict)
|
||||
respJSON := map[string]string{}
|
||||
DecodeJSON(t, resp, &respJSON)
|
||||
assert.Equal(t, respJSON["message"], "The repository with the same name already exists.")
|
||||
assert.Equal(t, "The repository with the same name already exists.", respJSON["message"])
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ func TestSignup(t *testing.T) {
|
|||
req := NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
|
||||
"user_name": "exampleUser",
|
||||
"email": "exampleUser@example.com",
|
||||
"password": "examplePassword",
|
||||
"retype": "examplePassword",
|
||||
"password": "examplePassword!1",
|
||||
"retype": "examplePassword!1",
|
||||
})
|
||||
MakeRequest(t, req, http.StatusFound)
|
||||
|
||||
|
|
|
@ -283,49 +283,6 @@ func (a *Action) GetIssueContent() string {
|
|||
return issue.Content
|
||||
}
|
||||
|
||||
func newRepoAction(e Engine, u *User, repo *Repository) (err error) {
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: u.ID,
|
||||
ActUser: u,
|
||||
OpType: ActionCreateRepo,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("notify watchers '%d/%d': %v", u.ID, repo.ID, err)
|
||||
}
|
||||
|
||||
log.Trace("action.newRepoAction: %s/%s", u.Name, repo.Name)
|
||||
return err
|
||||
}
|
||||
|
||||
// NewRepoAction adds new action for creating repository.
|
||||
func NewRepoAction(u *User, repo *Repository) (err error) {
|
||||
return newRepoAction(x, u, repo)
|
||||
}
|
||||
|
||||
func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) {
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: actUser.ID,
|
||||
ActUser: actUser,
|
||||
OpType: ActionRenameRepo,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
Content: oldRepoName,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("notify watchers: %v", err)
|
||||
}
|
||||
|
||||
log.Trace("action.renameRepoAction: %s/%s", actUser.Name, repo.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenameRepoAction adds new action for renaming a repository.
|
||||
func RenameRepoAction(actUser *User, oldRepoName string, repo *Repository) error {
|
||||
return renameRepoAction(x, actUser, oldRepoName, repo)
|
||||
}
|
||||
|
||||
// PushCommit represents a commit in a push operation.
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
|
|
|
@ -2,7 +2,6 @@ package models
|
|||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -28,58 +27,6 @@ func TestAction_GetRepoLink(t *testing.T) {
|
|||
assert.Equal(t, expected, action.GetRepoLink())
|
||||
}
|
||||
|
||||
func TestNewRepoAction(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{OwnerID: user.ID}).(*Repository)
|
||||
repo.Owner = user
|
||||
|
||||
actionBean := &Action{
|
||||
OpType: ActionCreateRepo,
|
||||
ActUserID: user.ID,
|
||||
RepoID: repo.ID,
|
||||
ActUser: user,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}
|
||||
|
||||
AssertNotExistsBean(t, actionBean)
|
||||
assert.NoError(t, NewRepoAction(user, repo))
|
||||
AssertExistsAndLoadBean(t, actionBean)
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func TestRenameRepoAction(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{OwnerID: user.ID}).(*Repository)
|
||||
repo.Owner = user
|
||||
|
||||
oldRepoName := repo.Name
|
||||
const newRepoName = "newRepoName"
|
||||
repo.Name = newRepoName
|
||||
repo.LowerName = strings.ToLower(newRepoName)
|
||||
|
||||
actionBean := &Action{
|
||||
OpType: ActionRenameRepo,
|
||||
ActUserID: user.ID,
|
||||
ActUser: user,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
Content: oldRepoName,
|
||||
}
|
||||
AssertNotExistsBean(t, actionBean)
|
||||
assert.NoError(t, RenameRepoAction(user, oldRepoName, repo))
|
||||
AssertExistsAndLoadBean(t, actionBean)
|
||||
|
||||
_, err := x.ID(repo.ID).Cols("name", "lower_name").Update(repo)
|
||||
assert.NoError(t, err)
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
|
||||
pushCommits := NewPushCommits()
|
||||
pushCommits.Commits = []*PushCommit{
|
||||
|
|
|
@ -1075,7 +1075,8 @@ func sortIssuesSession(sess *xorm.Session, sortType string, priorityRepoID int64
|
|||
case "priority":
|
||||
sess.Desc("issue.priority")
|
||||
case "nearduedate":
|
||||
sess.Asc("issue.deadline_unix")
|
||||
// 253370764800 is 01/01/9999 @ 12:00am (UTC)
|
||||
sess.OrderBy("CASE WHEN issue.deadline_unix = 0 THEN 253370764800 ELSE issue.deadline_unix END ASC")
|
||||
case "farduedate":
|
||||
sess.Desc("issue.deadline_unix")
|
||||
case "priorityrepo":
|
||||
|
|
|
@ -243,6 +243,21 @@ func (t *Team) addAllRepositories(e Engine) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AddAllRepositories adds all repositories to the team
|
||||
func (t *Team) AddAllRepositories() (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = t.addAllRepositories(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// AddRepository adds new repository to team of organization.
|
||||
func (t *Team) AddRepository(repo *Repository) (err error) {
|
||||
if repo.OwnerID != t.OrgID {
|
||||
|
@ -264,6 +279,69 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
|
|||
return sess.Commit()
|
||||
}
|
||||
|
||||
// RemoveAllRepositories removes all repositories from team and recalculates access
|
||||
func (t *Team) RemoveAllRepositories() (err error) {
|
||||
if t.IncludesAllRepositories {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = t.removeAllRepositories(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// removeAllRepositories removes all repositories from team and recalculates access
|
||||
// Note: Shall not be called if team includes all repositories
|
||||
func (t *Team) removeAllRepositories(e Engine) (err error) {
|
||||
// Delete all accesses.
|
||||
for _, repo := range t.Repos {
|
||||
if err := repo.recalculateTeamAccesses(e, t.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove watches from all users and now unaccessible repos
|
||||
for _, user := range t.Members {
|
||||
has, err := hasAccess(e, user.ID, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = watchRepo(e, user.ID, repo.ID, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove all IssueWatches a user has subscribed to in the repositories
|
||||
if err = removeIssueWatchersByRepoID(e, user.ID, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete team-repo
|
||||
if _, err := e.
|
||||
Where("team_id=?", t.ID).
|
||||
Delete(new(TeamRepo)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.NumRepos = 0
|
||||
if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeRepository removes a repository from a team and recalculates access
|
||||
// Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
|
||||
func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
|
||||
|
@ -577,36 +655,7 @@ func DeleteTeam(t *Team) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Delete all accesses.
|
||||
for _, repo := range t.Repos {
|
||||
if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove watches from all users and now unaccessible repos
|
||||
for _, user := range t.Members {
|
||||
has, err := hasAccess(sess, user.ID, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = watchRepo(sess, user.ID, repo.ID, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove all IssueWatches a user has subscribed to in the repositories
|
||||
if err = removeIssueWatchersByRepoID(sess, user.ID, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete team-repo
|
||||
if _, err := sess.
|
||||
Where("team_id=?", t.ID).
|
||||
Delete(new(TeamRepo)); err != nil {
|
||||
if err := t.removeAllRepositories(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1469,8 +1469,15 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err
|
|||
return fmt.Errorf("watchRepo: %v", err)
|
||||
}
|
||||
}
|
||||
if err = newRepoAction(e, doer, repo); err != nil {
|
||||
return fmt.Errorf("newRepoAction: %v", err)
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: doer.ID,
|
||||
ActUser: doer,
|
||||
OpType: ActionCreateRepo,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
|
||||
}
|
||||
|
||||
if err = copyDefaultWebhooksToRepo(e, repo.ID); err != nil {
|
||||
|
@ -2833,3 +2840,9 @@ func (repo *Repository) GetTreePathLock(treePath string) (*LFSLock, error) {
|
|||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// UpdateRepositoryCols updates repository's columns
|
||||
func UpdateRepositoryCols(repo *Repository, cols ...string) error {
|
||||
_, err := x.ID(repo.ID).Cols(cols...).Update(repo)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -118,12 +118,6 @@ func TestGitHubDownloadRepo(t *testing.T) {
|
|||
"2018-09-05 16:34:22 +0000 UTC",
|
||||
"2018-08-11 08:45:01 +0000 UTC",
|
||||
"closed", milestone)
|
||||
case "1.6.0":
|
||||
assertMilestoneEqual(t, "1.6.0", "2018-09-25 07:00:00 +0000 UTC",
|
||||
"2018-05-11 05:37:01 +0000 UTC",
|
||||
"2019-01-27 19:21:22 +0000 UTC",
|
||||
"2018-11-23 13:23:16 +0000 UTC",
|
||||
"closed", milestone)
|
||||
case "1.7.0":
|
||||
assertMilestoneEqual(t, "1.7.0", "2018-12-25 08:00:00 +0000 UTC",
|
||||
"2018-08-28 14:20:14 +0000 UTC",
|
||||
|
|
|
@ -20,7 +20,7 @@ var (
|
|||
_ base.Notifier = &actionNotifier{}
|
||||
)
|
||||
|
||||
// NewNotifier create a new webhookNotifier notifier
|
||||
// NewNotifier create a new actionNotifier notifier
|
||||
func NewNotifier() base.Notifier {
|
||||
return &actionNotifier{}
|
||||
}
|
||||
|
@ -75,3 +75,19 @@ func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
|
|||
log.Error("NotifyWatchers: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string) {
|
||||
if err := models.NotifyWatchers(&models.Action{
|
||||
ActUserID: doer.ID,
|
||||
ActUser: doer,
|
||||
OpType: models.ActionRenameRepo,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
Content: oldName,
|
||||
}); err != nil {
|
||||
log.Error("notify watchers: %v", err)
|
||||
} else {
|
||||
log.Trace("action.renameRepoAction: %s/%s", doer.Name, repo.Name)
|
||||
}
|
||||
}
|
||||
|
|
47
modules/notification/action/action_test.go
Normal file
47
modules/notification/action/action_test.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package action
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
models.MainTest(m, filepath.Join("..", "..", ".."))
|
||||
}
|
||||
|
||||
func TestRenameRepoAction(t *testing.T) {
|
||||
assert.NoError(t, models.PrepareTestDatabase())
|
||||
|
||||
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
|
||||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{OwnerID: user.ID}).(*models.Repository)
|
||||
repo.Owner = user
|
||||
|
||||
oldRepoName := repo.Name
|
||||
const newRepoName = "newRepoName"
|
||||
repo.Name = newRepoName
|
||||
repo.LowerName = strings.ToLower(newRepoName)
|
||||
|
||||
actionBean := &models.Action{
|
||||
OpType: models.ActionRenameRepo,
|
||||
ActUserID: user.ID,
|
||||
ActUser: user,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
Content: oldRepoName,
|
||||
}
|
||||
models.AssertNotExistsBean(t, actionBean)
|
||||
|
||||
NewNotifier().NotifyRenameRepository(user, repo, oldRepoName)
|
||||
|
||||
models.AssertExistsAndLoadBean(t, actionBean)
|
||||
models.CheckConsistencyFor(t, &models.Action{})
|
||||
}
|
|
@ -17,6 +17,7 @@ type Notifier interface {
|
|||
NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository)
|
||||
NotifyDeleteRepository(doer *models.User, repo *models.Repository)
|
||||
NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository)
|
||||
NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string)
|
||||
|
||||
NotifyNewIssue(*models.Issue)
|
||||
NotifyIssueChangeStatus(*models.User, *models.Issue, bool)
|
||||
|
|
|
@ -66,6 +66,10 @@ func (*NullNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repo
|
|||
func (*NullNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
|
||||
}
|
||||
|
||||
// NotifyRenameRepository places a place holder function
|
||||
func (*NullNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string) {
|
||||
}
|
||||
|
||||
// NotifyNewRelease places a place holder function
|
||||
func (*NullNotifier) NotifyNewRelease(rel *models.Release) {
|
||||
}
|
||||
|
|
|
@ -115,6 +115,13 @@ func NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
|
|||
}
|
||||
}
|
||||
|
||||
// NotifyRenameRepository notifies repository renamed
|
||||
func NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string) {
|
||||
for _, notifier := range notifiers {
|
||||
notifier.NotifyRenameRepository(doer, repo, oldName)
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyNewRelease notifies new release to notifiers
|
||||
func NotifyNewRelease(rel *models.Release) {
|
||||
for _, notifier := range notifiers {
|
||||
|
|
|
@ -27,7 +27,7 @@ var (
|
|||
// TODO: fix invalid linking issue
|
||||
|
||||
// mentionPattern matches all mentions in the form of "@user"
|
||||
mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[0-9a-zA-Z-_\.]+)(?:\s|$|\)|\])`)
|
||||
mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[0-9a-zA-Z-_]+|@[0-9a-zA-Z-_][0-9a-zA-Z-_.]+[0-9a-zA-Z-_])(?:\s|[:,;.?!]\s|[:,;.?!]?$|\)|\])`)
|
||||
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
|
||||
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(#[0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`)
|
||||
// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
|
||||
|
|
|
@ -208,14 +208,32 @@ func testFixtures(t *testing.T, fixtures []testFixture, context string) {
|
|||
}
|
||||
|
||||
func TestRegExp_mentionPattern(t *testing.T) {
|
||||
trueTestCases := []string{
|
||||
"@Unknwon",
|
||||
"@ANT_123",
|
||||
"@xxx-DiN0-z-A..uru..s-xxx",
|
||||
" @lol ",
|
||||
" @Te-st",
|
||||
"(@gitea)",
|
||||
"[@gitea]",
|
||||
trueTestCases := []struct {
|
||||
pat string
|
||||
exp string
|
||||
}{
|
||||
{"@Unknwon", "@Unknwon"},
|
||||
{"@ANT_123", "@ANT_123"},
|
||||
{"@xxx-DiN0-z-A..uru..s-xxx", "@xxx-DiN0-z-A..uru..s-xxx"},
|
||||
{" @lol ", "@lol"},
|
||||
{" @Te-st", "@Te-st"},
|
||||
{"(@gitea)", "@gitea"},
|
||||
{"[@gitea]", "@gitea"},
|
||||
{"@gitea! this", "@gitea"},
|
||||
{"@gitea? this", "@gitea"},
|
||||
{"@gitea. this", "@gitea"},
|
||||
{"@gitea, this", "@gitea"},
|
||||
{"@gitea; this", "@gitea"},
|
||||
{"@gitea!\nthis", "@gitea"},
|
||||
{"\n@gitea?\nthis", "@gitea"},
|
||||
{"\t@gitea.\nthis", "@gitea"},
|
||||
{"@gitea,\nthis", "@gitea"},
|
||||
{"@gitea;\nthis", "@gitea"},
|
||||
{"@gitea!", "@gitea"},
|
||||
{"@gitea?", "@gitea"},
|
||||
{"@gitea.", "@gitea"},
|
||||
{"@gitea,", "@gitea"},
|
||||
{"@gitea;", "@gitea"},
|
||||
}
|
||||
falseTestCases := []string{
|
||||
"@ 0",
|
||||
|
@ -223,17 +241,24 @@ func TestRegExp_mentionPattern(t *testing.T) {
|
|||
"@",
|
||||
"",
|
||||
"ABC",
|
||||
"@.ABC",
|
||||
"/home/gitea/@gitea",
|
||||
"\"@gitea\"",
|
||||
"@@gitea",
|
||||
"@gitea!this",
|
||||
"@gitea?this",
|
||||
"@gitea,this",
|
||||
"@gitea;this",
|
||||
}
|
||||
|
||||
for _, testCase := range trueTestCases {
|
||||
res := mentionPattern.MatchString(testCase)
|
||||
assert.True(t, res)
|
||||
found := mentionPattern.FindStringSubmatch(testCase.pat)
|
||||
assert.Len(t, found, 2)
|
||||
assert.Equal(t, testCase.exp, found[1])
|
||||
}
|
||||
for _, testCase := range falseTestCases {
|
||||
res := mentionPattern.MatchString(testCase)
|
||||
assert.False(t, res)
|
||||
assert.False(t, res, "[%s] should be false", testCase)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
|
||||
package setting
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// Webhook settings
|
||||
Webhook = struct {
|
||||
|
@ -12,11 +18,16 @@ var (
|
|||
SkipTLSVerify bool
|
||||
Types []string
|
||||
PagingNum int
|
||||
ProxyURL string
|
||||
ProxyURLFixed *url.URL
|
||||
ProxyHosts []string
|
||||
}{
|
||||
QueueLength: 1000,
|
||||
DeliverTimeout: 5,
|
||||
SkipTLSVerify: false,
|
||||
PagingNum: 10,
|
||||
ProxyURL: "",
|
||||
ProxyHosts: []string{},
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -27,4 +38,14 @@ func newWebhookService() {
|
|||
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
|
||||
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams"}
|
||||
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
|
||||
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
|
||||
if Webhook.ProxyURL != "" {
|
||||
var err error
|
||||
Webhook.ProxyURLFixed, err = url.Parse(Webhook.ProxyURL)
|
||||
if err != nil {
|
||||
log.Error("Webhook PROXY_URL is not valid")
|
||||
Webhook.ProxyURL = ""
|
||||
}
|
||||
}
|
||||
Webhook.ProxyHosts = sec.Key("PROXY_HOSTS").Strings(",")
|
||||
}
|
||||
|
|
|
@ -97,8 +97,6 @@ func runMigrateTask(t *models.Task) (err error) {
|
|||
opts.MigrateToRepoID = t.RepoID
|
||||
repo, err := migrations.MigrateRepository(t.Doer, t.Owner.Name, *opts)
|
||||
if err == nil {
|
||||
notification.NotifyMigrateRepository(t.Doer, t.Owner, repo)
|
||||
|
||||
log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,11 +12,13 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/unknwon/com"
|
||||
)
|
||||
|
||||
|
@ -182,7 +184,36 @@ func DeliverHooks() {
|
|||
}
|
||||
}
|
||||
|
||||
var webhookHTTPClient *http.Client
|
||||
var (
|
||||
webhookHTTPClient *http.Client
|
||||
once sync.Once
|
||||
hostMatchers []glob.Glob
|
||||
)
|
||||
|
||||
func webhookProxy() func(req *http.Request) (*url.URL, error) {
|
||||
if setting.Webhook.ProxyURL == "" {
|
||||
return http.ProxyFromEnvironment
|
||||
}
|
||||
|
||||
once.Do(func() {
|
||||
for _, h := range setting.Webhook.ProxyHosts {
|
||||
if g, err := glob.Compile(h); err == nil {
|
||||
hostMatchers = append(hostMatchers, g)
|
||||
} else {
|
||||
log.Error("glob.Compile %s failed: %v", h, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return func(req *http.Request) (*url.URL, error) {
|
||||
for _, v := range hostMatchers {
|
||||
if v.Match(req.URL.Host) {
|
||||
return http.ProxyURL(setting.Webhook.ProxyURLFixed)(req)
|
||||
}
|
||||
}
|
||||
return http.ProxyFromEnvironment(req)
|
||||
}
|
||||
}
|
||||
|
||||
// InitDeliverHooks starts the hooks delivery thread
|
||||
func InitDeliverHooks() {
|
||||
|
@ -191,7 +222,7 @@ func InitDeliverHooks() {
|
|||
webhookHTTPClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Proxy: webhookProxy(),
|
||||
Dial: func(netw, addr string) (net.Conn, error) {
|
||||
conn, err := net.DialTimeout(netw, addr, timeout)
|
||||
if err != nil {
|
||||
|
@ -199,7 +230,6 @@ func InitDeliverHooks() {
|
|||
}
|
||||
|
||||
return conn, conn.SetDeadline(time.Now().Add(timeout))
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
39
modules/webhook/deliver_test.go
Normal file
39
modules/webhook/deliver_test.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWebhookProxy(t *testing.T) {
|
||||
setting.Webhook.ProxyURL = "http://localhost:8080"
|
||||
setting.Webhook.ProxyURLFixed, _ = url.Parse(setting.Webhook.ProxyURL)
|
||||
setting.Webhook.ProxyHosts = []string{"*.discordapp.com", "discordapp.com"}
|
||||
|
||||
var kases = map[string]string{
|
||||
"https://discordapp.com/api/webhooks/xxxxxxxxx/xxxxxxxxxxxxxxxxxxx": "http://localhost:8080",
|
||||
"http://s.discordapp.com/assets/xxxxxx": "http://localhost:8080",
|
||||
"http://github.com/a/b": "",
|
||||
}
|
||||
|
||||
for reqURL, proxyURL := range kases {
|
||||
req, err := http.NewRequest("POST", reqURL, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
u, err := webhookProxy()(req)
|
||||
assert.NoError(t, err)
|
||||
if proxyURL == "" {
|
||||
assert.Nil(t, u)
|
||||
} else {
|
||||
assert.EqualValues(t, proxyURL, u.String())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -550,8 +550,6 @@ teams.members=Участници в екипа
|
|||
teams.update_settings=Запази настройките
|
||||
teams.add_team_member=Добави участник в екипа
|
||||
teams.repositories=Хранилища на екипа
|
||||
teams.add_team_repository=Добави хранилище на екипа
|
||||
teams.remove_repo=Премахни
|
||||
teams.add_nonexistent_repo=Хранилището, което се опитвате да добавите не съществува. Моля първо го създайте!
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -1527,8 +1527,6 @@ teams.write_permission_desc=Členství v tom týmu poskytuje právo <strong>záp
|
|||
teams.admin_permission_desc=Členství v tom týmu poskytuje právo <strong>správce</strong>: členové mohou číst z, nahrávat do a přidávat spolupracovníky do repozitářů týmu.
|
||||
teams.repositories=Repozitáře týmu
|
||||
teams.search_repo_placeholder=Hledat repozitář…
|
||||
teams.add_team_repository=Přidat repozitář týmu
|
||||
teams.remove_repo=Smazat
|
||||
teams.add_nonexistent_repo=Repozitář, který se snažíte přidat, neexistuje. Prosím, nejdříve jej vytvořte.
|
||||
teams.add_duplicate_users=Uživatel je již členem týmu.
|
||||
teams.repos.none=Tento tým nemůže přistoupit k žádným repozitářům.
|
||||
|
|
|
@ -1582,8 +1582,6 @@ teams.write_permission_desc=Dieses Team hat <strong>Schreibzugriff</strong>: Mit
|
|||
teams.admin_permission_desc=Dieses Team hat <strong>Adminzugriff</strong>: Mitglieder dieses Teams können Team-Repositories ansehen, auf sie pushen und Mitarbeiter hinzufügen.
|
||||
teams.repositories=Team-Repositories
|
||||
teams.search_repo_placeholder=Repository durchsuchen…
|
||||
teams.add_team_repository=Team-Repository hinzufügen
|
||||
teams.remove_repo=Entfernen
|
||||
teams.add_nonexistent_repo=Das Repository, das du hinzufügen möchten, existiert nicht. Bitte erstelle es zuerst.
|
||||
teams.add_duplicate_users=Dieser Benutzer ist bereits ein Teammitglied.
|
||||
teams.repos.none=Dieses Team hat Zugang zu keinem Repository.
|
||||
|
|
|
@ -68,6 +68,10 @@ pull_requests = Pull Requests
|
|||
issues = Issues
|
||||
|
||||
cancel = Cancel
|
||||
add = Add
|
||||
add_all = Add All
|
||||
remove = Remove
|
||||
remove_all = Remove All
|
||||
|
||||
write = Write
|
||||
preview = Preview
|
||||
|
@ -1583,8 +1587,10 @@ teams.write_permission_desc = This team grants <strong>Write</strong> access: me
|
|||
teams.admin_permission_desc = This team grants <strong>Admin</strong> access: members can read from, push to and add collaborators to team repositories.
|
||||
teams.repositories = Team Repositories
|
||||
teams.search_repo_placeholder = Search repository…
|
||||
teams.add_team_repository = Add Team Repository
|
||||
teams.remove_repo = Remove
|
||||
teams.remove_all_repos_title = Remove all team repositories
|
||||
teams.remove_all_repos_desc = This will remove all repositories from the team.
|
||||
teams.add_all_repos_title = Add all repositories
|
||||
teams.add_all_repos_desc = This will add all the organization's repositories to the team.
|
||||
teams.add_nonexistent_repo = "The repository you're trying to add does not exist; please create it first."
|
||||
teams.add_duplicate_users = User is already a team member.
|
||||
teams.repos.none = No repositories could be accessed by this team.
|
||||
|
|
|
@ -1561,8 +1561,6 @@ teams.write_permission_desc=Este equipo tiene permisos de <strong>Escritura</str
|
|||
teams.admin_permission_desc=Este equipo tiene permisos de <strong>Administración</strong>: los miembros pueden ver, hacer push y añadir colaboradores a los repositorios del equipo.
|
||||
teams.repositories=Repositorios del equipo
|
||||
teams.search_repo_placeholder=Buscar repositorio…
|
||||
teams.add_team_repository=Añadir repositorio al equipo
|
||||
teams.remove_repo=Eliminar
|
||||
teams.add_nonexistent_repo=El repositorio que estás intentando añadir no existe, por favor, créalo primero.
|
||||
teams.add_duplicate_users=El usuario ya es miembro del equipo.
|
||||
teams.repos.none=Este equipo no tiene repositorios accesibles.
|
||||
|
|
|
@ -1563,8 +1563,6 @@ teams.write_permission_desc=این تیم دسترسی <strong>نوشتن</stron
|
|||
teams.admin_permission_desc=این تیم دسترسی <strong>نوشتن</strong> خواهد داشت: اعضا خواهند توانست مخازن تیم را خوانده ، تغییراتی در آنها اعمال کرده و یا همکارانشان را به مخازن اضافه نمایند.
|
||||
teams.repositories=مخازن تیم
|
||||
teams.search_repo_placeholder=جستجوی مخزن...
|
||||
teams.add_team_repository=افزودن مخزن تیمی
|
||||
teams.remove_repo=حذف
|
||||
teams.add_nonexistent_repo=مخزنی را که شما قصد افزودن آن را دارید موجود نیست، لطفا ابتدا آن را ایجاد کنید.
|
||||
teams.add_duplicate_users=این کاربر پیش از این عضو تیم بوده است.
|
||||
teams.repos.none=این تیم به هیچ مخزنی دسترسی ندارد.
|
||||
|
|
|
@ -609,8 +609,6 @@ teams.members=Ryhmän jäsenet
|
|||
teams.update_settings=Päivitä asetukset
|
||||
teams.add_team_member=Lisää tiimin jäsen
|
||||
teams.repositories=Tiimin repot
|
||||
teams.add_team_repository=Lisää tiimirepo
|
||||
teams.remove_repo=Poista
|
||||
teams.add_nonexistent_repo=Repo jota yrität lisätä ei ole vielä olemassa, ole hyvä ja luo se ensin.
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -1550,8 +1550,6 @@ teams.write_permission_desc=Cette équipe permet l'accès en <strong>écriture</
|
|||
teams.admin_permission_desc=Cette équipe permet l'accès <strong>administrateur</strong> : les membres peuvent voir, participer et ajouter des collaborateurs à ses dépôts.
|
||||
teams.repositories=Dépôts de l'Équipe
|
||||
teams.search_repo_placeholder=Rechercher dans le dépôt…
|
||||
teams.add_team_repository=Ajouter un Dépôt à l'Équipe
|
||||
teams.remove_repo=Supprimer
|
||||
teams.add_nonexistent_repo=Dépôt inexistant, veuillez d'abord le créer.
|
||||
teams.add_duplicate_users=L’utilisateur est déjà un membre de l’équipe.
|
||||
teams.repos.none=Aucun dépôt n'est accessible par cette équipe.
|
||||
|
|
|
@ -699,8 +699,6 @@ teams.add_team_member=Csapattag hozzáadása
|
|||
teams.delete_team_success=A csoport törölve lett.
|
||||
teams.repositories=Csoport tárolói
|
||||
teams.search_repo_placeholder=Tároló keresése…
|
||||
teams.add_team_repository=Új csoport tároló
|
||||
teams.remove_repo=Eltávolítás
|
||||
teams.add_nonexistent_repo=A tároló, melybe feltölteni szeretne, még nem létezik; először hozza létre.
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -745,8 +745,6 @@ teams.add_team_member=Tambahkan Anggota Tim
|
|||
teams.delete_team_success=Tim sudah di hapus.
|
||||
teams.repositories=Tim repositori
|
||||
teams.search_repo_placeholder=Cari repositori…
|
||||
teams.add_team_repository=Tambahkan Tim Repositori
|
||||
teams.remove_repo=Menghapus
|
||||
teams.add_nonexistent_repo=Repositori yang ingin Anda tambahkan tidak ada; Silahkan buat terlebih dahulu.
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -1195,8 +1195,6 @@ teams.write_permission_desc=Questo team concede l'accesso di <strong>Scrittura</
|
|||
teams.admin_permission_desc=Questo team concede l'accesso di <strong>Amministratore</strong>: i membri possono leggere da, pushare su e aggiungere collaboratori ai repository del team.
|
||||
teams.repositories=Repository di Squadra
|
||||
teams.search_repo_placeholder=Ricerca repository…
|
||||
teams.add_team_repository=Aggiungere Repository di Squadra
|
||||
teams.remove_repo=Rimuovi
|
||||
teams.add_nonexistent_repo=Il repository che stai tentando di aggiungere non esiste, crealo prima.
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -68,6 +68,10 @@ pull_requests=プルリクエスト
|
|||
issues=課題
|
||||
|
||||
cancel=キャンセル
|
||||
add=追加
|
||||
add_all=すべて追加
|
||||
remove=除去
|
||||
remove_all=すべて除去
|
||||
|
||||
write=書き込み
|
||||
preview=プレビュー
|
||||
|
@ -1514,6 +1518,7 @@ team_name=チーム名
|
|||
team_desc=説明
|
||||
team_name_helper=チーム名は短く覚えやすいものにしましょう。
|
||||
team_desc_helper=チームの目的や役割を説明します。
|
||||
team_access_desc=リポジトリアクセス
|
||||
team_permission_desc=権限
|
||||
team_unit_desc=リポジトリのセクションへのアクセスを許可
|
||||
|
||||
|
@ -1581,12 +1586,21 @@ teams.write_permission_desc=このチームは<strong>書き込み</strong>ア
|
|||
teams.admin_permission_desc=このチームは<strong>管理者</strong>アクセス権を持ちます: メンバーはチームリポジトリの読み取り、プッシュ、共同作業者の追加が可能です。
|
||||
teams.repositories=チームのリポジトリ
|
||||
teams.search_repo_placeholder=リポジトリを検索…
|
||||
teams.add_team_repository=チームのリポジトリを追加
|
||||
teams.remove_repo=削除
|
||||
teams.remove_all_repos_title=チームリポジトリをすべて除去
|
||||
teams.remove_all_repos_desc=チームからすべてのリポジトリを除去します。
|
||||
teams.add_all_repos_title=すべてのリポジトリを追加
|
||||
teams.add_all_repos_desc=組織のすべてのリポジトリをチームに追加します。
|
||||
teams.add_nonexistent_repo=追加しようとしているリポジトリは存在しません。 先にリポジトリを作成してください。
|
||||
teams.add_duplicate_users=ユーザーは既にチームのメンバーです。
|
||||
teams.repos.none=このチームがアクセスできるリポジトリはありません。
|
||||
teams.members.none=このチームにはメンバーがいません。
|
||||
teams.specific_repositories=指定したリポジトリ
|
||||
teams.specific_repositories_helper=メンバーは、明示的にチームへ追加したリポジトリにのみアクセスできます。 これを選択しても、すでに<i>すべてのリポジトリ</i>で追加されたリポジトリは自動的に除去<strong>されません</strong>。
|
||||
teams.all_repositories=すべてのリポジトリ
|
||||
teams.all_repositories_helper=チームはすべてのリポジトリにアクセスできます。 これを選択すると、<strong>既存のすべての</strong>リポジトリをチームに追加します。
|
||||
teams.all_repositories_read_permission_desc=このチームは<strong>すべてのリポジトリ</strong>の<strong>読み取り</strong>アクセス権を持ちます: メンバーはリポジトリの閲覧とクローンが可能です。
|
||||
teams.all_repositories_write_permission_desc=このチームは<strong>すべてのリポジトリ</strong>の<strong>書き込み</strong>アクセス権を持ちます: メンバーはリポジトリの読み取りとプッシュが可能です。
|
||||
teams.all_repositories_admin_permission_desc=このチームは<strong>すべてのリポジトリ</strong>の<strong>管理者</strong>アクセス権を持ちます: メンバーはリポジトリの読み取り、プッシュ、共同作業者の追加が可能です。
|
||||
|
||||
[admin]
|
||||
dashboard=ダッシュボード
|
||||
|
|
|
@ -570,8 +570,6 @@ teams.update_settings=설정 업데이트
|
|||
teams.add_team_member=팀 구성원 추가
|
||||
teams.delete_team_success=팀이 삭제되었습니다.
|
||||
teams.repositories=팀 저장소
|
||||
teams.add_team_repository=팀 저장소 추가
|
||||
teams.remove_repo=삭제
|
||||
teams.add_nonexistent_repo=추가하려는 저장소를 존재하지 않습니다. 먼저 생성해주세요.
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -1539,8 +1539,6 @@ teams.write_permission_desc=Šai komandai ir <strong>rakstīšanas</strong> ties
|
|||
teams.admin_permission_desc=Šai komandai ir <strong>administratora</strong> tiesības: dalībnieki var lasīt, rakstīt un pievienot citus dalībniekus komandas repozitorijiem.
|
||||
teams.repositories=Komandas repozitoriji
|
||||
teams.search_repo_placeholder=Meklēt repozitorijā…
|
||||
teams.add_team_repository=Pievienot komandas repozitoriju
|
||||
teams.remove_repo=Noņemt
|
||||
teams.add_nonexistent_repo=Repozitorijs, kuram Jūs mēģinat pievienot neeksistē, sākumā izveidojiet to.
|
||||
teams.add_duplicate_users=Lietotājs jau ir šajā komandā.
|
||||
teams.repos.none=Šai komandai nav piekļuves nevienam repozitorijam.
|
||||
|
|
|
@ -1156,8 +1156,6 @@ teams.delete_team_title=Verwijder team
|
|||
teams.delete_team_success=Het team is verwijderd.
|
||||
teams.repositories=Teamrepositories
|
||||
teams.search_repo_placeholder=Repository zoeken…
|
||||
teams.add_team_repository=Nieuwe teamrepositorie aanmaken
|
||||
teams.remove_repo=Verwijder
|
||||
teams.add_nonexistent_repo=De opslagplaats die u probeert toe te voegen bestaat niet: maak deze eerst aan.
|
||||
teams.add_duplicate_users=Gebruiker is al een teamlid.
|
||||
|
||||
|
|
|
@ -1468,8 +1468,6 @@ teams.write_permission_desc=Ten zespół udziela dostępu <strong>z zapisem</str
|
|||
teams.admin_permission_desc=Ten zespół udziela dostępu <strong>administratora</strong>: członkowie mogą wyświetlać i wypychać zmiany oraz dodawać współpracowników do repozytoriów zespołu.
|
||||
teams.repositories=Repozytoria zespołu
|
||||
teams.search_repo_placeholder=Szukaj repozytorium…
|
||||
teams.add_team_repository=Dodaj repozytorium zespołu
|
||||
teams.remove_repo=Usuń
|
||||
teams.add_nonexistent_repo=Repozytorium, które próbujesz dodać, nie istnieje. Proszę je najpierw utworzyć.
|
||||
teams.add_duplicate_users=Użytkownik jest już członkiem zespołu.
|
||||
teams.repos.none=Ten zespół nie ma dostępu do żadnego repozytorium.
|
||||
|
|
|
@ -1582,8 +1582,6 @@ teams.write_permission_desc=Esta equipe concede acesso para <strong>escrita</str
|
|||
teams.admin_permission_desc=Esta equipe concede acesso de <strong>Administrador</strong>: Membros podem ler, fazer push e adicionar outros colaboradores para os repositórios da equipe.
|
||||
teams.repositories=Repositórios da equipe
|
||||
teams.search_repo_placeholder=Pesquisar repositório...
|
||||
teams.add_team_repository=Adicionar repositório da equipe
|
||||
teams.remove_repo=Remover
|
||||
teams.add_nonexistent_repo=O repositório que você está tentando adicionar não existe, por favor, crie-o primeiro.
|
||||
teams.add_duplicate_users=Usuário já é um membro da equipe.
|
||||
teams.repos.none=Nenhum repositório pode ser acessado por essa equipe.
|
||||
|
|
|
@ -1423,8 +1423,6 @@ teams.write_permission_desc=Эта команда предоставляет д
|
|||
teams.admin_permission_desc=Эта команда дает <strong>административный</strong> доступ: участники могут читать, пушить и добавлять соавторов к ее репозиториям.
|
||||
teams.repositories=Репозитории группы разработки
|
||||
teams.search_repo_placeholder=Поиск репозитория…
|
||||
teams.add_team_repository=Добавить репозиторий группы разработки
|
||||
teams.remove_repo=Удалить
|
||||
teams.add_nonexistent_repo=Вы добавляете в отсутствующий репозиторий, пожалуйста сначала его создайте.
|
||||
teams.add_duplicate_users=Пользователь уже состоит в команде.
|
||||
teams.repos.none=Для этой команды нет доступных репозиториев.
|
||||
|
|
|
@ -488,8 +488,6 @@ teams.members=Чланови тима
|
|||
teams.update_settings=Примени промене
|
||||
teams.add_team_member=Додај члан тиму
|
||||
teams.repositories=Тимска спремишта
|
||||
teams.add_team_repository=Додај тимско спремиште
|
||||
teams.remove_repo=Уклони
|
||||
teams.add_nonexistent_repo=Овакво спремиште не постоји, молим вас прво да га направите.
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -1231,8 +1231,6 @@ teams.write_permission_desc=Medlemskap i detta team ger <strong>skrivrättighete
|
|||
teams.admin_permission_desc=Medlemskap i detta team ger <strong>administratörsrättigheter</strong>: medlemmar kan läsa, pusha och lägga till medarbetare till teamets utvecklingskataloger.
|
||||
teams.repositories=Teamförråd
|
||||
teams.search_repo_placeholder=Sök utvecklingskatalog…
|
||||
teams.add_team_repository=Lägg till teamförråd
|
||||
teams.remove_repo=Ta bort
|
||||
teams.add_nonexistent_repo=Förrådet du försöka lägga till finns inte, vänligen skapa det först.
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -1517,8 +1517,6 @@ teams.write_permission_desc=Bu takım <strong>Yazma</strong> erişimi veriyor.
|
|||
teams.admin_permission_desc=Bu takım <strong>Yönetici</strong> erişimi veriyor. Üyeler takım depolarını okuyabilir, itebilir ve katkıcı ekleyebilir.
|
||||
teams.repositories=Ekip Depoları
|
||||
teams.search_repo_placeholder=Depo ara…
|
||||
teams.add_team_repository=Ekip Deposu Ekle
|
||||
teams.remove_repo=Kaldır
|
||||
teams.add_nonexistent_repo=Eklemeye çalıştığınz depo mevcut değil. Lütfen önce oluşturun.
|
||||
teams.add_duplicate_users=Kullanıcı zaten takımın üyesi.
|
||||
teams.repos.none=Bu takım tarafından hiçbir depoya erişilemedi.
|
||||
|
|
|
@ -1294,8 +1294,6 @@ teams.write_permission_desc=Ця команда надає доступ на <st
|
|||
teams.admin_permission_desc=Ця команда надає <strong>адміністраторський</strong> доступ: учасники можуть читати, виконувати push команди та додавати співробітників до репозиторію.
|
||||
teams.repositories=Репозиторії команди
|
||||
teams.search_repo_placeholder=Пошук репозиторію…
|
||||
teams.add_team_repository=Додати репозиторій команди
|
||||
teams.remove_repo=Видалити
|
||||
teams.add_nonexistent_repo=Ви намагаєтеся додати у репозиторій якого не існує. Будь ласка, спочатку створіть його.
|
||||
teams.add_duplicate_users=Користувач уже є членом команди.
|
||||
|
||||
|
|
|
@ -1577,8 +1577,6 @@ teams.write_permission_desc=该团队拥有对所属仓库的 <strong>读取</st
|
|||
teams.admin_permission_desc=该团队拥有一定的 <strong>管理</strong> 权限,团队成员可以读取、克隆、推送以及添加其它仓库协作者。
|
||||
teams.repositories=团队仓库
|
||||
teams.search_repo_placeholder=搜索仓库...
|
||||
teams.add_team_repository=添加团队仓库
|
||||
teams.remove_repo=移除仓库
|
||||
teams.add_nonexistent_repo=您尝试添加到团队的仓库不存在,请先创建仓库!
|
||||
teams.add_duplicate_users=用户已经是团队成员。
|
||||
teams.repos.none=此团队无法访问任何仓库。
|
||||
|
|
|
@ -588,8 +588,6 @@ teams.update_settings=更新團隊設定
|
|||
teams.add_team_member=新增團隊成員
|
||||
teams.delete_team_success=該團隊已被刪除。
|
||||
teams.repositories=團隊儲存庫
|
||||
teams.add_team_repository=新增團隊儲存庫
|
||||
teams.remove_repo=移除儲存庫
|
||||
teams.add_nonexistent_repo=您嘗試新增到團隊的儲存庫不存在,請先建立儲存庫!
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -1037,8 +1037,6 @@ teams.delete_team_title=刪除團隊
|
|||
teams.delete_team_success=該團隊已被刪除。
|
||||
teams.repositories=團隊儲存庫
|
||||
teams.search_repo_placeholder=搜尋儲存庫...
|
||||
teams.add_team_repository=新增團隊儲存庫
|
||||
teams.remove_repo=移除儲存庫
|
||||
teams.add_nonexistent_repo=您嘗試新增到團隊的儲存庫不存在,請先建立儲存庫!
|
||||
|
||||
[admin]
|
||||
|
|
|
@ -945,8 +945,9 @@ tbody.commit-list{vertical-align:baseline}
|
|||
.organization.teams .members .item,.organization.teams .repositories .item{padding:10px 20px;line-height:32px}
|
||||
.organization.teams .members .item:not(:last-child),.organization.teams .repositories .item:not(:last-child){border-bottom:1px solid #ddd}
|
||||
.organization.teams .members .item .button,.organization.teams .repositories .item .button{padding:9px 10px}
|
||||
.organization.teams #add-member-form input,.organization.teams #add-repo-form input{margin-left:0}
|
||||
.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button{margin-left:5px;margin-top:-3px}
|
||||
.organization.teams #add-member-form input,.organization.teams #add-repo-form input,.organization.teams #repo-multiple-form input{margin-left:0}
|
||||
.organization.teams #add-member-form .ui.button,.organization.teams #add-repo-form .ui.button,.organization.teams #repo-multiple-form .ui.button{margin-left:5px;margin-top:-3px}
|
||||
.organization.teams #repo-top-segment{height:60px}
|
||||
.user:not(.icon){padding-top:15px}
|
||||
.user.profile .ui.card .username{display:block}
|
||||
.user.profile .ui.card .extra.content{padding:0}
|
||||
|
|
|
@ -2263,6 +2263,7 @@ $(document).ready(function () {
|
|||
|
||||
// Helpers.
|
||||
$('.delete-button').click(showDeletePopup);
|
||||
$('.add-all-button').click(showAddAllPopup);
|
||||
|
||||
$('.delete-branch-button').click(showDeletePopup);
|
||||
|
||||
|
@ -2501,6 +2502,35 @@ function showDeletePopup() {
|
|||
return false;
|
||||
}
|
||||
|
||||
function showAddAllPopup() {
|
||||
const $this = $(this);
|
||||
let filter = "";
|
||||
if ($this.attr("id")) {
|
||||
filter += "#" + $this.attr("id")
|
||||
}
|
||||
|
||||
const dialog = $('.addall.modal' + filter);
|
||||
dialog.find('.name').text($this.data('name'));
|
||||
|
||||
dialog.modal({
|
||||
closable: false,
|
||||
onApprove: function() {
|
||||
if ($this.data('type') == "form") {
|
||||
$($this.data('form')).submit();
|
||||
return;
|
||||
}
|
||||
|
||||
$.post($this.data('url'), {
|
||||
"_csrf": csrf,
|
||||
"id": $this.data("id")
|
||||
}).done(function(data) {
|
||||
window.location.href = data.redirect;
|
||||
});
|
||||
}
|
||||
}).modal('show');
|
||||
return false;
|
||||
}
|
||||
|
||||
function initVueComponents(){
|
||||
const vueDelimeters = ['${', '}'];
|
||||
|
||||
|
|
4023
public/js/semantic.dropdown.custom.js
Normal file
4023
public/js/semantic.dropdown.custom.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -152,6 +152,7 @@
|
|||
}
|
||||
|
||||
#add-repo-form,
|
||||
#repo-multiple-form,
|
||||
#add-member-form {
|
||||
input {
|
||||
margin-left: 0;
|
||||
|
@ -162,5 +163,9 @@
|
|||
margin-top: -3px;
|
||||
}
|
||||
}
|
||||
|
||||
#repo-top-segment {
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
5
public/vendor/librejs.html
vendored
5
public/vendor/librejs.html
vendored
|
@ -30,6 +30,11 @@
|
|||
<td><a href="https://semantic-ui.mit-license.org/">Expat</a></td>
|
||||
<td><a href="https://github.com/Semantic-Org/Semantic-UI/archive/2.3.1.tar.gz">semantic-UI-2.3.1.tar.gz</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="../js/semantic.dropdown.custom.js">semantic.dropdown.custom.js</a></td>
|
||||
<td><a href="https://semantic-ui.mit-license.org/">Expat</a></td>
|
||||
<td><a href="https://github.com/go-gitea/gitea/tree/master/public/js">semantic.dropdown.custom.js</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="../js/index.js">index.js</a></td>
|
||||
<td><a href="https://github.com/go-gitea/gitea/blob/master/LICENSE">Expat</a></td>
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -425,15 +427,54 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
|
|||
opts.Releases = false
|
||||
}
|
||||
|
||||
repo, err := migrations.MigrateRepository(ctx.User, ctxUser.Name, opts)
|
||||
if err == nil {
|
||||
notification.NotifyMigrateRepository(ctx.User, ctxUser, repo)
|
||||
|
||||
log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
|
||||
ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
|
||||
repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
|
||||
Name: opts.RepoName,
|
||||
Description: opts.Description,
|
||||
OriginalURL: opts.CloneAddr,
|
||||
IsPrivate: opts.Private,
|
||||
IsMirror: opts.Mirror,
|
||||
Status: models.RepositoryBeingMigrated,
|
||||
})
|
||||
if err != nil {
|
||||
handleMigrateError(ctx, ctxUser, remoteAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
opts.MigrateToRepoID = repo.ID
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "Handler crashed with error: %v", log.Stack(2))
|
||||
|
||||
err = errors.New(buf.String())
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
repo.Status = models.RepositoryReady
|
||||
if err := models.UpdateRepositoryCols(repo, "status"); err == nil {
|
||||
notification.NotifyMigrateRepository(ctx.User, ctxUser, repo)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if repo != nil {
|
||||
if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
|
||||
log.Error("DeleteRepository: %v", errDelete)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err = migrations.MigrateRepository(ctx.User, ctxUser.Name, opts); err != nil {
|
||||
handleMigrateError(ctx, ctxUser, remoteAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
|
||||
ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
|
||||
}
|
||||
|
||||
func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteAddr string, err error) {
|
||||
switch {
|
||||
case models.IsErrRepoAlreadyExist(err):
|
||||
ctx.Error(409, "", "The repository with the same name already exists.")
|
||||
|
@ -442,7 +483,7 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
|
|||
case migrations.IsTwoFactorAuthError(err):
|
||||
ctx.Error(422, "", "Remote visit required two factors authentication.")
|
||||
case models.IsErrReachLimitOfRepo(err):
|
||||
ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", ctxUser.MaxCreationLimit()))
|
||||
ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
|
||||
case models.IsErrNameReserved(err):
|
||||
ctx.Error(422, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
|
||||
case models.IsErrNamePatternNotAllowed(err):
|
||||
|
@ -603,11 +644,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
|
|||
return err
|
||||
}
|
||||
|
||||
if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil {
|
||||
log.Error("RenameRepoAction: %v", err)
|
||||
ctx.Error(http.StatusInternalServerError, "RenameRepoActions", err)
|
||||
return err
|
||||
}
|
||||
notification.NotifyRenameRepository(ctx.User, repo, oldRepoName)
|
||||
|
||||
log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
|
||||
}
|
||||
|
|
|
@ -155,6 +155,10 @@ func TeamsRepoAction(ctx *context.Context) {
|
|||
err = ctx.Org.Team.AddRepository(repo)
|
||||
case "remove":
|
||||
err = ctx.Org.Team.RemoveRepository(com.StrTo(ctx.Query("repoid")).MustInt64())
|
||||
case "addall":
|
||||
err = ctx.Org.Team.AddAllRepositories()
|
||||
case "removeall":
|
||||
err = ctx.Org.Team.RemoveAllRepositories()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -162,6 +166,10 @@ func TeamsRepoAction(ctx *context.Context) {
|
|||
ctx.ServerError("TeamsRepoAction", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"redirect": ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories",
|
||||
})
|
||||
ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/validation"
|
||||
|
@ -121,9 +122,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
|||
log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||
|
||||
if isNameChanged {
|
||||
if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil {
|
||||
log.Error("RenameRepoAction: %v", err)
|
||||
}
|
||||
notification.NotifyRenameRepository(ctx.User, repo, oldRepoName)
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
|
||||
|
|
|
@ -707,7 +707,7 @@ func oAuth2UserLoginCallback(loginSource *models.LoginSource, request *http.Requ
|
|||
|
||||
// LinkAccount shows the page where the user can decide to login or create a new account
|
||||
func LinkAccount(ctx *context.Context) {
|
||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationCaptcha || setting.Service.AllowOnlyExternalRegistration
|
||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
|
||||
ctx.Data["Title"] = ctx.Tr("link_account")
|
||||
ctx.Data["LinkAccountMode"] = true
|
||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha
|
||||
|
@ -757,7 +757,7 @@ func LinkAccount(ctx *context.Context) {
|
|||
|
||||
// LinkAccountPostSignIn handle the coupling of external account with another account using signIn
|
||||
func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
|
||||
ctx.Data["DisablePassword"] = setting.Service.AllowOnlyExternalRegistration
|
||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
|
||||
ctx.Data["Title"] = ctx.Tr("link_account")
|
||||
ctx.Data["LinkAccountMode"] = true
|
||||
ctx.Data["LinkAccountModeSignIn"] = true
|
||||
|
@ -840,7 +840,7 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
|
|||
func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterForm) {
|
||||
// TODO Make insecure passwords optional for local accounts also,
|
||||
// once email-based Second-Factor Auth is available
|
||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationCaptcha || setting.Service.AllowOnlyExternalRegistration
|
||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
|
||||
ctx.Data["Title"] = ctx.Tr("link_account")
|
||||
ctx.Data["LinkAccountMode"] = true
|
||||
ctx.Data["LinkAccountModeRegister"] = true
|
||||
|
@ -1070,6 +1070,11 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
|
|||
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplSignUp, &form)
|
||||
return
|
||||
}
|
||||
if !password.IsComplexEnough(form.Password) {
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplSignUp, &form)
|
||||
return
|
||||
}
|
||||
|
||||
u := &models.User{
|
||||
Name: form.UserName,
|
||||
|
|
|
@ -63,11 +63,13 @@
|
|||
noMatchTemplate: function () { return null },
|
||||
menuItemTemplate: function (item) {
|
||||
var user = item.original;
|
||||
var itemStr = '<img src="' + user.avatar + '"/><span class="name">' + user.name + '</span>';
|
||||
var item = $('<div/>')
|
||||
item.append($('<img/>', {'src': user.avatar}))
|
||||
item.append($('<span/>', {'class': 'name'}).text(user.name))
|
||||
if (user.fullname && user.fullname != '') {
|
||||
itemStr += '<span class="fullname">' + user.fullname + '</span>';
|
||||
item.append($('<span/>', {'class': 'fullname'}).text(user.fullname))
|
||||
}
|
||||
return itemStr;
|
||||
return item.html();
|
||||
}
|
||||
});
|
||||
var content = document.getElementById('content');
|
||||
|
@ -121,6 +123,7 @@
|
|||
|
||||
<!-- JavaScript -->
|
||||
<script src="{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.js"></script>
|
||||
<script src="{{StaticUrlPrefix}}/js/semantic.dropdown.custom.js?v={{MD5 AppVer}}"></script>
|
||||
<script src="{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}"></script>
|
||||
{{if .EnableHeatmap}}
|
||||
<script src="{{StaticUrlPrefix}}/vendor/plugins/moment/moment.min.js" charset="utf-8"></script>
|
||||
|
|
|
@ -9,25 +9,33 @@
|
|||
{{template "org/team/navbar" .}}
|
||||
{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
|
||||
{{if $canAddRemove}}
|
||||
<div class="ui attached segment">
|
||||
<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="inline field ui left">
|
||||
<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
|
||||
<div class="ui input">
|
||||
<input class="prompt" name="repo_name" placeholder="{{.i18n.Tr "org.teams.search_repo_placeholder"}}" autocomplete="off" required>
|
||||
<div class="ui attached segment" id="repo-top-segment">
|
||||
<div class="inline ui field left">
|
||||
<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="inline field ui left">
|
||||
<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
|
||||
<div class="ui input">
|
||||
<input class="prompt" name="repo_name" placeholder="{{.i18n.Tr "org.teams.search_repo_placeholder"}}" autocomplete="off" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ui green button">{{.i18n.Tr "org.teams.add_team_repository"}}</button>
|
||||
</form>
|
||||
<button class="ui green button">{{.i18n.Tr "add"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="inline ui field right">
|
||||
<form class="ui form" id="repo-multiple-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/repositories" method="post">
|
||||
<button class="ui red button delete-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/removeall">{{.i18n.Tr "remove_all"}}</button>
|
||||
<button class="ui green button add-all-button right" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/addall">{{.i18n.Tr "add_all"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="ui bottom attached table segment repositories">
|
||||
{{range .Team.Repos}}
|
||||
<div class="item">
|
||||
{{if $canAddRemove}}
|
||||
<a class="ui red small button right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove?repoid={{.ID}}">{{$.i18n.Tr "org.teams.remove_repo"}}</a>
|
||||
<a class="ui red small button right" href="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/remove?repoid={{.ID}}">{{$.i18n.Tr "remove"}}</a>
|
||||
{{end}}
|
||||
<a class="member" href="{{AppSubUrl}}/{{$.Org.Name}}/{{.Name}}">
|
||||
<i class="octicon octicon-{{if .IsPrivate}}lock{{else if .IsFork}}repo-forked{{else if .IsMirror}}repo-clone{{else}}repo{{end}}"></i>
|
||||
|
@ -44,4 +52,27 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui small basic delete modal">
|
||||
<div class="ui icon header">
|
||||
<i class="trash icon"></i>
|
||||
{{.i18n.Tr "org.teams.remove_all_repos_title"}}
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>{{.i18n.Tr "org.teams.remove_all_repos_desc"}}</p>
|
||||
</div>
|
||||
{{template "base/delete_modal_actions" .}}
|
||||
</div>
|
||||
|
||||
<div class="ui small basic addall modal">
|
||||
<div class="ui icon header">
|
||||
<i class="globe icon"></i>
|
||||
{{.i18n.Tr "org.teams.add_all_repos_title"}}
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>{{.i18n.Tr "org.teams.add_all_repos_desc"}}</p>
|
||||
</div>
|
||||
{{template "base/delete_modal_actions" .}}
|
||||
</div>
|
||||
|
||||
{{template "base/footer" .}}
|
||||
|
|
|
@ -6,6 +6,7 @@ var urlsToCache = [
|
|||
'{{StaticUrlPrefix}}/vendor/plugins/jquery-migrate/jquery-migrate.min.js?v=3.0.1',
|
||||
'{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.js',
|
||||
'{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}',
|
||||
'{{StaticUrlPrefix}}/js/semantic.dropdown.custom.js?v={{MD5 AppVer}}',
|
||||
'{{StaticUrlPrefix}}/js/draw.js',
|
||||
'{{StaticUrlPrefix}}/vendor/plugins/clipboard/clipboard.min.js',
|
||||
'{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.js',
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<div class="content">
|
||||
<div class="ui top attached header">
|
||||
{{if .Issue.OriginalAuthor }}
|
||||
<span class="text black"><i class="fa {{MigrationIcon .Repository.GetOriginalURLHostname}}" aria-hidden="true"></i> {{ .Issue.OriginalAuthor }}</span><span class="text grey"> {{.i18n.Tr "repo.issues.commented_at" .Issue.HashTag $createdStr | Safe}}<span> <span class="text migrate">{{if .Repository.OriginalURL}} ({{$.i18n.Tr "repo.migrated_from" .Repository.OriginalURL .Repository.GetOriginalURLHostname | Safe }}){{end}}</span>
|
||||
<span class="text black"><i class="fa {{MigrationIcon .Repository.GetOriginalURLHostname}}" aria-hidden="true"></i> {{ .Issue.OriginalAuthor }}</span><span class="text grey"> {{.i18n.Tr "repo.issues.commented_at" .Issue.HashTag $createdStr | Safe}}</span> <span class="text migrate">{{if .Repository.OriginalURL}} ({{$.i18n.Tr "repo.migrated_from" .Repository.OriginalURL .Repository.GetOriginalURLHostname | Safe }}){{end}}</span>
|
||||
{{else}}
|
||||
<span class="text grey"><a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a> {{.i18n.Tr "repo.issues.commented_at" .Issue.HashTag $createdStr | Safe}}</span>
|
||||
{{end}}
|
||||
|
|
Loading…
Reference in a new issue