Compare commits

..

No commits in common. "28625fba5bec01ef8278d6bf4ae40a3319ba301a" and "ddf61373f691a584844f141bc4728948fe142dc3" have entirely different histories.

36 changed files with 116 additions and 194 deletions

View File

@ -153,7 +153,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
return DefaultAvatarLink()
}
enableFederatedAvatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureEnableFederatedAvatar)
enableFederatedAvatar := system_model.GetSettingBool(ctx, system_model.KeyPictureEnableFederatedAvatar)
var err error
if enableFederatedAvatar && system_model.LibravatarService != nil {
@ -174,7 +174,7 @@ func generateEmailAvatarLink(ctx context.Context, email string, size int, final
return urlStr
}
disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar)
if !disableGravatar {
// copy GravatarSourceURL, because we will modify its Path.
avatarURLCopy := *system_model.GravatarSourceURL

View File

@ -28,7 +28,7 @@ func enableGravatar(t *testing.T) {
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
assert.NoError(t, err)
setting.GravatarSource = gravatarSource
err = system_model.Init(db.DefaultContext)
err = system_model.Init()
assert.NoError(t, err)
}

View File

@ -134,7 +134,7 @@ func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error {
if !opts.IsListAll() {
sess.Limit(opts.GetSkipTake())
}
return sess.Find(objects)
return sess.Find(&objects)
}
// 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() {
sess.Limit(opts.GetSkipTake())
}
return sess.FindAndCount(objects)
return sess.FindAndCount(&objects)
}

View File

@ -1,48 +0,0 @@
// 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)
}

View File

