Merge pull request '[gitea] v1.21 cherry-pick' (#2340) from earl-warren/forgejo:wip-v1.21-forgejo into v1.21/forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2340
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
Earl Warren 2024-02-14 10:54:42 +00:00
commit 31ca6d8160
15 changed files with 86 additions and 67 deletions

View file

@ -743,7 +743,7 @@ rules:
wc/no-constructor-params: [2] wc/no-constructor-params: [2]
wc/no-constructor: [2] wc/no-constructor: [2]
wc/no-customized-built-in-elements: [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-element-name: [2]
wc/no-invalid-extends: [2] wc/no-invalid-extends: [2]
wc/no-method-prefixed-with-on: [2] wc/no-method-prefixed-with-on: [2]

View file

@ -180,12 +180,18 @@ func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha st
} }
if len(reqs) > 0 { if len(reqs) > 0 {
esBatchSize := 50
for i := 0; i < len(reqs); i += esBatchSize {
_, err := b.inner.Client.Bulk(). _, err := b.inner.Client.Bulk().
Index(b.inner.VersionedIndexName()). Index(b.inner.VersionedIndexName()).
Add(reqs...). Add(reqs[i:min(i+esBatchSize, len(reqs))]...).
Do(ctx) Do(ctx)
if err != nil {
return err return err
} }
}
}
return nil return nil
} }

View file

