files.html 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. {{template "base" .}}
  2. {{define "title"}}{{.Title}}{{end}}
  3. {{define "extra_css"}}
  4. <link href="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.css" rel="stylesheet">
  5. <link href="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.css" rel="stylesheet">
  6. <link href="{{.StaticURL}}/vendor/datatables/fixedHeader.bootstrap4.min.css" rel="stylesheet">
  7. <link href="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.css" rel="stylesheet">
  8. <link href="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.css" rel="stylesheet">
  9. <link href="{{.StaticURL}}/vendor/lightbox2/css/lightbox.min.css" rel="stylesheet">
  10. <style>
  11. div.dataTables_wrapper span.selected-info,
  12. div.dataTables_wrapper span.selected-item {
  13. margin-left: 0.5em;
  14. }
  15. </style>
  16. {{end}}
  17. {{define "page_body"}}
  18. <div id="errorMsg" class="card mb-4 border-left-warning" style="display: none;">
  19. <div id="errorTxt" class="card-body text-form-error"></div>
  20. </div>
  21. <div class="card shadow mb-4">
  22. <div class="card-header py-3">
  23. <h6 class="m-0 font-weight-bold"><a href="{{.FilesURL}}?path=%2F"><i class="fas fa-home"></i>&nbsp;Home</a>&nbsp;{{range .Paths}}{{if eq .Href ""}}/{{.DirName}}{{else}}<a href="{{.Href}}">/{{.DirName}}</a>{{end}}{{end}}</h6>
  24. </div>
  25. <div class="card-body">
  26. {{if .Error}}
  27. <div class="card mb-4 border-left-warning">
  28. <div class="card-body text-form-error">{{.Error}}</div>
  29. </div>
  30. {{end}}
  31. <div class="table-responsive">
  32. <table class="table table-hover nowrap" id="dataTable" width="100%" cellspacing="0">
  33. <thead>
  34. <tr>
  35. <th></th>
  36. <th>Type</th>
  37. <th>Name</th>
  38. <th>Size</th>
  39. <th>Last modified</th>
  40. <th></th>
  41. <th></th>
  42. </tr>
  43. </thead>
  44. </table>
  45. </div>
  46. </div>
  47. </div>
  48. {{end}}
  49. {{define "dialog"}}
  50. <div class="modal fade" id="createDirModal" tabindex="-1" role="dialog" aria-labelledby="createDirModalLabel"
  51. aria-hidden="true">
  52. <div class="modal-dialog" role="document">
  53. <div class="modal-content">
  54. <div class="modal-header">
  55. <h5 class="modal-title" id="createDirModalLabel">
  56. Create a new directory
  57. </h5>
  58. <button class="close" type="button" data-dismiss="modal" aria-label="Close">
  59. <span aria-hidden="true">&times;</span>
  60. </button>
  61. </div>
  62. <form id="create_dir_form" action="" method="POST">
  63. <div class="modal-body">
  64. <div class="form-group">
  65. <label for="directory_name" class="col-form-label">Name</label>
  66. <input type="text" class="form-control" id="directory_name" required>
  67. </div>
  68. </div>
  69. <div class="modal-footer">
  70. <button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
  71. <button type="submit" class="btn btn-primary">Submit</button>
  72. </div>
  73. </form>
  74. </div>
  75. </div>
  76. </div>
  77. <div class="modal fade" id="uploadFilesModal" tabindex="-1" role="dialog" aria-labelledby="uploadFilesModalLabel"
  78. aria-hidden="true">
  79. <div class="modal-dialog" role="document">
  80. <div class="modal-content">
  81. <div class="modal-header">
  82. <h5 class="modal-title" id="uploadFilesModalLabel">
  83. Upload one or more files
  84. </h5>
  85. <button class="close" type="button" data-dismiss="modal" aria-label="Close">
  86. <span aria-hidden="true">&times;</span>
  87. </button>
  88. </div>
  89. <form id="upload_files_form" action="{{.FilesURL}}?path={{.CurrentDir}}" method="POST" enctype="multipart/form-data">
  90. <div class="modal-body">
  91. <input type="file" class="form-control-file" id="files_name" name="filenames" required multiple>
  92. </div>
  93. <div class="modal-footer">
  94. <input type="hidden" name="_form_token" value="{{.CSRFToken}}">
  95. <button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
  96. <button type="submit" class="btn btn-primary">Submit</button>
  97. </div>
  98. </form>
  99. </div>
  100. </div>
  101. </div>
  102. <div class="modal fade" id="renameModal" tabindex="-1" role="dialog" aria-labelledby="renameModalLabel"
  103. aria-hidden="true">
  104. <div class="modal-dialog" role="document">
  105. <div class="modal-content">
  106. <div class="modal-header">
  107. <h5 class="modal-title" id="renameModalLabel">
  108. Rename the selected item
  109. </h5>
  110. <button class="close" type="button" data-dismiss="modal" aria-label="Close">
  111. <span aria-hidden="true">&times;</span>
  112. </button>
  113. </div>
  114. <form id="rename_form" action="" method="POST">
  115. <div class="modal-body">
  116. <div class="form-group">
  117. <label for="rename_old_name" class="col-form-label">Old name</label>
  118. <input type="text" class="form-control" id="rename_old_name" readonly>
  119. </div>
  120. <div class="form-group">
  121. <label for="rename_new_name" class="col-form-label">New name</label>
  122. <input type="text" class="form-control" id="rename_new_name" required>
  123. </div>
  124. </div>
  125. <div class="modal-footer">
  126. <button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
  127. <button type="submit" class="btn btn-primary">Submit</button>
  128. </div>
  129. </form>
  130. </div>
  131. </div>
  132. </div>
  133. <div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel"
  134. aria-hidden="true">
  135. <div class="modal-dialog" role="document">
  136. <div class="modal-content">
  137. <div class="modal-header">
  138. <h5 class="modal-title" id="deleteModalLabel">
  139. Confirmation required
  140. </h5>
  141. <button class="close" type="button" data-dismiss="modal" aria-label="Close">
  142. <span aria-hidden="true">&times;</span>
  143. </button>
  144. </div>
  145. <div class="modal-body">Do you want to delete the selected item/s?</div>
  146. <div class="modal-footer">
  147. <button class="btn btn-secondary" type="button" data-dismiss="modal">
  148. Cancel
  149. </button>
  150. <a class="btn btn-warning" href="#" onclick="deleteAction()">
  151. Delete
  152. </a>
  153. </div>
  154. </div>
  155. </div>
  156. </div>
  157. <div class="modal fade" id="spinnerModal" tabindex="-1" role="dialog" data-keyboard="false" data-backdrop="static">
  158. <div class="modal-dialog modal-dialog-centered justify-content-center" role="document">
  159. <span style="color: #333333;" class="fa fa-spinner fa-spin fa-3x"></span>
  160. <!-- <span id="uploadProgress" style="color: #3A3B3C;" class="mx-3"></span> -->
  161. </div>
  162. </div>
  163. {{end}}
  164. {{define "extra_js"}}
  165. <script src="{{.StaticURL}}/vendor/datatables/jquery.dataTables.min.js"></script>
  166. <script src="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.js"></script>
  167. <script src="{{.StaticURL}}/vendor/datatables/dataTables.buttons.min.js"></script>
  168. <script src="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.js"></script>
  169. <script src="{{.StaticURL}}/vendor/datatables/dataTables.fixedHeader.min.js"></script>
  170. <script src="{{.StaticURL}}/vendor/datatables/dataTables.responsive.min.js"></script>
  171. <script src="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.js"></script>
  172. <script src="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.min.js"></script>
  173. <script src="{{.StaticURL}}/vendor/lightbox2/js/lightbox.min.js"></script>
  174. <script src="{{.StaticURL}}/vendor/pdfobject/pdfobject.min.js"></script>
  175. <script src="{{.StaticURL}}/vendor/codemirror/codemirror.js"></script>
  176. <script src="{{.StaticURL}}/vendor/codemirror/meta.js"></script>
  177. {{if .HasIntegrations}}
  178. <script type="text/javascript">
  179. var childReference = null;
  180. var checkerStarted = false;
  181. const childProps = new Map();
  182. function openExternalURL(url, fileLink, fileName){
  183. if (childReference == null || childReference.closed) {
  184. childProps.set('link', fileLink);
  185. childProps.set('url', url);
  186. childProps.set('file_name', fileName);
  187. childReference = window.open(url, '_blank');
  188. if (!checkerStarted){
  189. keepAlive();
  190. setInterval(checkExternalWindow, 300000);
  191. checkerStarted = true;
  192. }
  193. } else {
  194. $('#errorTxt').text('An external window is already open, please close it before trying to open a new one');
  195. $('#errorMsg').show();
  196. setTimeout(function () {
  197. $('#errorMsg').hide();
  198. }, 8000);
  199. }
  200. }
  201. function notifySave(status, message){
  202. if (childReference == null || childReference.closed) {
  203. console.log("external window null or closed, cannot notify save");
  204. return;
  205. }
  206. childReference.postMessage({
  207. type: 'blobSaveResult',
  208. status: status,
  209. message: message
  210. }, childProps.get('url'));
  211. }
  212. window.addEventListener('message', (event) => {
  213. var url = childProps.get('url');
  214. if (!url || !url.startsWith(event.origin)){
  215. console.log("origin: "+event.origin+" does not match the expected one: "+url+" refusing message");
  216. return;
  217. }
  218. if (childReference == null || childReference.closed) {
  219. console.log("external window null or closed, refusing message");
  220. return;
  221. }
  222. switch (event.data.type){
  223. case 'ready':
  224. // the child is ready send some details
  225. childReference.postMessage({
  226. type: 'readyResponse',
  227. user: '{{.LoggedUser.Username}}',
  228. file_name: childProps.get('file_name')
  229. }, childProps.get('url'));
  230. break;
  231. case 'sendBlob':
  232. // we have to download the blob, this can require some time so
  233. // we first send a blobDownloadStart message so the child can
  234. // show a spinner or something similar
  235. childReference.postMessage({
  236. type: 'blobDownloadStart'
  237. }, childProps.get('url'));
  238. // download the file and send as blob to the child window
  239. fetch(childProps.get('link'))
  240. .then(response => response.blob())
  241. .then(function(responseBlob){
  242. let fileBlob = new File([responseBlob], childProps.get('file_name'), {type: responseBlob.type, lastModified: ""});
  243. childReference.postMessage({
  244. type: 'blob',
  245. file: fileBlob
  246. }, childProps.get('url'));
  247. });
  248. break;
  249. case 'saveBlob':
  250. // get the blob from the message and save it
  251. var path = '{{.FilesURL}}?path={{.CurrentDir}}';
  252. spinnerDone = false;
  253. var file = new File([event.data.file], childProps.get('file_name'));
  254. var data = new FormData();
  255. data.append('filenames', file);
  256. $.ajax({
  257. url: path,
  258. type: 'POST',
  259. data: data,
  260. processData: false,
  261. contentType: false,
  262. headers: { 'X-CSRF-TOKEN': '{{.CSRFToken}}' },
  263. timeout: 0,
  264. beforeSend: function () {
  265. $('#spinnerModal').modal('show');
  266. },
  267. success: function (result) {
  268. $('#spinnerModal').modal('hide');
  269. notifySave("OK", "");
  270. setTimeout(function () {
  271. location.reload();
  272. }, 2000);
  273. },
  274. error: function ($xhr, textStatus, errorThrown) {
  275. $('#spinnerModal').modal('hide');
  276. var txt = "Error saving external file";
  277. if ($xhr) {
  278. var json = $xhr.responseJSON;
  279. if (json) {
  280. if (json.message) {
  281. txt = json.message;
  282. }
  283. if (json.error) {
  284. txt += ": " + json.error;
  285. }
  286. }
  287. }
  288. notifySave("KO", txt);
  289. $('#errorTxt').text(txt);
  290. $('#errorMsg').show();
  291. setTimeout(function () {
  292. $('#errorMsg').hide();
  293. }, 5000);
  294. }
  295. });
  296. break;
  297. default:
  298. console.log("Unsupported message: "+JSON.stringify(event.data));
  299. }
  300. });
  301. function checkExternalWindow() {
  302. if (childReference == null || childReference.closed) {
  303. return;
  304. }
  305. keepAlive();
  306. }
  307. </script>
  308. {{end}}
  309. <script type="text/javascript">
  310. var spinnerDone = false;
  311. function getIconForFile(filename) {
  312. var extension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
  313. switch (extension) {
  314. case "doc":
  315. case "docx":
  316. case "odt":
  317. case "wps":
  318. return "far fa-file-word";
  319. case "ppt":
  320. case "pptx":
  321. return "far fa-file-powerpoint";
  322. case "xls":
  323. case "xlsx":
  324. case "ods":
  325. return "far fa-file-excel";
  326. case "pdf":
  327. return "far fa-file-pdf";
  328. case "webm":
  329. case "mkv":
  330. case "flv":
  331. case "vob":
  332. case "ogv":
  333. case "ogg":
  334. case "avi":
  335. case "ts":
  336. case "mov":
  337. case "wmv":
  338. case "asf":
  339. case "mpeg":
  340. case "mpv":
  341. case "3gp":
  342. return "far fa-file-video";
  343. case "jpeg":
  344. case "jpg":
  345. case "png":
  346. case "gif":
  347. case "webp":
  348. case "tiff":
  349. case "psd":
  350. case "bmp":
  351. case "svg":
  352. case "jp2":
  353. return "far fa-file-image";
  354. case "go":
  355. case "sh":
  356. case "bat":
  357. case "java":
  358. case "php":
  359. case "cs":
  360. case "asp":
  361. case "aspx":
  362. case "css":
  363. case "html":
  364. case "xhtml":
  365. case "htm":
  366. case "js":
  367. case "jsp":
  368. case "py":
  369. case "rb":
  370. case "cgi":
  371. case "c":
  372. case "cpp":
  373. case "h":
  374. case "hpp":
  375. case "kt":
  376. case "ktm":
  377. case "kts":
  378. case "swift":
  379. case "r":
  380. return "far fa-file-code";
  381. case "zip":
  382. case "zipx":
  383. case "rar":
  384. case "tar":
  385. case "gz":
  386. case "bz2":
  387. case "zstd":
  388. case "zst":
  389. case "sz":
  390. case "lz":
  391. case "lz4":
  392. case "xz":
  393. case "jar":
  394. return "far fa-file-archive";
  395. case "txt":
  396. case "rtf":
  397. case "json":
  398. case "xml":
  399. case "yaml":
  400. case "toml":
  401. case "log":
  402. case "csv":
  403. case "ini":
  404. case "cfg":
  405. return "far fa-file-alt";
  406. default:
  407. return "far fa-file";
  408. }
  409. }
  410. function getNameFromMeta(meta) {
  411. return meta.split('_').slice(1).join('_');
  412. }
  413. function getTypeFromMeta(meta) {
  414. return meta.split('_')[0];
  415. }
  416. function deleteAction() {
  417. var table = $('#dataTable').DataTable();
  418. table.button('delete:name').enable(false);
  419. var selectedItems = table.column(0).checkboxes.selected()
  420. var has_errors = false;
  421. var index = 0;
  422. var success = 0;
  423. spinnerDone = false;
  424. $('#deleteModal').modal('hide');
  425. $('#spinnerModal').modal('show');
  426. function deleteItem() {
  427. if (index >= selectedItems.length || has_errors){
  428. $('#spinnerModal').modal('hide');
  429. spinnerDone = true;
  430. if (!has_errors){
  431. location.reload();
  432. }
  433. return;
  434. }
  435. var selected = selectedItems[index];
  436. var itemType = getTypeFromMeta(selected);
  437. var itemName = getNameFromMeta(selected);
  438. var path;
  439. if (itemType == "1"){
  440. path = '{{.DirsURL}}';
  441. } else {
  442. path = '{{.FilesURL}}';
  443. }
  444. path+='?path={{.CurrentDir}}'+fixedEncodeURIComponent("/"+itemName);
  445. $.ajax({
  446. url: path,
  447. type: 'DELETE',
  448. dataType: 'json',
  449. headers: { 'X-CSRF-TOKEN': '{{.CSRFToken}}' },
  450. timeout: 60000,
  451. success: function (result) {
  452. index++;
  453. success++;
  454. deleteItem();
  455. },
  456. error: function ($xhr, textStatus, errorThrown) {
  457. index++;
  458. has_errors = true;
  459. var txt = "Unable to delete the selected item/s";
  460. if (success > 0){
  461. txt = "Not all the selected items have been deleted, please reload the page";
  462. }
  463. if ($xhr) {
  464. var json = $xhr.responseJSON;
  465. if (json) {
  466. if (json.message) {
  467. txt = json.message;
  468. }
  469. if (json.error) {
  470. txt += ": " + json.error;
  471. }
  472. }
  473. }
  474. $('#errorTxt').text(txt);
  475. $('#errorMsg').show();
  476. setTimeout(function () {
  477. $('#errorMsg').hide();
  478. }, 10000);
  479. deleteItem();
  480. }
  481. });
  482. }
  483. deleteItem();
  484. }
  485. function keepAlive() {
  486. $.ajax({
  487. url: '{{.ProfileURL}}',
  488. timeout: 15000
  489. });
  490. }
  491. $(document).ready(function () {
  492. $('#spinnerModal').on('shown.bs.modal', function () {
  493. if (spinnerDone){
  494. $('#spinnerModal').modal('hide');
  495. }
  496. });
  497. $("#create_dir_form").submit(function (event) {
  498. event.preventDefault();
  499. $('#createDirModal').modal('hide');
  500. var dirName = replaceSlash($("#directory_name").val());
  501. var path = '{{.DirsURL}}?path={{.CurrentDir}}' + fixedEncodeURIComponent("/"+dirName);
  502. $.ajax({
  503. url: path,
  504. type: 'POST',
  505. dataType: 'json',
  506. headers: { 'X-CSRF-TOKEN': '{{.CSRFToken}}' },
  507. timeout: 15000,
  508. success: function (result) {
  509. location.reload();
  510. },
  511. error: function ($xhr, textStatus, errorThrown) {
  512. var txt = "Unable to create the requested directory";
  513. if ($xhr) {
  514. var json = $xhr.responseJSON;
  515. if (json) {
  516. if (json.message) {
  517. txt = json.message;
  518. }
  519. if (json.error) {
  520. txt += ": " + json.error;
  521. }
  522. }
  523. }
  524. $('#errorTxt').text(txt);
  525. $('#errorMsg').show();
  526. setTimeout(function () {
  527. $('#errorMsg').hide();
  528. }, 5000);
  529. }
  530. });
  531. });
  532. $("#upload_files_form").submit(function (event){
  533. event.preventDefault();
  534. keepAlive();
  535. var keepAliveTimer = setInterval(keepAlive, 300000);
  536. var path = '{{.FilesURL}}?path={{.CurrentDir}}';
  537. var files = $("#files_name")[0].files;
  538. var has_errors = false;
  539. var index = 0;
  540. var success = 0;
  541. spinnerDone = false;
  542. $('#uploadFilesModal').modal('hide');
  543. $('#spinnerModal').modal('show');
  544. function uploadFile() {
  545. if (index >= files.length || has_errors){
  546. //console.log("upload done, index: "+index+" has errors: "+has_errors+" ok: "+success);
  547. clearInterval(keepAliveTimer);
  548. $('#spinnerModal').modal('hide');
  549. spinnerDone = true;
  550. if (!has_errors){
  551. location.reload();
  552. }
  553. return;
  554. }
  555. //console.log("upload file, index: "+index);
  556. var data = new FormData();
  557. data.append('filenames', files[index]);
  558. $.ajax({
  559. url: path,
  560. type: 'POST',
  561. data: data,
  562. processData: false,
  563. contentType: false,
  564. headers: { 'X-CSRF-TOKEN': '{{.CSRFToken}}' },
  565. timeout: 0,
  566. success: function (result) {
  567. index++;
  568. success++;
  569. uploadFile();
  570. },
  571. error: function ($xhr, textStatus, errorThrown) {
  572. index++;
  573. has_errors = true;
  574. var txt = "Error uploading files";
  575. if (success > 0){
  576. txt = "Not all files have been uploaded, please reload the page";
  577. }
  578. if ($xhr) {
  579. var json = $xhr.responseJSON;
  580. if (json) {
  581. if (json.message) {
  582. txt = json.message;
  583. }
  584. if (json.error) {
  585. txt += ": " + json.error;
  586. }
  587. }
  588. }
  589. $('#errorTxt').text(txt);
  590. $('#errorMsg').show();
  591. setTimeout(function () {
  592. $('#errorMsg').hide();
  593. }, 10000);
  594. uploadFile();
  595. }
  596. });
  597. }
  598. uploadFile();
  599. });
  600. $("#rename_form").submit(function (event){
  601. event.preventDefault();
  602. var table = $('#dataTable').DataTable();
  603. table.button('rename:name').enable(false);
  604. var selected = table.column(0).checkboxes.selected()[0];
  605. var itemType = getTypeFromMeta(selected);
  606. var itemName = getNameFromMeta(selected);
  607. var targetName = replaceSlash($("#rename_new_name").val());
  608. var path;
  609. if (itemType == "1"){
  610. path = '{{.DirsURL}}';
  611. } else {
  612. path = '{{.FilesURL}}';
  613. }
  614. path+='?path={{.CurrentDir}}'+fixedEncodeURIComponent("/"+itemName)+'&target={{.CurrentDir}}'+fixedEncodeURIComponent("/"+targetName);
  615. $('renameModal').modal('hide');
  616. $.ajax({
  617. url: path,
  618. type: 'PATCH',
  619. dataType: 'json',
  620. headers: { 'X-CSRF-TOKEN': '{{.CSRFToken}}' },
  621. timeout: 15000,
  622. success: function (result) {
  623. location.reload();
  624. },
  625. error: function ($xhr, textStatus, errorThrown) {
  626. var txt = "Error renaming item";
  627. if ($xhr) {
  628. var json = $xhr.responseJSON;
  629. if (json) {
  630. if (json.message) {
  631. txt = json.message;
  632. }
  633. if (json.error) {
  634. txt += ": " + json.error;
  635. }
  636. }
  637. }
  638. $('#errorTxt').text(txt);
  639. $('#errorMsg').show();
  640. setTimeout(function () {
  641. $('#errorMsg').hide();
  642. }, 5000);
  643. }
  644. });
  645. });
  646. $.fn.dataTable.ext.buttons.refresh = {
  647. text: '<i class="fas fa-sync-alt"></i>',
  648. name: 'refresh',
  649. titleAttr: "Refresh",
  650. action: function (e, dt, node, config) {
  651. location.reload();
  652. }
  653. };
  654. $.fn.dataTable.ext.buttons.download = {
  655. text: '<i class="fas fa-download"></i>',
  656. name: 'download',
  657. titleAttr: "Download Zip",
  658. action: function (e, dt, node, config) {
  659. var filesArray = [];
  660. var selected = dt.column(0).checkboxes.selected();
  661. for (i = 0; i < selected.length; i++) {
  662. filesArray.push(getNameFromMeta(selected[i]));
  663. }
  664. var files = fixedEncodeURIComponent(JSON.stringify(filesArray));
  665. var downloadURL = '{{.DownloadURL}}';
  666. var currentDir = '{{.CurrentDir}}';
  667. var ts = new Date().getTime().toString();
  668. window.location = `${downloadURL}?path=${currentDir}&files=${files}&_=${ts}`;
  669. },
  670. enabled: false
  671. };
  672. $.fn.dataTable.ext.buttons.addFiles = {
  673. text: '<i class="fas fa-file-upload"></i>',
  674. name: 'addFiles',
  675. titleAttr: "Upload files",
  676. action: function (e, dt, node, config) {
  677. document.getElementById("files_name").value = null;
  678. $('#uploadFilesModal').modal('show');
  679. },
  680. enabled: true
  681. };
  682. $.fn.dataTable.ext.buttons.addDirectory = {
  683. text: '<i class="fas fa-folder-plus"></i>',
  684. name: 'addDirectory',
  685. titleAttr: "Add directory",
  686. action: function (e, dt, node, config) {
  687. $("#directory_name").val("");
  688. $('#createDirModal').modal('show');
  689. },
  690. enabled: true
  691. };
  692. $.fn.dataTable.ext.buttons.rename = {
  693. text: '<i class="fas fa-pen"></i>',
  694. name: 'rename',
  695. titleAttr: "Rename",
  696. action: function (e, dt, node, config) {
  697. var selected = table.column(0).checkboxes.selected()[0];
  698. var itemName = getNameFromMeta(selected);
  699. $("#rename_old_name").val(itemName);
  700. $("#rename_new_name").val("");
  701. $('#renameModal').modal('show');
  702. },
  703. enabled: false
  704. };
  705. $.fn.dataTable.ext.buttons.delete = {
  706. text: '<i class="fas fa-trash"></i>',
  707. name: 'delete',
  708. titleAttr: "Delete",
  709. action: function (e, dt, node, config) {
  710. $('#deleteModal').modal('show');
  711. },
  712. enabled: false
  713. };
  714. $.fn.dataTable.ext.buttons.share = {
  715. text: '<i class="fas fa-share-alt"></i>',
  716. name: 'share',
  717. titleAttr: "Share",
  718. action: function (e, dt, node, config) {
  719. var filesArray = [];
  720. var selected = dt.column(0).checkboxes.selected();
  721. for (i = 0; i < selected.length; i++) {
  722. filesArray.push(getNameFromMeta(selected[i]));
  723. }
  724. var files = fixedEncodeURIComponent(JSON.stringify(filesArray));
  725. var shareURL = '{{.ShareURL}}';
  726. var currentDir = '{{.CurrentDir}}';
  727. var ts = new Date().getTime().toString();
  728. window.location = `${shareURL}?path=${currentDir}&files=${files}&_=${ts}`;
  729. },
  730. enabled: false
  731. };
  732. var table = $('#dataTable').DataTable({
  733. "ajax": {
  734. "url": "{{.DirsURL}}?path={{.CurrentDir}}",
  735. "dataSrc": "",
  736. "error": function ($xhr, textStatus, errorThrown) {
  737. $(".dataTables_processing").hide();
  738. var txt = "Failed to get directory listing";
  739. if ($xhr) {
  740. var json = $xhr.responseJSON;
  741. if (json) {
  742. if (json.message){
  743. txt += ": " + json.message;
  744. } else {
  745. txt += ": " + json.error;
  746. }
  747. }
  748. }
  749. $('#errorTxt').text(txt);
  750. $('#errorMsg').show();
  751. setTimeout(function () {
  752. $('#errorMsg').hide();
  753. }, 10000);
  754. }
  755. },
  756. "deferRender": true,
  757. "processing": true,
  758. "lengthMenu": [ 10, 25, 50, 100, 250, 500 ],
  759. "stateSave": true,
  760. "stateDuration": 0,
  761. "stateSaveParams": function (settings, data) {
  762. data.sftpgo_dir = '{{.CurrentDir}}';
  763. },
  764. "stateLoadParams": function (settings, data) {
  765. if (!data.sftpgo_dir || data.sftpgo_dir != '{{.CurrentDir}}'){
  766. data.start = 0;
  767. data.search.search = "";
  768. }
  769. },
  770. "columns": [
  771. { "data": "meta" },
  772. { "data": "type" },
  773. {
  774. "data": "name",
  775. "render": function (data, type, row) {
  776. if (type === 'display') {
  777. if (row["type"] == "1") {
  778. return `<i class="fas fa-folder"></i>&nbsp;<a href="${row['url']}">${data}</a>`;
  779. }
  780. if (row["size"] == "") {
  781. return `<i class="fas fa-external-link-alt"></i>&nbsp;<a href="${row['url']}">${data}</a>`;
  782. }
  783. var icon = getIconForFile(data);
  784. return `<i class="${icon}"></i>&nbsp;<a href="${row['url']}">${data}</a>`;
  785. }
  786. return data;
  787. }
  788. },
  789. { "data": "size" },
  790. { "data": "last_modified" },
  791. { "data": "edit_url",
  792. "render": function (data, type, row) {
  793. if (type === 'display') {
  794. var filename = row["name"];
  795. var extension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
  796. if (data){
  797. if (extension == "csv" || extension == "bat" || CodeMirror.findModeByExtension(extension) != null){
  798. {{if .CanAddFiles}}
  799. return `<a href="${data}"><i class="fas fa-edit"></i></a>`;
  800. {{else}}
  801. return `<a href="${data}"><i class="fas fa-eye"></i></a>`;
  802. {{end}}
  803. }
  804. }
  805. if (row["type"] == "2") {
  806. switch (extension) {
  807. case "jpeg":
  808. case "jpg":
  809. case "png":
  810. case "gif":
  811. case "webp":
  812. case "bmp":
  813. case "svg":
  814. case "ico":
  815. var view_url = row['url']+"&inline=1";
  816. return `<a href="${view_url}" data-lightbox="${filename}" data-title="${filename}"><i class="fas fa-eye"></i></a>`;
  817. case "pdf":
  818. if (PDFObject.supportsPDFs){
  819. var view_url = row['url'];
  820. view_url = view_url.replace('{{.FilesURL}}','{{.ViewPDFURL}}');
  821. return `<a href="${view_url}" target="_blank"><i class="fas fa-eye"></i></a>`;
  822. }
  823. }
  824. }
  825. }
  826. return "";
  827. }
  828. },
  829. { "data": "ext_url",
  830. "render": function (data, type, row) {
  831. {{if .HasIntegrations}}
  832. if (type === 'display') {
  833. if (data){
  834. return `<a href="#" onclick="openExternalURL('${data}', '${row["url"]}', '${row["name"]}');"><i class="fas fa-external-link-alt"></i></a>`;
  835. }
  836. }
  837. {{end}}
  838. return "";
  839. }
  840. }
  841. ],
  842. "buttons": [],
  843. "lengthChange": false,
  844. "columnDefs": [
  845. {
  846. "targets": [0],
  847. "checkboxes": {
  848. "selectCallback": function (nodes, selected) {
  849. var selectedItems = table.column(0).checkboxes.selected().length;
  850. var selectedText = "";
  851. if (selectedItems == 1) {
  852. selectedText = "1 item selected";
  853. } else if (selectedItems > 1) {
  854. selectedText = `${selectedItems} items selected`;
  855. }
  856. {{if .CanDownload}}
  857. table.button('download:name').enable(selectedItems > 0);
  858. {{end}}
  859. {{if .CanRename}}
  860. table.button('rename:name').enable(selectedItems == 1);
  861. {{end}}
  862. {{if .CanDelete}}
  863. table.button('delete:name').enable(selectedItems > 0);
  864. {{end}}
  865. {{if .CanShare}}
  866. table.button('share:name').enable(selectedItems > 0);
  867. {{end}}
  868. $('#dataTable_info').find('span').remove();
  869. $("#dataTable_info").append('<span class="selected-info"><span class="selected-item">' + selectedText + '</span></span>');
  870. }
  871. },
  872. "orderable": false,
  873. "searchable": false
  874. },
  875. {
  876. "targets": [1],
  877. "visible": false,
  878. "searchable": false
  879. },
  880. {
  881. "targets": [3, 4],
  882. "searchable": false
  883. },
  884. {
  885. "targets": [5, 6],
  886. "orderable": false,
  887. "searchable": false
  888. }
  889. ],
  890. "scrollX": false,
  891. "scrollY": false,
  892. "responsive": true,
  893. "language": {
  894. "processing": '<i class="fas fa-spinner fa-spin fa-3x fa-fw"></i><span class="sr-only">Loading...</span>',
  895. "loadingRecords": "",
  896. "emptyTable": "No files or folders"
  897. },
  898. "initComplete": function (settings, json) {
  899. table.button().add(0, 'refresh');
  900. table.button().add(0, 'pageLength');
  901. {{if .CanShare}}
  902. table.button().add(0, 'share');
  903. {{end}}
  904. {{if .CanDownload}}
  905. table.button().add(0, 'download');
  906. {{end}}
  907. {{if .CanDelete}}
  908. table.button().add(0, 'delete');
  909. {{end}}
  910. {{if .CanRename}}
  911. table.button().add(0, 'rename');
  912. {{end}}
  913. {{if .CanCreateDirs}}
  914. table.button().add(0, 'addDirectory');
  915. {{end}}
  916. {{if .CanAddFiles}}
  917. table.button().add(0, 'addFiles');
  918. {{end}}
  919. table.buttons().container().appendTo('.col-md-6:eq(0)', table.table().container());
  920. },
  921. "orderFixed": [1, 'asc'],
  922. "order": [[2, 'asc']]
  923. });
  924. new $.fn.dataTable.FixedHeader(table);
  925. $.fn.dataTable.ext.errMode = 'none';
  926. });
  927. </script>
  928. {{end}}