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}}
- {{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}}
-
+
@@ -12,7 +12,7 @@
{{else if .EscapeStatus.HasAmbiguous}}
-
+
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;
+});