editfile.html 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <!--
  2. Copyright (C) 2023 Nicola Murino
  3. This WebUI uses the KeenThemes Mega Bundle, a proprietary theme:
  4. https://keenthemes.com/products/templates-mega-bundle
  5. KeenThemes HTML/CSS/JS components are allowed for use only within the
  6. SFTPGo product and restricted to be used in a resealable HTML template
  7. that can compete with KeenThemes products anyhow.
  8. This WebUI is allowed for use only within the SFTPGo product and
  9. therefore cannot be used in derivative works/products without an
  10. explicit grant from the SFTPGo Team ([email protected]).
  11. -->
  12. {{template "base" .}}
  13. {{- define "extra_css"}}
  14. <style {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
  15. .shortcut {font-family: monospace; color: #666;}
  16. .cm-editor {
  17. height: 100%;
  18. width: 100%;
  19. }
  20. </style>
  21. {{- end}}
  22. {{- define "additionalnavitems"}}
  23. <div class="d-flex align-items-center ms-2 ms-lg-3" id="kt_header_user_menu_toggle">
  24. <div class="btn btn-icon btn-active-light-primary w-35px h-35px w-md-40px h-md-40px" data-bs-toggle="modal" data-bs-target="#info_modal">
  25. <i class="ki-duotone ki-information-2 fs-2">
  26. <i class="path1"></i>
  27. <i class="path2"></i>
  28. <i class="path3"></i>
  29. </i>
  30. </div>
  31. </div>
  32. {{- end}}
  33. {{- define "page_body"}}
  34. {{- template "errmsg" ""}}
  35. <div class="card shadow-sm">
  36. <div class="card-header">
  37. <h6 id="card_title" class="card-title section-title"></h6>
  38. <div class="card-toolbar">
  39. <a data-i18n="general.back" class="btn btn-light-primary px-10 me-5" href='{{.FilesURL}}?path={{.CurrentDir}}' role="button">Back</a>
  40. {{- if not .ReadOnly}}
  41. <a id="save_button" type="button" class="btn btn-primary px-10" href="#" role="button">
  42. <span data-i18n="general.submit" class="indicator-label">Save</span>
  43. <span data-i18n="general.wait" class="indicator-progress">
  44. Please wait...
  45. <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
  46. </span>
  47. </a>
  48. {{- end}}
  49. </div>
  50. </div>
  51. <div class="card-body">
  52. <div id="editor" class="col-sm-12 border border-light-primary"></div>
  53. </div>
  54. </div>
  55. {{- end}}
  56. {{- define "modals"}}
  57. <div class="modal fade" id="info_modal" tabindex="-1">
  58. <div class="modal-dialog">
  59. <div class="modal-content">
  60. <div class="modal-header">
  61. <h3 data-i18n="editor.keybinding" class="modal-title">Editor keybindings</h3>
  62. <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-light-primary" data-bs-dismiss="modal" aria-label="Close">
  63. <i class="ki-solid ki-cross fs-2x text-gray-700"></i>
  64. </div>
  65. </div>
  66. <div class="modal-body">
  67. <p>
  68. <span class="shortcut">Ctrl-F / Cmd-F</span> => <span data-i18n="editor.search" class="fw-semibold">Open search panel</span>
  69. </p>
  70. <p>
  71. <span class="shortcut">Alt-G</span> => <span data-i18n="editor.goto" class="fw-semibold">Jump to line</span>
  72. </p>
  73. <p>
  74. <span class="shortcut">Tab</span> => <span data-i18n="editor.indent_more" class="fw-semibold">Indent more</span>
  75. </p>
  76. <p>
  77. <span class="shortcut">Shift-Tab</span> => <span data-i18n="editor.indent_less" class="fw-semibold">Indent less</span>
  78. </p>
  79. </div>
  80. <div class="modal-footer">
  81. <button data-i18n="general.ok" class="btn btn-primary" type="button" data-bs-dismiss="modal">OK</button>
  82. </div>
  83. </div>
  84. </div>
  85. </div>
  86. {{- end}}
  87. {{- define "extra_js"}}
  88. <script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/vendor/codemirror/cm6.bundle.min.js"></script>
  89. <script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
  90. var cmView;
  91. function keepAlive() {
  92. axios.get('{{.PingURL}}',{
  93. timeout: 15000,
  94. responseType: 'text'
  95. }).catch(function (error){});
  96. }
  97. //{{- if not .ReadOnly}}
  98. function saveFile() {
  99. $('#errorMsg').addClass("d-none");
  100. let saveButton = document.querySelector('#save_button');
  101. saveButton.setAttribute('data-kt-indicator', 'on');
  102. saveButton.disabled = true;
  103. let uploadPath = '{{.FileURL}}?path='+encodeURIComponent('{{.CurrentDir}}/{{.Name}}');
  104. let blob = new Blob([cmView.state.doc.toString()]);
  105. axios.post(uploadPath, blob, {
  106. headers: {
  107. 'X-CSRF-TOKEN': '{{.CSRFToken}}'
  108. },
  109. timeout: 600000,
  110. validateStatus: function (status) {
  111. return status == 201;
  112. }
  113. }).then(function (response) {
  114. window.location.replace('{{.FilesURL}}?path='+encodeURIComponent('{{.CurrentDir}}'));
  115. }).catch(function (error) {
  116. saveButton.removeAttribute('data-kt-indicator');
  117. saveButton.disabled = false;
  118. let errorMessage = "";
  119. if (error && error.response) {
  120. switch (error.response.status) {
  121. case 403:
  122. errorMessage = "fs.save.err403";
  123. break;
  124. case 429:
  125. errorMessage = "fs.save.err429";
  126. break;
  127. }
  128. }
  129. if (!errorMessage){
  130. errorMessage = "fs.save.err_generic";
  131. }
  132. setI18NData($('#errorTxt'), errorMessage);
  133. $('#errorMsg').removeClass("d-none");
  134. });
  135. }
  136. //{{- end}}
  137. $(document).on("i18nload", function(){
  138. let message = 'fs.edit_file';
  139. //{{- if .ReadOnly}}
  140. message = 'fs.view_file';
  141. //{{- end}}
  142. setI18NData($('#card_title'), message, { path: '{{.Path}}'});
  143. });
  144. $(document).on("i18nshow", function(){
  145. let filename = "{{.Name}}";
  146. let extension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
  147. let options = {
  148. oneDark: KTThemeMode.getMode() == "dark",
  149. fileExt: extension
  150. };
  151. //{{- if .ReadOnly}}
  152. options.readOnly = true;
  153. //{{- end}}
  154. //{{- if .CSPNonce}}
  155. options.cspNonce = "{{.CSPNonce}}";
  156. //{{- end}}
  157. let editorState = cm6.createEditorState("{{.Data}}", options);
  158. cmView = cm6.createEditorView(editorState, document.getElementById("editor"));
  159. var saveBtn = $('#save_button');
  160. if (saveBtn){
  161. saveBtn.on("click", function(){
  162. saveFile();
  163. });
  164. }
  165. setInterval(keepAlive, 300000);
  166. });
  167. </script>
  168. {{- end}}