mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-18 00:01:32 -04:00
Compare commits
9 Commits
ddf61373f6
...
28625fba5b
Author | SHA1 | Date | |
---|---|---|---|
|
28625fba5b | ||
|
7c3196ceac | ||
|
80c1264f4b | ||
|
f0340c28f1 | ||
|
5beb29ad35 | ||
|
27e307142b | ||
|
e02e752f68 | ||
|
5ddf67a9c2 | ||
|
4d3e2b23b8 |
@ -153,7 +153,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
|
|||||||
return DefaultAvatarLink()
|
return DefaultAvatarLink()
|
||||||
}
|
}
|
||||||
|
|
||||||
enableFederatedAvatar := system_model.GetSettingBool(ctx, system_model.KeyPictureEnableFederatedAvatar)
|
enableFederatedAvatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureEnableFederatedAvatar)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if enableFederatedAvatar && system_model.LibravatarService != nil {
|
if enableFederatedAvatar && system_model.LibravatarService != nil {
|
||||||
@ -174,7 +174,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
|
|||||||
return urlStr
|
return urlStr
|
||||||
}
|
}
|
||||||
|
|
||||||
disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar)
|
disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
||||||
if !disableGravatar {
|
if !disableGravatar {
|
||||||
// copy GravatarSourceURL, because we will modify its Path.
|
// copy GravatarSourceURL, because we will modify its Path.
|
||||||
avatarURLCopy := *system_model.GravatarSourceURL
|
avatarURLCopy := *system_model.GravatarSourceURL
|
||||||
|
@ -28,7 +28,7 @@ func enableGravatar(t *testing.T) {
|
|||||||
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
|
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
setting.GravatarSource = gravatarSource
|
setting.GravatarSource = gravatarSource
|
||||||
err = system_model.Init()
|
err = system_model.Init(db.DefaultContext)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error {
|
|||||||
if !opts.IsListAll() {
|
if !opts.IsListAll() {
|
||||||
sess.Limit(opts.GetSkipTake())
|
sess.Limit(opts.GetSkipTake())
|
||||||
}
|
}
|
||||||
return sess.Find(&objects)
|
return sess.Find(objects)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count represents a common count function which accept an options interface
|
// Count represents a common count function which accept an options interface
|
||||||
@ -148,5 +148,5 @@ func FindAndCount[T any](ctx context.Context, opts FindOptions, objects *[]T) (i
|
|||||||
if !opts.IsListAll() {
|
if !opts.IsListAll() {
|
||||||
sess.Limit(opts.GetSkipTake())
|
sess.Limit(opts.GetSkipTake())
|
||||||
}
|
}
|
||||||
return sess.FindAndCount(&objects)
|
return sess.FindAndCount(objects)
|
||||||
}
|
}
|
48
models/db/list_test.go
Normal file
48
models/db/list_test.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package db_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"xorm.io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockListOptions struct {
|
||||||
|
db.ListOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *mockListOptions) IsListAll() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *mockListOptions) ToConds() builder.Cond {
|
||||||
|
return builder.NewCond()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFind(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
xe := unittest.GetXORMEngine()
|
||||||
|
assert.NoError(t, xe.Sync(&repo_model.RepoUnit{}))
|
||||||
|
|
||||||
|
opts := mockListOptions{}
|
||||||
|
var repoUnits []repo_model.RepoUnit
|
||||||
|
err := db.Find(db.DefaultContext, &opts, &repoUnits)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 83, len(repoUnits))
|
||||||
|
|
||||||
|
cnt, err := db.Count(db.DefaultContext, &opts, new(repo_model.RepoUnit))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 83, cnt)
|
||||||
|
|
||||||
|
repoUnits = make([]repo_model.RepoUnit, 0, 10)
|
||||||
|
newCnt, err := db.FindAndCount(db.DefaultContext, &opts, &repoUnits)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, cnt, newCnt)
|
||||||
|
}
|
@ -39,9 +39,9 @@ import (
|
|||||||
var ItemsPerPage = 40
|
var ItemsPerPage = 40
|
||||||
|
|
||||||
// Init initialize model
|
// Init initialize model
|
||||||
func Init() error {
|
func Init(ctx context.Context) error {
|
||||||
unit.LoadUnitConfig()
|
unit.LoadUnitConfig()
|
||||||
return system_model.Init()
|
return system_model.Init(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRepository deletes a repository for a user or organization.
|
// DeleteRepository deletes a repository for a user or organization.
|
||||||
|
@ -79,8 +79,8 @@ func IsErrDataExpired(err error) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSettingNoCache returns specific setting without using the cache
|
// GetSetting returns specific setting without using the cache
|
||||||
func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) {
|
func GetSetting(ctx context.Context, key string) (*Setting, error) {
|
||||||
v, err := GetSettings(ctx, []string{key})
|
v, err := GetSettings(ctx, []string{key})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -93,11 +93,11 @@ func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) {
|
|||||||
|
|
||||||
const contextCacheKey = "system_setting"
|
const contextCacheKey = "system_setting"
|
||||||
|
|
||||||
// GetSetting returns the setting value via the key
|
// GetSettingWithCache returns the setting value via the key
|
||||||
func GetSetting(ctx context.Context, key string) (string, error) {
|
func GetSettingWithCache(ctx context.Context, key string) (string, error) {
|
||||||
return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) {
|
return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) {
|
||||||
return cache.GetString(genSettingCacheKey(key), func() (string, error) {
|
return cache.GetString(genSettingCacheKey(key), func() (string, error) {
|
||||||
res, err := GetSettingNoCache(ctx, key)
|
res, err := GetSetting(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -110,6 +110,15 @@ func GetSetting(ctx context.Context, key string) (string, error) {
|
|||||||
// none existing keys and errors are ignored and result in false
|
// none existing keys and errors are ignored and result in false
|
||||||
func GetSettingBool(ctx context.Context, key string) bool {
|
func GetSettingBool(ctx context.Context, key string) bool {
|
||||||
s, _ := GetSetting(ctx, key)
|
s, _ := GetSetting(ctx, key)
|
||||||
|
if s == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
v, _ := strconv.ParseBool(s.SettingValue)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSettingWithCacheBool(ctx context.Context, key string) bool {
|
||||||
|
s, _ := GetSettingWithCache(ctx, key)
|
||||||
v, _ := strconv.ParseBool(s)
|
v, _ := strconv.ParseBool(s)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
@ -120,7 +129,7 @@ func GetSettings(ctx context.Context, keys []string) (map[string]*Setting, error
|
|||||||
keys[i] = strings.ToLower(keys[i])
|
keys[i] = strings.ToLower(keys[i])
|
||||||
}
|
}
|
||||||
settings := make([]*Setting, 0, len(keys))
|
settings := make([]*Setting, 0, len(keys))
|
||||||
if err := db.GetEngine(db.DefaultContext).
|
if err := db.GetEngine(ctx).
|
||||||
Where(builder.In("setting_key", keys)).
|
Where(builder.In("setting_key", keys)).
|
||||||
Find(&settings); err != nil {
|
Find(&settings); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -151,9 +160,9 @@ func (settings AllSettings) GetVersion(key string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAllSettings returns all settings from user
|
// GetAllSettings returns all settings from user
|
||||||
func GetAllSettings() (AllSettings, error) {
|
func GetAllSettings(ctx context.Context) (AllSettings, error) {
|
||||||
settings := make([]*Setting, 0, 5)
|
settings := make([]*Setting, 0, 5)
|
||||||
if err := db.GetEngine(db.DefaultContext).
|
if err := db.GetEngine(ctx).
|
||||||
Find(&settings); err != nil {
|
Find(&settings); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -168,12 +177,12 @@ func GetAllSettings() (AllSettings, error) {
|
|||||||
func DeleteSetting(ctx context.Context, setting *Setting) error {
|
func DeleteSetting(ctx context.Context, setting *Setting) error {
|
||||||
cache.RemoveContextData(ctx, contextCacheKey, setting.SettingKey)
|
cache.RemoveContextData(ctx, contextCacheKey, setting.SettingKey)
|
||||||
cache.Remove(genSettingCacheKey(setting.SettingKey))
|
cache.Remove(genSettingCacheKey(setting.SettingKey))
|
||||||
_, err := db.GetEngine(db.DefaultContext).Delete(setting)
|
_, err := db.GetEngine(ctx).Delete(setting)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetSettingNoVersion(ctx context.Context, key, value string) error {
|
func SetSettingNoVersion(ctx context.Context, key, value string) error {
|
||||||
s, err := GetSettingNoCache(ctx, key)
|
s, err := GetSetting(ctx, key)
|
||||||
if IsErrSettingIsNotExist(err) {
|
if IsErrSettingIsNotExist(err) {
|
||||||
return SetSetting(ctx, &Setting{
|
return SetSetting(ctx, &Setting{
|
||||||
SettingKey: key,
|
SettingKey: key,
|
||||||
@ -189,7 +198,7 @@ func SetSettingNoVersion(ctx context.Context, key, value string) error {
|
|||||||
|
|
||||||
// SetSetting updates a users' setting for a specific key
|
// SetSetting updates a users' setting for a specific key
|
||||||
func SetSetting(ctx context.Context, setting *Setting) error {
|
func SetSetting(ctx context.Context, setting *Setting) error {
|
||||||
if err := upsertSettingValue(strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil {
|
if err := upsertSettingValue(ctx, strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +214,8 @@ func SetSetting(ctx context.Context, setting *Setting) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func upsertSettingValue(key, value string, version int) error {
|
func upsertSettingValue(parentCtx context.Context, key, value string, version int) error {
|
||||||
return db.WithTx(db.DefaultContext, func(ctx context.Context) error {
|
return db.WithTx(parentCtx, func(ctx context.Context) error {
|
||||||
e := db.GetEngine(ctx)
|
e := db.GetEngine(ctx)
|
||||||
|
|
||||||
// here we use a general method to do a safe upsert for different databases (and most transaction levels)
|
// here we use a general method to do a safe upsert for different databases (and most transaction levels)
|
||||||
@ -249,9 +258,9 @@ var (
|
|||||||
LibravatarService *libravatar.Libravatar
|
LibravatarService *libravatar.Libravatar
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init() error {
|
func Init(ctx context.Context) error {
|
||||||
var disableGravatar bool
|
var disableGravatar bool
|
||||||
disableGravatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureDisableGravatar)
|
disableGravatarSetting, err := GetSetting(ctx, KeyPictureDisableGravatar)
|
||||||
if IsErrSettingIsNotExist(err) {
|
if IsErrSettingIsNotExist(err) {
|
||||||
disableGravatar = setting_module.GetDefaultDisableGravatar()
|
disableGravatar = setting_module.GetDefaultDisableGravatar()
|
||||||
disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)}
|
disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)}
|
||||||
@ -262,7 +271,7 @@ func Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var enableFederatedAvatar bool
|
var enableFederatedAvatar bool
|
||||||
enableFederatedAvatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureEnableFederatedAvatar)
|
enableFederatedAvatarSetting, err := GetSetting(ctx, KeyPictureEnableFederatedAvatar)
|
||||||
if IsErrSettingIsNotExist(err) {
|
if IsErrSettingIsNotExist(err) {
|
||||||
enableFederatedAvatar = setting_module.GetDefaultEnableFederatedAvatar(disableGravatar)
|
enableFederatedAvatar = setting_module.GetDefaultEnableFederatedAvatar(disableGravatar)
|
||||||
enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)}
|
enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)}
|
||||||
@ -275,13 +284,13 @@ func Init() error {
|
|||||||
if setting_module.OfflineMode {
|
if setting_module.OfflineMode {
|
||||||
disableGravatar = true
|
disableGravatar = true
|
||||||
enableFederatedAvatar = false
|
enableFederatedAvatar = false
|
||||||
if !GetSettingBool(db.DefaultContext, KeyPictureDisableGravatar) {
|
if !GetSettingBool(ctx, KeyPictureDisableGravatar) {
|
||||||
if err := SetSettingNoVersion(db.DefaultContext, KeyPictureDisableGravatar, "true"); err != nil {
|
if err := SetSettingNoVersion(ctx, KeyPictureDisableGravatar, "true"); err != nil {
|
||||||
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err)
|
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if GetSettingBool(db.DefaultContext, KeyPictureEnableFederatedAvatar) {
|
if GetSettingBool(ctx, KeyPictureEnableFederatedAvatar) {
|
||||||
if err := SetSettingNoVersion(db.DefaultContext, KeyPictureEnableFederatedAvatar, "false"); err != nil {
|
if err := SetSettingNoVersion(ctx, KeyPictureEnableFederatedAvatar, "false"); err != nil {
|
||||||
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err)
|
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,10 +40,10 @@ func TestSettings(t *testing.T) {
|
|||||||
|
|
||||||
value, err := system.GetSetting(db.DefaultContext, keyName)
|
value, err := system.GetSetting(db.DefaultContext, keyName)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, updatedSetting.SettingValue, value)
|
assert.EqualValues(t, updatedSetting.SettingValue, value.SettingValue)
|
||||||
|
|
||||||
// get all settings
|
// get all settings
|
||||||
settings, err = system.GetAllSettings()
|
settings, err = system.GetAllSettings(db.DefaultContext)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, settings, 3)
|
assert.Len(t, settings, 3)
|
||||||
assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue)
|
assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue)
|
||||||
@ -51,7 +51,7 @@ func TestSettings(t *testing.T) {
|
|||||||
// delete setting
|
// delete setting
|
||||||
err = system.DeleteSetting(db.DefaultContext, &system.Setting{SettingKey: strings.ToLower(keyName)})
|
err = system.DeleteSetting(db.DefaultContext, &system.Setting{SettingKey: strings.ToLower(keyName)})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
settings, err = system.GetAllSettings()
|
settings, err = system.GetAllSettings(db.DefaultContext)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, settings, 2)
|
assert.Len(t, settings, 2)
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
|
|||||||
if err = storage.Init(); err != nil {
|
if err = storage.Init(); err != nil {
|
||||||
fatalTestError("storage.Init: %v\n", err)
|
fatalTestError("storage.Init: %v\n", err)
|
||||||
}
|
}
|
||||||
if err = system_model.Init(); err != nil {
|
if err = system_model.Init(db.DefaultContext); err != nil {
|
||||||
fatalTestError("models.Init: %v\n", err)
|
fatalTestError("models.Init: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
|
|||||||
useLocalAvatar := false
|
useLocalAvatar := false
|
||||||
autoGenerateAvatar := false
|
autoGenerateAvatar := false
|
||||||
|
|
||||||
disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar)
|
disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case u.UseCustomAvatar:
|
case u.UseCustomAvatar:
|
||||||
|
@ -106,7 +106,7 @@ func enableGravatar(t *testing.T) {
|
|||||||
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
|
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
setting.GravatarSource = "https://secure.gravatar.com/avatar"
|
setting.GravatarSource = "https://secure.gravatar.com/avatar"
|
||||||
err = system_model.Init()
|
err = system_model.Init(db.DefaultContext)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ func NewFuncMap() []template.FuncMap {
|
|||||||
return setting.AssetVersion
|
return setting.AssetVersion
|
||||||
},
|
},
|
||||||
"DisableGravatar": func(ctx context.Context) bool {
|
"DisableGravatar": func(ctx context.Context) bool {
|
||||||
return system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar)
|
return system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
|
||||||
},
|
},
|
||||||
"DefaultShowFullName": func() bool {
|
"DefaultShowFullName": func() bool {
|
||||||
return setting.UI.DefaultShowFullName
|
return setting.UI.DefaultShowFullName
|
||||||
@ -174,8 +174,9 @@ func NewFuncMap() []template.FuncMap {
|
|||||||
"RenderEmojiPlain": emoji.ReplaceAliases,
|
"RenderEmojiPlain": emoji.ReplaceAliases,
|
||||||
"ReactionToEmoji": ReactionToEmoji,
|
"ReactionToEmoji": ReactionToEmoji,
|
||||||
"RenderNote": RenderNote,
|
"RenderNote": RenderNote,
|
||||||
"RenderMarkdownToHtml": func(input string) template.HTML {
|
"RenderMarkdownToHtml": func(ctx context.Context, input string) template.HTML {
|
||||||
output, err := markdown.RenderString(&markup.RenderContext{
|
output, err := markdown.RenderString(&markup.RenderContext{
|
||||||
|
Ctx: ctx,
|
||||||
URLPrefix: setting.AppSubURL,
|
URLPrefix: setting.AppSubURL,
|
||||||
}, input)
|
}, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -150,7 +150,7 @@ func GlobalInitInstalled(ctx context.Context) {
|
|||||||
mustInit(system.Init)
|
mustInit(system.Init)
|
||||||
mustInit(oauth2.Init)
|
mustInit(oauth2.Init)
|
||||||
|
|
||||||
mustInit(models.Init)
|
mustInitCtx(ctx, models.Init)
|
||||||
mustInit(repo_service.Init)
|
mustInit(repo_service.Init)
|
||||||
|
|
||||||
// Booting long running goroutines.
|
// Booting long running goroutines.
|
||||||
|
@ -103,7 +103,7 @@ func Config(ctx *context.Context) {
|
|||||||
ctx.Data["PageIsAdmin"] = true
|
ctx.Data["PageIsAdmin"] = true
|
||||||
ctx.Data["PageIsAdminConfig"] = true
|
ctx.Data["PageIsAdminConfig"] = true
|
||||||
|
|
||||||
systemSettings, err := system_model.GetAllSettings()
|
systemSettings, err := system_model.GetAllSettings(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("system_model.GetAllSettings", err)
|
ctx.ServerError("system_model.GetAllSettings", err)
|
||||||
return
|
return
|
||||||
|
@ -36,6 +36,7 @@ func Repos(ctx *context.Context) {
|
|||||||
Private: true,
|
Private: true,
|
||||||
PageSize: setting.UI.Admin.RepoPagingNum,
|
PageSize: setting.UI.Admin.RepoPagingNum,
|
||||||
TplName: tplRepos,
|
TplName: tplRepos,
|
||||||
|
OnlyShowRelevant: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,10 +27,13 @@ type RepoSearchOptions struct {
|
|||||||
Private bool
|
Private bool
|
||||||
Restricted bool
|
Restricted bool
|
||||||
PageSize int
|
PageSize int
|
||||||
|
OnlyShowRelevant bool
|
||||||
TplName base.TplName
|
TplName base.TplName
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderRepoSearch render repositories search page
|
// RenderRepoSearch render repositories search page
|
||||||
|
// This function is also used to render the Admin Repository Management page.
|
||||||
|
// The isAdmin param should be set to true when rendering the Admin page.
|
||||||
func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
||||||
// Sitemap index for sitemap paths
|
// Sitemap index for sitemap paths
|
||||||
page := int(ctx.ParamsInt64("idx"))
|
page := int(ctx.ParamsInt64("idx"))
|
||||||
@ -52,7 +55,6 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
|||||||
count int64
|
count int64
|
||||||
err error
|
err error
|
||||||
orderBy db.SearchOrderBy
|
orderBy db.SearchOrderBy
|
||||||
onlyShowRelevant bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx.Data["SortType"] = ctx.FormString("sort")
|
ctx.Data["SortType"] = ctx.FormString("sort")
|
||||||
@ -84,11 +86,9 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
|||||||
orderBy = db.SearchOrderByRecentUpdated
|
orderBy = db.SearchOrderByRecentUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
onlyShowRelevant = !ctx.FormBool(relevantReposOnlyParam)
|
|
||||||
|
|
||||||
keyword := ctx.FormTrim("q")
|
keyword := ctx.FormTrim("q")
|
||||||
|
|
||||||
ctx.Data["OnlyShowRelevant"] = onlyShowRelevant
|
ctx.Data["OnlyShowRelevant"] = opts.OnlyShowRelevant
|
||||||
|
|
||||||
topicOnly := ctx.FormBool("topic")
|
topicOnly := ctx.FormBool("topic")
|
||||||
ctx.Data["TopicOnly"] = topicOnly
|
ctx.Data["TopicOnly"] = topicOnly
|
||||||
@ -111,7 +111,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
|||||||
TopicOnly: topicOnly,
|
TopicOnly: topicOnly,
|
||||||
Language: language,
|
Language: language,
|
||||||
IncludeDescription: setting.UI.SearchRepoDescription,
|
IncludeDescription: setting.UI.SearchRepoDescription,
|
||||||
OnlyShowRelevant: onlyShowRelevant,
|
OnlyShowRelevant: opts.OnlyShowRelevant,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("SearchRepository", err)
|
ctx.ServerError("SearchRepository", err)
|
||||||
@ -162,5 +162,6 @@ func Repos(ctx *context.Context) {
|
|||||||
OwnerID: ownerID,
|
OwnerID: ownerID,
|
||||||
Private: ctx.Doer != nil,
|
Private: ctx.Doer != nil,
|
||||||
TplName: tplExploreRepos,
|
TplName: tplExploreRepos,
|
||||||
|
OnlyShowRelevant: !ctx.FormBool(relevantReposOnlyParam),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,6 @@ func NewDiffPatch(ctx *context.Context) {
|
|||||||
|
|
||||||
ctx.Data["PageIsPatch"] = true
|
ctx.Data["PageIsPatch"] = true
|
||||||
|
|
||||||
ctx.Data["TreePath"] = ""
|
|
||||||
|
|
||||||
ctx.Data["commit_summary"] = ""
|
ctx.Data["commit_summary"] = ""
|
||||||
ctx.Data["commit_message"] = ""
|
ctx.Data["commit_message"] = ""
|
||||||
if canCommit {
|
if canCommit {
|
||||||
@ -54,7 +52,6 @@ func NewDiffPatchPost(ctx *context.Context) {
|
|||||||
branchName = form.NewBranchName
|
branchName = form.NewBranchName
|
||||||
}
|
}
|
||||||
ctx.Data["PageIsPatch"] = true
|
ctx.Data["PageIsPatch"] = true
|
||||||
ctx.Data["TreePath"] = ""
|
|
||||||
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||||
ctx.Data["FileContent"] = form.Content
|
ctx.Data["FileContent"] = form.Content
|
||||||
ctx.Data["commit_summary"] = form.CommitSummary
|
ctx.Data["commit_summary"] = form.CommitSummary
|
||||||
@ -89,13 +86,14 @@ func NewDiffPatchPost(ctx *context.Context) {
|
|||||||
message += "\n\n" + form.CommitMessage
|
message += "\n\n" + form.CommitMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{
|
fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{
|
||||||
LastCommitID: form.LastCommit,
|
LastCommitID: form.LastCommit,
|
||||||
OldBranch: ctx.Repo.BranchName,
|
OldBranch: ctx.Repo.BranchName,
|
||||||
NewBranch: branchName,
|
NewBranch: branchName,
|
||||||
Message: message,
|
Message: message,
|
||||||
Content: strings.ReplaceAll(form.Content, "\r", ""),
|
Content: strings.ReplaceAll(form.Content, "\r", ""),
|
||||||
}); err != nil {
|
})
|
||||||
|
if err != nil {
|
||||||
if models.IsErrBranchAlreadyExists(err) {
|
if models.IsErrBranchAlreadyExists(err) {
|
||||||
// User has specified a branch that already exists
|
// User has specified a branch that already exists
|
||||||
branchErr := err.(models.ErrBranchAlreadyExists)
|
branchErr := err.(models.ErrBranchAlreadyExists)
|
||||||
@ -114,6 +112,6 @@ func NewDiffPatchPost(ctx *context.Context) {
|
|||||||
if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) {
|
if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) {
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName))
|
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName))
|
||||||
} else {
|
} else {
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath))
|
ctx.Redirect(ctx.Repo.RepoLink + "/commit/" + fileResponse.Commit.SHA)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ func ToPushMirror(pm *repo_model.PushMirror) (*api.PushMirror, error) {
|
|||||||
LastUpdateUnix: pm.LastUpdateUnix.FormatLong(),
|
LastUpdateUnix: pm.LastUpdateUnix.FormatLong(),
|
||||||
LastError: pm.LastError,
|
LastError: pm.LastError,
|
||||||
Interval: pm.Interval.String(),
|
Interval: pm.Interval.String(),
|
||||||
|
SyncOnCommit: pm.SyncOnCommit,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ git-fetch-with-cli = true</code></pre></div>
|
|||||||
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}
|
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}
|
||||||
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
|
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
|
||||||
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>{{end}}
|
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>{{end}}
|
||||||
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Readme}}</div>{{end}}
|
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if .PackageDescriptor.Metadata.Dependencies}}
|
{{if .PackageDescriptor.Metadata.Dependencies}}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
|
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
|
||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
{{if .PackageDescriptor.Metadata.Description}}<p>{{.PackageDescriptor.Metadata.Description}}</p>{{end}}
|
{{if .PackageDescriptor.Metadata.Description}}<p>{{.PackageDescriptor.Metadata.Description}}</p>{{end}}
|
||||||
{{if .PackageDescriptor.Metadata.LongDescription}}{{RenderMarkdownToHtml .PackageDescriptor.Metadata.LongDescription}}{{end}}
|
{{if .PackageDescriptor.Metadata.LongDescription}}{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.LongDescription}}{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
{{if .PackageDescriptor.Metadata.Readme}}
|
{{if .PackageDescriptor.Metadata.Readme}}
|
||||||
<div class="markup markdown">
|
<div class="markup markdown">
|
||||||
{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Readme}}
|
{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}
|
||||||
</div>
|
</div>
|
||||||
{{else if .PackageDescriptor.Metadata.Description}}
|
{{else if .PackageDescriptor.Metadata.Description}}
|
||||||
{{.PackageDescriptor.Metadata.Description}}
|
{{.PackageDescriptor.Metadata.Description}}
|
||||||
|
@ -14,6 +14,6 @@
|
|||||||
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}
|
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}
|
||||||
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
|
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
|
||||||
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>{{end}}
|
{{if .PackageDescriptor.Metadata.Description}}<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>{{end}}
|
||||||
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Readme}}</div>{{end}}
|
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
<p>{{if .PackageDescriptor.Metadata.Summary}}{{.PackageDescriptor.Metadata.Summary}}{{end}}</p>
|
<p>{{if .PackageDescriptor.Metadata.Summary}}{{.PackageDescriptor.Metadata.Summary}}{{end}}</p>
|
||||||
{{if .PackageDescriptor.Metadata.LongDescription}}
|
{{if .PackageDescriptor.Metadata.LongDescription}}
|
||||||
{{RenderMarkdownToHtml .PackageDescriptor.Metadata.LongDescription}}
|
{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.LongDescription}}
|
||||||
{{else if .PackageDescriptor.Metadata.Description}}
|
{{else if .PackageDescriptor.Metadata.Description}}
|
||||||
{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Description}}
|
{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Description}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
<input type="hidden" name="template-file" value="{{.TemplateFile}}">
|
<input type="hidden" name="template-file" value="{{.TemplateFile}}">
|
||||||
{{range .Fields}}
|
{{range .Fields}}
|
||||||
{{if eq .Type "input"}}
|
{{if eq .Type "input"}}
|
||||||
{{template "repo/issue/fields/input" .}}
|
{{template "repo/issue/fields/input" Dict "Context" $.Context "item" .}}
|
||||||
{{else if eq .Type "markdown"}}
|
{{else if eq .Type "markdown"}}
|
||||||
{{template "repo/issue/fields/markdown" .}}
|
{{template "repo/issue/fields/markdown" Dict "Context" $.Context "item" .}}
|
||||||
{{else if eq .Type "textarea"}}
|
{{else if eq .Type "textarea"}}
|
||||||
{{template "repo/issue/fields/textarea" .}}
|
{{template "repo/issue/fields/textarea" Dict "Context" $.Context "item" .}}
|
||||||
{{else if eq .Type "dropdown"}}
|
{{else if eq .Type "dropdown"}}
|
||||||
{{template "repo/issue/fields/dropdown" .}}
|
{{template "repo/issue/fields/dropdown" Dict "Context" $.Context "item" .}}
|
||||||
{{else if eq .Type "checkboxes"}}
|
{{else if eq .Type "checkboxes"}}
|
||||||
{{template "repo/issue/fields/checkboxes" .}}
|
{{template "repo/issue/fields/checkboxes" Dict "Context" $.Context "item" .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
{{template "repo/issue/fields/header" .}}
|
{{template "repo/issue/fields/header" .}}
|
||||||
{{$field := .}}
|
{{$field := .}}
|
||||||
{{range $i, $opt := .Attributes.options}}
|
{{range $i, $opt := .item.Attributes.options}}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" name="form-field-{{$field.ID}}-{{$i}}" {{if $opt.required}}readonly checked{{end}}>
|
<input type="checkbox" name="form-field-{{$field.ID}}-{{$i}}" {{if $opt.required}}readonly checked{{end}}>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
{{template "repo/issue/fields/header" .}}
|
{{template "repo/issue/fields/header" .}}
|
||||||
{{/* FIXME: required validation */}}
|
{{/* FIXME: required validation */}}
|
||||||
<div class="ui fluid selection dropdown {{if .Attributes.multiple}}multiple clearable{{end}}">
|
<div class="ui fluid selection dropdown {{if .item.Attributes.multiple}}multiple clearable{{end}}">
|
||||||
<input type="hidden" name="form-field-{{.ID}}" value="0">
|
<input type="hidden" name="form-field-{{.item.ID}}" value="0">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
<div class="default text"></div>
|
<div class="default text"></div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
{{range $i, $opt := .Attributes.options}}
|
{{range $i, $opt := .item.Attributes.options}}
|
||||||
<div class="item" data-value="{{$i}}">{{$opt}}</div>
|
<div class="item" data-value="{{$i}}">{{$opt}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{{if .Attributes.label}}
|
{{if .item.Attributes.label}}
|
||||||
<h3>{{.Attributes.label}}{{if .Validations.required}}<label class="required"></label>{{end}}</h3>
|
<h3>{{.item.Attributes.label}}{{if .item.Validations.required}}<label class="required"></label>{{end}}</h3>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .Attributes.description}}
|
{{if .item.Attributes.description}}
|
||||||
<span class="help">{{RenderMarkdownToHtml .Attributes.description}}</span>
|
<span class="help">{{RenderMarkdownToHtml .Context .item.Attributes.description}}</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
{{template "repo/issue/fields/header" .}}
|
{{template "repo/issue/fields/header" .}}
|
||||||
<input type="{{if .Validations.is_number}}number{{else}}text{{end}}" name="form-field-{{.ID}}" placeholder="{{.Attributes.placeholder}}" value="{{.Attributes.value}}" {{if .Validations.required}}required{{end}} {{if .Validations.regex}}pattern="{{.Validations.regex}}" title="{{.Validations.regex}}"{{end}}>
|
<input type="{{if .item.Validations.is_number}}number{{else}}text{{end}}" name="form-field-{{.item.ID}}" placeholder="{{.item.Attributes.placeholder}}" value="{{.item.Attributes.value}}" {{if .item.Validations.required}}required{{end}} {{if .item.Validations.regex}}pattern="{{.item.Validations.regex}}" title="{{.item.Validations.regex}}"{{end}}>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<div>{{RenderMarkdownToHtml .Attributes.value}}</div>
|
<div>{{RenderMarkdownToHtml .Context .item.Attributes.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
{{template "repo/issue/fields/header" .}}
|
{{template "repo/issue/fields/header" .}}
|
||||||
{{/* FIXME: preview markdown result */}}
|
{{/* FIXME: preview markdown result */}}
|
||||||
{{/* FIXME: required validation for markdown editor */}}
|
{{/* FIXME: required validation for markdown editor */}}
|
||||||
<textarea name="form-field-{{.ID}}" placeholder="{{.Attributes.placeholder}}" class="edit_area {{if .Attributes.render}}no-easymde{{end}}" {{if and .Validations.required .Attributes.render}}required{{end}}>{{.Attributes.value}}</textarea>
|
<textarea name="form-field-{{.item.ID}}" placeholder="{{.item.Attributes.placeholder}}" class="edit_area {{if .item.Attributes.render}}no-easymde{{end}}" {{if and .item.Validations.required .item.Attributes.render}}required{{end}}>{{.item.Attributes.value}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<div class="header">{{.ctx.locale.Tr "repo.pick_reaction"}}</div>
|
<div class="header">{{.ctx.locale.Tr "repo.pick_reaction"}}</div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
{{range $value := AllowedReactions}}
|
{{range $value := AllowedReactions}}
|
||||||
<div class="item reaction tooltip" data-content="{{$value}}">{{ReactionToEmoji $value}}</div>
|
<a class="item reaction tooltip" data-content="{{$value}}">{{ReactionToEmoji $value}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,16 +10,16 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
{{$referenceUrl = Printf "%s/files#%s" .ctx.Issue.Link .item.HashTag}}
|
{{$referenceUrl = Printf "%s/files#%s" .ctx.Issue.Link .item.HashTag}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="item context" data-clipboard-text-type="url" data-clipboard-text="{{AppSubUrl}}{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.copy_link"}}</div>
|
<a class="item context" data-clipboard-text-type="url" data-clipboard-text="{{AppSubUrl}}{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.copy_link"}}</a>
|
||||||
<div class="item context quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-raw">{{.ctx.locale.Tr "repo.issues.context.quote_reply"}}</div>
|
<a class="item context quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-raw">{{.ctx.locale.Tr "repo.issues.context.quote_reply"}}</a>
|
||||||
{{if not .ctx.UnitIssuesGlobalDisabled}}
|
{{if not .ctx.UnitIssuesGlobalDisabled}}
|
||||||
<div class="item context reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.reference_issue"}}</div>
|
<a class="item context reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.reference_issue"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if or .ctx.Permission.IsAdmin .IsCommentPoster .ctx.HasIssuesOrPullsWritePermission}}
|
{{if or .ctx.Permission.IsAdmin .IsCommentPoster .ctx.HasIssuesOrPullsWritePermission}}
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<div class="item context edit-content">{{.ctx.locale.Tr "repo.issues.context.edit"}}</div>
|
<a class="item context edit-content">{{.ctx.locale.Tr "repo.issues.context.edit"}}</a>
|
||||||
{{if .delete}}
|
{{if .delete}}
|
||||||
<div class="item context delete-comment" data-comment-id={{.item.HashTag}} data-url="{{.ctx.RepoLink}}/comments/{{.item.ID}}/delete" data-locale="{{.ctx.locale.Tr "repo.issues.delete_comment_confirm"}}">{{.ctx.locale.Tr "repo.issues.context.delete"}}</div>
|
<a class="item context delete-comment" data-comment-id={{.item.HashTag}} data-url="{{.ctx.RepoLink}}/comments/{{.item.ID}}/delete" data-locale="{{.ctx.locale.Tr "repo.issues.delete_comment_confirm"}}">{{.ctx.locale.Tr "repo.issues.context.delete"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}">
|
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}">
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="no-select item">{{.locale.Tr "repo.issues.new.clear_labels"}}</div>
|
<a class="no-select item" href="#">{{.locale.Tr "repo.issues.new.clear_labels"}}</a>
|
||||||
{{if or .Labels .OrgLabels}}
|
{{if or .Labels .OrgLabels}}
|
||||||
{{$previousExclusiveScope := "_no_scope"}}
|
{{$previousExclusiveScope := "_no_scope"}}
|
||||||
{{range .Labels}}
|
{{range .Labels}}
|
||||||
|
@ -81,7 +81,8 @@ function attachOneDropdownAria($dropdown) {
|
|||||||
$dropdown.on('keydown', (e) => {
|
$dropdown.on('keydown', (e) => {
|
||||||
// here it must use keydown event before dropdown's keyup handler, otherwise there is no Enter event in our keyup handler
|
// here it must use keydown event before dropdown's keyup handler, otherwise there is no Enter event in our keyup handler
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
const $item = $dropdown.dropdown('get item', $dropdown.dropdown('get value'));
|
let $item = $dropdown.dropdown('get item', $dropdown.dropdown('get value'));
|
||||||
|
if (!$item) $item = $menu.find('> .item.selected'); // when dropdown filters items by input, there is no "value", so query the "selected" item
|
||||||
// if the selected item is clickable, then trigger the click event. in the future there could be a special CSS class for it.
|
// if the selected item is clickable, then trigger the click event. in the future there could be a special CSS class for it.
|
||||||
if ($item && $item.is('a')) $item[0].click();
|
if ($item && $item.is('a')) $item[0].click();
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,26 @@ import {hideElem, showElem} from '../utils/dom.js';
|
|||||||
|
|
||||||
const {csrfToken} = window.config;
|
const {csrfToken} = window.config;
|
||||||
|
|
||||||
|
// if there are draft comments (more than 20 chars), confirm before reloading, to avoid losing comments
|
||||||
|
function reloadConfirmDraftComment() {
|
||||||
|
const commentTextareas = [
|
||||||
|
document.querySelector('.edit-content-zone:not(.gt-hidden) textarea'),
|
||||||
|
document.querySelector('.edit_area'),
|
||||||
|
];
|
||||||
|
for (const textarea of commentTextareas) {
|
||||||
|
// Most users won't feel too sad if they lose a comment with 10 or 20 chars, they can re-type these in seconds.
|
||||||
|
// But if they have typed more (like 50) chars and the comment is lost, they will be very unhappy.
|
||||||
|
if (textarea && textarea.value.trim().length > 20) {
|
||||||
|
textarea.parentElement.scrollIntoView();
|
||||||
|
if (!window.confirm('Page will be reloaded, but there are draft comments. Continuing to reload will discard the comments. Continue?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
export function initRepoCommentForm() {
|
export function initRepoCommentForm() {
|
||||||
const $commentForm = $('.comment.form');
|
const $commentForm = $('.comment.form');
|
||||||
if ($commentForm.length === 0) {
|
if ($commentForm.length === 0) {
|
||||||
@ -86,12 +106,15 @@ export function initRepoCommentForm() {
|
|||||||
let hasUpdateAction = $listMenu.data('action') === 'update';
|
let hasUpdateAction = $listMenu.data('action') === 'update';
|
||||||
const items = {};
|
const items = {};
|
||||||
|
|
||||||
$(`.${selector}`).dropdown('setting', 'onHide', () => {
|
$(`.${selector}`).dropdown({
|
||||||
|
'action': 'nothing', // do not hide the menu if user presses Enter
|
||||||
|
fullTextSearch: 'exact',
|
||||||
|
async onHide() {
|
||||||
hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
|
hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
|
||||||
if (hasUpdateAction) {
|
if (hasUpdateAction) {
|
||||||
// TODO: Add batch functionality and make this 1 network request.
|
// TODO: Add batch functionality and make this 1 network request.
|
||||||
(async function() {
|
const itemEntries = Object.entries(items);
|
||||||
for (const [elementId, item] of Object.entries(items)) {
|
for (const [elementId, item] of itemEntries) {
|
||||||
await updateIssuesMeta(
|
await updateIssuesMeta(
|
||||||
item['update-url'],
|
item['update-url'],
|
||||||
item.action,
|
item.action,
|
||||||
@ -99,9 +122,11 @@ export function initRepoCommentForm() {
|
|||||||
elementId,
|
elementId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
window.location.reload();
|
if (itemEntries.length) {
|
||||||
})();
|
reloadConfirmDraftComment();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
$listMenu.find('.item:not(.no-select)').on('click', function (e) {
|
$listMenu.find('.item:not(.no-select)').on('click', function (e) {
|
||||||
@ -196,7 +221,7 @@ export function initRepoCommentForm() {
|
|||||||
'clear',
|
'clear',
|
||||||
$listMenu.data('issue-id'),
|
$listMenu.data('issue-id'),
|
||||||
'',
|
'',
|
||||||
).then(() => window.location.reload());
|
).then(reloadConfirmDraftComment);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(this).parent().find('.item').each(function () {
|
$(this).parent().find('.item').each(function () {
|
||||||
@ -239,7 +264,7 @@ export function initRepoCommentForm() {
|
|||||||
'',
|
'',
|
||||||
$menu.data('issue-id'),
|
$menu.data('issue-id'),
|
||||||
$(this).data('id'),
|
$(this).data('id'),
|
||||||
).then(() => window.location.reload());
|
).then(reloadConfirmDraftComment);
|
||||||
}
|
}
|
||||||
|
|
||||||
let icon = '';
|
let icon = '';
|
||||||
@ -272,7 +297,7 @@ export function initRepoCommentForm() {
|
|||||||
'',
|
'',
|
||||||
$menu.data('issue-id'),
|
$menu.data('issue-id'),
|
||||||
$(this).data('id'),
|
$(this).data('id'),
|
||||||
).then(() => window.location.reload());
|
).then(reloadConfirmDraftComment);
|
||||||
}
|
}
|
||||||
|
|
||||||
$list.find('.selected').html('');
|
$list.find('.selected').html('');
|
||||||
|
@ -3269,17 +3269,9 @@ td.blob-excerpt {
|
|||||||
.ui.attached.header.diff-file-header {
|
.ui.attached.header.diff-file-header {
|
||||||
&.sticky-2nd-row {
|
&.sticky-2nd-row {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 46px;
|
top: 77px;
|
||||||
z-index: 7;
|
z-index: 7;
|
||||||
|
|
||||||
@media @mediaMd {
|
|
||||||
top: 77px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media @mediaSm {
|
|
||||||
top: 77px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
position: static;
|
position: static;
|
||||||
}
|
}
|
||||||
|
@ -415,6 +415,7 @@
|
|||||||
padding: .2em .4em;
|
padding: .2em .4em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 85%;
|
font-size: 85%;
|
||||||
|
white-space: break-spaces;
|
||||||
background-color: var(--color-markup-code-block);
|
background-color: var(--color-markup-code-block);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user