mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-22 20:05:48 +02:00
Merge branch 'main' into lunny/morevalidation
This commit is contained in:
@@ -699,7 +699,7 @@ func (c *Comment) LoadTime(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
c.Time, err = GetTrackedTimeByID(ctx, c.TimeID)
|
||||
c.Time, err = GetTrackedTimeByID(ctx, c.IssueID, c.TimeID)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -311,13 +311,13 @@ func deleteTime(ctx context.Context, t *TrackedTime) error {
|
||||
}
|
||||
|
||||
// GetTrackedTimeByID returns raw TrackedTime without loading attributes by id
|
||||
func GetTrackedTimeByID(ctx context.Context, id int64) (*TrackedTime, error) {
|
||||
func GetTrackedTimeByID(ctx context.Context, issueID, trackedTimeID int64) (*TrackedTime, error) {
|
||||
time := new(TrackedTime)
|
||||
has, err := db.GetEngine(ctx).ID(id).Get(time)
|
||||
has, err := db.GetEngine(ctx).ID(trackedTimeID).Where("issue_id = ?", issueID).Get(time)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, db.ErrNotExist{Resource: "tracked_time", ID: id}
|
||||
return nil, db.ErrNotExist{Resource: "tracked_time", ID: trackedTimeID}
|
||||
}
|
||||
return time, nil
|
||||
}
|
||||
|
||||
@@ -348,23 +348,6 @@ func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
|
||||
}
|
||||
}
|
||||
|
||||
// NewConfigProviderForLocale loads locale configuration from source and others. "string" if for a local file path, "[]byte" is for INI content
|
||||
func NewConfigProviderForLocale(source any, others ...any) (ConfigProvider, error) {
|
||||
iniFile, err := ini.LoadSources(ini.LoadOptions{
|
||||
IgnoreInlineComment: true,
|
||||
UnescapeValueCommentSymbols: true,
|
||||
IgnoreContinuation: true,
|
||||
}, source, others...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load locale ini: %w", err)
|
||||
}
|
||||
iniFile.BlockMode = false
|
||||
return &iniConfigProvider{
|
||||
ini: iniFile,
|
||||
loadedFromEmpty: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
ini.PrettyFormat = false
|
||||
}
|
||||
|
||||
@@ -113,24 +113,6 @@ func TestNewConfigProviderFromFile(t *testing.T) {
|
||||
assert.Equal(t, "[foo]\nk1 = a\n\n[bar]\nk1 = b\n", string(bs))
|
||||
}
|
||||
|
||||
func TestNewConfigProviderForLocale(t *testing.T) {
|
||||
// load locale from file
|
||||
localeFile := t.TempDir() + "/locale.ini"
|
||||
_ = os.WriteFile(localeFile, []byte(`k1=a`), 0o644)
|
||||
cfg, err := NewConfigProviderForLocale(localeFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "a", cfg.Section("").Key("k1").String())
|
||||
|
||||
// load locale from bytes
|
||||
cfg, err = NewConfigProviderForLocale([]byte("k1=foo\nk2=bar"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "foo", cfg.Section("").Key("k1").String())
|
||||
cfg, err = NewConfigProviderForLocale([]byte("k1=foo\nk2=bar"), []byte("k2=xxx"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "foo", cfg.Section("").Key("k1").String())
|
||||
assert.Equal(t, "xxx", cfg.Section("").Key("k2").String())
|
||||
}
|
||||
|
||||
func TestDisableSaving(t *testing.T) {
|
||||
testFile := t.TempDir() + "/test.ini"
|
||||
_ = os.WriteFile(testFile, []byte("k1=a\nk2=b"), 0o644)
|
||||
|
||||
@@ -356,7 +356,7 @@ func DeleteTime(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
time, err := issues_model.GetTrackedTimeByID(ctx, ctx.PathParamInt64("id"))
|
||||
time, err := issues_model.GetTrackedTimeByID(ctx, issue.ID, ctx.PathParamInt64("id"))
|
||||
if err != nil {
|
||||
if db.IsErrNotExist(err) {
|
||||
ctx.APIErrorNotFound(err)
|
||||
|
||||
@@ -60,7 +60,7 @@ func DeleteTime(c *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
t, err := issues_model.GetTrackedTimeByID(c, c.PathParamInt64("timeid"))
|
||||
t, err := issues_model.GetTrackedTimeByID(c, issue.ID, c.PathParamInt64("timeid"))
|
||||
if err != nil {
|
||||
if db.IsErrNotExist(err) {
|
||||
c.NotFound(err)
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/migrations"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
)
|
||||
@@ -172,7 +173,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*repo_module.SyncResu
|
||||
if m.LFS && setting.LFS.StartServer {
|
||||
log.Trace("SyncMirrors [repo: %-v]: syncing LFS objects...", m.Repo)
|
||||
endpoint := lfs.DetermineEndpoint(remoteURL.String(), m.LFSEndpoint)
|
||||
lfsClient := lfs.NewClient(endpoint, nil)
|
||||
lfsClient := lfs.NewClient(endpoint, migrations.NewMigrationHTTPTransport())
|
||||
if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, m.Repo, gitRepo, lfsClient); err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: failed to synchronize LFS objects for repository: %v", m.Repo.FullName(), err)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/migrations"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
)
|
||||
|
||||
@@ -144,7 +145,7 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
|
||||
defer gitRepo.Close()
|
||||
|
||||
endpoint := lfs.DetermineEndpoint(remoteURL.String(), "")
|
||||
lfsClient := lfs.NewClient(endpoint, nil)
|
||||
lfsClient := lfs.NewClient(endpoint, migrations.NewMigrationHTTPTransport())
|
||||
if err := pushAllLFSObjects(ctx, gitRepo, lfsClient); err != nil {
|
||||
return util.SanitizeErrorCredentialURLs(err)
|
||||
}
|
||||
|
||||
@@ -79,6 +79,12 @@ func TestAPIDeleteTrackedTime(t *testing.T) {
|
||||
AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusForbidden)
|
||||
|
||||
// Deletion should be scoped to the issue in the URL
|
||||
time5 := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{ID: 5})
|
||||
req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d", user2.Name, issue2.Repo.Name, issue2.Index, time5.ID).
|
||||
AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
time3 := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{ID: 3})
|
||||
req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d", user2.Name, issue2.Repo.Name, issue2.Index, time3.ID).
|
||||
AddTokenAuth(token)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIssueTimeDeleteScoped(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
|
||||
assert.NoError(t, issue1.LoadRepo(t.Context()))
|
||||
tracked := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{ID: 5})
|
||||
|
||||
session := loginUser(t, issue1.Repo.OwnerName)
|
||||
url := fmt.Sprintf("/%s/%s/issues/%d/times/%d/delete", issue1.Repo.OwnerName, issue1.Repo.Name, issue1.Index, tracked.ID)
|
||||
req := NewRequestWithValues(t, "POST", url, map[string]string{})
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
tracked = unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{ID: tracked.ID})
|
||||
assert.False(t, tracked.Deleted)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import {colord} from 'colord';
|
||||
import {basename, extname, isObject, isDarkTheme} from '../utils.ts';
|
||||
import {onInputDebounce} from '../utils/dom.ts';
|
||||
import {onInputDebounce, toggleElem} from '../utils/dom.ts';
|
||||
import type MonacoNamespace from 'monaco-editor';
|
||||
|
||||
type Monaco = typeof MonacoNamespace;
|
||||
@@ -197,19 +197,19 @@ function getFileBasedOptions(filename: string, lineWrapExts: string[]): MonacoOp
|
||||
}
|
||||
|
||||
function togglePreviewDisplay(previewable: boolean): void {
|
||||
// FIXME: here and below, the selector is too broad, it should only query in the editor related scope
|
||||
const previewTab = document.querySelector<HTMLElement>('a[data-tab="preview"]');
|
||||
// the "preview tab" exists for "file code editor", but doesn't exist for "git hook editor"
|
||||
if (!previewTab) return;
|
||||
|
||||
if (previewable) {
|
||||
previewTab.style.display = '';
|
||||
} else {
|
||||
previewTab.style.display = 'none';
|
||||
// If the "preview" tab was active, user changes the filename to a non-previewable one,
|
||||
// then the "preview" tab becomes inactive (hidden), so the "write" tab should become active
|
||||
if (previewTab.classList.contains('active')) {
|
||||
const writeTab = document.querySelector<HTMLElement>('a[data-tab="write"]');
|
||||
writeTab?.click();
|
||||
}
|
||||
toggleElem(previewTab, previewable);
|
||||
if (previewable) return;
|
||||
|
||||
// If not previewable but the "preview" tab was active (user changes the filename to a non-previewable one),
|
||||
// then the "preview" tab becomes inactive (hidden), so the "write" tab should become active
|
||||
if (previewTab.classList.contains('active')) {
|
||||
const writeTab = document.querySelector<HTMLElement>('a[data-tab="write"]');
|
||||
writeTab?.click(); // TODO: it shouldn't need null-safe operator, writeTab must exist
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export function initRepositoryActionView() {
|
||||
// TODO: the parent element's full height doesn't work well now,
|
||||
// but we can not pollute the global style at the moment, only fix the height problem for pages with this component
|
||||
const parentFullHeight = document.querySelector<HTMLElement>('body > div.full.height');
|
||||
if (parentFullHeight) parentFullHeight.style.paddingBottom = '0';
|
||||
if (parentFullHeight) parentFullHeight.classList.add('tw-pb-0');
|
||||
|
||||
const view = createApp(RepoActionView, {
|
||||
runIndex: el.getAttribute('data-run-index'),
|
||||
|
||||
@@ -113,8 +113,8 @@ export function initRepoEditor() {
|
||||
warningDiv = document.createElement('div');
|
||||
warningDiv.classList.add('ui', 'warning', 'message', 'flash-message', 'flash-warning', 'space-related');
|
||||
warningDiv.innerHTML = html`<p>File path contains leading or trailing whitespace.</p>`;
|
||||
// Add display 'block' because display is set to 'none' in formantic\build\semantic.css
|
||||
warningDiv.style.display = 'block';
|
||||
// Change to `block` display because it is set to 'none' in fomantic/build/semantic.css
|
||||
warningDiv.classList.add('tw-block');
|
||||
const inputContainer = document.querySelector('.repo-editor-header')!;
|
||||
inputContainer.insertAdjacentElement('beforebegin', warningDiv);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user