mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	converted export dialog to new pattern
This commit is contained in:
		| @@ -1,137 +0,0 @@ | ||||
| import treeService from "../services/tree.js"; | ||||
| import utils from "../services/utils.js"; | ||||
| import ws from "../services/ws.js"; | ||||
| import toastService from "../services/toast.js"; | ||||
| import froca from "../services/froca.js"; | ||||
| import openService from "../services/open.js"; | ||||
|  | ||||
| const $dialog = $("#export-dialog"); | ||||
| const $form = $("#export-form"); | ||||
| const $noteTitle = $dialog.find(".export-note-title"); | ||||
| const $subtreeFormats = $("#export-subtree-formats"); | ||||
| const $singleFormats = $("#export-single-formats"); | ||||
| const $subtreeType = $("#export-type-subtree"); | ||||
| const $singleType = $("#export-type-single"); | ||||
| const $exportButton = $("#export-button"); | ||||
| const $opmlVersions = $("#opml-versions"); | ||||
|  | ||||
| let taskId = ''; | ||||
| let branchId = null; | ||||
|  | ||||
| export async function showDialog(notePath, defaultType) { | ||||
|     // each opening of the dialog resets the taskId so we don't associate it with previous exports anymore | ||||
|     taskId = ''; | ||||
|     $exportButton.removeAttr("disabled"); | ||||
|  | ||||
|     if (defaultType === 'subtree') { | ||||
|         $subtreeType.prop("checked", true).trigger('change'); | ||||
|  | ||||
|         // to show/hide OPML versions | ||||
|         $("input[name=export-subtree-format]:checked").trigger('change'); | ||||
|     } | ||||
|     else if (defaultType === 'single') { | ||||
|         $singleType.prop("checked", true).trigger('change'); | ||||
|     } | ||||
|     else { | ||||
|         throw new Error("Unrecognized type " + defaultType); | ||||
|     } | ||||
|  | ||||
|     $("#opml-v2").prop("checked", true); // setting default | ||||
|  | ||||
|     utils.openDialog($dialog); | ||||
|  | ||||
|     const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath); | ||||
|  | ||||
|     branchId = await froca.getBranchId(parentNoteId, noteId); | ||||
|  | ||||
|     const noteTitle = await treeService.getNoteTitle(noteId); | ||||
|  | ||||
|     $noteTitle.html(noteTitle); | ||||
| } | ||||
|  | ||||
| $form.on('submit', () => { | ||||
|     $dialog.modal('hide'); | ||||
|  | ||||
|     const exportType = $dialog.find("input[name='export-type']:checked").val(); | ||||
|  | ||||
|     if (!exportType) { | ||||
|         // this shouldn't happen as we always choose default export type | ||||
|         alert("Choose export type first please"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const exportFormat = exportType === 'subtree' | ||||
|         ? $("input[name=export-subtree-format]:checked").val() | ||||
|         : $("input[name=export-single-format]:checked").val(); | ||||
|  | ||||
|     const exportVersion = exportFormat === 'opml' ? $dialog.find("input[name='opml-version']:checked").val() : "1.0"; | ||||
|  | ||||
|     exportBranch(branchId, exportType, exportFormat, exportVersion); | ||||
|  | ||||
|     return false; | ||||
| }); | ||||
|  | ||||
| function exportBranch(branchId, type, format, version) { | ||||
|     taskId = utils.randomString(10); | ||||
|  | ||||
|     const url = openService.getUrlForDownload(`api/notes/${branchId}/export/${type}/${format}/${version}/${taskId}`); | ||||
|  | ||||
|     openService.download(url); | ||||
| } | ||||
|  | ||||
| $('input[name=export-type]').on('change', function () { | ||||
|     if (this.value === 'subtree') { | ||||
|         if ($("input[name=export-subtree-format]:checked").length === 0) { | ||||
|             $("input[name=export-subtree-format]:first").prop("checked", true); | ||||
|         } | ||||
|  | ||||
|         $subtreeFormats.slideDown(); | ||||
|         $singleFormats.slideUp(); | ||||
|     } | ||||
|     else { | ||||
|         if ($("input[name=export-single-format]:checked").length === 0) { | ||||
|             $("input[name=export-single-format]:first").prop("checked", true); | ||||
|         } | ||||
|  | ||||
|         $subtreeFormats.slideUp(); | ||||
|         $singleFormats.slideDown(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| $('input[name=export-subtree-format]').on('change', function () { | ||||
|     if (this.value === 'opml') { | ||||
|         $opmlVersions.slideDown(); | ||||
|     } | ||||
|     else { | ||||
|         $opmlVersions.slideUp(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| function makeToast(id, message) { | ||||
|     return { | ||||
|         id: id, | ||||
|         title: "Export status", | ||||
|         message: message, | ||||
|         icon: "arrow-square-up-right" | ||||
|     }; | ||||
| } | ||||
|  | ||||
| ws.subscribeToMessages(async message => { | ||||
|     if (message.taskType !== 'export') { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (message.type === 'taskError') { | ||||
|         toastService.closePersistent(message.taskId); | ||||
|         toastService.showError(message.message); | ||||
|     } | ||||
|     else if (message.type === 'taskProgressCount') { | ||||
|         toastService.showPersistent(makeToast(message.taskId, "Export in progress: " + message.progressCount)); | ||||
|     } | ||||
|     else if (message.type === 'taskSucceeded') { | ||||
|         const toast = makeToast(message.taskId, "Export finished successfully."); | ||||
|         toast.closeAfter = 5000; | ||||
|  | ||||
|         toastService.showPersistent(toast); | ||||
|     } | ||||
| }); | ||||
| @@ -66,6 +66,7 @@ import AddLinkDialog from "../widgets/dialogs/add_link.js"; | ||||
| import CloneToDialog from "../widgets/dialogs/clone_to.js"; | ||||
| import MoveToDialog from "../widgets/dialogs/move_to.js"; | ||||
| import ImportDialog from "../widgets/dialogs/import.js"; | ||||
| import ExportDialog from "../widgets/dialogs/export.js"; | ||||
|  | ||||
| export default class DesktopLayout { | ||||
|     constructor(customWidgets) { | ||||
| @@ -206,6 +207,7 @@ export default class DesktopLayout { | ||||
|             .child(new AddLinkDialog()) | ||||
|             .child(new CloneToDialog()) | ||||
|             .child(new MoveToDialog()) | ||||
|             .child(new ImportDialog()); | ||||
|             .child(new ImportDialog()) | ||||
|             .child(new ExportDialog()); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -53,7 +53,10 @@ export default class NoteActionsWidget extends NoteContextAwareWidget { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             import('../../dialogs/export.js').then(d => d.showDialog(this.noteContext.notePath, 'single')); | ||||
|             this.triggerCommand("showExportDialog", { | ||||
|                 notePath: this.noteContext.notePath, | ||||
|                 defaultType: "single" | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         this.$importNoteButton = this.$widget.find('.import-files-button'); | ||||
|   | ||||
							
								
								
									
										256
									
								
								src/public/app/widgets/dialogs/export.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								src/public/app/widgets/dialogs/export.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | ||||
| import treeService from "../../services/tree.js"; | ||||
| import utils from "../../services/utils.js"; | ||||
| import ws from "../../services/ws.js"; | ||||
| import toastService from "../../services/toast.js"; | ||||
| import froca from "../../services/froca.js"; | ||||
| import openService from "../../services/open.js"; | ||||
| import BasicWidget from "../basic_widget.js"; | ||||
|  | ||||
| const TPL = ` | ||||
| <div class="export-dialog modal fade mx-auto" tabindex="-1" role="dialog"> | ||||
|     <style> | ||||
|     .export-form .form-check { | ||||
|         padding-top: 10px; | ||||
|         padding-bottom: 10px; | ||||
|     } | ||||
|      | ||||
|     .export-form .format-choice { | ||||
|         padding-left: 40px; | ||||
|         display: none; | ||||
|     } | ||||
|      | ||||
|     .export-form .opml-versions { | ||||
|         padding-left: 60px; | ||||
|         display: none; | ||||
|     } | ||||
|      | ||||
|     .export-form .form-check-label { | ||||
|         padding: 2px; | ||||
|     } | ||||
|     </style> | ||||
|  | ||||
|     <div class="modal-dialog modal-lg" role="document"> | ||||
|         <div class="modal-content"> | ||||
|             <div class="modal-header"> | ||||
|                 <h5 class="modal-title">Export note "<span class="export-note-title"></span>"</h5> | ||||
|                 <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||
|                     <span aria-hidden="true">×</span> | ||||
|                 </button> | ||||
|             </div> | ||||
|             <form class="export-form"> | ||||
|                 <div class="modal-body"> | ||||
|                     <div class="form-check"> | ||||
|                         <label class="form-check-label"> | ||||
|                             <input class="export-type-subtree form-check-input" type="radio" name="export-type" value="subtree"> | ||||
|                             this note and all of its descendants | ||||
|                         </label> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="export-subtree-formats format-choice"> | ||||
|                         <div class="form-check"> | ||||
|                             <label class="form-check-label"> | ||||
|                                 <input class="form-check-input" type="radio" name="export-subtree-format" value="html"> | ||||
|                                 HTML in ZIP archive - this is recommended since this preserves all the formatting. | ||||
|                             </label> | ||||
|                         </div> | ||||
|  | ||||
|                         <div class="form-check"> | ||||
|                             <label class="form-check-label"> | ||||
|                                 <input class="form-check-input" type="radio" name="export-subtree-format" value="markdown"> | ||||
|                                 Markdown - this preserves most of the formatting. | ||||
|                             </label> | ||||
|                         </div> | ||||
|  | ||||
|                         <div class="form-check"> | ||||
|                             <label class="form-check-label"> | ||||
|                                 <input class="form-check-input" type="radio" name="export-subtree-format" value="opml"> | ||||
|                                 OPML - outliner interchange format for text only. Formatting, images and files are not included. | ||||
|                             </label> | ||||
|                         </div> | ||||
|  | ||||
|                         <div class="opml-versions"> | ||||
|                             <div class="form-check"> | ||||
|                                 <label class="form-check-label"> | ||||
|                                     <input class="form-check-input" type="radio" name="opml-version" value="1.0"> | ||||
|                                     OPML v1.0 - plain text only | ||||
|                                 </label> | ||||
|                             </div> | ||||
|  | ||||
|                             <div class="form-check"> | ||||
|                                 <label class="form-check-label"> | ||||
|                                     <input class="form-check-input" type="radio" name="opml-version" value="2.0"> | ||||
|                                     OMPL v2.0 - allows also HTML | ||||
|                                 </label> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="form-check"> | ||||
|                         <label class="form-check-label"> | ||||
|                             <input class="form-check-input" type="radio" name="export-type" value="single"> | ||||
|                             only this note without its descendants | ||||
|                         </label> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="export-single-formats format-choice"> | ||||
|                         <div class="form-check"> | ||||
|                             <label class="form-check-label"> | ||||
|                                 <input class="form-check-input" type="radio" name="export-single-format" value="html"> | ||||
|                                 HTML - this is recommended since this preserves all the formatting. | ||||
|                             </label> | ||||
|                         </div> | ||||
|  | ||||
|                         <div class="form-check"> | ||||
|                             <label class="form-check-label"> | ||||
|                                 <input class="form-check-input" type="radio" name="export-single-format" value="markdown">                             | ||||
|                                 Markdown - this preserves most of the formatting. | ||||
|                             </label> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="modal-footer"> | ||||
|                     <button class="export-button btn btn-primary">Export</button> | ||||
|                 </div> | ||||
|             </form> | ||||
|         </div> | ||||
|     </div> | ||||
| </div>`; | ||||
|  | ||||
| export default class ExportDialog extends BasicWidget { | ||||
|     constructor() { | ||||
|         super(); | ||||
|  | ||||
|         this.taskId = ''; | ||||
|         this.branchId = null; | ||||
|     } | ||||
|  | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
|         this.$form = this.$widget.find(".export-form"); | ||||
|         this.$noteTitle = this.$widget.find(".export-note-title"); | ||||
|         this.$subtreeFormats = this.$widget.find(".export-subtree-formats"); | ||||
|         this.$singleFormats = this.$widget.find(".export-single-formats"); | ||||
|         this.$subtreeType = this.$widget.find(".export-type-subtree"); | ||||
|         this.$singleType = this.$widget.find(".export-type-single"); | ||||
|         this.$exportButton = this.$widget.find(".export-button"); | ||||
|         this.$opmlVersions = this.$widget.find(".opml-versions"); | ||||
|  | ||||
|         this.$form.on('submit', () => { | ||||
|             this.$widget.modal('hide'); | ||||
|  | ||||
|             const exportType = this.$widget.find("input[name='export-type']:checked").val(); | ||||
|  | ||||
|             if (!exportType) { | ||||
|                 // this shouldn't happen as we always choose default export type | ||||
|                 alert("Choose export type first please"); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const exportFormat = exportType === 'subtree' | ||||
|                 ? this.$widget.find("input[name=export-subtree-format]:checked").val() | ||||
|                 : this.$widget.find("input[name=export-single-format]:checked").val(); | ||||
|  | ||||
|             const exportVersion = exportFormat === 'opml' | ||||
|                 ? this.$widget.find("input[name='opml-version']:checked").val() | ||||
|                 : "1.0"; | ||||
|  | ||||
|             this.exportBranch(this.branchId, exportType, exportFormat, exportVersion); | ||||
|  | ||||
|             return false; | ||||
|         }); | ||||
|  | ||||
|         this.$widget.find('input[name=export-type]').on('change', e => { | ||||
|             if (e.currentTarget.value === 'subtree') { | ||||
|                 if (this.$widget.find("input[name=export-subtree-format]:checked").length === 0) { | ||||
|                     this.$widget.find("input[name=export-subtree-format]:first").prop("checked", true); | ||||
|                 } | ||||
|  | ||||
|                 this.$subtreeFormats.slideDown(); | ||||
|                 this.$singleFormats.slideUp(); | ||||
|             } | ||||
|             else { | ||||
|                 if (this.$widget.find("input[name=export-single-format]:checked").length === 0) { | ||||
|                     this.$widget.find("input[name=export-single-format]:first").prop("checked", true); | ||||
|                 } | ||||
|  | ||||
|                 this.$subtreeFormats.slideUp(); | ||||
|                 this.$singleFormats.slideDown(); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         this.$widget.find('input[name=export-subtree-format]').on('change', e => { | ||||
|             if (e.currentTarget.value === 'opml') { | ||||
|                 this.$opmlVersions.slideDown(); | ||||
|             } | ||||
|             else { | ||||
|                 this.$opmlVersions.slideUp(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     async showExportDialogEvent({notePath, defaultType}) { | ||||
|         // each opening of the dialog resets the taskId, so we don't associate it with previous exports anymore | ||||
|         this.taskId = ''; | ||||
|         this.$exportButton.removeAttr("disabled"); | ||||
|  | ||||
|         if (defaultType === 'subtree') { | ||||
|             this.$subtreeType.prop("checked", true).trigger('change'); | ||||
|  | ||||
|             // to show/hide OPML versions | ||||
|             this.$widget.find("input[name=export-subtree-format]:checked").trigger('change'); | ||||
|         } | ||||
|         else if (defaultType === 'single') { | ||||
|             this.$singleType.prop("checked", true).trigger('change'); | ||||
|         } | ||||
|         else { | ||||
|             throw new Error("Unrecognized type " + defaultType); | ||||
|         } | ||||
|  | ||||
|         this.$widget.find(".opml-v2").prop("checked", true); // setting default | ||||
|  | ||||
|         utils.openDialog(this.$widget); | ||||
|  | ||||
|         const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath); | ||||
|  | ||||
|         this.branchId = await froca.getBranchId(parentNoteId, noteId); | ||||
|  | ||||
|         const noteTitle = await treeService.getNoteTitle(noteId); | ||||
|  | ||||
|         this.$noteTitle.html(noteTitle); | ||||
|     } | ||||
|  | ||||
|     exportBranch(branchId, type, format, version) { | ||||
|         this.taskId = utils.randomString(10); | ||||
|  | ||||
|         const url = openService.getUrlForDownload(`api/notes/${branchId}/export/${type}/${format}/${version}/${this.taskId}`); | ||||
|  | ||||
|         openService.download(url); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ws.subscribeToMessages(async message => { | ||||
|     const makeToast = (id, message) => ({ | ||||
|         id: id, | ||||
|         title: "Export status", | ||||
|         message: message, | ||||
|         icon: "arrow-square-up-right" | ||||
|     }); | ||||
|  | ||||
|     if (message.taskType !== 'export') { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (message.type === 'taskError') { | ||||
|         toastService.closePersistent(message.taskId); | ||||
|         toastService.showError(message.message); | ||||
|     } | ||||
|     else if (message.type === 'taskProgressCount') { | ||||
|         toastService.showPersistent(makeToast(message.taskId, "Export in progress: " + message.progressCount)); | ||||
|     } | ||||
|     else if (message.type === 'taskSucceeded') { | ||||
|         const toast = makeToast(message.taskId, "Export finished successfully."); | ||||
|         toast.closeAfter = 5000; | ||||
|  | ||||
|         toastService.showPersistent(toast); | ||||
|     } | ||||
| }); | ||||
| @@ -1451,10 +1451,9 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | ||||
|     } | ||||
|  | ||||
|     async exportNoteCommand({node}) { | ||||
|         const exportDialog = await import('../dialogs/export.js'); | ||||
|         const notePath = treeService.getNotePath(node); | ||||
|  | ||||
|         exportDialog.showDialog(notePath,"subtree"); | ||||
|         this.triggerCommand("showExportDialog", {notePath, defaultType: "subtree"}); | ||||
|     } | ||||
|  | ||||
|     async importIntoNoteCommand({node}) { | ||||
|   | ||||
| @@ -540,25 +540,6 @@ div[data-notify="container"] { | ||||
|     font-size: 150%; | ||||
| } | ||||
|  | ||||
| #export-form .form-check { | ||||
|     padding-top: 10px; | ||||
|     padding-bottom: 10px; | ||||
| } | ||||
|  | ||||
| #export-form .format-choice { | ||||
|     padding-left: 40px; | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| #export-form #opml-versions { | ||||
|     padding-left: 60px; | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| #export-form .form-check-label { | ||||
|     padding: 2px; | ||||
| } | ||||
|  | ||||
| .ck-editor__is-empty.ck-content.ck-editor__editable::before { | ||||
|     content: 'You can start writing note here ...'; | ||||
|     position: absolute; | ||||
|   | ||||
| @@ -17,7 +17,6 @@ | ||||
|  | ||||
| <div class="dropdown-menu dropdown-menu-sm" id="context-menu-container"></div> | ||||
|  | ||||
| <%- include('dialogs/export.ejs') %> | ||||
| <%- include('dialogs/markdown_import.ejs') %> | ||||
| <%- include('dialogs/note_revisions.ejs') %> | ||||
| <%- include('dialogs/options.ejs') %> | ||||
|   | ||||
| @@ -1,79 +0,0 @@ | ||||
| <div id="export-dialog" class="modal fade mx-auto" tabindex="-1" role="dialog"> | ||||
|     <div class="modal-dialog modal-lg" role="document"> | ||||
|         <div class="modal-content"> | ||||
|             <div class="modal-header"> | ||||
|                 <h5 class="modal-title">Export note "<span class="export-note-title"></span>"</h5> | ||||
|                 <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||
|                     <span aria-hidden="true">×</span> | ||||
|                 </button> | ||||
|             </div> | ||||
|             <form id="export-form"> | ||||
|                 <div class="modal-body"> | ||||
|                     <div class="form-check"> | ||||
|                         <input class="form-check-input" type="radio" name="export-type" id="export-type-subtree" value="subtree"> | ||||
|                         <label class="form-check-label" for="export-type-subtree">this note and all of its descendants</label> | ||||
|                     </div> | ||||
|  | ||||
|                     <div id="export-subtree-formats" class="format-choice"> | ||||
|                         <div class="form-check"> | ||||
|                             <input class="form-check-input" type="radio" name="export-subtree-format" id="export-subtree-format-html" | ||||
|                                    value="html"> | ||||
|                             <label class="form-check-label" for="export-subtree-format-html">HTML in ZIP archive - this is recommended since this preserves all the formatting.</label> | ||||
|                         </div> | ||||
|  | ||||
|                         <div class="form-check"> | ||||
|                             <input class="form-check-input" type="radio" name="export-subtree-format" id="export-subtree-format-markdown" | ||||
|                                    value="markdown"> | ||||
|                             <label class="form-check-label" for="export-subtree-format-markdown"> | ||||
|                                 Markdown - this preserves most of the formatting. | ||||
|                             </label> | ||||
|                         </div> | ||||
|  | ||||
|                         <div class="form-check"> | ||||
|                             <input class="form-check-input" type="radio" name="export-subtree-format" id="export-subtree-format-opml" | ||||
|                                    value="opml"> | ||||
|                             <label class="form-check-label" for="export-subtree-format-opml"> | ||||
|                                 OPML - outliner interchange format for text only. Formatting, images and files are not included. | ||||
|                             </label> | ||||
|                         </div> | ||||
|  | ||||
|                         <div id="opml-versions"> | ||||
|                             <div class="form-check"> | ||||
|                                 <input class="form-check-input" type="radio" name="opml-version" id="opml-v1" value="1.0"> | ||||
|                                 <label class="form-check-label" for="opml-v1">OPML v1.0 - plain text only</label> | ||||
|                             </div> | ||||
|  | ||||
|                             <div class="form-check"> | ||||
|                                 <input class="form-check-input" type="radio" name="opml-version" id="opml-v2" value="2.0"> | ||||
|                                 <label class="form-check-label" for="opml-v2">OMPL v2.0 - allows also HTML</label> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="form-check"> | ||||
|                         <input class="form-check-input" type="radio" name="export-type" id="export-type-single" value="single"> | ||||
|                         <label class="form-check-label" for="export-type-single">only this note without its descendants</label> | ||||
|                     </div> | ||||
|  | ||||
|                     <div id="export-single-formats" class="format-choice"> | ||||
|                         <div class="form-check"> | ||||
|                             <input class="form-check-input" type="radio" name="export-single-format" id="export-single-format-html" value="html"> | ||||
|                             <label class="form-check-label" for="export-single-format-html">HTML - this is recommended since this preserves all the formatting.</label> | ||||
|                         </div> | ||||
|  | ||||
|                         <div class="form-check"> | ||||
|                             <input class="form-check-input" type="radio" name="export-single-format" id="export-single-format-markdown" | ||||
|                                    value="markdown"> | ||||
|                             <label class="form-check-label" for="export-single-format-markdown"> | ||||
|                                 Markdown - this preserves most of the formatting. | ||||
|                             </label> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="modal-footer"> | ||||
|                     <button class="btn btn-primary" id="export-button">Export</button> | ||||
|                 </div> | ||||
|             </form> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
		Reference in New Issue
	
	Block a user