diff --git a/.eslintrc.yaml b/.eslintrc.yaml index f9d2d30e9e..f6a7fb3ed0 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -743,7 +743,7 @@ rules: wc/no-constructor-params: [2] wc/no-constructor: [2] wc/no-customized-built-in-elements: [2] - wc/no-exports-with-element: [2] + wc/no-exports-with-element: [0] wc/no-invalid-element-name: [2] wc/no-invalid-extends: [2] wc/no-method-prefixed-with-on: [2] diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go index 2fadbfeb06..0f70f13485 100644 --- a/modules/indexer/code/elasticsearch/elasticsearch.go +++ b/modules/indexer/code/elasticsearch/elasticsearch.go @@ -180,11 +180,17 @@ func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha st } if len(reqs) > 0 { - _, err := b.inner.Client.Bulk(). - Index(b.inner.VersionedIndexName()). - Add(reqs...). - Do(ctx) - return err + esBatchSize := 50 + + for i := 0; i < len(reqs); i += esBatchSize { + _, err := b.inner.Client.Bulk(). + Index(b.inner.VersionedIndexName()). + Add(reqs[i:min(i+esBatchSize, len(reqs))]...). + Do(ctx) + if err != nil { + return err + } + } } return nil } diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go index 427e262d06..6ad289e51e 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -157,7 +157,7 @@ func EnumeratePackageVersions(ctx *context.Context) { } type Resource struct { - Name string `json:"id"` + Name string `json:"name"` Type string `json:"type"` Checksum string `json:"checksum"` } diff --git a/services/release/release.go b/services/release/release.go index 29fc7a617a..01a52e9d00 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -278,15 +278,13 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo } } - if !isCreated { - notify_service.UpdateRelease(gitRepo.Ctx, doer, rel) - return nil - } - if !rel.IsDraft { + if !isCreated { + notify_service.UpdateRelease(gitRepo.Ctx, doer, rel) + return nil + } notify_service.NewRelease(gitRepo.Ctx, rel) } - return nil } @@ -351,7 +349,8 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re } } - notify_service.DeleteRelease(ctx, doer, rel) - + if !rel.IsDraft { + notify_service.DeleteRelease(ctx, doer, rel) + } return nil } diff --git a/templates/repo/commit_load_branches_and_tags.tmpl b/templates/repo/commit_load_branches_and_tags.tmpl index 6d5eb29d8a..883230ac29 100644 --- a/templates/repo/commit_load_branches_and_tags.tmpl +++ b/templates/repo/commit_load_branches_and_tags.tmpl @@ -9,11 +9,11 @@
{{ctx.Locale.Tr "repo.commit.contained_in"}}
{{svg "octicon-git-branch"}}
-
+
{{svg "octicon-tag"}}
-
+
diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index f7d2025733..b48a226e35 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -8,8 +8,8 @@ {{range $idx, $release := .Releases}}
  • - {{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}} - {{if .Sha1}} + {{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}} + {{if and .Sha1 ($.Permission.CanRead $.UnitTypeCode)}} {{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}} {{template "repo/branch_dropdown" dict "root" $ "release" .}} {{end}} @@ -22,36 +22,18 @@ {{ctx.Locale.Tr "repo.release.draft"}} {{else if .IsPrerelease}} {{ctx.Locale.Tr "repo.release.prerelease"}} - {{else if not .IsTag}} + {{else}} {{ctx.Locale.Tr "repo.release.stable"}} {{end}}
    - {{if and $.CanCreateRelease (not .IsTag)}} + {{if $.CanCreateRelease}} {{svg "octicon-pencil"}} {{end}}
    - {{if .IsTag}} -

    - {{if gt .Publisher.ID 0}} - - {{ctx.AvatarUtils.Avatar .Publisher 20 "gt-mr-2"}} - {{.Publisher.Name}} - - - {{ctx.Locale.Tr "repo.tagged_this"}} - - {{if .CreatedUnix}} - {{TimeSinceUnix .CreatedUnix ctx.Locale}} - {{end}} - | - {{end}} - {{ctx.Locale.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}} {{ctx.Locale.Tr "repo.tag.ahead.target" .TargetBehind}} -

    - {{else}}

    {{if .OriginalAuthor}} @@ -69,11 +51,10 @@ {{if .CreatedUnix}} {{TimeSinceUnix .CreatedUnix ctx.Locale}} {{end}} - {{if not .IsDraft}} + {{if and (not .IsDraft) ($.Permission.CanRead $.UnitTypeCode)}} | {{ctx.Locale.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}} {{ctx.Locale.Tr "repo.release.ahead.target" .TargetBehind}} {{end}}

    - {{end}}
    {{Str2html .Note}}
    diff --git a/templates/repo/settings/lfs_file.tmpl b/templates/repo/settings/lfs_file.tmpl index bb2e25e86c..0aeb2af178 100644 --- a/templates/repo/settings/lfs_file.tmpl +++ b/templates/repo/settings/lfs_file.tmpl @@ -33,7 +33,7 @@ {{else if .IsPDFFile}}
    {{else}} - {{ctx.Locale.Tr "repo.file_view_raw"}} + {{ctx.Locale.Tr "repo.file_view_raw"}} {{end}} {{else if .FileSize}} diff --git a/templates/repo/unicode_escape_prompt.tmpl b/templates/repo/unicode_escape_prompt.tmpl index 8f02a489e9..d0730f23c1 100644 --- a/templates/repo/unicode_escape_prompt.tmpl +++ b/templates/repo/unicode_escape_prompt.tmpl @@ -1,7 +1,7 @@ {{if .EscapeStatus}} {{if .EscapeStatus.HasInvisible}}
    - +
    {{ctx.Locale.Tr "repo.invisible_runes_header"}}
    @@ -12,7 +12,7 @@
    {{else if .EscapeStatus.HasAmbiguous}}
    - +
    {{ctx.Locale.Tr "repo.ambiguous_runes_header"}}
    diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index f377d4d722..5280378474 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -87,7 +87,7 @@ {{else if .IsPDFFile}}
    {{else}} - {{ctx.Locale.Tr "repo.file_view_raw"}} + {{ctx.Locale.Tr "repo.file_view_raw"}} {{end}}
    {{else if .FileSize}} diff --git a/web_src/js/markup/codecopy.js b/web_src/js/markup/codecopy.js index a12802ef73..078d741253 100644 --- a/web_src/js/markup/codecopy.js +++ b/web_src/js/markup/codecopy.js @@ -12,8 +12,10 @@ export function renderCodeCopy() { if (!els.length) return; for (const el of els) { + if (!el.textContent) continue; const btn = makeCodeCopyButton(); - btn.setAttribute('data-clipboard-text', el.textContent); + // remove final trailing newline introduced during HTML rendering + btn.setAttribute('data-clipboard-text', el.textContent.replace(/\r?\n$/, '')); el.after(btn); } } diff --git a/web_src/js/modules/dirauto.js b/web_src/js/modules/dirauto.js index c917bf8cff..cd90f8155b 100644 --- a/web_src/js/modules/dirauto.js +++ b/web_src/js/modules/dirauto.js @@ -1,5 +1,6 @@ -// for performance considerations, it only uses performant syntax +import {isDocumentFragmentOrElementNode} from '../utils/dom.js'; +// for performance considerations, it only uses performant syntax function attachDirAuto(el) { if (el.type !== 'hidden' && el.type !== 'checkbox' && @@ -18,7 +19,7 @@ export function initDirAuto() { const len = mutation.addedNodes.length; for (let i = 0; i < len; i++) { const addedNode = mutation.addedNodes[i]; - if (addedNode.nodeType !== Node.ELEMENT_NODE && addedNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) continue; + if (!isDocumentFragmentOrElementNode(addedNode)) continue; if (addedNode.nodeName === 'INPUT' || addedNode.nodeName === 'TEXTAREA') attachDirAuto(addedNode); const children = addedNode.querySelectorAll('input, textarea'); const len = children.length; diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js index 9b2f3691cc..6eae0c9168 100644 --- a/web_src/js/modules/tippy.js +++ b/web_src/js/modules/tippy.js @@ -1,4 +1,5 @@ import tippy, {followCursor} from 'tippy.js'; +import {isDocumentFragmentOrElementNode} from '../utils/dom.js'; const visibleInstances = new Set(); @@ -136,8 +137,6 @@ function attachChildrenLazyTooltip(target) { } } -const elementNodeTypes = new Set([Node.ELEMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE]); - export function initGlobalTooltips() { // use MutationObserver to detect new "data-tooltip-content" elements added to the DOM, or attributes changed const observerConnect = (observer) => observer.observe(document, { @@ -152,11 +151,10 @@ export function initGlobalTooltips() { if (mutation.type === 'childList') { // mainly for Vue components and AJAX rendered elements for (const el of mutation.addedNodes) { - if (elementNodeTypes.has(el.nodeType)) { - attachChildrenLazyTooltip(el); - if (el.hasAttribute('data-tooltip-content')) { - attachLazyTooltip(el); - } + if (!isDocumentFragmentOrElementNode(el)) continue; + attachChildrenLazyTooltip(el); + if (el.hasAttribute('data-tooltip-content')) { + attachLazyTooltip(el); } } } else if (mutation.type === 'attributes') { diff --git a/web_src/js/utils/dom.js b/web_src/js/utils/dom.js index 64a6a5affc..4dc55a518a 100644 --- a/web_src/js/utils/dom.js +++ b/web_src/js/utils/dom.js @@ -59,6 +59,17 @@ export function onDomReady(cb) { } } +// checks whether an element is owned by the current document, and whether it is a document fragment or element node +// if it is, it means it is a "normal" element managed by us, which can be modified safely. +export function isDocumentFragmentOrElementNode(el) { + try { + return el.ownerDocument === document && el.nodeType === Node.ELEMENT_NODE || el.nodeType === Node.DOCUMENT_FRAGMENT_NODE; + } catch { + // in case the el is not in the same origin, then the access to nodeType would fail + return false; + } +} + // autosize a textarea to fit content. Based on // https://github.com/github/textarea-autosize // --------------------------------------------------------------------- diff --git a/web_src/js/webcomponents/GiteaOriginUrl.js b/web_src/js/webcomponents/GiteaOriginUrl.js index fca736064c..5d71d95c60 100644 --- a/web_src/js/webcomponents/GiteaOriginUrl.js +++ b/web_src/js/webcomponents/GiteaOriginUrl.js @@ -1,17 +1,21 @@ // Convert an absolute or relative URL to an absolute URL with the current origin +export function toOriginUrl(urlStr) { + try { + // only process absolute HTTP/HTTPS URL or relative URLs ('/xxx' or '//host/xxx') + if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { + const {origin, protocol, hostname, port} = window.location; + const url = new URL(urlStr, origin); + url.protocol = protocol; + url.hostname = hostname; + url.port = port || (protocol === 'https:' ? '443' : '80'); + return url.toString(); + } + } catch {} + return urlStr; +} + window.customElements.define('gitea-origin-url', class extends HTMLElement { connectedCallback() { - const urlStr = this.getAttribute('data-url'); - try { - // only process absolute HTTP/HTTPS URL or relative URLs ('/xxx' or '//host/xxx') - if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { - const url = new URL(urlStr, window.origin); - url.protocol = window.location.protocol; - url.host = window.location.host; - this.textContent = url.toString(); - return; - } - } catch {} - this.textContent = urlStr; + this.textContent = toOriginUrl(this.getAttribute('data-url')); } }); diff --git a/web_src/js/webcomponents/GiteaOriginUrl.test.js b/web_src/js/webcomponents/GiteaOriginUrl.test.js new file mode 100644 index 0000000000..f0629842b8 --- /dev/null +++ b/web_src/js/webcomponents/GiteaOriginUrl.test.js @@ -0,0 +1,17 @@ +import {toOriginUrl} from './GiteaOriginUrl.js'; + +test('toOriginUrl', () => { + const oldLocation = window.location; + for (const origin of ['https://example.com', 'https://example.com:3000']) { + window.location = new URL(`${origin}/`); + expect(toOriginUrl('/')).toEqual(`${origin}/`); + expect(toOriginUrl('/org/repo.git')).toEqual(`${origin}/org/repo.git`); + expect(toOriginUrl('https://another.com')).toEqual(`${origin}/`); + expect(toOriginUrl('https://another.com/')).toEqual(`${origin}/`); + expect(toOriginUrl('https://another.com/org/repo.git')).toEqual(`${origin}/org/repo.git`); + expect(toOriginUrl('https://another.com:4000')).toEqual(`${origin}/`); + expect(toOriginUrl('https://another.com:4000/')).toEqual(`${origin}/`); + expect(toOriginUrl('https://another.com:4000/org/repo.git')).toEqual(`${origin}/org/repo.git`); + } + window.location = oldLocation; +});