mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Backport #34348 Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		| @@ -3723,13 +3723,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