mirror of
https://github.com/go-gitea/gitea.git
synced 2025-05-29 11:38:45 +03:00
Add a button editing action secret (#34348)
Add a button editing action secret Closes #34190 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@ -3722,13 +3722,18 @@ owner.settings.chef.keypair.description = A key pair is necessary to authenticat
|
|||||||
secrets = Secrets
|
secrets = Secrets
|
||||||
description = Secrets will be passed to certain actions and cannot be read otherwise.
|
description = Secrets will be passed to certain actions and cannot be read otherwise.
|
||||||
none = There are no secrets yet.
|
none = There are no secrets yet.
|
||||||
creation = Add Secret
|
|
||||||
|
; These keys are also for "edit secret", the keys are kept as-is to avoid unnecessary re-translation
|
||||||
creation.description = Description
|
creation.description = Description
|
||||||
creation.name_placeholder = case-insensitive, alphanumeric characters or underscores only, cannot start with GITEA_ or GITHUB_
|
creation.name_placeholder = case-insensitive, alphanumeric characters or underscores only, cannot start with GITEA_ or GITHUB_
|
||||||
creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted.
|
creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted.
|
||||||
creation.description_placeholder = Enter short description (optional).
|
creation.description_placeholder = Enter short description (optional).
|
||||||
creation.success = The secret "%s" has been added.
|
|
||||||
creation.failed = Failed to add secret.
|
save_success = The secret "%s" has been saved.
|
||||||
|
save_failed = Failed to save secret.
|
||||||
|
|
||||||
|
add_secret = Add secret
|
||||||
|
edit_secret = Edit secret
|
||||||
deletion = Remove secret
|
deletion = Remove secret
|
||||||
deletion.description = Removing a secret is permanent and cannot be undone. Continue?
|
deletion.description = Removing a secret is permanent and cannot be undone. Continue?
|
||||||
deletion.success = The secret has been removed.
|
deletion.success = The secret has been removed.
|
||||||
|
@ -32,11 +32,11 @@ func PerformSecretsPost(ctx *context.Context, ownerID, repoID int64, redirectURL
|
|||||||
s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.ReserveLineBreakForTextarea(form.Data), form.Description)
|
s, _, err := secret_service.CreateOrUpdateSecret(ctx, ownerID, repoID, form.Name, util.ReserveLineBreakForTextarea(form.Data), form.Description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("CreateOrUpdateSecret failed: %v", err)
|
log.Error("CreateOrUpdateSecret failed: %v", err)
|
||||||
ctx.JSONError(ctx.Tr("secrets.creation.failed"))
|
ctx.JSONError(ctx.Tr("secrets.save_failed"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Flash.Success(ctx.Tr("secrets.creation.success", s.Name))
|
ctx.Flash.Success(ctx.Tr("secrets.save_success", s.Name))
|
||||||
ctx.JSONRedirect(redirectURL)
|
ctx.JSONRedirect(redirectURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,13 @@
|
|||||||
<button class="ui primary tiny button show-modal"
|
<button class="ui primary tiny button show-modal"
|
||||||
data-modal="#add-secret-modal"
|
data-modal="#add-secret-modal"
|
||||||
data-modal-form.action="{{.Link}}"
|
data-modal-form.action="{{.Link}}"
|
||||||
data-modal-header="{{ctx.Locale.Tr "secrets.creation"}}"
|
data-modal-header="{{ctx.Locale.Tr "secrets.add_secret"}}"
|
||||||
|
data-modal-secret-name.value=""
|
||||||
|
data-modal-secret-name.read-only="false"
|
||||||
|
data-modal-secret-data=""
|
||||||
|
data-modal-secret-description=""
|
||||||
>
|
>
|
||||||
{{ctx.Locale.Tr "secrets.creation"}}
|
{{ctx.Locale.Tr "secrets.add_secret"}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</h4>
|
</h4>
|
||||||
@ -33,6 +37,18 @@
|
|||||||
<span class="color-text-light-2">
|
<span class="color-text-light-2">
|
||||||
{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}
|
{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}
|
||||||
</span>
|
</span>
|
||||||
|
<button class="ui btn interact-bg show-modal tw-p-2"
|
||||||
|
data-modal="#add-secret-modal"
|
||||||
|
data-modal-form.action="{{$.Link}}"
|
||||||
|
data-modal-header="{{ctx.Locale.Tr "secrets.edit_secret"}}"
|
||||||
|
data-tooltip-content="{{ctx.Locale.Tr "secrets.edit_secret"}}"
|
||||||
|
data-modal-secret-name.value="{{.Name}}"
|
||||||
|
data-modal-secret-name.read-only="true"
|
||||||
|
data-modal-secret-data=""
|
||||||
|
data-modal-secret-description="{{if .Description}}{{.Description}}{{end}}"
|
||||||
|
>
|
||||||
|
{{svg "octicon-pencil"}}
|
||||||
|
</button>
|
||||||
<button class="ui btn interact-bg link-action tw-p-2"
|
<button class="ui btn interact-bg link-action tw-p-2"
|
||||||
data-url="{{$.Link}}/delete?id={{.ID}}"
|
data-url="{{$.Link}}/delete?id={{.ID}}"
|
||||||
data-modal-confirm="{{ctx.Locale.Tr "secrets.deletion.description"}}"
|
data-modal-confirm="{{ctx.Locale.Tr "secrets.deletion.description"}}"
|
||||||
@ -51,9 +67,7 @@
|
|||||||
|
|
||||||
{{/* Add secret dialog */}}
|
{{/* Add secret dialog */}}
|
||||||
<div class="ui small modal" id="add-secret-modal">
|
<div class="ui small modal" id="add-secret-modal">
|
||||||
<div class="header">
|
<div class="header"></div>
|
||||||
<span id="actions-modal-header"></span>
|
|
||||||
</div>
|
|
||||||
<form class="ui form form-fetch-action" method="post">
|
<form class="ui form form-fetch-action" method="post">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
|
14
web_src/js/features/common-button.test.ts
Normal file
14
web_src/js/features/common-button.test.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {assignElementProperty} from './common-button.ts';
|
||||||
|
|
||||||
|
test('assignElementProperty', () => {
|
||||||
|
const elForm = document.createElement('form');
|
||||||
|
assignElementProperty(elForm, 'action', '/test-link');
|
||||||
|
expect(elForm.action).contains('/test-link'); // the DOM always returns absolute URL
|
||||||
|
assignElementProperty(elForm, 'text-content', 'dummy');
|
||||||
|
expect(elForm.textContent).toBe('dummy');
|
||||||
|
|
||||||
|
const elInput = document.createElement('input');
|
||||||
|
expect(elInput.readOnly).toBe(false);
|
||||||
|
assignElementProperty(elInput, 'read-only', 'true');
|
||||||
|
expect(elInput.readOnly).toBe(true);
|
||||||
|
});
|
@ -102,6 +102,21 @@ function onHidePanelClick(el: HTMLElement, e: MouseEvent) {
|
|||||||
throw new Error('no panel to hide'); // should never happen, otherwise there is a bug in code
|
throw new Error('no panel to hide'); // should never happen, otherwise there is a bug in code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function assignElementProperty(el: any, name: string, val: string) {
|
||||||
|
name = camelize(name);
|
||||||
|
const old = el[name];
|
||||||
|
if (typeof old === 'boolean') {
|
||||||
|
el[name] = val === 'true';
|
||||||
|
} else if (typeof old === 'number') {
|
||||||
|
el[name] = parseFloat(val);
|
||||||
|
} else if (typeof old === 'string') {
|
||||||
|
el[name] = val;
|
||||||
|
} else {
|
||||||
|
// in the future, we could introduce a better typing system like `data-modal-form.action:string="..."`
|
||||||
|
throw new Error(`cannot assign element property ${name} by value ${val}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onShowModalClick(el: HTMLElement, e: MouseEvent) {
|
function onShowModalClick(el: HTMLElement, e: MouseEvent) {
|
||||||
// A ".show-modal" button will show a modal dialog defined by its "data-modal" attribute.
|
// A ".show-modal" button will show a modal dialog defined by its "data-modal" attribute.
|
||||||
// Each "data-modal-{target}" attribute will be filled to target element's value or text-content.
|
// Each "data-modal-{target}" attribute will be filled to target element's value or text-content.
|
||||||
@ -109,7 +124,7 @@ function onShowModalClick(el: HTMLElement, e: MouseEvent) {
|
|||||||
// * Then, try to query '[name=target]'
|
// * Then, try to query '[name=target]'
|
||||||
// * Then, try to query '.target'
|
// * Then, try to query '.target'
|
||||||
// * Then, try to query 'target' as HTML tag
|
// * Then, try to query 'target' as HTML tag
|
||||||
// If there is a ".{attr}" part like "data-modal-form.action", then the form's "action" attribute will be set.
|
// If there is a ".{prop-name}" part like "data-modal-form.action", the "form" element's "action" property will be set, the "prop-name" will be camel-cased to "propName".
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const modalSelector = el.getAttribute('data-modal');
|
const modalSelector = el.getAttribute('data-modal');
|
||||||
const elModal = document.querySelector(modalSelector);
|
const elModal = document.querySelector(modalSelector);
|
||||||
@ -122,7 +137,7 @@ function onShowModalClick(el: HTMLElement, e: MouseEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const attrTargetCombo = attrib.name.substring(modalAttrPrefix.length);
|
const attrTargetCombo = attrib.name.substring(modalAttrPrefix.length);
|
||||||
const [attrTargetName, attrTargetAttr] = attrTargetCombo.split('.');
|
const [attrTargetName, attrTargetProp] = attrTargetCombo.split('.');
|
||||||
// try to find target by: "#target" -> "[name=target]" -> ".target" -> "<target> tag"
|
// try to find target by: "#target" -> "[name=target]" -> ".target" -> "<target> tag"
|
||||||
const attrTarget = elModal.querySelector(`#${attrTargetName}`) ||
|
const attrTarget = elModal.querySelector(`#${attrTargetName}`) ||
|
||||||
elModal.querySelector(`[name=${attrTargetName}]`) ||
|
elModal.querySelector(`[name=${attrTargetName}]`) ||
|
||||||
@ -133,8 +148,8 @@ function onShowModalClick(el: HTMLElement, e: MouseEvent) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrTargetAttr) {
|
if (attrTargetProp) {
|
||||||
(attrTarget as any)[camelize(attrTargetAttr)] = attrib.value;
|
assignElementProperty(attrTarget, attrTargetProp, attrib.value);
|
||||||
} else if (attrTarget.matches('input, textarea')) {
|
} else if (attrTarget.matches('input, textarea')) {
|
||||||
(attrTarget as HTMLInputElement | HTMLTextAreaElement).value = attrib.value; // FIXME: add more supports like checkbox
|
(attrTarget as HTMLInputElement | HTMLTextAreaElement).value = attrib.value; // FIXME: add more supports like checkbox
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user