mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Improve AJAX link and modal confirm dialog (#25210)
Clarify the "link-action" behavior: > // A "link-action" can post AJAX request to its "data-url" > // Then the browser is redirect to: the "redirect" in response, or "data-redirect" attribute, or current URL by reloading. And enhance the "link-action" to support showing a modal dialog for confirm. A similar general approach could also help PRs like https://github.com/go-gitea/gitea/pull/22344#discussion_r1062883436 > // If the "link-action" has "data-modal-confirm(-html)" attribute, a confirm modal dialog will be shown before taking action. And a lot of duplicate code can be removed now. A good framework design can help to avoid code copying&pasting. --------- Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
		@@ -186,7 +186,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				<div class="field">
 | 
									<div class="field">
 | 
				
			||||||
					<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
 | 
										<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
 | 
				
			||||||
					<a class="ui red button delete-post" data-request-url="{{.Link}}/avatar/delete" data-done-url="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</a>{{/* TODO: Convert links without href to buttons for a11y */}}
 | 
										<button class="ui red button link-action" data-url="{{.Link}}/avatar/delete" data-redirect="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</button>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</form>
 | 
								</form>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,11 +32,13 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly.
 | 
				
			|||||||
		mermaidMaxSourceCharacters: {{MermaidMaxSourceCharacters}},
 | 
							mermaidMaxSourceCharacters: {{MermaidMaxSourceCharacters}},
 | 
				
			||||||
		{{/* this global i18n object should only contain general texts. for specialized texts, it should be provided inside the related modules by: (1) API response (2) HTML data-attribute (3) PageData */}}
 | 
							{{/* this global i18n object should only contain general texts. for specialized texts, it should be provided inside the related modules by: (1) API response (2) HTML data-attribute (3) PageData */}}
 | 
				
			||||||
		i18n: {
 | 
							i18n: {
 | 
				
			||||||
			copy_success: '{{.locale.Tr "copy_success"}}',
 | 
								copy_success: {{.locale.Tr "copy_success"}},
 | 
				
			||||||
			copy_error: '{{.locale.Tr "copy_error"}}',
 | 
								copy_error: {{.locale.Tr "copy_error"}},
 | 
				
			||||||
			error_occurred: '{{.locale.Tr "error.occurred"}}',
 | 
								error_occurred: {{.locale.Tr "error.occurred"}},
 | 
				
			||||||
			network_error: '{{.locale.Tr "error.network_error"}}',
 | 
								network_error: {{.locale.Tr "error.network_error"}},
 | 
				
			||||||
			remove_label_str: '{{.locale.Tr "remove_label_str"}}',
 | 
								remove_label_str: {{.locale.Tr "remove_label_str"}},
 | 
				
			||||||
 | 
								modal_confirm: {{.locale.Tr "modal.confirm"}},
 | 
				
			||||||
 | 
								modal_cancel: {{.locale.Tr "modal.cancel"}},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	{{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}}
 | 
						{{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,7 +90,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
						<div class="field">
 | 
											<div class="field">
 | 
				
			||||||
							<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
 | 
												<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
 | 
				
			||||||
							<a class="ui red button delete-post" data-request-url="{{.Link}}/avatar/delete" data-done-url="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</a>
 | 
												<button class="ui red button link-action" data-url="{{.Link}}/avatar/delete" data-redirect="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</button>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</form>
 | 
										</form>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,16 +9,14 @@
 | 
				
			|||||||
				{{template "org/team/navbar" .}}
 | 
									{{template "org/team/navbar" .}}
 | 
				
			||||||
				{{if .IsOrganizationOwner}}
 | 
									{{if .IsOrganizationOwner}}
 | 
				
			||||||
					<div class="ui attached segment">
 | 
										<div class="ui attached segment">
 | 
				
			||||||
						<form class="ui form ignore-dirty" id="add-member-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/add" method="post">
 | 
											<form class="ui form ignore-dirty gt-df gt-fw gt-gap-3" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/add" method="post">
 | 
				
			||||||
							{{.CsrfTokenHtml}}
 | 
												{{.CsrfTokenHtml}}
 | 
				
			||||||
							<input type="hidden" name="uid" value="{{.SignedUser.ID}}">
 | 
												<input type="hidden" name="uid" value="{{.SignedUser.ID}}">
 | 
				
			||||||
							<div class="inline field ui left">
 | 
												<div id="search-user-box" class="ui search gt-mr-3"{{if .IsEmailInviteEnabled}} data-allow-email="true" data-allow-email-description="{{.locale.Tr "org.teams.invite_team_member" $.Team.Name}}"{{end}}>
 | 
				
			||||||
								<div id="search-user-box" class="ui search"{{if .IsEmailInviteEnabled}} data-allow-email="true" data-allow-email-description="{{.locale.Tr "org.teams.invite_team_member" $.Team.Name}}"{{end}}>
 | 
					 | 
				
			||||||
								<div class="ui input">
 | 
													<div class="ui input">
 | 
				
			||||||
									<input class="prompt" name="uname" placeholder="{{.locale.Tr "repo.settings.search_user_placeholder"}}" autocomplete="off" required>
 | 
														<input class="prompt" name="uname" placeholder="{{.locale.Tr "repo.settings.search_user_placeholder"}}" autocomplete="off" required>
 | 
				
			||||||
								</div>
 | 
													</div>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
							</div>
 | 
					 | 
				
			||||||
							<button class="ui green button">{{.locale.Tr "org.teams.add_team_member"}}</button>
 | 
												<button class="ui green button">{{.locale.Tr "org.teams.add_team_member"}}</button>
 | 
				
			||||||
						</form>
 | 
											</form>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,25 +9,19 @@
 | 
				
			|||||||
				{{template "org/team/navbar" .}}
 | 
									{{template "org/team/navbar" .}}
 | 
				
			||||||
				{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
 | 
									{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
 | 
				
			||||||
				{{if $canAddRemove}}
 | 
									{{if $canAddRemove}}
 | 
				
			||||||
					<div class="ui attached segment" id="repo-top-segment">
 | 
										<div class="ui attached segment gt-df gt-fw gt-gap-3">
 | 
				
			||||||
						<div class="inline ui field left">
 | 
											<form class="ui form ignore-dirty gt-f1 gt-dif" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
 | 
				
			||||||
							<form class="ui form ignore-dirty" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/add" method="post">
 | 
					 | 
				
			||||||
							{{.CsrfTokenHtml}}
 | 
												{{.CsrfTokenHtml}}
 | 
				
			||||||
								<div class="inline field ui left">
 | 
					 | 
				
			||||||
							<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
 | 
												<div id="search-repo-box" data-uid="{{.Org.ID}}" class="ui search">
 | 
				
			||||||
								<div class="ui input">
 | 
													<div class="ui input">
 | 
				
			||||||
									<input class="prompt" name="repo_name" placeholder="{{.locale.Tr "org.teams.search_repo_placeholder"}}" autocomplete="off" required>
 | 
														<input class="prompt" name="repo_name" placeholder="{{.locale.Tr "org.teams.search_repo_placeholder"}}" autocomplete="off" required>
 | 
				
			||||||
								</div>
 | 
													</div>
 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
								</div>
 | 
												<button class="ui green button gt-ml-3">{{.locale.Tr "add"}}</button>
 | 
				
			||||||
								<button class="ui green button">{{.locale.Tr "add"}}</button>
 | 
					 | 
				
			||||||
							</form>
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
						<div class="inline ui field right">
 | 
					 | 
				
			||||||
							<form class="ui form" id="repo-multiple-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/repositories" method="post">
 | 
					 | 
				
			||||||
								<button class="ui green button add-all-button" data-modal-id="org-team-add-all-repo" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/addall">{{.locale.Tr "add_all"}}</button>
 | 
					 | 
				
			||||||
								<button class="ui red button delete-button" data-modal-id="org-team-remove-all-repo" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/removeall">{{.locale.Tr "remove_all"}}</button>
 | 
					 | 
				
			||||||
						</form>
 | 
											</form>
 | 
				
			||||||
 | 
											<div class="gt-dib">
 | 
				
			||||||
 | 
												<button class="ui green button link-action" data-modal-confirm="{{.locale.Tr "org.teams.add_all_repos_desc"}}" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/addall">{{.locale.Tr "add_all"}}</button>
 | 
				
			||||||
 | 
												<button class="ui red button link-action" data-modal-confirm="{{.locale.Tr "org.teams.remove_all_repos_desc"}}" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/removeall">{{.locale.Tr "remove_all"}}</button>
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
@@ -64,26 +58,4 @@
 | 
				
			|||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="ui g-modal-confirm delete modal" id="org-team-remove-all-repo">
 | 
					 | 
				
			||||||
	<div class="header">
 | 
					 | 
				
			||||||
		{{svg "octicon-trash"}}
 | 
					 | 
				
			||||||
		{{.locale.Tr "org.teams.remove_all_repos_title"}}
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
	<div class="content">
 | 
					 | 
				
			||||||
		<p>{{.locale.Tr "org.teams.remove_all_repos_desc"}}</p>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
	{{template "base/modal_actions_confirm" .}}
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div class="ui g-modal-confirm addall modal" id="org-team-add-all-repo">
 | 
					 | 
				
			||||||
	<div class="header">
 | 
					 | 
				
			||||||
		{{svg "octicon-globe"}}
 | 
					 | 
				
			||||||
		{{.locale.Tr "org.teams.add_all_repos_title"}}
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
	<div class="content">
 | 
					 | 
				
			||||||
		<p>{{.locale.Tr "org.teams.add_all_repos_desc"}}</p>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
	{{template "base/modal_actions_confirm" .}}
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{{template "base/footer" .}}
 | 
					{{template "base/footer" .}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -165,7 +165,7 @@
 | 
				
			|||||||
										{{end}}
 | 
															{{end}}
 | 
				
			||||||
										{{if and $.IsWriter (not $.IsMirror) (not $.Repository.IsArchived) (not .IsProtected)}}
 | 
															{{if and $.IsWriter (not $.IsMirror) (not $.Repository.IsArchived) (not .IsProtected)}}
 | 
				
			||||||
											{{if .IsDeleted}}
 | 
																{{if .IsDeleted}}
 | 
				
			||||||
												<button class="btn interact-bg gt-p-3 undo-button" data-url="{{$.Link}}/restore?branch_id={{.DeletedBranch.ID}}&name={{.DeletedBranch.Name}}&page={{$.Page.Paginater.Current}}" data-tooltip-content="{{$.locale.Tr "repo.branch.restore" (.Name)}}">
 | 
																	<button class="btn interact-bg gt-p-3 link-action restore-branch-button" data-url="{{$.Link}}/restore?branch_id={{.DeletedBranch.ID}}&name={{.DeletedBranch.Name}}&page={{$.Page.Paginater.Current}}" data-tooltip-content="{{$.locale.Tr "repo.branch.restore" (.Name)}}">
 | 
				
			||||||
													<span class="text blue">
 | 
																		<span class="text blue">
 | 
				
			||||||
														{{svg "octicon-reply"}}
 | 
																			{{svg "octicon-reply"}}
 | 
				
			||||||
													</span>
 | 
																		</span>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -61,7 +61,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				<div class="field">
 | 
									<div class="field">
 | 
				
			||||||
					<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
 | 
										<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
 | 
				
			||||||
					<a class="ui red button delete-post" data-request-url="{{.Link}}/avatar/delete" data-done-url="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</a>
 | 
										<button class="ui red button link-action" data-url="{{.Link}}/avatar/delete" data-redirect="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</button>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</form>
 | 
								</form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -125,7 +125,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				<div class="field">
 | 
									<div class="field">
 | 
				
			||||||
					<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
 | 
										<button class="ui green button">{{$.locale.Tr "settings.update_avatar"}}</button>
 | 
				
			||||||
					<button class="ui red button delete-post" data-request-url="{{.Link}}/avatar/delete" data-done-url="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</button>
 | 
										<button class="ui red button link-action" data-url="{{.Link}}/avatar/delete" data-redirect="{{.Link}}">{{$.locale.Tr "settings.delete_current_avatar"}}</button>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</form>
 | 
								</form>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,7 @@ func TestDeleteBranch(t *testing.T) {
 | 
				
			|||||||
func TestUndoDeleteBranch(t *testing.T) {
 | 
					func TestUndoDeleteBranch(t *testing.T) {
 | 
				
			||||||
	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 | 
						onGiteaRun(t, func(t *testing.T, u *url.URL) {
 | 
				
			||||||
		deleteBranch(t)
 | 
							deleteBranch(t)
 | 
				
			||||||
		htmlDoc, name := branchAction(t, ".undo-button")
 | 
							htmlDoc, name := branchAction(t, ".restore-branch-button")
 | 
				
			||||||
		assert.Contains(t,
 | 
							assert.Contains(t,
 | 
				
			||||||
			htmlDoc.doc.Find(".ui.positive.message").Text(),
 | 
								htmlDoc.doc.Find(".ui.positive.message").Text(),
 | 
				
			||||||
			translation.NewLocale("en-US").Tr("repo.branch.restore_success", name),
 | 
								translation.NewLocale("en-US").Tr("repo.branch.restore_success", name),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -205,29 +205,6 @@
 | 
				
			|||||||
  margin: 0;
 | 
					  margin: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.organization.teams #add-repo-form input,
 | 
					 | 
				
			||||||
.organization.teams #repo-multiple-form input,
 | 
					 | 
				
			||||||
.organization.teams #add-member-form input {
 | 
					 | 
				
			||||||
  margin-left: 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.organization.teams #add-repo-form .ui.button,
 | 
					 | 
				
			||||||
.organization.teams #repo-multiple-form .ui.button,
 | 
					 | 
				
			||||||
.organization.teams #add-member-form .ui.button {
 | 
					 | 
				
			||||||
  margin-left: 5px;
 | 
					 | 
				
			||||||
  margin-top: -3px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.organization.teams #repo-top-segment {
 | 
					 | 
				
			||||||
  height: 60px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@media (max-width: 767.98px) {
 | 
					 | 
				
			||||||
  .organization.teams #repo-top-segment {
 | 
					 | 
				
			||||||
    height: 100px;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.org-team-navbar .active.item {
 | 
					.org-team-navbar .active.item {
 | 
				
			||||||
  background: var(--color-box-body) !important;
 | 
					  background: var(--color-box-body) !important;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ import {svg} from '../svg.js';
 | 
				
			|||||||
import {hideElem, showElem, toggleElem} from '../utils/dom.js';
 | 
					import {hideElem, showElem, toggleElem} from '../utils/dom.js';
 | 
				
			||||||
import {htmlEscape} from 'escape-goat';
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {appUrl, csrfToken} = window.config;
 | 
					const {appUrl, csrfToken, i18n} = window.config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function initGlobalFormDirtyLeaveConfirm() {
 | 
					export function initGlobalFormDirtyLeaveConfirm() {
 | 
				
			||||||
  // Warn users that try to leave a page after entering data into a form.
 | 
					  // Warn users that try to leave a page after entering data into a form.
 | 
				
			||||||
@@ -172,6 +172,62 @@ export function initGlobalDropzone() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function linkAction(e) {
 | 
				
			||||||
 | 
					  e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // A "link-action" can post AJAX request to its "data-url"
 | 
				
			||||||
 | 
					  // Then the browser is redirect to: the "redirect" in response, or "data-redirect" attribute, or current URL by reloading.
 | 
				
			||||||
 | 
					  // If the "link-action" has "data-modal-confirm(-html)" attribute, a confirm modal dialog will be shown before taking action.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const $this = $(e.target);
 | 
				
			||||||
 | 
					  const redirect = $this.attr('data-redirect');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const request = () => {
 | 
				
			||||||
 | 
					    $this.prop('disabled', true);
 | 
				
			||||||
 | 
					    $.post($this.attr('data-url'), {
 | 
				
			||||||
 | 
					      _csrf: csrfToken
 | 
				
			||||||
 | 
					    }).done((data) => {
 | 
				
			||||||
 | 
					      if (data && data.redirect) {
 | 
				
			||||||
 | 
					        window.location.href = data.redirect;
 | 
				
			||||||
 | 
					      } else if (redirect) {
 | 
				
			||||||
 | 
					        window.location.href = redirect;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        window.location.reload();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }).always(() => {
 | 
				
			||||||
 | 
					      $this.prop('disabled', false);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const modalConfirmHtml = htmlEscape($this.attr('data-modal-confirm') || '');
 | 
				
			||||||
 | 
					  if (!modalConfirmHtml) {
 | 
				
			||||||
 | 
					    request();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const okButtonColor = $this.hasClass('red') || $this.hasClass('yellow') || $this.hasClass('orange') || $this.hasClass('negative') ? 'orange' : 'green';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const $modal = $(`
 | 
				
			||||||
 | 
					<div class="ui g-modal-confirm modal">
 | 
				
			||||||
 | 
					  <div class="content">${modalConfirmHtml}</div>
 | 
				
			||||||
 | 
					  <div class="actions">
 | 
				
			||||||
 | 
					    <button class="ui basic cancel button">${svg('octicon-x')} ${i18n.modal_cancel}</button>
 | 
				
			||||||
 | 
					    <button class="ui ${okButtonColor} ok button">${svg('octicon-check')} ${i18n.modal_confirm}</button>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $modal.appendTo(document.body);
 | 
				
			||||||
 | 
					  $modal.modal({
 | 
				
			||||||
 | 
					    onApprove() {
 | 
				
			||||||
 | 
					      request();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    onHidden() {
 | 
				
			||||||
 | 
					      $modal.remove();
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  }).modal('show');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function initGlobalLinkActions() {
 | 
					export function initGlobalLinkActions() {
 | 
				
			||||||
  function showDeletePopup(e) {
 | 
					  function showDeletePopup(e) {
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
@@ -217,75 +273,9 @@ export function initGlobalLinkActions() {
 | 
				
			|||||||
    }).modal('show');
 | 
					    }).modal('show');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function showAddAllPopup(e) {
 | 
					 | 
				
			||||||
    e.preventDefault();
 | 
					 | 
				
			||||||
    const $this = $(this);
 | 
					 | 
				
			||||||
    let filter = '';
 | 
					 | 
				
			||||||
    if ($this.attr('data-modal-id')) {
 | 
					 | 
				
			||||||
      filter += `#${$this.attr('data-modal-id')}`;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const dialog = $(`.addall.modal${filter}`);
 | 
					 | 
				
			||||||
    dialog.find('.name').text($this.data('name'));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dialog.modal({
 | 
					 | 
				
			||||||
      closable: false,
 | 
					 | 
				
			||||||
      onApprove() {
 | 
					 | 
				
			||||||
        if ($this.data('type') === 'form') {
 | 
					 | 
				
			||||||
          $($this.data('form')).trigger('submit');
 | 
					 | 
				
			||||||
          return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $.post($this.data('url'), {
 | 
					 | 
				
			||||||
          _csrf: csrfToken,
 | 
					 | 
				
			||||||
          id: $this.data('id')
 | 
					 | 
				
			||||||
        }).done((data) => {
 | 
					 | 
				
			||||||
          window.location.href = data.redirect;
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }).modal('show');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function linkAction(e) {
 | 
					 | 
				
			||||||
    e.preventDefault();
 | 
					 | 
				
			||||||
    const $this = $(this);
 | 
					 | 
				
			||||||
    const redirect = $this.data('redirect');
 | 
					 | 
				
			||||||
    $this.prop('disabled', true);
 | 
					 | 
				
			||||||
    $.post($this.data('url'), {
 | 
					 | 
				
			||||||
      _csrf: csrfToken
 | 
					 | 
				
			||||||
    }).done((data) => {
 | 
					 | 
				
			||||||
      if (data.redirect) {
 | 
					 | 
				
			||||||
        window.location.href = data.redirect;
 | 
					 | 
				
			||||||
      } else if (redirect) {
 | 
					 | 
				
			||||||
        window.location.href = redirect;
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        window.location.reload();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }).always(() => {
 | 
					 | 
				
			||||||
      $this.prop('disabled', false);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Helpers.
 | 
					  // Helpers.
 | 
				
			||||||
  $('.delete-button').on('click', showDeletePopup);
 | 
					  $('.delete-button').on('click', showDeletePopup);
 | 
				
			||||||
  $('.link-action').on('click', linkAction);
 | 
					  $('.link-action').on('click', linkAction);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  // FIXME: this function is only used once, and not common, not well designed. should be refactored later
 | 
					 | 
				
			||||||
  $('.add-all-button').on('click', showAddAllPopup);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // FIXME: this is only used once, and should be replace with `link-action` instead
 | 
					 | 
				
			||||||
  $('.undo-button').on('click', function () {
 | 
					 | 
				
			||||||
    const $this = $(this);
 | 
					 | 
				
			||||||
    $this.prop('disabled', true);
 | 
					 | 
				
			||||||
    $.post($this.data('url'), {
 | 
					 | 
				
			||||||
      _csrf: csrfToken,
 | 
					 | 
				
			||||||
      id: $this.data('id')
 | 
					 | 
				
			||||||
    }).done((data) => {
 | 
					 | 
				
			||||||
      window.location.href = data.redirect;
 | 
					 | 
				
			||||||
    }).always(() => {
 | 
					 | 
				
			||||||
      $this.prop('disabled', false);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function initGlobalButtons() {
 | 
					export function initGlobalButtons() {
 | 
				
			||||||
@@ -346,16 +336,6 @@ export function initGlobalButtons() {
 | 
				
			|||||||
      initCompColorPicker();
 | 
					      initCompColorPicker();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					 | 
				
			||||||
  $('.delete-post.button').on('click', function (e) {
 | 
					 | 
				
			||||||
    e.preventDefault();
 | 
					 | 
				
			||||||
    const $this = $(this);
 | 
					 | 
				
			||||||
    $.post($this.attr('data-request-url'), {
 | 
					 | 
				
			||||||
      _csrf: csrfToken
 | 
					 | 
				
			||||||
    }).done(() => {
 | 
					 | 
				
			||||||
      window.location.href = $this.attr('data-done-url');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user