@ -39,9 +39,9 @@ import (
var ItemsPerPage = 40
// Init initialize model
func Init(ctx context.Context) error {
func Init() error {
unit.LoadUnitConfig()
return system_model.Init(ctx)
return system_model.Init()
}
// DeleteRepository deletes a repository for a user or organization.

View File

@ -79,8 +79,8 @@ func IsErrDataExpired(err error) bool {
return ok
}
// GetSetting returns specific setting without using the cache
func GetSetting(ctx context.Context, key string) (*Setting, error) {
// GetSettingNoCache returns specific setting without using the cache
func GetSettingNoCache(ctx context.Context, key string) (*Setting, error) {
v, err := GetSettings(ctx, []string{key})
if err != nil {
return nil, err
@ -93,11 +93,11 @@ func GetSetting(ctx context.Context, key string) (*Setting, error) {
const contextCacheKey = "system_setting"
// GetSettingWithCache returns the setting value via the key
func GetSettingWithCache(ctx context.Context, key string) (string, error) {
// GetSetting returns the setting value via the key
func GetSetting(ctx context.Context, key string) (string, error) {
return cache.GetWithContextCache(ctx, contextCacheKey, key, func() (string, error) {
return cache.GetString(genSettingCacheKey(key), func() (string, error) {
res, err := GetSetting(ctx, key)
res, err := GetSettingNoCache(ctx, key)
if err != nil {
return "", err
}
@ -110,15 +110,6 @@ func GetSettingWithCache(ctx context.Context, key string) (string, error) {
// none existing keys and errors are ignored and result in false
func GetSettingBool(ctx context.Context, key string) bool {
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)
return v
}
@ -129,7 +120,7 @@ func GetSettings(ctx context.Context, keys []string) (map[string]*Setting, error
keys[i] = strings.ToLower(keys[i])
}
settings := make([]*Setting, 0, len(keys))
if err := db.GetEngine(ctx).
if err := db.GetEngine(db.DefaultContext).
Where(builder.In("setting_key", keys)).
Find(&settings); err != nil {
return nil, err
@ -160,9 +151,9 @@ func (settings AllSettings) GetVersion(key string) int {
}
// GetAllSettings returns all settings from user
func GetAllSettings(ctx context.Context) (AllSettings, error) {
func GetAllSettings() (AllSettings, error) {
settings := make([]*Setting, 0, 5)
if err := db.GetEngine(ctx).
if err := db.GetEngine(db.DefaultContext).
Find(&settings); err != nil {
return nil, err
}
@ -177,12 +168,12 @@ func GetAllSettings(ctx context.Context) (AllSettings, error) {
func DeleteSetting(ctx context.Context, setting *Setting) error {
cache.RemoveContextData(ctx, contextCacheKey, setting.SettingKey)
cache.Remove(genSettingCacheKey(setting.SettingKey))
_, err := db.GetEngine(ctx).Delete(setting)
_, err := db.GetEngine(db.DefaultContext).Delete(setting)
return err
}
func SetSettingNoVersion(ctx context.Context, key, value string) error {
s, err := GetSetting(ctx, key)
s, err := GetSettingNoCache(ctx, key)
if IsErrSettingIsNotExist(err) {
return SetSetting(ctx, &Setting{
SettingKey: key,
@ -198,7 +189,7 @@ func SetSettingNoVersion(ctx context.Context, key, value string) error {
// SetSetting updates a users' setting for a specific key
func SetSetting(ctx context.Context, setting *Setting) error {
if err := upsertSettingValue(ctx, strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil {
if err := upsertSettingValue(strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil {
return err
}
@ -214,8 +205,8 @@ func SetSetting(ctx context.Context, setting *Setting) error {
return nil
}
func upsertSettingValue(parentCtx context.Context, key, value string, version int) error {
return db.WithTx(parentCtx, func(ctx context.Context) error {
func upsertSettingValue(key, value string, version int) error {
return db.WithTx(db.DefaultContext, func(ctx context.Context) error {
e := db.GetEngine(ctx)
// here we use a general method to do a safe upsert for different databases (and most transaction levels)
@ -258,9 +249,9 @@ var (
LibravatarService *libravatar.Libravatar
)
func Init(ctx context.Context) error {
func Init() error {
var disableGravatar bool
disableGravatarSetting, err := GetSetting(ctx, KeyPictureDisableGravatar)
disableGravatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureDisableGravatar)
if IsErrSettingIsNotExist(err) {
disableGravatar = setting_module.GetDefaultDisableGravatar()
disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)}
@ -271,7 +262,7 @@ func Init(ctx context.Context) error {
}
var enableFederatedAvatar bool
enableFederatedAvatarSetting, err := GetSetting(ctx, KeyPictureEnableFederatedAvatar)
enableFederatedAvatarSetting, err := GetSettingNoCache(db.DefaultContext, KeyPictureEnableFederatedAvatar)
if IsErrSettingIsNotExist(err) {
enableFederatedAvatar = setting_module.GetDefaultEnableFederatedAvatar(disableGravatar)
enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)}
@ -284,13 +275,13 @@ func Init(ctx context.Context) error {
if setting_module.OfflineMode {
disableGravatar = true
enableFederatedAvatar = false
if !GetSettingBool(ctx, KeyPictureDisableGravatar) {
if err := SetSettingNoVersion(ctx, KeyPictureDisableGravatar, "true"); err != nil {
if !GetSettingBool(db.DefaultContext, KeyPictureDisableGravatar) {
if err := SetSettingNoVersion(db.DefaultContext, KeyPictureDisableGravatar, "true"); err != nil {
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureDisableGravatar, err)
}
}
if GetSettingBool(ctx, KeyPictureEnableFederatedAvatar) {
if err := SetSettingNoVersion(ctx, KeyPictureEnableFederatedAvatar, "false"); err != nil {
if GetSettingBool(db.DefaultContext, KeyPictureEnableFederatedAvatar) {
if err := SetSettingNoVersion(db.DefaultContext, KeyPictureEnableFederatedAvatar, "false"); err != nil {
return fmt.Errorf("Failed to set setting %q: %w", KeyPictureEnableFederatedAvatar, err)
}
}

View File

@ -40,10 +40,10 @@ func TestSettings(t *testing.T) {
value, err := system.GetSetting(db.DefaultContext, keyName)
assert.NoError(t, err)
assert.EqualValues(t, updatedSetting.SettingValue, value.SettingValue)
assert.EqualValues(t, updatedSetting.SettingValue, value)
// get all settings
settings, err = system.GetAllSettings(db.DefaultContext)
settings, err = system.GetAllSettings()
assert.NoError(t, err)
assert.Len(t, settings, 3)
assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue)
@ -51,7 +51,7 @@ func TestSettings(t *testing.T) {
// delete setting
err = system.DeleteSetting(db.DefaultContext, &system.Setting{SettingKey: strings.ToLower(keyName)})
assert.NoError(t, err)
settings, err = system.GetAllSettings(db.DefaultContext)
settings, err = system.GetAllSettings()
assert.NoError(t, err)
assert.Len(t, settings, 2)
}

View File

@ -113,7 +113,7 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
if err = storage.Init(); err != nil {
fatalTestError("storage.Init: %v\n", err)
}
if err = system_model.Init(db.DefaultContext); err != nil {
if err = system_model.Init(); err != nil {
fatalTestError("models.Init: %v\n", err)
}

View File

@ -67,7 +67,7 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
useLocalAvatar := false
autoGenerateAvatar := false
disableGravatar := system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
disableGravatar := system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar)
switch {
case u.UseCustomAvatar:

View File

@ -106,7 +106,7 @@ func enableGravatar(t *testing.T) {
err := system_model.SetSettingNoVersion(db.DefaultContext, system_model.KeyPictureDisableGravatar, "false")
assert.NoError(t, err)
setting.GravatarSource = "https://secure.gravatar.com/avatar"
err = system_model.Init(db.DefaultContext)
err = system_model.Init()
assert.NoError(t, err)
}

View File

@ -92,7 +92,7 @@ func NewFuncMap() []template.FuncMap {
return setting.AssetVersion
},
"DisableGravatar": func(ctx context.Context) bool {
return system_model.GetSettingWithCacheBool(ctx, system_model.KeyPictureDisableGravatar)
return system_model.GetSettingBool(ctx, system_model.KeyPictureDisableGravatar)
},
"DefaultShowFullName": func() bool {
return setting.UI.DefaultShowFullName
@ -174,9 +174,8 @@ func NewFuncMap() []template.FuncMap {
"RenderEmojiPlain": emoji.ReplaceAliases,
"ReactionToEmoji": ReactionToEmoji,
"RenderNote": RenderNote,
"RenderMarkdownToHtml": func(ctx context.Context, input string) template.HTML {
"RenderMarkdownToHtml": func(input string) template.HTML {
output, err := markdown.RenderString(&markup.RenderContext{
Ctx: ctx,
URLPrefix: setting.AppSubURL,
}, input)
if err != nil {

View File

@ -150,7 +150,7 @@ func GlobalInitInstalled(ctx context.Context) {
mustInit(system.Init)
mustInit(oauth2.Init)
mustInitCtx(ctx, models.Init)
mustInit(models.Init)
mustInit(repo_service.Init)
// Booting long running goroutines.

View File

@ -103,7 +103,7 @@ func Config(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminConfig"] = true
systemSettings, err := system_model.GetAllSettings(ctx)
systemSettings, err := system_model.GetAllSettings()
if err != nil {
ctx.ServerError("system_model.GetAllSettings", err)
return

View File

@ -36,7 +36,6 @@ func Repos(ctx *context.Context) {
Private: true,
PageSize: setting.UI.Admin.RepoPagingNum,
TplName: tplRepos,
OnlyShowRelevant: false,
})
}

View File

@ -27,13 +27,10 @@ type RepoSearchOptions struct {
Private bool
Restricted bool
PageSize int
OnlyShowRelevant bool
TplName base.TplName
}
// 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) {
// Sitemap index for sitemap paths
page := int(ctx.ParamsInt64("idx"))
@ -55,6 +52,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
count int64
err error
orderBy db.SearchOrderBy
onlyShowRelevant bool
)
ctx.Data["SortType"] = ctx.FormString("sort")
@ -86,9 +84,11 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
orderBy = db.SearchOrderByRecentUpdated
}
onlyShowRelevant = !ctx.FormBool(relevantReposOnlyParam)
keyword := ctx.FormTrim("q")
ctx.Data["OnlyShowRelevant"] = opts.OnlyShowRelevant
ctx.Data["OnlyShowRelevant"] = onlyShowRelevant
topicOnly := ctx.FormBool("topic")
ctx.Data["TopicOnly"] = topicOnly
@ -111,7 +111,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
TopicOnly: topicOnly,
Language: language,
IncludeDescription: setting.UI.SearchRepoDescription,
OnlyShowRelevant: opts.OnlyShowRelevant,
OnlyShowRelevant: onlyShowRelevant,
})
if err != nil {
ctx.ServerError("SearchRepository", err)
@ -162,6 +162,5 @@ func Repos(ctx *context.Context) {
OwnerID: ownerID,
Private: ctx.Doer != nil,
TplName: tplExploreRepos,
OnlyShowRelevant: !ctx.FormBool(relevantReposOnlyParam),
})
}

View File

@ -27,6 +27,8 @@ func NewDiffPatch(ctx *context.Context) {
ctx.Data["PageIsPatch"] = true
ctx.Data["TreePath"] = ""
ctx.Data["commit_summary"] = ""
ctx.Data["commit_message"] = ""
if canCommit {
@ -52,6 +54,7 @@ func NewDiffPatchPost(ctx *context.Context) {
branchName = form.NewBranchName
}
ctx.Data["PageIsPatch"] = true
ctx.Data["TreePath"] = ""
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
ctx.Data["FileContent"] = form.Content
ctx.Data["commit_summary"] = form.CommitSummary
@ -86,14 +89,13 @@ func NewDiffPatchPost(ctx *context.Context) {
message += "\n\n" + form.CommitMessage
}
fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{
if _, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{
LastCommitID: form.LastCommit,
OldBranch: ctx.Repo.BranchName,
NewBranch: branchName,
Message: message,
Content: strings.ReplaceAll(form.Content, "\r", ""),
})
if err != nil {
}); err != nil {
if models.IsErrBranchAlreadyExists(err) {
// User has specified a branch that already exists
branchErr := err.(models.ErrBranchAlreadyExists)
@ -112,6 +114,6 @@ func NewDiffPatchPost(ctx *context.Context) {
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))
} else {
ctx.Redirect(ctx.Repo.RepoLink + "/commit/" + fileResponse.Commit.SHA)
ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath))
}
}

View File

@ -24,7 +24,6 @@ func ToPushMirror(pm *repo_model.PushMirror) (*api.PushMirror, error) {
LastUpdateUnix: pm.LastUpdateUnix.FormatLong(),
LastError: pm.LastError,
Interval: pm.Interval.String(),
SyncOnCommit: pm.SyncOnCommit,
}, nil
}

View File

@ -26,7 +26,7 @@ git-fetch-with-cli = true</code></pre></div>
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}
<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.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Readme}}</div>{{end}}
{{end}}
{{if .PackageDescriptor.Metadata.Dependencies}}

View File

@ -20,7 +20,7 @@
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
<div class="ui attached segment">
{{if .PackageDescriptor.Metadata.Description}}<p>{{.PackageDescriptor.Metadata.Description}}</p>{{end}}
{{if .PackageDescriptor.Metadata.LongDescription}}{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.LongDescription}}{{end}}
{{if .PackageDescriptor.Metadata.LongDescription}}{{RenderMarkdownToHtml .PackageDescriptor.Metadata.LongDescription}}{{end}}
</div>
{{end}}

View File

@ -25,7 +25,7 @@
<div class="ui attached segment">
{{if .PackageDescriptor.Metadata.Readme}}
<div class="markup markdown">
{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}
{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Readme}}
</div>
{{else if .PackageDescriptor.Metadata.Description}}
{{.PackageDescriptor.Metadata.Description}}

View File

@ -14,6 +14,6 @@
{{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.Readme}}
<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.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}</div>{{end}}
{{if .PackageDescriptor.Metadata.Readme}}<div class="ui attached segment">{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Readme}}</div>{{end}}
{{end}}
{{end}}

View File

@ -16,9 +16,9 @@
<div class="ui attached segment">
<p>{{if .PackageDescriptor.Metadata.Summary}}{{.PackageDescriptor.Metadata.Summary}}{{end}}</p>
{{if .PackageDescriptor.Metadata.LongDescription}}
{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.LongDescription}}
{{RenderMarkdownToHtml .PackageDescriptor.Metadata.LongDescription}}
{{else if .PackageDescriptor.Metadata.Description}}
{{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Description}}
{{RenderMarkdownToHtml .PackageDescriptor.Metadata.Description}}
{{end}}
</div>
{{end}}

View File

@ -2,15 +2,15 @@
<input type="hidden" name="template-file" value="{{.TemplateFile}}">
{{range .Fields}}
{{if eq .Type "input"}}
{{template "repo/issue/fields/input" Dict "Context" $.Context "item" .}}
{{template "repo/issue/fields/input" .}}
{{else if eq .Type "markdown"}}
{{template "repo/issue/fields/markdown" Dict "Context" $.Context "item" .}}
{{template "repo/issue/fields/markdown" .}}
{{else if eq .Type "textarea"}}
{{template "repo/issue/fields/textarea" Dict "Context" $.Context "item" .}}
{{template "repo/issue/fields/textarea" .}}
{{else if eq .Type "dropdown"}}
{{template "repo/issue/fields/dropdown" Dict "Context" $.Context "item" .}}
{{template "repo/issue/fields/dropdown" .}}
{{else if eq .Type "checkboxes"}}
{{template "repo/issue/fields/checkboxes" Dict "Context" $.Context "item" .}}
{{template "repo/issue/fields/checkboxes" .}}
{{end}}
{{end}}
{{else}}

View File

@ -1,7 +1,7 @@
<div class="field">
{{template "repo/issue/fields/header" .}}
{{$field := .}}
{{range $i, $opt := .item.Attributes.options}}
{{range $i, $opt := .Attributes.options}}
<div class="field">
<div class="ui checkbox">
<input type="checkbox" name="form-field-{{$field.ID}}-{{$i}}" {{if $opt.required}}readonly checked{{end}}>

View File

@ -1,12 +1,12 @@
<div class="field">
{{template "repo/issue/fields/header" .}}
{{/* FIXME: required validation */}}
<div class="ui fluid selection dropdown {{if .item.Attributes.multiple}}multiple clearable{{end}}">
<input type="hidden" name="form-field-{{.item.ID}}" value="0">
<div class="ui fluid selection dropdown {{if .Attributes.multiple}}multiple clearable{{end}}">
<input type="hidden" name="form-field-{{.ID}}" value="0">
<i class="dropdown icon"></i>
<div class="default text"></div>
<div class="menu">
{{range $i, $opt := .item.Attributes.options}}
{{range $i, $opt := .Attributes.options}}
<div class="item" data-value="{{$i}}">{{$opt}}</div>
{{end}}
</div>

View File

@ -1,6 +1,6 @@
{{if .item.Attributes.label}}
<h3>{{.item.Attributes.label}}{{if .item.Validations.required}}<label class="required"></label>{{end}}</h3>
{{if .Attributes.label}}
<h3>{{.Attributes.label}}{{if .Validations.required}}<label class="required"></label>{{end}}</h3>
{{end}}
{{if .item.Attributes.description}}
<span class="help">{{RenderMarkdownToHtml .Context .item.Attributes.description}}</span>
{{if .Attributes.description}}
<span class="help">{{RenderMarkdownToHtml .Attributes.description}}</span>
{{end}}

View File

@ -1,4 +1,4 @@
<div class="field">
{{template "repo/issue/fields/header" .}}
<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}}>
<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}}>
</div>

View File

@ -1,3 +1,3 @@
<div class="field">
<div>{{RenderMarkdownToHtml .Context .item.Attributes.value}}</div>
<div>{{RenderMarkdownToHtml .Attributes.value}}</div>
</div>

View File

@ -2,5 +2,5 @@
{{template "repo/issue/fields/header" .}}
{{/* FIXME: preview markdown result */}}
{{/* FIXME: required validation for markdown editor */}}
<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>
<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>
</div>

View File

@ -7,7 +7,7 @@
<div class="header">{{.ctx.locale.Tr "repo.pick_reaction"}}</div>
<div class="divider"></div>
{{range $value := AllowedReactions}}
<a class="item reaction tooltip" data-content="{{$value}}">{{ReactionToEmoji $value}}</a>
<div class="item reaction tooltip" data-content="{{$value}}">{{ReactionToEmoji $value}}</div>
{{end}}
</div>
</div>

View File

@ -10,16 +10,16 @@
{{else}}
{{$referenceUrl = Printf "%s/files#%s" .ctx.Issue.Link .item.HashTag}}
{{end}}
<a class="item context" data-clipboard-text-type="url" data-clipboard-text="{{AppSubUrl}}{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.copy_link"}}</a>
<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>
<div class="item context" data-clipboard-text-type="url" data-clipboard-text="{{AppSubUrl}}{{$referenceUrl}}">{{.ctx.locale.Tr "repo.issues.context.copy_link"}}</div>
<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>
{{if not .ctx.UnitIssuesGlobalDisabled}}
<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>
<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>
{{end}}
{{if or .ctx.Permission.IsAdmin .IsCommentPoster .ctx.HasIssuesOrPullsWritePermission}}
<div class="divider"></div>
<a class="item context edit-content">{{.ctx.locale.Tr "repo.issues.context.edit"}}</a>
<div class="item context edit-content">{{.ctx.locale.Tr "repo.issues.context.edit"}}</div>
{{if .delete}}
<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>
<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>
{{end}}
{{end}}
</div>

View File

@ -121,7 +121,7 @@
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_labels"}}">
</div>
{{end}}
<a class="no-select item" href="#">{{.locale.Tr "repo.issues.new.clear_labels"}}</a>
<div class="no-select item">{{.locale.Tr "repo.issues.new.clear_labels"}}</div>
{{if or .Labels .OrgLabels}}
{{$previousExclusiveScope := "_no_scope"}}
{{range .Labels}}

View File

@ -81,8 +81,7 @@ function attachOneDropdownAria($dropdown) {
$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
if (e.key === 'Enter') {
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
const $item = $dropdown.dropdown('get item', $dropdown.dropdown('get value'));
// 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();
}

View File

@ -29,26 +29,6 @@ import {hideElem, showElem} from '../utils/dom.js';
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() {
const $commentForm = $('.comment.form');
if ($commentForm.length === 0) {
@ -106,15 +86,12 @@ export function initRepoCommentForm() {
let hasUpdateAction = $listMenu.data('action') === 'update';
const items = {};
$(`.${selector}`).dropdown({
'action': 'nothing', // do not hide the menu if user presses Enter
fullTextSearch: 'exact',
async onHide() {
$(`.${selector}`).dropdown('setting', 'onHide', () => {
hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var
if (hasUpdateAction) {
// TODO: Add batch functionality and make this 1 network request.
const itemEntries = Object.entries(items);
for (const [elementId, item] of itemEntries) {
(async function() {
for (const [elementId, item] of Object.entries(items)) {
await updateIssuesMeta(
item['update-url'],
item.action,
@ -122,11 +99,9 @@ export function initRepoCommentForm() {
elementId,
);
}
if (itemEntries.length) {
reloadConfirmDraftComment();
window.location.reload();
})();
}
}
},
});
$listMenu.find('.item:not(.no-select)').on('click', function (e) {
@ -221,7 +196,7 @@ export function initRepoCommentForm() {
'clear',
$listMenu.data('issue-id'),
'',
).then(reloadConfirmDraftComment);
).then(() => window.location.reload());
}
$(this).parent().find('.item').each(function () {
@ -264,7 +239,7 @@ export function initRepoCommentForm() {
'',
$menu.data('issue-id'),
$(this).data('id'),
).then(reloadConfirmDraftComment);
).then(() => window.location.reload());
}
let icon = '';
@ -297,7 +272,7 @@ export function initRepoCommentForm() {
'',
$menu.data('issue-id'),
$(this).data('id'),
).then(reloadConfirmDraftComment);
).then(() => window.location.reload());
}
$list.find('.selected').html('');

View File

@ -3269,9 +3269,17 @@ td.blob-excerpt {
.ui.attached.header.diff-file-header {
&.sticky-2nd-row {
position: sticky;
top: 77px;
top: 46px;
z-index: 7;
@media @mediaMd {
top: 77px;
}
@media @mediaSm {
top: 77px;
}
@media (max-width: 480px) {
position: static;
}

View File

@ -415,7 +415,6 @@
padding: .2em .4em;
margin: 0;
font-size: 85%;
white-space: break-spaces;
background-color: var(--color-markup-code-block);
border-radius: 4px;
}