mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 19:38:23 +00: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 | ||||
| description = Secrets will be passed to certain actions and cannot be read otherwise. | ||||
| 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.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.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.description = Removing a secret is permanent and cannot be undone. Continue? | ||||
| 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) | ||||
| 	if err != nil { | ||||
| 		log.Error("CreateOrUpdateSecret failed: %v", err) | ||||
| 		ctx.JSONError(ctx.Tr("secrets.creation.failed")) | ||||
| 		ctx.JSONError(ctx.Tr("secrets.save_failed")) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	ctx.Flash.Success(ctx.Tr("secrets.creation.success", s.Name)) | ||||
| 	ctx.Flash.Success(ctx.Tr("secrets.save_success", s.Name)) | ||||
| 	ctx.JSONRedirect(redirectURL) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -4,9 +4,13 @@ | ||||
| 		<button class="ui primary tiny button show-modal" | ||||
| 			data-modal="#add-secret-modal" | ||||
| 			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> | ||||
| 	</div> | ||||
| </h4> | ||||
| @@ -33,6 +37,18 @@ | ||||
| 				<span class="color-text-light-2"> | ||||
| 					{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}} | ||||
| 				</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" | ||||
| 					data-url="{{$.Link}}/delete?id={{.ID}}" | ||||
| 					data-modal-confirm="{{ctx.Locale.Tr "secrets.deletion.description"}}" | ||||
| @@ -51,9 +67,7 @@ | ||||
|  | ||||
| {{/* Add secret dialog */}} | ||||
| <div class="ui small modal" id="add-secret-modal"> | ||||
| 	<div class="header"> | ||||
| 		<span id="actions-modal-header"></span> | ||||
| 	</div> | ||||
| 	<div class="header"></div> | ||||
| 	<form class="ui form form-fetch-action" method="post"> | ||||
| 		<div class="content"> | ||||
| 			{{.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 | ||||
| } | ||||
|  | ||||
| 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) { | ||||
|   // 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. | ||||
| @@ -109,7 +124,7 @@ function onShowModalClick(el: HTMLElement, e: MouseEvent) { | ||||
|   // * Then, try to query '[name=target]' | ||||
|   // * Then, try to query '.target' | ||||
|   // * 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(); | ||||
|   const modalSelector = el.getAttribute('data-modal'); | ||||
|   const elModal = document.querySelector(modalSelector); | ||||
| @@ -122,7 +137,7 @@ function onShowModalClick(el: HTMLElement, e: MouseEvent) { | ||||
|     } | ||||
|  | ||||
|     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" | ||||
|     const attrTarget = elModal.querySelector(`#${attrTargetName}`) || | ||||
|       elModal.querySelector(`[name=${attrTargetName}]`) || | ||||
| @@ -133,8 +148,8 @@ function onShowModalClick(el: HTMLElement, e: MouseEvent) { | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     if (attrTargetAttr) { | ||||
|       (attrTarget as any)[camelize(attrTargetAttr)] = attrib.value; | ||||
|     if (attrTargetProp) { | ||||
|       assignElementProperty(attrTarget, attrTargetProp, attrib.value); | ||||
|     } else if (attrTarget.matches('input, textarea')) { | ||||
|       (attrTarget as HTMLInputElement | HTMLTextAreaElement).value = attrib.value; // FIXME: add more supports like checkbox | ||||
|     } else { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user