mirror of
https://github.com/go-gitea/gitea.git
synced 2024-09-01 14:56:30 +00:00
Merge branch 'go-gitea:main' into pacman-packages
This commit is contained in:
commit
73fe874862
@ -5,7 +5,6 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@ -13,9 +12,6 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// EnvironmentPrefix environment variables prefixed with this represent ini values to write
|
||||
const EnvironmentPrefix = "GITEA"
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "environment-to-ini"
|
||||
@ -70,15 +66,6 @@ func main() {
|
||||
Value: "",
|
||||
Usage: "Destination file to write to",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "clear",
|
||||
Usage: "Clears the matched variables from the environment",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prefix, p",
|
||||
Value: EnvironmentPrefix,
|
||||
Usage: "Environment prefix to look for - will be suffixed by __ (2 underscores)",
|
||||
},
|
||||
}
|
||||
app.Action = runEnvironmentToIni
|
||||
err := app.Run(os.Args)
|
||||
@ -99,9 +86,7 @@ func runEnvironmentToIni(c *cli.Context) error {
|
||||
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||
}
|
||||
|
||||
prefixGitea := c.String("prefix") + "__"
|
||||
suffixFile := "__FILE"
|
||||
changed := setting.EnvironmentToConfig(cfg, prefixGitea, suffixFile, os.Environ())
|
||||
changed := setting.EnvironmentToConfig(cfg, os.Environ())
|
||||
|
||||
// try to save the config file
|
||||
destination := c.String("out")
|
||||
@ -116,19 +101,5 @@ func runEnvironmentToIni(c *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// clear Gitea's specific environment variables if requested
|
||||
if c.Bool("clear") {
|
||||
for _, kv := range os.Environ() {
|
||||
idx := strings.IndexByte(kv, '=')
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
eKey := kv[:idx]
|
||||
if strings.HasPrefix(eKey, prefixGitea) {
|
||||
_ = os.Unsetenv(eKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
#
|
||||
# And place the original in /usr/lib/gitea with working files in /data/gitea
|
||||
GITEA="/app/gitea/gitea"
|
||||
WORK_DIR="/app/gitea"
|
||||
WORK_DIR="/data/gitea"
|
||||
CUSTOM_PATH="/data/gitea"
|
||||
|
||||
# Provide docker defaults
|
||||
|
@ -288,7 +288,7 @@ docker-compose up -d
|
||||
|
||||
In addition to the environment variables above, any settings in `app.ini` can be set
|
||||
or overridden with an environment variable of the form: `GITEA__SECTION_NAME__KEY_NAME`.
|
||||
These settings are applied each time the docker container starts.
|
||||
These settings are applied each time the docker container starts, and won't be passed into Gitea's sub-processes.
|
||||
Full information [here](https://github.com/go-gitea/gitea/tree/main/contrib/environment-to-ini).
|
||||
|
||||
These environment variables can be passed to the docker container in `docker-compose.yml`.
|
||||
|
@ -289,7 +289,7 @@ docker-compose up -d
|
||||
|
||||
In addition to the environment variables above, any settings in `app.ini` can be set
|
||||
or overridden with an environment variable of the form: `GITEA__SECTION_NAME__KEY_NAME`.
|
||||
These settings are applied each time the docker container starts.
|
||||
These settings are applied each time the docker container starts, and won't be passed into Gitea's sub-processes.
|
||||
Full information [here](https://github.com/go-gitea/gitea/tree/master/contrib/environment-to-ini).
|
||||
|
||||
These environment variables can be passed to the docker container in `docker-compose.yml`.
|
||||
|
@ -81,3 +81,21 @@
|
||||
uid: 5
|
||||
org_id: 23
|
||||
is_public: false
|
||||
|
||||
-
|
||||
id: 15
|
||||
uid: 1
|
||||
org_id: 35
|
||||
is_public: true
|
||||
|
||||
-
|
||||
id: 16
|
||||
uid: 1
|
||||
org_id: 36
|
||||
is_public: true
|
||||
|
||||
-
|
||||
id: 17
|
||||
uid: 5
|
||||
org_id: 36
|
||||
is_public: true
|
||||
|
@ -184,3 +184,36 @@
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
id: 18
|
||||
org_id: 35
|
||||
lower_name: owners
|
||||
name: Owners
|
||||
authorize: 4 # owner
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
id: 19
|
||||
org_id: 36
|
||||
lower_name: owners
|
||||
name: Owners
|
||||
authorize: 4 # owner
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
id: 20
|
||||
org_id: 36
|
||||
lower_name: team20writepackage
|
||||
name: team20writepackage
|
||||
authorize: 1
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
@ -273,4 +273,10 @@
|
||||
id: 46
|
||||
team_id: 17
|
||||
type: 9 # package
|
||||
access_mode: 0
|
||||
access_mode: 2
|
||||
|
||||
-
|
||||
id: 47
|
||||
team_id: 20
|
||||
type: 9 # package
|
||||
access_mode: 2
|
||||
|
@ -105,3 +105,21 @@
|
||||
org_id: 23
|
||||
team_id: 17
|
||||
uid: 5
|
||||
|
||||
-
|
||||
id: 19
|
||||
org_id: 35
|
||||
team_id: 18
|
||||
uid: 1
|
||||
|
||||
-
|
||||
id: 20
|
||||
org_id: 36
|
||||
team_id: 19
|
||||
uid: 1
|
||||
|
||||
-
|
||||
id: 21
|
||||
org_id: 36
|
||||
team_id: 20
|
||||
uid: 5
|
||||
|
@ -1258,3 +1258,77 @@
|
||||
repo_admin_change_team_access: false
|
||||
theme: ""
|
||||
keep_activity_private: false
|
||||
|
||||
-
|
||||
id: 35
|
||||
lower_name: private_org35
|
||||
name: private_org35
|
||||
full_name: Private Org 35
|
||||
email: private_org35@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: ZogKvWdyEx:password
|
||||
passwd_hash_algo: dummy
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: private_org35
|
||||
type: 1
|
||||
salt: ZogKvWdyEx
|
||||
max_repo_creation: -1
|
||||
is_active: true
|
||||
is_admin: false
|
||||
is_restricted: false
|
||||
allow_git_hook: false
|
||||
allow_import_local: false
|
||||
allow_create_organization: true
|
||||
prohibit_login: false
|
||||
avatar: avatar35
|
||||
avatar_email: private_org35@example.com
|
||||
use_custom_avatar: false
|
||||
num_followers: 0
|
||||
num_following: 0
|
||||
num_stars: 0
|
||||
num_repos: 0
|
||||
num_teams: 1
|
||||
num_members: 1
|
||||
visibility: 2
|
||||
repo_admin_change_team_access: false
|
||||
theme: ""
|
||||
keep_activity_private: false
|
||||
|
||||
-
|
||||
id: 36
|
||||
lower_name: limited_org36
|
||||
name: limited_org36
|
||||
full_name: Limited Org 36
|
||||
email: limited_org36@example.com
|
||||
keep_email_private: false
|
||||
email_notifications_preference: enabled
|
||||
passwd: ZogKvWdyEx:password
|
||||
passwd_hash_algo: dummy
|
||||
must_change_password: false
|
||||
login_source: 0
|
||||
login_name: limited_org36
|
||||
type: 1
|
||||
salt: ZogKvWdyEx
|
||||
max_repo_creation: -1
|
||||
is_active: true
|
||||
is_admin: false
|
||||
is_restricted: false
|
||||
allow_git_hook: false
|
||||
allow_import_local: false
|
||||
allow_create_organization: true
|
||||
prohibit_login: false
|
||||
avatar: avatar22
|
||||
avatar_email: limited_org36@example.com
|
||||
use_custom_avatar: false
|
||||
num_followers: 0
|
||||
num_following: 0
|
||||
num_stars: 0
|
||||
num_repos: 0
|
||||
num_teams: 2
|
||||
num_members: 2
|
||||
visibility: 1
|
||||
repo_admin_change_team_access: false
|
||||
theme: ""
|
||||
keep_activity_private: false
|
||||
|
@ -382,7 +382,8 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
||||
}
|
||||
|
||||
// FindRecentlyPushedNewBranches return at most 2 new branches pushed by the user in 6 hours which has no opened PRs created
|
||||
func FindRecentlyPushedNewBranches(ctx context.Context, repoID, userID int64) (BranchList, error) {
|
||||
// except the indicate branch
|
||||
func FindRecentlyPushedNewBranches(ctx context.Context, repoID, userID int64, excludeBranchName string) (BranchList, error) {
|
||||
branches := make(BranchList, 0, 2)
|
||||
subQuery := builder.Select("head_branch").From("pull_request").
|
||||
InnerJoin("issue", "issue.id = pull_request.issue_id").
|
||||
@ -392,6 +393,7 @@ func FindRecentlyPushedNewBranches(ctx context.Context, repoID, userID int64) (B
|
||||
})
|
||||
err := db.GetEngine(ctx).
|
||||
Where("pusher_id=? AND is_deleted=?", userID, false).
|
||||
And("name <> ?", excludeBranchName).
|
||||
And("updated_unix >= ?", time.Now().Add(-time.Hour*6).Unix()).
|
||||
NotIn("name", subQuery).
|
||||
OrderBy("branch.updated_unix DESC").
|
||||
|
@ -65,13 +65,6 @@ func (a *Attachment) DownloadURL() string {
|
||||
return setting.AppURL + "attachments/" + url.PathEscape(a.UUID)
|
||||
}
|
||||
|
||||
// _____ __ __ .__ __
|
||||
// / _ \_/ |__/ |______ ____ | |__ _____ ____ _____/ |_
|
||||
// / /_\ \ __\ __\__ \ _/ ___\| | \ / \_/ __ \ / \ __\
|
||||
// / | \ | | | / __ \\ \___| Y \ Y Y \ ___/| | \ |
|
||||
// \____|__ /__| |__| (____ /\___ >___| /__|_| /\___ >___| /__|
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
// ErrAttachmentNotExist represents a "AttachmentNotExist" kind of error.
|
||||
type ErrAttachmentNotExist struct {
|
||||
ID int64
|
||||
|
@ -11,7 +11,7 @@ type MergeStyle string
|
||||
const (
|
||||
// MergeStyleMerge create merge commit
|
||||
MergeStyleMerge MergeStyle = "merge"
|
||||
// MergeStyleRebase rebase before merging
|
||||
// MergeStyleRebase rebase before merging, and fast-forward
|
||||
MergeStyleRebase MergeStyle = "rebase"
|
||||
// MergeStyleRebaseMerge rebase before merging with merge commit (--no-ff)
|
||||
MergeStyleRebaseMerge MergeStyle = "rebase-merge"
|
||||
|
@ -215,6 +215,7 @@ func (l *LayeredFS) WatchLocalChanges(ctx context.Context, callback func()) {
|
||||
log.Error("Unable to list directories for asset local file-system %q: %v", layer.localPath, err)
|
||||
continue
|
||||
}
|
||||
layerDirs = append(layerDirs, ".")
|
||||
for _, dir := range layerDirs {
|
||||
if err = watcher.Add(util.FilePathJoinAbs(layer.localPath, dir)); err != nil {
|
||||
log.Error("Unable to watch directory %s: %v", dir, err)
|
||||
|
@ -108,18 +108,28 @@ func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.A
|
||||
|
||||
if doer != nil && !doer.IsGhost() {
|
||||
// 1. If user is logged in, check all team packages permissions
|
||||
teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID)
|
||||
var err error
|
||||
accessMode, err = org.GetOrgUserMaxAuthorizeLevel(doer.ID)
|
||||
if err != nil {
|
||||
return accessMode, err
|
||||
}
|
||||
for _, t := range teams {
|
||||
perm := t.UnitAccessMode(ctx, unit.TypePackages)
|
||||
if accessMode < perm {
|
||||
accessMode = perm
|
||||
// If access mode is less than write check every team for more permissions
|
||||
// The minimum possible access mode is read for org members
|
||||
if accessMode < perm.AccessModeWrite {
|
||||
teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID)
|
||||
if err != nil {
|
||||
return accessMode, err
|
||||
}
|
||||
for _, t := range teams {
|
||||
perm := t.UnitAccessMode(ctx, unit.TypePackages)
|
||||
if accessMode < perm {
|
||||
accessMode = perm
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) {
|
||||
// 2. If user is non-login, check if org is visible to non-login user
|
||||
}
|
||||
if accessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) {
|
||||
// 2. If user is unauthorized or no org member, check if org is visible
|
||||
accessMode = perm.AccessModeRead
|
||||
}
|
||||
} else {
|
||||
|
@ -12,10 +12,31 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
const (
|
||||
EnvConfigKeyPrefixGitea = "GITEA__"
|
||||
EnvConfigKeySuffixFile = "__FILE"
|
||||
)
|
||||
|
||||
const escapeRegexpString = "_0[xX](([0-9a-fA-F][0-9a-fA-F])+)_"
|
||||
|
||||
var escapeRegex = regexp.MustCompile(escapeRegexpString)
|
||||
|
||||
func CollectEnvConfigKeys() (keys []string) {
|
||||
for _, env := range os.Environ() {
|
||||
if strings.HasPrefix(env, EnvConfigKeyPrefixGitea) {
|
||||
k, _, _ := strings.Cut(env, "=")
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func ClearEnvConfigKeys() {
|
||||
for _, k := range CollectEnvConfigKeys() {
|
||||
_ = os.Unsetenv(k)
|
||||
}
|
||||
}
|
||||
|
||||
// decodeEnvSectionKey will decode a portable string encoded Section__Key pair
|
||||
// Portable strings are considered to be of the form [A-Z0-9_]*
|
||||
// We will encode a disallowed value as the UTF8 byte string preceded by _0X and
|
||||
@ -87,7 +108,7 @@ func decodeEnvironmentKey(prefixGitea, suffixFile, envKey string) (ok bool, sect
|
||||
return ok, section, key, useFileValue
|
||||
}
|
||||
|
||||
func EnvironmentToConfig(cfg ConfigProvider, prefixGitea, suffixFile string, envs []string) (changed bool) {
|
||||
func EnvironmentToConfig(cfg ConfigProvider, envs []string) (changed bool) {
|
||||
for _, kv := range envs {
|
||||
idx := strings.IndexByte(kv, '=')
|
||||
if idx < 0 {
|
||||
@ -97,7 +118,7 @@ func EnvironmentToConfig(cfg ConfigProvider, prefixGitea, suffixFile string, env
|
||||
// parse the environment variable to config section name and key name
|
||||
envKey := kv[:idx]
|
||||
envValue := kv[idx+1:]
|
||||
ok, sectionName, keyName, useFileValue := decodeEnvironmentKey(prefixGitea, suffixFile, envKey)
|
||||
ok, sectionName, keyName, useFileValue := decodeEnvironmentKey(EnvConfigKeyPrefixGitea, EnvConfigKeySuffixFile, envKey)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ func TestDecodeEnvironmentKey(t *testing.T) {
|
||||
func TestEnvironmentToConfig(t *testing.T) {
|
||||
cfg, _ := NewConfigProviderFromData("")
|
||||
|
||||
changed := EnvironmentToConfig(cfg, "GITEA__", "__FILE", nil)
|
||||
changed := EnvironmentToConfig(cfg, nil)
|
||||
assert.False(t, changed)
|
||||
|
||||
cfg, err := NewConfigProviderFromData(`
|
||||
@ -81,16 +81,16 @@ key = old
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
|
||||
changed = EnvironmentToConfig(cfg, "GITEA__", "__FILE", []string{"GITEA__sec__key=new"})
|
||||
changed = EnvironmentToConfig(cfg, []string{"GITEA__sec__key=new"})
|
||||
assert.True(t, changed)
|
||||
assert.Equal(t, "new", cfg.Section("sec").Key("key").String())
|
||||
|
||||
changed = EnvironmentToConfig(cfg, "GITEA__", "__FILE", []string{"GITEA__sec__key=new"})
|
||||
changed = EnvironmentToConfig(cfg, []string{"GITEA__sec__key=new"})
|
||||
assert.False(t, changed)
|
||||
|
||||
tmpFile := t.TempDir() + "/the-file"
|
||||
_ = os.WriteFile(tmpFile, []byte("value-from-file"), 0o644)
|
||||
changed = EnvironmentToConfig(cfg, "GITEA__", "__FILE", []string{"GITEA__sec__key__FILE=" + tmpFile})
|
||||
changed = EnvironmentToConfig(cfg, []string{"GITEA__sec__key__FILE=" + tmpFile})
|
||||
assert.True(t, changed)
|
||||
assert.Equal(t, "value-from-file", cfg.Section("sec").Key("key").String())
|
||||
}
|
||||
|
@ -171,6 +171,9 @@ func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkP
|
||||
|
||||
// only read the config but do not load/init anything more, because the AppWorkPath and CustomPath are not ready
|
||||
InitCfgProvider(tmpCustomConf.Value)
|
||||
if HasInstallLock(CfgProvider) {
|
||||
ClearEnvConfigKeys() // if the instance has been installed, do not pass the environment variables to sub-processes
|
||||
}
|
||||
configWorkPath := ConfigSectionKeyString(CfgProvider.Section(""), "WORK_PATH")
|
||||
if configWorkPath != "" {
|
||||
if !filepath.IsAbs(configWorkPath) {
|
||||
|
@ -102,7 +102,7 @@ func generateSaveInternalToken(rootCfg ConfigProvider) {
|
||||
|
||||
func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("security")
|
||||
InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
|
||||
InstallLock = HasInstallLock(rootCfg)
|
||||
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
|
||||
CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
|
||||
SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY")
|
||||
|
@ -183,10 +183,14 @@ func loadRunModeFrom(rootCfg ConfigProvider) {
|
||||
}
|
||||
}
|
||||
|
||||
// HasInstallLock checks the install-lock in ConfigProvider directly, because sometimes the config file is not loaded into setting variables yet.
|
||||
func HasInstallLock(rootCfg ConfigProvider) bool {
|
||||
return rootCfg.Section("security").Key("INSTALL_LOCK").MustBool(false)
|
||||
}
|
||||
|
||||
func mustCurrentRunUserMatch(rootCfg ConfigProvider) {
|
||||
// Does not check run user when the "InstallLock" is off.
|
||||
installLock := rootCfg.Section("security").Key("INSTALL_LOCK").MustBool(false)
|
||||
if installLock {
|
||||
if HasInstallLock(rootCfg) {
|
||||
currentUser, match := IsRunUserMatchCurrentUser(RunUser)
|
||||
if !match {
|
||||
log.Fatal("Expect user '%s' but current user is: %s", RunUser, currentUser)
|
||||
|
@ -296,6 +296,8 @@ invalid_password_algorithm = Invalid password hash algorithm
|
||||
password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems.
|
||||
enable_update_checker = Enable Update Checker
|
||||
enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io.
|
||||
env_config_keys = Environment Configuration
|
||||
env_config_keys_prompt = The following environment variables will also be applied to your configuration file:
|
||||
|
||||
[home]
|
||||
uname_holder = Username or Email Address
|
||||
|
@ -12,4 +12,4 @@ djlint = "1.31.1"
|
||||
|
||||
[tool.djlint]
|
||||
profile="golang"
|
||||
ignore="H005,H006,H008,H013,H014,H016,H020,H021,H023,H026,H030,H031,T027"
|
||||
ignore="H005,H006,H008,H013,H016,H020,H021,H026,H030,H031,T027"
|
||||
|
@ -1034,7 +1034,7 @@ func Routes() *web.Route {
|
||||
m.Group("/assets", func() {
|
||||
m.Combo("").Get(repo.ListReleaseAttachments).
|
||||
Post(reqToken(), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment)
|
||||
m.Combo("/{asset}").Get(repo.GetReleaseAttachment).
|
||||
m.Combo("/{attachment_id}").Get(repo.GetReleaseAttachment).
|
||||
Patch(reqToken(), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment).
|
||||
Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment)
|
||||
})
|
||||
@ -1179,7 +1179,7 @@ func Routes() *web.Route {
|
||||
m.Combo("").
|
||||
Get(repo.ListIssueCommentAttachments).
|
||||
Post(reqToken(), mustNotBeArchived, repo.CreateIssueCommentAttachment)
|
||||
m.Combo("/{asset}").
|
||||
m.Combo("/{attachment_id}").
|
||||
Get(repo.GetIssueCommentAttachment).
|
||||
Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment).
|
||||
Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment)
|
||||
@ -1231,7 +1231,7 @@ func Routes() *web.Route {
|
||||
m.Combo("").
|
||||
Get(repo.ListIssueAttachments).
|
||||
Post(reqToken(), mustNotBeArchived, repo.CreateIssueAttachment)
|
||||
m.Combo("/{asset}").
|
||||
m.Combo("/{attachment_id}").
|
||||
Get(repo.GetIssueAttachment).
|
||||
Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment).
|
||||
Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueAttachment)
|
||||
|
@ -64,7 +64,7 @@ func GetIssueAttachment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAttachment(attach))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
|
||||
}
|
||||
|
||||
// ListIssueAttachments lists all attachments of the issue
|
||||
@ -194,7 +194,7 @@ func CreateIssueAttachment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment))
|
||||
}
|
||||
|
||||
// EditIssueAttachment updates the given attachment
|
||||
@ -254,7 +254,7 @@ func EditIssueAttachment(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateAttachment", err)
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment))
|
||||
}
|
||||
|
||||
// DeleteIssueAttachment delete a given attachment
|
||||
@ -332,7 +332,7 @@ func getIssueAttachmentSafeWrite(ctx *context.APIContext) *repo_model.Attachment
|
||||
}
|
||||
|
||||
func getIssueAttachmentSafeRead(ctx *context.APIContext, issue *issues_model.Issue) *repo_model.Attachment {
|
||||
attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("asset"))
|
||||
attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("attachment_id"))
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err)
|
||||
return nil
|
||||
|
@ -103,7 +103,7 @@ func ListIssueComments(ctx *context.APIContext) {
|
||||
apiComments := make([]*api.Comment, len(comments))
|
||||
for i, comment := range comments {
|
||||
comment.Issue = issue
|
||||
apiComments[i] = convert.ToComment(ctx, comments[i])
|
||||
apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, comments[i])
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(totalCount)
|
||||
@ -191,7 +191,7 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
|
||||
for _, comment := range comments {
|
||||
if comment.Type != issues_model.CommentTypeCode && isXRefCommentAccessible(ctx, ctx.Doer, comment, issue.RepoID) {
|
||||
comment.Issue = issue
|
||||
apiComments = append(apiComments, convert.ToTimelineComment(ctx, comment, ctx.Doer))
|
||||
apiComments = append(apiComments, convert.ToTimelineComment(ctx, issue.Repo, comment, ctx.Doer))
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,7 +308,7 @@ func ListRepoIssueComments(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
for i := range comments {
|
||||
apiComments[i] = convert.ToComment(ctx, comments[i])
|
||||
apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, comments[i])
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(totalCount)
|
||||
@ -368,7 +368,7 @@ func CreateIssueComment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToComment(ctx, comment))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
|
||||
}
|
||||
|
||||
// GetIssueComment Get a comment by ID
|
||||
@ -436,7 +436,7 @@ func GetIssueComment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToComment(ctx, comment))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
|
||||
}
|
||||
|
||||
// EditIssueComment modify a comment of an issue
|
||||
@ -561,7 +561,7 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToComment(ctx, comment))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
|
||||
}
|
||||
|
||||
// DeleteIssueComment delete a comment from an issue
|
||||
|
@ -68,7 +68,7 @@ func GetIssueCommentAttachment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAttachment(attachment))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attachment))
|
||||
}
|
||||
|
||||
// ListIssueCommentAttachments lists all attachments of the comment
|
||||
@ -110,7 +110,7 @@ func ListIssueCommentAttachments(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAttachments(comment.Attachments))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIAttachments(ctx.Repo.Repository, comment.Attachments))
|
||||
}
|
||||
|
||||
// CreateIssueCommentAttachment creates an attachment and saves the given file
|
||||
@ -201,7 +201,7 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment))
|
||||
}
|
||||
|
||||
// EditIssueCommentAttachment updates the given attachment
|
||||
@ -259,7 +259,7 @@ func EditIssueCommentAttachment(ctx *context.APIContext) {
|
||||
if err := repo_model.UpdateAttachment(ctx, attach); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach)
|
||||
}
|
||||
ctx.JSON(http.StatusCreated, convert.ToAttachment(attach))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
|
||||
}
|
||||
|
||||
// DeleteIssueCommentAttachment delete a given attachment
|
||||
@ -352,7 +352,7 @@ func canUserWriteIssueCommentAttachment(ctx *context.APIContext, comment *issues
|
||||
}
|
||||
|
||||
func getIssueCommentAttachmentSafeRead(ctx *context.APIContext, comment *issues_model.Comment) *repo_model.Attachment {
|
||||
attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("asset"))
|
||||
attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("attachment_id"))
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err)
|
||||
return nil
|
||||
|
@ -64,7 +64,7 @@ func GetRelease(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release))
|
||||
}
|
||||
|
||||
// GetLatestRelease gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at
|
||||
@ -105,7 +105,7 @@ func GetLatestRelease(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release))
|
||||
}
|
||||
|
||||
// ListReleases list a repository's releases
|
||||
@ -174,7 +174,7 @@ func ListReleases(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
rels[i] = convert.ToRelease(ctx, release)
|
||||
rels[i] = convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)
|
||||
}
|
||||
|
||||
filteredCount, err := repo_model.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts)
|
||||
@ -272,7 +272,7 @@ func CreateRelease(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.JSON(http.StatusCreated, convert.ToRelease(ctx, rel))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIRelease(ctx, ctx.Repo.Repository, rel))
|
||||
}
|
||||
|
||||
// EditRelease edit a release
|
||||
@ -357,7 +357,7 @@ func EditRelease(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToRelease(ctx, rel))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, rel))
|
||||
}
|
||||
|
||||
// DeleteRelease delete a release from a repository
|
||||
|
@ -52,7 +52,7 @@ func GetReleaseAttachment(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/Attachment"
|
||||
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
attachID := ctx.ParamsInt64(":asset")
|
||||
attachID := ctx.ParamsInt64(":attachment_id")
|
||||
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
|
||||
if err != nil {
|
||||
if repo_model.IsErrAttachmentNotExist(err) {
|
||||
@ -68,7 +68,7 @@ func GetReleaseAttachment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
// FIXME Should prove the existence of the given repo, but results in unnecessary database requests
|
||||
ctx.JSON(http.StatusOK, convert.ToAttachment(attach))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
|
||||
}
|
||||
|
||||
// ListReleaseAttachments lists all attachments of the release
|
||||
@ -117,7 +117,7 @@ func ListReleaseAttachments(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release).Attachments)
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release).Attachments)
|
||||
}
|
||||
|
||||
// CreateReleaseAttachment creates an attachment and saves the given file
|
||||
@ -209,7 +209,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAttachment(attach))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
|
||||
}
|
||||
|
||||
// EditReleaseAttachment updates the given attachment
|
||||
@ -256,7 +256,7 @@ func EditReleaseAttachment(ctx *context.APIContext) {
|
||||
|
||||
// Check if release exists an load release
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
attachID := ctx.ParamsInt64(":asset")
|
||||
attachID := ctx.ParamsInt64(":attachment_id")
|
||||
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
|
||||
if err != nil {
|
||||
if repo_model.IsErrAttachmentNotExist(err) {
|
||||
@ -279,7 +279,7 @@ func EditReleaseAttachment(ctx *context.APIContext) {
|
||||
if err := repo_model.UpdateAttachment(ctx, attach); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach)
|
||||
}
|
||||
ctx.JSON(http.StatusCreated, convert.ToAttachment(attach))
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach))
|
||||
}
|
||||
|
||||
// DeleteReleaseAttachment delete a given attachment
|
||||
@ -318,7 +318,7 @@ func DeleteReleaseAttachment(ctx *context.APIContext) {
|
||||
|
||||
// Check if release exists an load release
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
attachID := ctx.ParamsInt64(":asset")
|
||||
attachID := ctx.ParamsInt64(":attachment_id")
|
||||
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
|
||||
if err != nil {
|
||||
if repo_model.IsErrAttachmentNotExist(err) {
|
||||
|
@ -63,7 +63,7 @@ func GetReleaseByTag(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release))
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release))
|
||||
}
|
||||
|
||||
// DeleteReleaseByTag delete a release from a repository by tag name
|
||||
|
@ -56,6 +56,7 @@ func getSupportedDbTypeNames() (dbTypeNames []map[string]string) {
|
||||
func Contexter() func(next http.Handler) http.Handler {
|
||||
rnd := templates.HTMLRenderer()
|
||||
dbTypeNames := getSupportedDbTypeNames()
|
||||
envConfigKeys := setting.CollectEnvConfigKeys()
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
base, baseCleanUp := context.NewBaseContext(resp, req)
|
||||
@ -70,11 +71,13 @@ func Contexter() func(next http.Handler) http.Handler {
|
||||
ctx.AppendContextValue(context.WebContextKey, ctx)
|
||||
ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
|
||||
ctx.Data.MergeFrom(middleware.ContextData{
|
||||
"locale": ctx.Locale,
|
||||
"Title": ctx.Locale.Tr("install.install"),
|
||||
"PageIsInstall": true,
|
||||
"DbTypeNames": dbTypeNames,
|
||||
"AllLangs": translation.AllLangs(),
|
||||
"locale": ctx.Locale,
|
||||
"Title": ctx.Locale.Tr("install.install"),
|
||||
"PageIsInstall": true,
|
||||
"DbTypeNames": dbTypeNames,
|
||||
"EnvConfigKeys": envConfigKeys,
|
||||
"CustomConfFile": setting.CustomConf,
|
||||
"AllLangs": translation.AllLangs(),
|
||||
|
||||
"PasswordHashAlgorithms": hash.RecommendedHashAlgorithms,
|
||||
})
|
||||
@ -218,7 +221,7 @@ func checkDatabase(ctx *context.Context, form *forms.InstallForm) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
log.Info("User confirmed reinstallation of Gitea into a pre-existing database")
|
||||
log.Info("User confirmed re-installation of Gitea into a pre-existing database")
|
||||
}
|
||||
|
||||
if hasPostInstallationUser || dbMigrationVersion > 0 {
|
||||
@ -502,6 +505,8 @@ func SubmitInstall(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
setting.EnvironmentToConfig(cfg, os.Environ())
|
||||
|
||||
if err = cfg.SaveTo(setting.CustomConf); err != nil {
|
||||
ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
|
||||
return
|
||||
@ -568,6 +573,7 @@ func SubmitInstall(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
setting.ClearEnvConfigKeys()
|
||||
log.Info("First-time run install finished!")
|
||||
InstallDone(ctx)
|
||||
|
||||
|
@ -2058,7 +2058,7 @@ func GetIssueInfo(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue))
|
||||
ctx.JSON(http.StatusOK, convert.ToIssue(ctx, issue))
|
||||
}
|
||||
|
||||
// UpdateIssueTitle change issue's title
|
||||
@ -2563,7 +2563,7 @@ func SearchIssues(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(filteredCount)
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
|
||||
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
|
||||
}
|
||||
|
||||
func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
|
||||
@ -2724,7 +2724,7 @@ func ListIssues(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(filteredCount)
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
|
||||
ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues))
|
||||
}
|
||||
|
||||
func BatchDeleteIssues(ctx *context.Context) {
|
||||
@ -3290,7 +3290,7 @@ func GetIssueAttachments(ctx *context.Context) {
|
||||
}
|
||||
attachments := make([]*api.Attachment, len(issue.Attachments))
|
||||
for i := 0; i < len(issue.Attachments); i++ {
|
||||
attachments[i] = convert.ToAttachment(issue.Attachments[i])
|
||||
attachments[i] = convert.ToAttachment(ctx.Repo.Repository, issue.Attachments[i])
|
||||
}
|
||||
ctx.JSON(http.StatusOK, attachments)
|
||||
}
|
||||
@ -3314,7 +3314,7 @@ func GetCommentAttachments(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(comment.Attachments); i++ {
|
||||
attachments = append(attachments, convert.ToAttachment(comment.Attachments[i]))
|
||||
attachments = append(attachments, convert.ToAttachment(ctx.Repo.Repository, comment.Attachments[i]))
|
||||
}
|
||||
ctx.JSON(http.StatusOK, attachments)
|
||||
}
|
||||
|
@ -723,6 +723,9 @@ func ViewPullCommits(ctx *context.Context) {
|
||||
ctx.Data["Commits"] = commits
|
||||
ctx.Data["CommitCount"] = len(commits)
|
||||
|
||||
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
|
||||
ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.Doer.ID)
|
||||
|
||||
getBranchData(ctx, issue)
|
||||
ctx.HTML(http.StatusOK, tplPullCommits)
|
||||
}
|
||||
|
@ -982,7 +982,7 @@ func renderCode(ctx *context.Context) {
|
||||
ctx.ServerError("GetBaseRepo", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID)
|
||||
ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID, ctx.Repo.Repository.DefaultBranch)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRecentlyPushedBranches", err)
|
||||
return
|
||||
|
@ -186,7 +186,7 @@ func NotificationStatusPost(ctx *context.Context) {
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
ctx.Data["Link"] = setting.AppURL + "notifications"
|
||||
ctx.Data["Link"] = setting.AppSubURL + "/notifications"
|
||||
ctx.Data["SequenceNumber"] = ctx.Req.PostFormValue("sequence-number")
|
||||
|
||||
ctx.HTML(http.StatusOK, tplNotificationDiv)
|
||||
|
@ -171,7 +171,7 @@ func (n *actionsNotifier) NotifyCreateIssueComment(ctx context.Context, doer *us
|
||||
WithPayload(&api.IssueCommentPayload{
|
||||
Action: api.HookIssueCommentCreated,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Comment: convert.ToComment(ctx, comment),
|
||||
Comment: convert.ToAPIComment(ctx, repo, comment),
|
||||
Repository: convert.ToRepo(ctx, repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
IsPull: true,
|
||||
@ -185,7 +185,7 @@ func (n *actionsNotifier) NotifyCreateIssueComment(ctx context.Context, doer *us
|
||||
WithPayload(&api.IssueCommentPayload{
|
||||
Action: api.HookIssueCommentCreated,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Comment: convert.ToComment(ctx, comment),
|
||||
Comment: convert.ToAPIComment(ctx, repo, comment),
|
||||
Repository: convert.ToRepo(ctx, repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
IsPull: false,
|
||||
|
@ -260,7 +260,7 @@ func notifyRelease(ctx context.Context, doer *user_model.User, rel *repo_model.R
|
||||
WithRef(git.RefNameFromTag(rel.TagName).String()).
|
||||
WithPayload(&api.ReleasePayload{
|
||||
Action: action,
|
||||
Release: convert.ToRelease(ctx, rel),
|
||||
Release: convert.ToAPIRelease(ctx, rel.Repo, rel),
|
||||
Repository: convert.ToRepo(ctx, rel.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}).
|
||||
|
@ -37,7 +37,7 @@ func ToActivity(ctx context.Context, ac *activities_model.Action, doer *user_mod
|
||||
|
||||
if ac.Comment != nil {
|
||||
result.CommentID = ac.CommentID
|
||||
result.Comment = ToComment(ctx, ac.Comment)
|
||||
result.Comment = ToAPIComment(ctx, ac.Repo, ac.Comment)
|
||||
}
|
||||
|
||||
return result
|
||||
|
@ -4,12 +4,38 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// ToAttachment converts models.Attachment to api.Attachment
|
||||
func ToAttachment(a *repo_model.Attachment) *api.Attachment {
|
||||
func WebAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string {
|
||||
return attach.DownloadURL()
|
||||
}
|
||||
|
||||
func APIAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string {
|
||||
if attach.CustomDownloadURL != "" {
|
||||
return attach.CustomDownloadURL
|
||||
}
|
||||
|
||||
// /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id}
|
||||
return setting.AppURL + "api/repos/" + repo.FullName() + "/releases/" + strconv.FormatInt(attach.ReleaseID, 10) + "/assets/" + strconv.FormatInt(attach.ID, 10)
|
||||
}
|
||||
|
||||
// ToAttachment converts models.Attachment to api.Attachment for API usage
|
||||
func ToAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment {
|
||||
return toAttachment(repo, a, WebAssetDownloadURL)
|
||||
}
|
||||
|
||||
// ToAPIAttachment converts models.Attachment to api.Attachment for API usage
|
||||
func ToAPIAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment {
|
||||
return toAttachment(repo, a, APIAssetDownloadURL)
|
||||
}
|
||||
|
||||
// toAttachment converts models.Attachment to api.Attachment for API usage
|
||||
func toAttachment(repo *repo_model.Repository, a *repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Attachment {
|
||||
return &api.Attachment{
|
||||
ID: a.ID,
|
||||
Name: a.Name,
|
||||
@ -17,14 +43,18 @@ func ToAttachment(a *repo_model.Attachment) *api.Attachment {
|
||||
DownloadCount: a.DownloadCount,
|
||||
Size: a.Size,
|
||||
UUID: a.UUID,
|
||||
DownloadURL: a.DownloadURL(),
|
||||
DownloadURL: getDownloadURL(repo, a), // for web request json and api request json, return different download urls
|
||||
}
|
||||
}
|
||||
|
||||
func ToAttachments(attachments []*repo_model.Attachment) []*api.Attachment {
|
||||
func ToAPIAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment) []*api.Attachment {
|
||||
return toAttachments(repo, attachments, APIAssetDownloadURL)
|
||||
}
|
||||
|
||||
func toAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) []*api.Attachment {
|
||||
converted := make([]*api.Attachment, 0, len(attachments))
|
||||
for _, attachment := range attachments {
|
||||
converted = append(converted, ToAttachment(attachment))
|
||||
converted = append(converted, toAttachment(repo, attachment, getDownloadURL))
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
@ -19,11 +19,19 @@ import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
func ToIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
|
||||
return toIssue(ctx, issue, WebAssetDownloadURL)
|
||||
}
|
||||
|
||||
// ToAPIIssue converts an Issue to API format
|
||||
// it assumes some fields assigned with values:
|
||||
// Required - Poster, Labels,
|
||||
// Optional - Milestone, Assignee, PullRequest
|
||||
func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
|
||||
return toIssue(ctx, issue, APIAssetDownloadURL)
|
||||
}
|
||||
|
||||
func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue {
|
||||
if err := issue.LoadLabels(ctx); err != nil {
|
||||
return &api.Issue{}
|
||||
}
|
||||
@ -40,7 +48,7 @@ func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
|
||||
Poster: ToUser(ctx, issue.Poster, nil),
|
||||
Title: issue.Title,
|
||||
Body: issue.Content,
|
||||
Attachments: ToAttachments(issue.Attachments),
|
||||
Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL),
|
||||
Ref: issue.Ref,
|
||||
State: issue.State(),
|
||||
IsLocked: issue.IsLocked,
|
||||
@ -105,6 +113,15 @@ func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue {
|
||||
return apiIssue
|
||||
}
|
||||
|
||||
// ToIssueList converts an IssueList to API format
|
||||
func ToIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
|
||||
result := make([]*api.Issue, len(il))
|
||||
for i := range il {
|
||||
result[i] = ToIssue(ctx, il[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ToAPIIssueList converts an IssueList to API format
|
||||
func ToAPIIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue {
|
||||
result := make([]*api.Issue, len(il))
|
||||
|
@ -14,8 +14,8 @@ import (
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ToComment converts a issues_model.Comment to the api.Comment format
|
||||
func ToComment(ctx context.Context, c *issues_model.Comment) *api.Comment {
|
||||
// ToAPIComment converts a issues_model.Comment to the api.Comment format for API usage
|
||||
func ToAPIComment(ctx context.Context, repo *repo_model.Repository, c *issues_model.Comment) *api.Comment {
|
||||
return &api.Comment{
|
||||
ID: c.ID,
|
||||
Poster: ToUser(ctx, c.Poster, nil),
|
||||
@ -23,14 +23,14 @@ func ToComment(ctx context.Context, c *issues_model.Comment) *api.Comment {
|
||||
IssueURL: c.IssueURL(),
|
||||
PRURL: c.PRURL(),
|
||||
Body: c.Content,
|
||||
Attachments: ToAttachments(c.Attachments),
|
||||
Attachments: ToAPIAttachments(repo, c.Attachments),
|
||||
Created: c.CreatedUnix.AsTime(),
|
||||
Updated: c.UpdatedUnix.AsTime(),
|
||||
}
|
||||
}
|
||||
|
||||
// ToTimelineComment converts a issues_model.Comment to the api.TimelineComment format
|
||||
func ToTimelineComment(ctx context.Context, c *issues_model.Comment, doer *user_model.User) *api.TimelineComment {
|
||||
func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issues_model.Comment, doer *user_model.User) *api.TimelineComment {
|
||||
err := c.LoadMilestone(ctx)
|
||||
if err != nil {
|
||||
log.Error("LoadMilestone: %v", err)
|
||||
@ -143,7 +143,7 @@ func ToTimelineComment(ctx context.Context, c *issues_model.Comment, doer *user_
|
||||
log.Error("LoadPoster: %v", err)
|
||||
return nil
|
||||
}
|
||||
comment.RefComment = ToComment(ctx, com)
|
||||
comment.RefComment = ToAPIComment(ctx, repo, com)
|
||||
}
|
||||
|
||||
if c.Label != nil {
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// ToRelease convert a repo_model.Release to api.Release
|
||||
func ToRelease(ctx context.Context, r *repo_model.Release) *api.Release {
|
||||
// ToAPIRelease convert a repo_model.Release to api.Release
|
||||
func ToAPIRelease(ctx context.Context, repo *repo_model.Repository, r *repo_model.Release) *api.Release {
|
||||
return &api.Release{
|
||||
ID: r.ID,
|
||||
TagName: r.TagName,
|
||||
@ -27,6 +27,6 @@ func ToRelease(ctx context.Context, r *repo_model.Release) *api.Release {
|
||||
CreatedAt: r.CreatedUnix.AsTime(),
|
||||
PublishedAt: r.CreatedUnix.AsTime(),
|
||||
Publisher: ToUser(ctx, r.Publisher, nil),
|
||||
Attachments: ToAttachments(r.Attachments),
|
||||
Attachments: ToAPIAttachments(repo, r.Attachments),
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +110,11 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue
|
||||
}
|
||||
}
|
||||
|
||||
if mergeStyle == repo_model.MergeStyleRebase {
|
||||
// for fast-forward rebase, do not amend the last commit if there is no template
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
// Squash merge has a different from other styles.
|
||||
if mergeStyle == repo_model.MergeStyleSquash {
|
||||
return fmt.Sprintf("%s (%s%d)", pr.Issue.Title, issueReference, pr.Issue.Index), "", nil
|
||||
|
@ -386,7 +386,7 @@ func (m *webhookNotifier) NotifyUpdateComment(ctx context.Context, doer *user_mo
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{
|
||||
Action: api.HookIssueCommentEdited,
|
||||
Issue: convert.ToAPIIssue(ctx, c.Issue),
|
||||
Comment: convert.ToComment(ctx, c),
|
||||
Comment: convert.ToAPIComment(ctx, c.Issue.Repo, c),
|
||||
Changes: &api.ChangesPayload{
|
||||
Body: &api.ChangesFromPayload{
|
||||
From: oldContent,
|
||||
@ -414,7 +414,7 @@ func (m *webhookNotifier) NotifyCreateIssueComment(ctx context.Context, doer *us
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{
|
||||
Action: api.HookIssueCommentCreated,
|
||||
Issue: convert.ToAPIIssue(ctx, issue),
|
||||
Comment: convert.ToComment(ctx, comment),
|
||||
Comment: convert.ToAPIComment(ctx, repo, comment),
|
||||
Repository: convert.ToRepo(ctx, repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
IsPull: issue.IsPull,
|
||||
@ -451,7 +451,7 @@ func (m *webhookNotifier) NotifyDeleteComment(ctx context.Context, doer *user_mo
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{
|
||||
Action: api.HookIssueCommentDeleted,
|
||||
Issue: convert.ToAPIIssue(ctx, comment.Issue),
|
||||
Comment: convert.ToComment(ctx, comment),
|
||||
Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment),
|
||||
Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
IsPull: comment.Issue.IsPull,
|
||||
@ -808,7 +808,7 @@ func sendReleaseHook(ctx context.Context, doer *user_model.User, rel *repo_model
|
||||
permission, _ := access_model.GetUserRepoPermission(ctx, rel.Repo, doer)
|
||||
if err := PrepareWebhooks(ctx, EventSource{Repository: rel.Repo}, webhook_module.HookEventRelease, &api.ReleasePayload{
|
||||
Action: action,
|
||||
Release: convert.ToRelease(ctx, rel),
|
||||
Release: convert.ToAPIRelease(ctx, rel.Repo, rel),
|
||||
Repository: convert.ToRepo(ctx, rel.Repo, permission),
|
||||
Sender: convert.ToUser(ctx, doer, nil),
|
||||
}); err != nil {
|
||||
|
@ -106,7 +106,6 @@
|
||||
<input id="attribute_avatar" name="attribute_avatar" value="{{$cfg.AttributeAvatar}}" placeholder="jpegPhoto">
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ldap group begin -->
|
||||
<div class="inline field">
|
||||
<div class="ui checkbox">
|
||||
|
@ -23,7 +23,6 @@
|
||||
<button class="item gt-w-auto ui icon mini button gt-p-3 gt-m-0" id="navbar-expand-toggle">{{svg "octicon-three-bars"}}</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- navbar links non-mobile -->
|
||||
{{if and .IsSigned .MustChangePassword}}
|
||||
{{/* No links */}}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{{template "base/head" .}}
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content install">
|
||||
<div class="ui middle very relaxed page grid">
|
||||
<div class="ui grid install-config-container">
|
||||
<div class="sixteen wide center aligned centered column">
|
||||
<h3 class="ui top attached header">
|
||||
{{.locale.Tr "install.title"}}
|
||||
@ -149,19 +149,18 @@
|
||||
</div>
|
||||
<div class="inline field">
|
||||
<div class="ui checkbox">
|
||||
<label for="enable_update_checker">{{.locale.Tr "install.enable_update_checker"}}</label>
|
||||
<label>{{.locale.Tr "install.enable_update_checker"}}</label>
|
||||
<input name="enable_update_checker" type="checkbox">
|
||||
</div>
|
||||
<span class="help">{{.locale.Tr "install.enable_update_checker_helper"}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Optional Settings -->
|
||||
<h4 class="ui dividing header">{{.locale.Tr "install.optional_title"}}</h4>
|
||||
|
||||
<!-- Email -->
|
||||
<details class="optional field">
|
||||
<summary class="title gt-py-3{{if .Err_SMTP}} text red{{end}}">
|
||||
<summary class="right-content gt-py-3{{if .Err_SMTP}} text red{{end}}">
|
||||
{{.locale.Tr "install.email_title"}}
|
||||
</summary>
|
||||
<div class="inline field">
|
||||
@ -201,7 +200,7 @@
|
||||
|
||||
<!-- Server and other services -->
|
||||
<details class="optional field">
|
||||
<summary class="title gt-py-3{{if .Err_Services}} text red{{end}}">
|
||||
<summary class="right-content gt-py-3{{if .Err_Services}} text red{{end}}">
|
||||
{{.locale.Tr "install.server_service_title"}}
|
||||
</summary>
|
||||
<div class="inline field">
|
||||
@ -299,7 +298,7 @@
|
||||
|
||||
<!-- Admin -->
|
||||
<details class="optional field">
|
||||
<summary class="title gt-py-3{{if .Err_Admin}} text red{{end}}">
|
||||
<summary class="right-content gt-py-3{{if .Err_Admin}} text red{{end}}">
|
||||
{{.locale.Tr "install.admin_title"}}
|
||||
</summary>
|
||||
<p class="center">{{.locale.Tr "install.admin_setting_desc"}}</p>
|
||||
@ -321,10 +320,27 @@
|
||||
</div>
|
||||
</details>
|
||||
|
||||
{{if .EnvConfigKeys}}
|
||||
<!-- Environment Config -->
|
||||
<h4 class="ui dividing header">{{.locale.Tr "install.env_config_keys"}}</h4>
|
||||
<div class="inline field">
|
||||
<div class="right-content">
|
||||
{{.locale.Tr "install.env_config_keys_prompt"}}
|
||||
</div>
|
||||
<div class="right-content gt-mt-3">
|
||||
{{range .EnvConfigKeys}}<span class="ui label">{{.}}</span>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="divider"></div>
|
||||
<div class="inline field">
|
||||
<label></label>
|
||||
<button class="ui primary button">{{.locale.Tr "install.install_btn_confirm"}}</button>
|
||||
<div class="right-content">
|
||||
These configuration options will be written into: {{.CustomConfFile}}
|
||||
</div>
|
||||
<div class="right-content gt-mt-3">
|
||||
<button class="ui primary button">{{.locale.Tr "install.install_btn_confirm"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -14,13 +14,13 @@
|
||||
</form>
|
||||
<div class="ui {{if .PackageDescriptors}}issue list{{end}}">
|
||||
{{range .PackageDescriptors}}
|
||||
<li class="item gt-df gt-py-3">
|
||||
<li class="item">
|
||||
<div class="issue-item-main">
|
||||
<div class="issue-item-top-row">
|
||||
<div class="issue-item-title">
|
||||
<a class="title" href="{{.FullWebLink}}">{{.Package.Name}}</a>
|
||||
<span class="ui label">{{svg .Package.Type.SVGName 16}} {{.Package.Type.Name}}</span>
|
||||
</div>
|
||||
<div class="desc issue-item-bottom-row">
|
||||
<div class="issue-item-body">
|
||||
{{$timeStr := TimeSinceUnix .Version.CreatedUnix $.locale}}
|
||||
{{$hasRepositoryAccess := false}}
|
||||
{{if .Repository}}
|
||||
|
@ -20,12 +20,12 @@
|
||||
</form>
|
||||
<div class="ui {{if .PackageDescriptors}}issue list{{end}}">
|
||||
{{range .PackageDescriptors}}
|
||||
<li class="item gt-df gt-py-3">
|
||||
<li class="item">
|
||||
<div class="issue-item-main">
|
||||
<div class="issue-item-top-row">
|
||||
<div class="issue-item-title">
|
||||
<a class="title" href="{{.FullWebLink}}">{{.Version.LowerVersion}}</a>
|
||||
</div>
|
||||
<div class="desc issue-item-bottom-row">
|
||||
<div class="issue-item-body">
|
||||
{{$.locale.Tr "packages.published_by" (TimeSinceUnix .Version.CreatedUnix $.locale) .Creator.HomeLink (.Creator.GetDisplayName | Escape) | Safe}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,17 +6,17 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{range .Runs}}
|
||||
<li class="item gt-df gt-py-3">
|
||||
<div class="issue-item-left issue-item-icon gt-df gt-items-start">
|
||||
<li class="item action-item">
|
||||
<div class="issue-item-left issue-item-icon">
|
||||
{{template "repo/actions/status" (dict "status" .Status.String "locale" $.locale)}}
|
||||
</div>
|
||||
<div class="issue-item-main action-item-main">
|
||||
<div class="issue-item-top-row">
|
||||
<div class="issue-item-title">
|
||||
<a class="index gt-no-underline title action-item-title" title="{{.Title}}" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
|
||||
{{- .Title -}}
|
||||
</a>
|
||||
</div>
|
||||
<div class="desc issue-item-bottom-row">
|
||||
<div class="issue-item-body">
|
||||
<b>{{if not $.CurWorkflow}}{{.WorkflowID}} {{end}}#{{.Index}}</b>
|
||||
: {{$.locale.Tr "actions.runs.commit"}}
|
||||
<a href="{{$.RepoLink}}/commit/{{.CommitSHA}}">{{ShortSha .CommitSHA}}</a>
|
||||
@ -32,8 +32,8 @@
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="action-item-right">
|
||||
<div>{{svg "octicon-calendar" 16 "gt-mr-2"}}{{TimeSinceUnix .Updated $.locale}}</div>
|
||||
<div>{{svg "octicon-stopwatch" 16 "gt-mr-2"}}{{.Duration}}</div>
|
||||
<div class="flex-text-block">{{svg "octicon-calendar" 16}}{{TimeSinceUnix .Updated $.locale}}</div>
|
||||
<div class="flex-text-block">{{svg "octicon-stopwatch" 16}}{{.Duration}}</div>
|
||||
</div>
|
||||
</li>
|
||||
{{end}}
|
||||
|
@ -101,7 +101,7 @@
|
||||
{{template "repo/diff/stats" dict "file" . "root" $}}
|
||||
{{end}}
|
||||
</div>
|
||||
<span class="file gt-mono"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}</a>{{if .IsLFSFile}} ({{$.locale.Tr "repo.stored_lfs"}}){{end}}</span>
|
||||
<span class="file gt-mono"><a class="muted file-link" title="{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}" href="#diff-{{$file.NameHash}}">{{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}</a>{{if .IsLFSFile}} ({{$.locale.Tr "repo.stored_lfs"}}){{end}}</span>
|
||||
<button class="btn interact-fg gt-p-3" data-clipboard-text="{{$file.Name}}">{{svg "octicon-copy" 14}}</button>
|
||||
{{if $file.IsGenerated}}
|
||||
<span class="ui label">{{$.locale.Tr "repo.diff.generated"}}</span>
|
||||
@ -110,7 +110,7 @@
|
||||
<span class="ui label">{{$.locale.Tr "repo.diff.vendored"}}</span>
|
||||
{{end}}
|
||||
{{if and $file.Mode $file.OldMode}}
|
||||
<span class="gt-ml-4 gt-mono">{{$file.OldMode}} → {{$file.Mode}}</span>
|
||||
<span class="gt-ml-4 gt-mono">{{$file.OldMode}} → {{$file.Mode}}</span>
|
||||
{{else if $file.Mode}}
|
||||
<span class="gt-ml-4 gt-mono">{{$file.Mode}}</span>
|
||||
{{end}}
|
||||
|
@ -52,8 +52,6 @@
|
||||
{{template "repo/editor/commit_form" .}}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="ui g-modal-confirm modal" id="edit-empty-content-modal">
|
||||
<div class="header">
|
||||
{{svg "octicon-file"}}
|
||||
@ -73,6 +71,5 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
||||
|
@ -44,30 +44,25 @@
|
||||
<label for="website">{{.locale.Tr "repo.settings.site"}}</label>
|
||||
<input id="website" name="website" type="url" maxlength="1024" value="{{.Repository.Website}}">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<button class="ui green button">{{$.locale.Tr "repo.settings.update_settings"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
|
||||
{{.CsrfTokenHtml}}
|
||||
<div class="inline field">
|
||||
<label for="avatar">{{.locale.Tr "settings.choose_new_avatar"}}</label>
|
||||
<input name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
|
||||
<button class="ui red button link-action" data-url="{{.Link}}/avatar/delete" data-redirect="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{{/* These variables exist to make the logic in the Settings window easier to comprehend and are not used later on. */}}
|
||||
{{$newMirrorsPartiallyEnabled := or (not .DisableNewPullMirrors) (not .DisableNewPushMirrors)}}
|
||||
{{/* .Repository.IsMirror is not always reliable if the repository is not actively acting as a mirror because of errors. */}}
|
||||
|
@ -75,5 +75,4 @@
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
||||
{{template "base/footer" .}}
|
||||
|
@ -30,16 +30,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</h4>
|
||||
|
||||
{{if and .Commits (gt .CommitCount 0)}}
|
||||
{{template "repo/commits_list" .}}
|
||||
{{end}}
|
||||
|
||||
{{template "base/paginate" .}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{{template "base/footer" .}}
|
||||
|
@ -1,8 +1,8 @@
|
||||
<div class="issue list">
|
||||
{{$approvalCounts := .ApprovalCounts}}
|
||||
{{range .Issues}}
|
||||
<li class="item gt-df gt-py-3">
|
||||
<div class="issue-item-left gt-df gt-items-start">
|
||||
<li class="item">
|
||||
<div class="issue-item-left">
|
||||
{{if $.CanWriteIssuesOrPulls}}
|
||||
<input type="checkbox" autocomplete="off" class="issue-checkbox gt-mt-2 gt-mr-4" data-issue-id={{.ID}} aria-label="{{$.locale.Tr "repo.issues.action_check"}} "{{.Title}}"">
|
||||
{{end}}
|
||||
@ -11,21 +11,49 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="issue-item-main">
|
||||
<div class="issue-item-top-row">
|
||||
<a class="title gt-no-underline issue-title" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">{{RenderEmoji $.Context .Title | RenderCodeBlock}}</a>
|
||||
{{if .IsPull}}
|
||||
{{if (index $.CommitStatuses .PullRequest.ID)}}
|
||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID) "root" $}}
|
||||
<div class="issue-item-header">
|
||||
<div class="issue-item-title">
|
||||
<a class="title gt-no-underline issue-title" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">{{RenderEmoji $.Context .Title | RenderCodeBlock}}</a>
|
||||
{{if .IsPull}}
|
||||
{{if (index $.CommitStatuses .PullRequest.ID)}}
|
||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID) "root" $}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
<span class="labels-list gt-ml-2">
|
||||
{{range .Labels}}
|
||||
<a href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{RenderLabel $.Context .}}</a>
|
||||
{{end}}
|
||||
</span>
|
||||
</div>
|
||||
{{if or .TotalTrackedTime .Assignees .NumComments}}
|
||||
<div class="issue-item-right">
|
||||
{{if .TotalTrackedTime}}
|
||||
<div class="text grey flex-text-block">
|
||||
{{svg "octicon-clock" 16}}
|
||||
{{.TotalTrackedTime | Sec2Time}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Assignees}}
|
||||
<div class="text grey">
|
||||
{{range .Assignees}}
|
||||
<a class="ui assignee gt-no-underline" href="{{.HomeLink}}" data-tooltip-content="{{.GetDisplayName}}">
|
||||
{{avatar $.Context . 20}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .NumComments}}
|
||||
<div class="text grey">
|
||||
<a class="gt-no-underline muted flex-text-inline" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
|
||||
{{svg "octicon-comment" 16}}{{.NumComments}}
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
<span class="labels-list gt-ml-2">
|
||||
{{range .Labels}}
|
||||
<a href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{RenderLabel $.Context .}}</a>
|
||||
{{end}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="desc issue-item-bottom-row">
|
||||
<a class="index gt-ml-0 gt-mr-2" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
|
||||
<div class="issue-item-body">
|
||||
<a class="index" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
|
||||
{{if eq $.listType "dashboard"}}
|
||||
{{.Repo.FullName}}#{{.Index}}
|
||||
{{else}}
|
||||
@ -41,14 +69,14 @@
|
||||
{{$.locale.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}}
|
||||
{{end}}
|
||||
{{if .IsPull}}
|
||||
<div class="branches gt-df gt-ac">
|
||||
<div class="branches flex-text-inline">
|
||||
<div class="branch">
|
||||
<a href="{{.PullRequest.BaseRepo.Link}}/src/branch/{{PathEscapeSegments .PullRequest.BaseBranch}}">
|
||||
{{/* inline to remove the spaces between spans */}}
|
||||
{{if ne .RepoID .PullRequest.BaseRepoID}}<span class="truncated-name">{{.PullRequest.BaseRepo.OwnerName}}</span>:{{end}}<span class="truncated-name">{{.PullRequest.BaseBranch}}</span>
|
||||
</a>
|
||||
</div>
|
||||
{{svg "gitea-double-chevron-left" 12 "gt-mx-1"}}
|
||||
{{svg "gitea-double-chevron-left" 12}}
|
||||
{{if .PullRequest.HeadRepo}}
|
||||
<div class="branch">
|
||||
<a href="{{.PullRequest.HeadRepo.Link}}/src/branch/{{PathEscapeSegments .PullRequest.HeadBranch}}">
|
||||
@ -60,32 +88,32 @@
|
||||
</div>
|
||||
{{end}}
|
||||
{{if and .Milestone (ne $.listType "milestone")}}
|
||||
<a class="milestone" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{.Repo.Link}}/milestone/{{.Milestone.ID}}"{{end}}>
|
||||
{{svg "octicon-milestone" 14 "gt-mr-2"}}{{.Milestone.Name}}
|
||||
<a class="milestone flex-text-inline" {{if $.RepoLink}}href="{{$.RepoLink}}/milestone/{{.Milestone.ID}}"{{else}}href="{{.Repo.Link}}/milestone/{{.Milestone.ID}}"{{end}}>
|
||||
{{svg "octicon-milestone" 14}}{{.Milestone.Name}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .Project}}
|
||||
<a class="project" href="{{.Project.Link}}">
|
||||
{{svg .Project.IconName 14 "gt-mr-2"}}{{.Project.Title}}
|
||||
<a class="project flex-text-inline" href="{{.Project.Link}}">
|
||||
{{svg .Project.IconName 14}}{{.Project.Title}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .Ref}}
|
||||
<a class="ref" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}>
|
||||
{{svg "octicon-git-branch" 14 "gt-mr-2"}}{{index $.IssueRefEndNames .ID}}
|
||||
<a class="ref flex-text-inline" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}>
|
||||
{{svg "octicon-git-branch" 14}}{{index $.IssueRefEndNames .ID}}
|
||||
</a>
|
||||
{{end}}
|
||||
{{$tasks := .GetTasks}}
|
||||
{{if gt $tasks 0}}
|
||||
{{$tasksDone := .GetTasksDone}}
|
||||
<span class="checklist">
|
||||
{{svg "octicon-checklist" 14 "gt-mr-2"}}{{$tasksDone}} / {{$tasks}}
|
||||
<span class="checklist flex-text-inline">
|
||||
{{svg "octicon-checklist" 14}}{{$tasksDone}} / {{$tasks}}
|
||||
<progress value="{{$tasksDone}}" max="{{$tasks}}"></progress>
|
||||
</span>
|
||||
{{end}}
|
||||
{{if ne .DeadlineUnix 0}}
|
||||
<span class="due-date" data-tooltip-content="{{$.locale.Tr "repo.issues.due_date"}}">
|
||||
<span class="due-date flex-text-inline" data-tooltip-content="{{$.locale.Tr "repo.issues.due_date"}}">
|
||||
<span{{if .IsOverdue}} class="text red"{{end}}>
|
||||
{{svg "octicon-calendar" 14 "gt-mr-2"}}
|
||||
{{svg "octicon-calendar" 14}}
|
||||
{{DateTime "short" .DeadlineUnix}}
|
||||
</span>
|
||||
</span>
|
||||
@ -95,25 +123,25 @@
|
||||
{{$rejectOfficial := call $approvalCounts .ID "reject"}}
|
||||
{{$waitingOfficial := call $approvalCounts .ID "waiting"}}
|
||||
{{if gt $approveOfficial 0}}
|
||||
<span class="approvals gt-df gt-ac green">
|
||||
{{svg "octicon-check" 14 "gt-mr-1"}}
|
||||
<span class="approvals green flex-text-inline">
|
||||
{{svg "octicon-check" 14}}
|
||||
{{$.locale.TrN $approveOfficial "repo.pulls.approve_count_1" "repo.pulls.approve_count_n" $approveOfficial}}
|
||||
</span>
|
||||
{{end}}
|
||||
{{if gt $rejectOfficial 0}}
|
||||
<span class="rejects gt-df gt-ac red">
|
||||
{{svg "octicon-diff" 14 "gt-mr-2"}}
|
||||
<span class="rejects red flex-text-inline">
|
||||
{{svg "octicon-diff" 14}}
|
||||
{{$.locale.TrN $rejectOfficial "repo.pulls.reject_count_1" "repo.pulls.reject_count_n" $rejectOfficial}}
|
||||
</span>
|
||||
{{end}}
|
||||
{{if gt $waitingOfficial 0}}
|
||||
<span class="waiting gt-df gt-ac">
|
||||
{{svg "octicon-eye" 14 "gt-mr-2"}}
|
||||
<span class="waiting flex-text-inline">
|
||||
{{svg "octicon-eye" 14}}
|
||||
{{$.locale.TrN $waitingOfficial "repo.pulls.waiting_count_1" "repo.pulls.waiting_count_n" $waitingOfficial}}
|
||||
</span>
|
||||
{{end}}
|
||||
{{if and (not .PullRequest.HasMerged) (gt (len .PullRequest.ConflictedFiles) 0)}}
|
||||
<span class="conflicting gt-df gt-ac">
|
||||
<span class="conflicting flex-text-inline">
|
||||
{{svg "octicon-x" 14}}
|
||||
{{$.locale.TrN (len .PullRequest.ConflictedFiles) "repo.pulls.num_conflicting_files_1" "repo.pulls.num_conflicting_files_n" (len .PullRequest.ConflictedFiles)}}
|
||||
</span>
|
||||
@ -121,32 +149,6 @@
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{if or .TotalTrackedTime .Assignees .NumComments}}
|
||||
<div class="issue-item-icons-right gt-df gt-p-2">
|
||||
{{if .TotalTrackedTime}}
|
||||
<div class="issue-item-icon-right text grey">
|
||||
{{svg "octicon-clock" 16 "gt-mr-2"}}
|
||||
{{.TotalTrackedTime | Sec2Time}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Assignees}}
|
||||
<div class="issue-item-icon-right text grey">
|
||||
{{range .Assignees}}
|
||||
<a class="ui assignee gt-no-underline" href="{{.HomeLink}}" data-tooltip-content="{{.GetDisplayName}}">
|
||||
{{avatar $.Context . 20}}
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .NumComments}}
|
||||
<div class="issue-item-icon-right text grey">
|
||||
<a class="gt-no-underline muted" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
|
||||
{{svg "octicon-comment" 16 "gt-mr-2"}}{{.NumComments}}
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</li>
|
||||
{{end}}
|
||||
{{if .IssueIndexerUnavailable}}
|
||||
|
@ -11,13 +11,10 @@
|
||||
<label for="password">{{.locale.Tr "password"}}</label>
|
||||
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="new-password" required>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
|
||||
<label for="retype">{{.locale.Tr "re_type"}}</label>
|
||||
<input id="retype" name="retype" type="password" autocomplete="new-password" required>
|
||||
</div>
|
||||
|
||||
<div class="inline field">
|
||||
<label></label>
|
||||
<button class="ui green button">{{.locale.Tr "settings.change_password"}}</button>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-params="{{.Page.GetParams}}" data-sequence-number="{{.SequenceNumber}}">
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_div" data-sequence-number="{{.SequenceNumber}}">
|
||||
<div class="ui container">
|
||||
{{$notificationUnreadCount := call .NotificationUnreadCount}}
|
||||
<div class="gt-df gt-ac gt-sb gt-mb-4">
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{template "base/head" .}}
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content user notification" id="notification_subscriptions" data-params="{{.Page.GetParams}}" data-sequence-number="{{.SequenceNumber}}">
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content user notification">
|
||||
<div class="ui container">
|
||||
<div class="ui top attached tabular menu">
|
||||
<a href="{{AppSubUrl}}/notifications/subscriptions" class="{{if eq .Status 1}}active {{end}}item">
|
||||
|
@ -45,11 +45,12 @@ func TestAPIGetCommentAttachment(t *testing.T) {
|
||||
var apiAttachment api.Attachment
|
||||
DecodeJSON(t, resp, &apiAttachment)
|
||||
|
||||
expect := convert.ToAttachment(attachment)
|
||||
expect := convert.ToAPIAttachment(repo, attachment)
|
||||
assert.Equal(t, expect.ID, apiAttachment.ID)
|
||||
assert.Equal(t, expect.Name, apiAttachment.Name)
|
||||
assert.Equal(t, expect.UUID, apiAttachment.UUID)
|
||||
assert.Equal(t, expect.Created.Unix(), apiAttachment.Created.Unix())
|
||||
assert.Equal(t, expect.DownloadURL, apiAttachment.DownloadURL)
|
||||
}
|
||||
|
||||
func TestAPIListCommentAttachments(t *testing.T) {
|
||||
|
@ -128,7 +128,7 @@ func TestAPIGetComment(t *testing.T) {
|
||||
DecodeJSON(t, resp, &apiComment)
|
||||
|
||||
assert.NoError(t, comment.LoadPoster(db.DefaultContext))
|
||||
expect := convert.ToComment(db.DefaultContext, comment)
|
||||
expect := convert.ToAPIComment(db.DefaultContext, repo, comment)
|
||||
|
||||
assert.Equal(t, expect.ID, apiComment.ID)
|
||||
assert.Equal(t, expect.Poster.FullName, apiComment.Poster.FullName)
|
||||
|
@ -170,9 +170,9 @@ func TestAPIGetAll(t *testing.T) {
|
||||
var apiOrgList []*api.Organization
|
||||
|
||||
DecodeJSON(t, resp, &apiOrgList)
|
||||
assert.Len(t, apiOrgList, 9)
|
||||
assert.Equal(t, "org25", apiOrgList[1].FullName)
|
||||
assert.Equal(t, "public", apiOrgList[1].Visibility)
|
||||
assert.Len(t, apiOrgList, 11)
|
||||
assert.Equal(t, "Limited Org 36", apiOrgList[1].FullName)
|
||||
assert.Equal(t, "limited", apiOrgList[1].Visibility)
|
||||
|
||||
// accessing without a token will return only public orgs
|
||||
req = NewRequestf(t, "GET", "/api/v1/orgs")
|
||||
|
@ -157,29 +157,227 @@ func TestPackageAccess(t *testing.T) {
|
||||
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||
inactive := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 9})
|
||||
privatedOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23})
|
||||
limitedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 33})
|
||||
privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31})
|
||||
privateOrgMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23}) // user has package write access
|
||||
limitedOrgMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 36}) // user has package write access
|
||||
publicOrgMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 25}) // user has package read access
|
||||
privateOrgNoMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 35})
|
||||
limitedOrgNoMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 22})
|
||||
publicOrgNoMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17})
|
||||
|
||||
uploadPackage := func(doer, owner *user_model.User, expectedStatus int) {
|
||||
url := fmt.Sprintf("/api/packages/%s/generic/test-package/1.0/file.bin", owner.Name)
|
||||
uploadPackage := func(doer, owner *user_model.User, filename string, expectedStatus int) {
|
||||
url := fmt.Sprintf("/api/packages/%s/generic/test-package/1.0/%s.bin", owner.Name, filename)
|
||||
req := NewRequestWithBody(t, "PUT", url, bytes.NewReader([]byte{1}))
|
||||
AddBasicAuthHeader(req, doer.Name)
|
||||
if doer != nil {
|
||||
AddBasicAuthHeader(req, doer.Name)
|
||||
}
|
||||
MakeRequest(t, req, expectedStatus)
|
||||
}
|
||||
|
||||
uploadPackage(user, inactive, http.StatusUnauthorized)
|
||||
uploadPackage(inactive, inactive, http.StatusUnauthorized)
|
||||
uploadPackage(inactive, user, http.StatusUnauthorized)
|
||||
uploadPackage(admin, inactive, http.StatusCreated)
|
||||
uploadPackage(admin, user, http.StatusCreated)
|
||||
downloadPackage := func(doer, owner *user_model.User, expectedStatus int) {
|
||||
url := fmt.Sprintf("/api/packages/%s/generic/test-package/1.0/admin.bin", owner.Name)
|
||||
req := NewRequest(t, "GET", url)
|
||||
if doer != nil {
|
||||
AddBasicAuthHeader(req, doer.Name)
|
||||
}
|
||||
MakeRequest(t, req, expectedStatus)
|
||||
}
|
||||
|
||||
// team.authorize is write, but team_unit.access_mode is none
|
||||
// so the user can not upload packages or get package list
|
||||
uploadPackage(user, privatedOrg, http.StatusUnauthorized)
|
||||
type Target struct {
|
||||
Owner *user_model.User
|
||||
ExpectedStatus int
|
||||
}
|
||||
|
||||
session := loginUser(t, user.Name)
|
||||
tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?token=%s", privatedOrg.Name, tokenReadPackage))
|
||||
MakeRequest(t, req, http.StatusForbidden)
|
||||
t.Run("Upload", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
cases := []struct {
|
||||
Doer *user_model.User
|
||||
Filename string
|
||||
Targets []Target
|
||||
}{
|
||||
{ // Admins can upload to every owner
|
||||
Doer: admin,
|
||||
Filename: "admin",
|
||||
Targets: []Target{
|
||||
{admin, http.StatusCreated},
|
||||
{inactive, http.StatusCreated},
|
||||
{user, http.StatusCreated},
|
||||
{limitedUser, http.StatusCreated},
|
||||
{privateUser, http.StatusCreated},
|
||||
{privateOrgMember, http.StatusCreated},
|
||||
{limitedOrgMember, http.StatusCreated},
|
||||
{publicOrgMember, http.StatusCreated},
|
||||
{privateOrgNoMember, http.StatusCreated},
|
||||
{limitedOrgNoMember, http.StatusCreated},
|
||||
{publicOrgNoMember, http.StatusCreated},
|
||||
},
|
||||
},
|
||||
{ // Without credentials no upload should be possible
|
||||
Doer: nil,
|
||||
Filename: "nil",
|
||||
Targets: []Target{
|
||||
{admin, http.StatusUnauthorized},
|
||||
{inactive, http.StatusUnauthorized},
|
||||
{user, http.StatusUnauthorized},
|
||||
{limitedUser, http.StatusUnauthorized},
|
||||
{privateUser, http.StatusUnauthorized},
|
||||
{privateOrgMember, http.StatusUnauthorized},
|
||||
{limitedOrgMember, http.StatusUnauthorized},
|
||||
{publicOrgMember, http.StatusUnauthorized},
|
||||
{privateOrgNoMember, http.StatusUnauthorized},
|
||||
{limitedOrgNoMember, http.StatusUnauthorized},
|
||||
{publicOrgNoMember, http.StatusUnauthorized},
|
||||
},
|
||||
},
|
||||
{ // Inactive users can't upload anywhere
|
||||
Doer: inactive,
|
||||
Filename: "inactive",
|
||||
Targets: []Target{
|
||||
{admin, http.StatusUnauthorized},
|
||||
{inactive, http.StatusUnauthorized},
|
||||
{user, http.StatusUnauthorized},
|
||||
{limitedUser, http.StatusUnauthorized},
|
||||
{privateUser, http.StatusUnauthorized},
|
||||
{privateOrgMember, http.StatusUnauthorized},
|
||||
{limitedOrgMember, http.StatusUnauthorized},
|
||||
{publicOrgMember, http.StatusUnauthorized},
|
||||
{privateOrgNoMember, http.StatusUnauthorized},
|
||||
{limitedOrgNoMember, http.StatusUnauthorized},
|
||||
{publicOrgNoMember, http.StatusUnauthorized},
|
||||
},
|
||||
},
|
||||
{ // Normal users can upload to self and orgs in which they are members and have package write access
|
||||
Doer: user,
|
||||
Filename: "user",
|
||||
Targets: []Target{
|
||||
{admin, http.StatusUnauthorized},
|
||||
{inactive, http.StatusUnauthorized},
|
||||
{user, http.StatusCreated},
|
||||
{limitedUser, http.StatusUnauthorized},
|
||||
{privateUser, http.StatusUnauthorized},
|
||||
{privateOrgMember, http.StatusCreated},
|
||||
{limitedOrgMember, http.StatusCreated},
|
||||
{publicOrgMember, http.StatusUnauthorized},
|
||||
{privateOrgNoMember, http.StatusUnauthorized},
|
||||
{limitedOrgNoMember, http.StatusUnauthorized},
|
||||
{publicOrgNoMember, http.StatusUnauthorized},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
for _, t := range c.Targets {
|
||||
uploadPackage(c.Doer, t.Owner, c.Filename, t.ExpectedStatus)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Download", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
cases := []struct {
|
||||
Doer *user_model.User
|
||||
Filename string
|
||||
Targets []Target
|
||||
}{
|
||||
{ // Admins can access everything
|
||||
Doer: admin,
|
||||
Targets: []Target{
|
||||
{admin, http.StatusOK},
|
||||
{inactive, http.StatusOK},
|
||||
{user, http.StatusOK},
|
||||
{limitedUser, http.StatusOK},
|
||||
{privateUser, http.StatusOK},
|
||||
{privateOrgMember, http.StatusOK},
|
||||
{limitedOrgMember, http.StatusOK},
|
||||
{publicOrgMember, http.StatusOK},
|
||||
{privateOrgNoMember, http.StatusOK},
|
||||
{limitedOrgNoMember, http.StatusOK},
|
||||
{publicOrgNoMember, http.StatusOK},
|
||||
},
|
||||
},
|
||||
{ // Without credentials only public owners are accessible
|
||||
Doer: nil,
|
||||
Targets: []Target{
|
||||
{admin, http.StatusOK},
|
||||
{inactive, http.StatusOK},
|
||||
{user, http.StatusOK},
|
||||
{limitedUser, http.StatusUnauthorized},
|
||||
{privateUser, http.StatusUnauthorized},
|
||||
{privateOrgMember, http.StatusUnauthorized},
|
||||
{limitedOrgMember, http.StatusUnauthorized},
|
||||
{publicOrgMember, http.StatusOK},
|
||||
{privateOrgNoMember, http.StatusUnauthorized},
|
||||
{limitedOrgNoMember, http.StatusUnauthorized},
|
||||
{publicOrgNoMember, http.StatusOK},
|
||||
},
|
||||
},
|
||||
{ // Inactive users have no access
|
||||
Doer: inactive,
|
||||
Targets: []Target{
|
||||
{admin, http.StatusUnauthorized},
|
||||
{inactive, http.StatusUnauthorized},
|
||||
{user, http.StatusUnauthorized},
|
||||
{limitedUser, http.StatusUnauthorized},
|
||||
{privateUser, http.StatusUnauthorized},
|
||||
{privateOrgMember, http.StatusUnauthorized},
|
||||
{limitedOrgMember, http.StatusUnauthorized},
|
||||
{publicOrgMember, http.StatusUnauthorized},
|
||||
{privateOrgNoMember, http.StatusUnauthorized},
|
||||
{limitedOrgNoMember, http.StatusUnauthorized},
|
||||
{publicOrgNoMember, http.StatusUnauthorized},
|
||||
},
|
||||
},
|
||||
{ // Normal users can access self, public or limited users/orgs and private orgs in which they are members
|
||||
Doer: user,
|
||||
Targets: []Target{
|
||||
{admin, http.StatusOK},
|
||||
{inactive, http.StatusOK},
|
||||
{user, http.StatusOK},
|
||||
{limitedUser, http.StatusOK},
|
||||
{privateUser, http.StatusUnauthorized},
|
||||
{privateOrgMember, http.StatusOK},
|
||||
{limitedOrgMember, http.StatusOK},
|
||||
{publicOrgMember, http.StatusOK},
|
||||
{privateOrgNoMember, http.StatusUnauthorized},
|
||||
{limitedOrgNoMember, http.StatusOK},
|
||||
{publicOrgNoMember, http.StatusOK},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
for _, target := range c.Targets {
|
||||
downloadPackage(c.Doer, target.Owner, target.ExpectedStatus)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("API", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
session := loginUser(t, user.Name)
|
||||
tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
|
||||
|
||||
for _, target := range []Target{
|
||||
{admin, http.StatusOK},
|
||||
{inactive, http.StatusOK},
|
||||
{user, http.StatusOK},
|
||||
{limitedUser, http.StatusOK},
|
||||
{privateUser, http.StatusForbidden},
|
||||
{privateOrgMember, http.StatusOK},
|
||||
{limitedOrgMember, http.StatusOK},
|
||||
{publicOrgMember, http.StatusOK},
|
||||
{privateOrgNoMember, http.StatusForbidden},
|
||||
{limitedOrgNoMember, http.StatusOK},
|
||||
{publicOrgNoMember, http.StatusOK},
|
||||
} {
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s?token=%s", target.Owner.Name, tokenReadPackage))
|
||||
MakeRequest(t, req, target.ExpectedStatus)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPackageQuota(t *testing.T) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
.page-content.install {
|
||||
padding-top: 45px;
|
||||
.page-content.install .install-config-container {
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.page-content.install form.ui.form .inline.field > label {
|
||||
@ -9,26 +10,20 @@
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.page-content.install form.ui.form .inline.field > .ui.checkbox:first-child {
|
||||
.page-content.install .ui.form .field > .help,
|
||||
.page-content.install .ui.form .field > .ui.checkbox:first-child,
|
||||
.page-content.install .ui.form .field > .right-content {
|
||||
margin-left: 30%;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.page-content.install form.ui.form .inline.field > .ui.checkbox:first-child label {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.page-content.install form.ui.form .title {
|
||||
margin-left: 30%;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.page-content.install form.ui.form input {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.page-content.install form.ui.form details.optional.field[open] {
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
border-bottom: 1px dashed var(--color-secondary);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
@ -44,12 +39,6 @@
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.page-content.install form.ui.form .field .help {
|
||||
margin-left: 30%;
|
||||
padding-left: 5px;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.page-content.install .ui .reinstall-message {
|
||||
width: 70%;
|
||||
margin: 20px auto;
|
||||
|
@ -3,6 +3,30 @@
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.issue.list .item {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.issue.list .item .issue-item-left {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.issue.list .item .issue-item-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.issue.list .item .issue-item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.issue.list a:not(.label):hover {
|
||||
color: var(--color-primary) !important;
|
||||
}
|
||||
@ -12,14 +36,13 @@
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.issue.list > .item .issue-item-icons-right > * + * {
|
||||
margin-left: 0.5rem;
|
||||
.issue.list .item .issue-item-right {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.issue.list > .item .issue-item-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.issue.list > .action-item {
|
||||
align-items: normal;
|
||||
}
|
||||
|
||||
.issue.list > .item .action-item-center {
|
||||
@ -37,7 +60,7 @@
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
|
||||
.issue.list > .item .issue-item-top-row {
|
||||
.issue.list > .item .issue-item-title {
|
||||
max-width: 100%;
|
||||
color: var(--color-text);
|
||||
font-size: 16px;
|
||||
@ -45,7 +68,7 @@
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
|
||||
.issue.list > .item .issue-item-top-row a.index {
|
||||
.issue.list > .item .issue-item-title a.index {
|
||||
max-width: fit-content;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
@ -54,108 +77,49 @@
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.issue.list > .item .labels-list {
|
||||
position: relative;
|
||||
top: -1.5px;
|
||||
.issue.list > .item .title {
|
||||
color: var(--color-text);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.issue.list > .item .issue-item-bottom-row {
|
||||
.issue.list > .item .issue-item-body {
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin: .125rem 0;
|
||||
}
|
||||
|
||||
.issue.list > .item .title {
|
||||
color: var(--color-text);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.issue.list > .item .issue-item-icon-right {
|
||||
min-width: 2rem;
|
||||
}
|
||||
|
||||
.issue.list > .item .assignee {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.issue.list > .item .assignee img {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc {
|
||||
gap: .25rem;
|
||||
color: var(--color-text-light-2);
|
||||
}
|
||||
|
||||
.issue.list > .item .desc a {
|
||||
.issue.list > .item .issue-item-body a {
|
||||
color: inherit;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc .time-since,
|
||||
.issue.list > .item .desc a {
|
||||
margin-left: 0.25rem;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc .waiting,
|
||||
.issue.list > .item .desc .approvals,
|
||||
.issue.list > .item .desc .rejects {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc .checklist {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc .checklist progress {
|
||||
.issue.list > .item .issue-item-body .checklist progress {
|
||||
margin-left: 2px;
|
||||
width: 80px;
|
||||
height: 6px;
|
||||
display: inline-block;
|
||||
border-radius: 3px;
|
||||
vertical-align: 2px !important;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc .checklist progress::-webkit-progress-value {
|
||||
.issue.list > .item .issue-item-body .checklist progress::-webkit-progress-value {
|
||||
background-color: var(--color-secondary-dark-4);
|
||||
}
|
||||
|
||||
.issue.list > .item .desc .checklist progress::-moz-progress-bar {
|
||||
.issue.list > .item .issue-item-body .checklist progress::-moz-progress-bar {
|
||||
background-color: var(--color-secondary-dark-4);
|
||||
}
|
||||
|
||||
.issue.list > .item .desc .conflicting {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc .due-date {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc a.milestone,
|
||||
.issue.list > .item .desc a.project {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc a.ref {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.issue.list > .item .desc a.ref span {
|
||||
margin-right: -4px;
|
||||
}
|
||||
|
||||
.issue.list .branches {
|
||||
display: inline-flex;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.issue.list .branches .branch {
|
||||
background-color: var(--color-secondary-alpha-40);
|
||||
border-radius: 3px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.issue.list .branches .truncated-name {
|
||||
|
@ -165,7 +165,7 @@ async function updateNotificationTable() {
|
||||
if (notificationDiv.length > 0) {
|
||||
const data = await $.ajax({
|
||||
type: 'GET',
|
||||
url: `${appSubUrl}/notifications?${notificationDiv.data('params')}`,
|
||||
url: `${appSubUrl}/notifications${window.location.search}`,
|
||||
data: {
|
||||
'div-only': true,
|
||||
'sequence-number': ++notificationSequenceNumber,
|
||||
|
Loading…
Reference in New Issue
Block a user