@ -157,7 +157,7 @@ func EnumeratePackageVersions(ctx *context.Context) {
} }
type Resource struct { type Resource struct {
Name string `json:"id"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
Checksum string `json:"checksum"` Checksum string `json:"checksum"`
} }

View file

@ -278,15 +278,13 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo
} }
} }
if !rel.IsDraft {
if !isCreated { if !isCreated {
notify_service.UpdateRelease(gitRepo.Ctx, doer, rel) notify_service.UpdateRelease(gitRepo.Ctx, doer, rel)
return nil return nil
} }
if !rel.IsDraft {
notify_service.NewRelease(gitRepo.Ctx, rel) notify_service.NewRelease(gitRepo.Ctx, rel)
} }
return nil return nil
} }
@ -351,7 +349,8 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re
} }
} }
if !rel.IsDraft {
notify_service.DeleteRelease(ctx, doer, rel) notify_service.DeleteRelease(ctx, doer, rel)
}
return nil return nil
} }

View file

@ -9,11 +9,11 @@
<div>{{ctx.Locale.Tr "repo.commit.contained_in"}}</div> <div>{{ctx.Locale.Tr "repo.commit.contained_in"}}</div>
<div class="gt-df gt-mt-3"> <div class="gt-df gt-mt-3">
<div class="gt-p-2">{{svg "octicon-git-branch"}}</div> <div class="gt-p-2">{{svg "octicon-git-branch"}}</div>
<div class="branch-area flex-text-block gt-f1"></div> <div class="branch-area flex-text-block gt-fw gt-f1"></div>
</div> </div>
<div class="gt-df gt-mt-3"> <div class="gt-df gt-mt-3">
<div class="gt-p-2">{{svg "octicon-tag"}}</div> <div class="gt-p-2">{{svg "octicon-tag"}}</div>
<div class="tag-area flex-text-block gt-f1"></div> <div class="tag-area flex-text-block gt-fw gt-f1"></div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -8,8 +8,8 @@
{{range $idx, $release := .Releases}} {{range $idx, $release := .Releases}}
<li class="ui grid"> <li class="ui grid">
<div class="ui four wide column meta"> <div class="ui four wide column meta">
<a class="muted" href="{{if not .Sha1}}#{{else}}{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}{{end}}" rel="nofollow">{{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}}</a> <a class="muted" href="{{if not (and .Sha1 ($.Permission.CanRead $.UnitTypeCode))}}#{{else}}{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}{{end}}" rel="nofollow">{{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}}</a>
{{if .Sha1}} {{if and .Sha1 ($.Permission.CanRead $.UnitTypeCode)}}
<a class="muted gt-mono" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}}</a> <a class="muted gt-mono" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}}</a>
{{template "repo/branch_dropdown" dict "root" $ "release" .}} {{template "repo/branch_dropdown" dict "root" $ "release" .}}
{{end}} {{end}}
@ -22,36 +22,18 @@
<span class="ui yellow label">{{ctx.Locale.Tr "repo.release.draft"}}</span> <span class="ui yellow label">{{ctx.Locale.Tr "repo.release.draft"}}</span>
{{else if .IsPrerelease}} {{else if .IsPrerelease}}
<span class="ui orange label">{{ctx.Locale.Tr "repo.release.prerelease"}}</span> <span class="ui orange label">{{ctx.Locale.Tr "repo.release.prerelease"}}</span>
{{else if not .IsTag}} {{else}}
<span class="ui green label">{{ctx.Locale.Tr "repo.release.stable"}}</span> <span class="ui green label">{{ctx.Locale.Tr "repo.release.stable"}}</span>
{{end}} {{end}}
</h4> </h4>
<div> <div>
{{if and $.CanCreateRelease (not .IsTag)}} {{if $.CanCreateRelease}}
<a class="muted" data-tooltip-content="{{ctx.Locale.Tr "repo.release.edit"}}" href="{{$.RepoLink}}/releases/edit/{{.TagName | PathEscapeSegments}}" rel="nofollow"> <a class="muted" data-tooltip-content="{{ctx.Locale.Tr "repo.release.edit"}}" href="{{$.RepoLink}}/releases/edit/{{.TagName | PathEscapeSegments}}" rel="nofollow">
{{svg "octicon-pencil"}} {{svg "octicon-pencil"}}
</a> </a>
{{end}} {{end}}
</div> </div>
</div> </div>
{{if .IsTag}}
<p class="text grey">
{{if gt .Publisher.ID 0}}
<span class="author">
{{ctx.AvatarUtils.Avatar .Publisher 20 "gt-mr-2"}}
<a href="{{.Publisher.HomeLink}}">{{.Publisher.Name}}</a>
</span>
<span class="released">
{{ctx.Locale.Tr "repo.tagged_this"}}
</span>
{{if .CreatedUnix}}
<span class="time">{{TimeSinceUnix .CreatedUnix ctx.Locale}}</span>
{{end}}
|
{{end}}
<span class="ahead"><a href="{{$.RepoLink}}/compare/{{.TagName | PathEscapeSegments}}...{{.TargetBehind | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}}</a> {{ctx.Locale.Tr "repo.tag.ahead.target" .TargetBehind}}</span>
</p>
{{else}}
<p class="text grey"> <p class="text grey">
<span class="author"> <span class="author">
{{if .OriginalAuthor}} {{if .OriginalAuthor}}
@ -69,11 +51,10 @@
{{if .CreatedUnix}} {{if .CreatedUnix}}
<span class="time">{{TimeSinceUnix .CreatedUnix ctx.Locale}}</span> <span class="time">{{TimeSinceUnix .CreatedUnix ctx.Locale}}</span>
{{end}} {{end}}
{{if not .IsDraft}} {{if and (not .IsDraft) ($.Permission.CanRead $.UnitTypeCode)}}
| <span class="ahead"><a href="{{$.RepoLink}}/compare/{{.TagName | PathEscapeSegments}}...{{.TargetBehind | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}}</a> {{ctx.Locale.Tr "repo.release.ahead.target" .TargetBehind}}</span> | <span class="ahead"><a href="{{$.RepoLink}}/compare/{{.TagName | PathEscapeSegments}}...{{.TargetBehind | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}}</a> {{ctx.Locale.Tr "repo.release.ahead.target" .TargetBehind}}</span>
{{end}} {{end}}
</p> </p>
{{end}}
<div class="markup desc"> <div class="markup desc">
{{Str2html .Note}} {{Str2html .Note}}
</div> </div>

View file

@ -33,7 +33,7 @@
{{else if .IsPDFFile}} {{else if .IsPDFFile}}
<div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "diff.view_file"}}"></div> <div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "diff.view_file"}}"></div>
{{else}} {{else}}
<a href="{{$.RawFileLink}}" rel="nofollow" class="btn btn-gray btn-radius">{{ctx.Locale.Tr "repo.file_view_raw"}}</a> <a href="{{$.RawFileLink}}" rel="nofollow">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
{{end}} {{end}}
</div> </div>
{{else if .FileSize}} {{else if .FileSize}}

View file

@ -1,7 +1,7 @@
{{if .EscapeStatus}} {{if .EscapeStatus}}
{{if .EscapeStatus.HasInvisible}} {{if .EscapeStatus.HasInvisible}}
<div class="ui warning message unicode-escape-prompt gt-text-left"> <div class="ui warning message unicode-escape-prompt gt-text-left">
<button class="close icon hide-panel button" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</button> <button class="btn close icon hide-panel" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</button>
<div class="header"> <div class="header">
{{ctx.Locale.Tr "repo.invisible_runes_header"}} {{ctx.Locale.Tr "repo.invisible_runes_header"}}
</div> </div>
@ -12,7 +12,7 @@
</div> </div>
{{else if .EscapeStatus.HasAmbiguous}} {{else if .EscapeStatus.HasAmbiguous}}
<div class="ui warning message unicode-escape-prompt gt-text-left"> <div class="ui warning message unicode-escape-prompt gt-text-left">
<button class="close icon hide-panel button" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</button> <button class="btn close icon hide-panel" data-panel-closest=".message">{{svg "octicon-x" 16 "close inside"}}</button>
<div class="header"> <div class="header">
{{ctx.Locale.Tr "repo.ambiguous_runes_header"}} {{ctx.Locale.Tr "repo.ambiguous_runes_header"}}
</div> </div>

View file

@ -87,7 +87,7 @@
{{else if .IsPDFFile}} {{else if .IsPDFFile}}
<div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "repo.diff.view_file"}}"></div> <div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "repo.diff.view_file"}}"></div>
{{else}} {{else}}
<a href="{{$.RawFileLink}}" rel="nofollow" class="btn btn-gray btn-radius">{{ctx.Locale.Tr "repo.file_view_raw"}}</a> <a href="{{$.RawFileLink}}" rel="nofollow">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
{{end}} {{end}}
</div> </div>
{{else if .FileSize}} {{else if .FileSize}}

View file

@ -12,8 +12,10 @@ export function renderCodeCopy() {
if (!els.length) return; if (!els.length) return;
for (const el of els) { for (const el of els) {
if (!el.textContent) continue;
const btn = makeCodeCopyButton(); 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); el.after(btn);
} }
} }

View file

@ -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) { function attachDirAuto(el) {
if (el.type !== 'hidden' && if (el.type !== 'hidden' &&
el.type !== 'checkbox' && el.type !== 'checkbox' &&
@ -18,7 +19,7 @@ export function initDirAuto() {
const len = mutation.addedNodes.length; const len = mutation.addedNodes.length;
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
const addedNode = mutation.addedNodes[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); if (addedNode.nodeName === 'INPUT' || addedNode.nodeName === 'TEXTAREA') attachDirAuto(addedNode);
const children = addedNode.querySelectorAll('input, textarea'); const children = addedNode.querySelectorAll('input, textarea');
const len = children.length; const len = children.length;

View file

@ -1,4 +1,5 @@
import tippy, {followCursor} from 'tippy.js'; import tippy, {followCursor} from 'tippy.js';
import {isDocumentFragmentOrElementNode} from '../utils/dom.js';
const visibleInstances = new Set(); 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() { export function initGlobalTooltips() {
// use MutationObserver to detect new "data-tooltip-content" elements added to the DOM, or attributes changed // use MutationObserver to detect new "data-tooltip-content" elements added to the DOM, or attributes changed
const observerConnect = (observer) => observer.observe(document, { const observerConnect = (observer) => observer.observe(document, {
@ -152,13 +151,12 @@ export function initGlobalTooltips() {
if (mutation.type === 'childList') { if (mutation.type === 'childList') {
// mainly for Vue components and AJAX rendered elements // mainly for Vue components and AJAX rendered elements
for (const el of mutation.addedNodes) { for (const el of mutation.addedNodes) {
if (elementNodeTypes.has(el.nodeType)) { if (!isDocumentFragmentOrElementNode(el)) continue;
attachChildrenLazyTooltip(el); attachChildrenLazyTooltip(el);
if (el.hasAttribute('data-tooltip-content')) { if (el.hasAttribute('data-tooltip-content')) {
attachLazyTooltip(el); attachLazyTooltip(el);
} }
} }
}
} else if (mutation.type === 'attributes') { } else if (mutation.type === 'attributes') {
attachTooltip(mutation.target); attachTooltip(mutation.target);
} }

View file

@ -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 // autosize a textarea to fit content. Based on
// https://github.com/github/textarea-autosize // https://github.com/github/textarea-autosize
// --------------------------------------------------------------------- // ---------------------------------------------------------------------

View file

@ -1,17 +1,21 @@
// Convert an absolute or relative URL to an absolute URL with the current origin // Convert an absolute or relative URL to an absolute URL with the current origin
window.customElements.define('gitea-origin-url', class extends HTMLElement { export function toOriginUrl(urlStr) {
connectedCallback() {
const urlStr = this.getAttribute('data-url');
try { try {
// only process absolute HTTP/HTTPS URL or relative URLs ('/xxx' or '//host/xxx') // only process absolute HTTP/HTTPS URL or relative URLs ('/xxx' or '//host/xxx')
if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) {
const url = new URL(urlStr, window.origin); const {origin, protocol, hostname, port} = window.location;
url.protocol = window.location.protocol; const url = new URL(urlStr, origin);
url.host = window.location.host; url.protocol = protocol;
this.textContent = url.toString(); url.hostname = hostname;
return; url.port = port || (protocol === 'https:' ? '443' : '80');
return url.toString();
} }
} catch {} } catch {}
this.textContent = urlStr; return urlStr;
}
window.customElements.define('gitea-origin-url', class extends HTMLElement {
connectedCallback() {
this.textContent = toOriginUrl(this.getAttribute('data-url'));
} }
}); });

View file

@ -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;
});