files.html 92 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067
  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 (support@sftpgo.com).
  11. -->
  12. {{template "base" .}}
  13. {{- define "page_body"}}
  14. {{- template "errmsg" .Error}}
  15. <div class="card card-flush shadow-sm">
  16. <div class="card-header pt-8">
  17. <div class="card-title">
  18. <div class="d-flex align-items-center position-relative my-1">
  19. <i class="ki-duotone ki-magnifier fs-1 position-absolute ms-6">
  20. <span class="path1"></span>
  21. <span class="path2"></span>
  22. </i>
  23. <input data-i18n="[placeholder]general.search" type="text" data-kt-filemanager-table-filter="search" class="form-control form-control-solid w-250px ps-15" placeholder="Search Files & Folders" />
  24. </div>
  25. </div>
  26. <div class="card-toolbar">
  27. <div class="d-flex justify-content-end" data-kt-filemanager-table-toolbar="base">
  28. {{- if .CanCreateDirs}}
  29. <button id="id_create_dir_button" data-i18n="fs.new_folder" type="button" class="btn btn-flex btn-light-primary me-3">
  30. <i class="ki-duotone ki-add-folder fs-2">
  31. <span class="path1"></span>
  32. <span class="path2"></span>
  33. </i>
  34. New Folder
  35. </button>
  36. {{- end}}
  37. {{- if .CanAddFiles}}
  38. <button type="button" data-i18n="fs.upload.text" class="btn btn-flex btn-primary" data-bs-toggle="modal" data-bs-target="#modal_upload">
  39. <i class="ki-duotone ki-folder-up fs-2">
  40. <span class="path1"></span>
  41. <span class="path2"></span>
  42. </i>
  43. Upload Files
  44. </button>
  45. {{- end}}
  46. </div>
  47. <div class="d-flex justify-content-end align-items-center d-none" data-kt-filemanager-table-toolbar="selected">
  48. <div class="fw-bold me-5">
  49. <span class="me-2" data-kt-filemanager-table-select="selected_count"></span>
  50. </div>
  51. <div class="form-check form-switch form-check-custom form-check-solid me-5" data-kt-filemanager-table-select="select_all_pages_container">
  52. <input class="form-check-input" type="checkbox" id="id_select_all_pages" data-kt-filemanager-table-select="select_all_pages" />
  53. <label data-i18n="fs.select_across_pages" class="form-check-label fw-semibold text-gray-900" for="id_select_all_pages">
  54. Select across pages
  55. </label>
  56. </div>
  57. {{- if or .CanDownload .CanDelete}}
  58. <div>
  59. <button data-i18n="fs.actions" type="button" class="btn btn-light-primary rotate" data-kt-menu-trigger="click" data-kt-menu-placement="bottom">
  60. Actions
  61. <i class="ki-duotone ki-down fs-3 rotate-180 ms-3 me-0"></i>
  62. </button>
  63. <div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-800 menu-state-bg-light-primary fw-semibold w-auto min-w-200 mw-300px py-4" data-kt-menu="true">
  64. {{- if .CanDownload}}
  65. <div class="menu-item px-3">
  66. <a data-i18n="fs.download" href="#" class="menu-link px-3 fs-6" data-kt-filemanager-table-select="download_selected">
  67. Download
  68. </a>
  69. </div>
  70. {{- end}}
  71. {{- if not .ShareUploadBaseURL}}
  72. {{- if or .CanRename .CanAddFiles}}
  73. <div class="menu-item px-3">
  74. <a data-i18n="fs.move_copy" href="#" class="menu-link px-3 fs-6" data-kt-filemanager-table-select="move_or_copy_selected">
  75. Move or copy
  76. </a>
  77. </div>
  78. {{- end}}
  79. {{- if .CanShare}}
  80. <div class="menu-item px-3">
  81. <a data-i18n="fs.share" href="#" class="menu-link px-3 fs-6" data-kt-filemanager-table-select="share_selected">
  82. Share
  83. </a>
  84. </div>
  85. {{- end}}
  86. {{- end}}
  87. {{- if .CanDelete}}
  88. <div class="menu-item px-3">
  89. <a data-i18n="general.delete" href="#" class="menu-link px-3 text-danger fs-6" data-kt-filemanager-table-select="delete_selected">
  90. Delete
  91. </a>
  92. </div>
  93. {{- end}}
  94. </div>
  95. </div>
  96. {{- end}}
  97. </div>
  98. </div>
  99. </div>
  100. <div class="card-body">
  101. <div class="d-flex flex-stack">
  102. <div class="badge badge-lg badge-light-primary">
  103. <div class="d-flex align-items-center flex-wrap">
  104. <i class="ki-duotone ki-home fs-1 text-primary me-3"></i>
  105. <a data-i18n="fs.home" href="{{.FilesURL}}?path=%2F">Home</a>
  106. {{- range .Paths}}
  107. <i class="ki-duotone ki-right fs-2x text-primary mx-1"></i>
  108. {{- if eq .Href ""}}
  109. <span>{{.DirName}}</span>
  110. {{- else}}
  111. <a href="{{.Href}}">{{.DirName}}</a>
  112. {{- end}}
  113. {{- end}}
  114. </div>
  115. </div>
  116. </div>
  117. <div id="file_manager_new_folder" class="d-flex align-items-center py-7 d-none">
  118. <span>
  119. <i class="ki-duotone ki-folder fs-2x text-primary me-4">
  120. <span class="path1"></span>
  121. <span class="path2"></span>
  122. </i>
  123. </span>
  124. <input data-i18n="[placeholder]fs.create_folder_msg" id="file_manager_new_folder_input" type="text" name="new_folder_name" placeholder="Enter the new folder name" class="form-control mw-250px me-3" />
  125. <button class="btn btn-icon btn-light-primary me-3" id="file_manager_add_folder">
  126. <span class="indicator-label">
  127. <i class="ki-duotone ki-check fs-1"></i>
  128. </span>
  129. <span class="indicator-progress">
  130. <span class="spinner-border spinner-border-sm align-middle"></span>
  131. </span>
  132. </button>
  133. <button class="btn btn-icon btn-light-danger" id="file_manager_cancel_folder">
  134. <i class="ki-duotone ki-cross fs-1">
  135. <span class="path1"></span>
  136. <span class="path2"></span>
  137. </i>
  138. </button>
  139. </div>
  140. <table id="file_manager_list" class="table align-middle table-row-dashed fs-6 gy-5">
  141. <thead>
  142. <tr class="text-start text-muted fw-bold fs-6 gs-0">
  143. <th class="w-10px pe-2">
  144. <div class="form-check form-check-sm form-check-custom form-check-solid me-3">
  145. <input id="select_checkbox" class="form-check-input" type="checkbox"/>
  146. </div>
  147. </th>
  148. <th></th>
  149. <th data-i18n="general.name" class="min-w-250px">Name</th>
  150. <th data-i18n="general.size" class="min-w-10px">Size</th>
  151. <th data-i18n="general.last_modified" class="min-w-125px">Last Modified</th>
  152. <th class="w-125px"></th>
  153. </tr>
  154. </thead>
  155. <tbody id="file_manager_list_body" class="text-gray-700 fw-semibold">
  156. </tbody>
  157. </table>
  158. </div>
  159. </div>
  160. {{- end}}
  161. {{- define "extra_css"}}
  162. <link href="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.css" rel="stylesheet" type="text/css"/>
  163. <link href="{{.StaticURL}}/vendor/glightbox/glightbox.min.css" rel="stylesheet" type="text/css"/>
  164. <style {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
  165. .gslide-description-bg {
  166. background: var(--bs-app-bg-color) !important;
  167. opacity: 0.9;
  168. }
  169. </style>
  170. {{- end}}
  171. {{- define "extra_js"}}
  172. <script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/assets/plugins/custom/datatables/datatables.bundle.js"></script>
  173. <script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/vendor/glightbox/glightbox.min.js"></script>
  174. <script {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}} src="{{.StaticURL}}/vendor/pdfobject/pdfobject.min.js"></script>
  175. <script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
  176. //{{- if not .ShareUploadBaseURL}}
  177. const supportedEditExtensions = ["csv", "bat", "dyalog", "apl", "asc", "pgp", "sig", "asn", "asn1", "b", "bf",
  178. "c", "h", "ino", "cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx", "cob", "cpy", "cbl", "cs", "clj",
  179. "cljc", "cljx", "cljs", "gss", "cmake", "cmake.in", "coffee", "cl", "lisp", "el", "cyp", "cypher",
  180. "pyx", "pxd", "pxi", "cr", "css", "cql", "d", "dart", "diff", "patch", "dtd", "dylan", "dyl", "intr",
  181. "ecl", "edn", "e", "elm", "ejs", "erb", "erl", "sql", "factor", "forth", "fth", "4th", "f", "for", "f77",
  182. "f90", "f95", "fsharp", "s", "go", "groovy", "gradle", "haml", "hs", "lhs", "hx", "hxml", "aspx",
  183. "html", "htm", "handlebars", "hbs", "pro", "jade", "pug", "java", "jsp", "js", "json", "map", "jsonld",
  184. "jsx", "j2", "jinja", "jinja2", "jl", "kt", "less", "ls", "lua", "markdown", "md", "mkd", "m", "nb", "wl",
  185. "wls", "mo", "mps", "mbox", "nsh", "nsi", "nt", "nq", "m", "mm", "ml", "mli", "mll", "mly", "oz", "p",
  186. "pas", "pl", "pm", "php", "php3", "php4", "php5", "php7", "phtml", "pig", "txt", "text", "conf", "def",
  187. "list", "log", "pls", "ps1", "psd1", "psm1", "properties", "ini", "in", "proto", "BUILD", "bzl", "py",
  188. "pyw", "pp", "q", "r", "rst", "spec", "rb", "rs", "sas", "sass", "scala", "scm", "ss", "scss", "sh",
  189. "ksh", "bash", "siv", "sieve", "slim", "st", "tpl", "sml", "sig", "fun", "smackspec", "soy", "rq", "sparql",
  190. "nut", "styl", "swift", "text", "ltx", "tex", "v", "sv", "svh", "tcl", "textile", "toml", "1", "2", "3", "4",
  191. "5", "6", "7", "8", "9", "ttcn", "ttcn3", "ttcnpp", "cfg", "ttl", "ts", "tsx", "webidl", "vb", "vbs",
  192. "vtl", "vhd", "vhdl", "vue", "xml", "xsl", "xsd", "svg", "xy", "xquery", "ys", "yaml", "yml", "z80",
  193. "mscgen", "mscin", "msc", "xu", "msgenny", "wat", "wast"];
  194. const supportedEditFilenames = ["readme", "dockerfile", "pkgbuild"]
  195. //{{- end}}
  196. function keepAlive() {
  197. //{{- if not .ShareUploadBaseURL}}
  198. axios.get('{{.PingURL}}',{
  199. timeout: 15000,
  200. responseType: 'text'
  201. }).catch(function (error){});
  202. //{{- end}}
  203. }
  204. //{{- if not .ShareUploadBaseURL}}
  205. var KTDatatablesFoldersExplorer = function () {
  206. var dt;
  207. var curDir;
  208. var initDatatable = function (dirsURL) {
  209. $('#errorModalMsg').addClass("d-none");
  210. dt = $("#dirsbrowser_list").DataTable({
  211. ajax: {
  212. url: dirsURL,
  213. dataSrc: "",
  214. error: function ($xhr, textStatus, errorThrown) {
  215. $(".dataTables_processing").hide();
  216. let txt = "";
  217. if ($xhr) {
  218. let json = $xhr.responseJSON;
  219. if (json) {
  220. if (json.message) {
  221. txt = json.message;
  222. }
  223. }
  224. }
  225. if (!txt){
  226. txt = "fs.dir_list.err_generic";
  227. }
  228. setI18NData($('#errorModalTxt'), txt);
  229. $('#errorModalMsg').removeClass("d-none");
  230. }
  231. },
  232. deferRender: true,
  233. processing: true,
  234. stateSave: false,
  235. columns: [
  236. {
  237. data: "meta"
  238. },
  239. {
  240. data: "name",
  241. render: function (data, type, row) {
  242. if (type === 'display') {
  243. data = escapeHTML(data);
  244. return `<div class="d-flex align-items-center">
  245. <i class="ki-duotone ki-folder fs-1 text-primary me-4">
  246. <i class="path1"></i>
  247. <i class="path2"></i>
  248. </i>
  249. <a href="#" class="text-gray-700 text-hover-primary" data-dirbrowser-table-row="open">${data}</a>
  250. </div>`
  251. }
  252. return data;
  253. }
  254. }
  255. ],
  256. lengthChange: true,
  257. columnDefs: [
  258. {
  259. targets: 0,
  260. visible: false,
  261. searchable: false
  262. },
  263. {
  264. targets: 1,
  265. visible: true,
  266. searchable: true
  267. }
  268. ],
  269. language: {
  270. info: $.t('datatable.info'),
  271. infoEmpty: $.t('datatable.info_empty'),
  272. infoFiltered: $.t('datatable.info_filtered'),
  273. loadingRecords: "",
  274. processing: $.t('datatable.processing'),
  275. zeroRecords: "",
  276. emptyTable: $.t('fs.no_more_subfolders')
  277. },
  278. order: [1, 'asc']
  279. });
  280. dt.on('draw', function () {
  281. let dirBrowserNav = document.getElementById("dirs_browser_nav");
  282. clearChilds(dirBrowserNav);
  283. let mainNavIcon = document.createElement("i");
  284. mainNavIcon.classList.add("ki-duotone", "ki-home", "fs-1", "text-primary", "me-3");
  285. let mainNavLink = document.createElement("a");
  286. mainNavLink.textContent = $.t('fs.home');
  287. mainNavLink.href = "#";
  288. mainNavLink.addEventListener("click", function(e){
  289. e.preventDefault();
  290. onDirBrowserClick("%2F");
  291. });
  292. dirBrowserNav.appendChild(mainNavIcon);
  293. dirBrowserNav.appendChild(mainNavLink);
  294. let p = "/";
  295. if (curDir && curDir != "%2F") {
  296. p = decodeURIComponent(curDir.replace(/\+/g, '%20'));
  297. const dirPaths = p.split("/");
  298. let fullPath = "%2F";
  299. for (idx in dirPaths) {
  300. let dir = dirPaths[idx];
  301. if (dir) {
  302. if (fullPath.endsWith("%2F")){
  303. fullPath += encodeURIComponent(dir);
  304. } else {
  305. fullPath += encodeURIComponent("/" + dir);
  306. }
  307. dir = escapeHTML(dir);
  308. let navIcon = document.createElement("i");
  309. navIcon.classList.add("ki-duotone", "ki-right", "fs-2x", "text-primary", "mx-1");
  310. let navLink = document.createElement("a");
  311. navLink.textContent = dir;
  312. navLink.href = "#";
  313. let pathCopy = fullPath
  314. navLink.addEventListener("click", function(e){
  315. e.preventDefault();
  316. onDirBrowserClick(pathCopy);
  317. });
  318. dirBrowserNav.appendChild(navIcon);
  319. dirBrowserNav.appendChild(navLink);
  320. }
  321. }
  322. }
  323. $('#move_copy_folder').val(p);
  324. handleRowActions();
  325. });
  326. }
  327. var handleCreateNewFolder = function() {
  328. $('#dirsbrowser_add_folder').click(function(){
  329. let errDivEl = $('#errorModalMsg');
  330. let errTxtEl = $('#errorModalTxt');
  331. let dirName = $("#dirsbrowser_new_folder_input").val();
  332. let submitButton = document.querySelector('#dirsbrowser_add_folder');
  333. let cancelButton = document.querySelector('#dirsbrowser_cancel_folder');
  334. errDivEl.addClass("d-none");
  335. if (!dirName){
  336. setI18NData(errTxtEl, "fs.folder_name_required");
  337. errDivEl.removeClass("d-none");
  338. return;
  339. }
  340. if (dirName.includes("/")){
  341. setI18NData(errTxtEl, "fs.invalid_name");
  342. errDivEl.removeClass("d-none");
  343. return;
  344. }
  345. let path = '{{.DirsURL}}?path='+ curDir + encodeURIComponent("/"+dirName);
  346. submitButton.setAttribute('data-kt-indicator', 'on');
  347. submitButton.disabled = true;
  348. cancelButton.disabled = true;
  349. axios.post(path, null, {
  350. timeout: 15000,
  351. headers: {
  352. 'X-CSRF-TOKEN': '{{.CSRFToken}}'
  353. },
  354. validateStatus: function (status) {
  355. return status == 201;
  356. }
  357. }).then(function (response) {
  358. dt.ajax.reload();
  359. errDivEl.addClass("d-none");
  360. submitButton.removeAttribute('data-kt-indicator');
  361. submitButton.disabled = false;
  362. cancelButton.disabled = false;
  363. $('#dirsbrowser_new_folder').addClass("d-none");
  364. }).catch(function (error) {
  365. let errorMessage = "";
  366. if (error && error.response) {
  367. switch (error.response.status) {
  368. case 403:
  369. errorMessage = "fs.create_dir.err_403";
  370. break;
  371. case 429:
  372. errorMessage = "fs.create_dir.err_429";
  373. break;
  374. }
  375. }
  376. if (!errorMessage){
  377. errorMessage = "fs.create_dir.err_generic";
  378. }
  379. setI18NData(errTxtEl, errorMessage);
  380. errDivEl.removeClass("d-none");
  381. submitButton.removeAttribute('data-kt-indicator');
  382. submitButton.disabled = false;
  383. cancelButton.disabled = false;
  384. });
  385. });
  386. }
  387. function handleRowActions(){
  388. const openLinks = document.querySelectorAll('[data-dirbrowser-table-row="open"]');
  389. openLinks.forEach(d => {
  390. d.addEventListener("click", function(e){
  391. e.preventDefault();
  392. const parent = e.target.closest('tr');
  393. onDirBrowserClick(dt.row(parent).data()['dir_path']);
  394. });
  395. });
  396. }
  397. return {
  398. init: function (url, dirPath) {
  399. curDir = dirPath;
  400. if (dt) {
  401. dt.ajax.url(url).load();
  402. return;
  403. }
  404. initDatatable(url);
  405. handleCreateNewFolder();
  406. }
  407. }
  408. }();
  409. //{{- end}}
  410. </script>
  411. <script type="text/javascript" {{- if .CSPNonce}} nonce="{{.CSPNonce}}"{{- end}}>
  412. var KTDatatablesServerSide = function () {
  413. // Shared variables
  414. //var table;
  415. var dt;
  416. var lightbox;
  417. // Private functions
  418. var initDatatable = function () {
  419. $('#errorMsg').addClass("d-none");
  420. dt = $("#file_manager_list").DataTable({
  421. ajax: {
  422. url: "{{.DirsURL}}?path={{.CurrentDir}}",
  423. dataSrc: "",
  424. error: function ($xhr, textStatus, errorThrown) {
  425. $(".dataTables_processing").hide();
  426. let txt = "";
  427. if ($xhr) {
  428. let json = $xhr.responseJSON;
  429. if (json) {
  430. if (json.message){
  431. txt = json.message;
  432. }
  433. }
  434. }
  435. if (!txt){
  436. txt = "fs.dir_list.err_generic";
  437. }
  438. setI18NData($('#errorTxt'), txt);
  439. $('#errorMsg').removeClass("d-none");
  440. }
  441. },
  442. deferRender: true,
  443. processing: true,
  444. lengthMenu: [ 10, 25, 50, 100, 250, 500 ],
  445. select: {
  446. style: 'multi',
  447. selector: 'td:first-child input[type="checkbox"]',
  448. className: 'row-selected'
  449. },
  450. stateSave: true,
  451. stateDuration: 0,
  452. stateSaveParams: function (settings, data) {
  453. data.sftpgo_dir = '{{.CurrentDir}}';
  454. },
  455. stateLoadParams: function (settings, data) {
  456. if (!data.sftpgo_dir || data.sftpgo_dir != '{{.CurrentDir}}'){
  457. data.start = 0;
  458. data.search.search = "";
  459. }
  460. if (data.search.search){
  461. const filterSearch = document.querySelector('[data-kt-filemanager-table-filter="search"]');
  462. filterSearch.value = data.search.search;
  463. }
  464. },
  465. columns: [
  466. {
  467. data: "meta",
  468. render: function (data, type) {
  469. if (type == 'display'){
  470. return `
  471. <div class="form-check form-check-sm form-check-custom form-check-solid">
  472. <input class="form-check-input" type="checkbox" />
  473. </div>`;
  474. }
  475. return data;
  476. }
  477. },
  478. { data: "type" },
  479. {
  480. data: "name",
  481. render: function (data, type, row) {
  482. if (type === 'display') {
  483. data = escapeHTML(data);
  484. let icon_name = "ki-file";
  485. if (row["type"] == "1") {
  486. icon_name = "ki-folder";
  487. } else if (row["size"] === "") {
  488. icon_name = "ki-file-right"
  489. }
  490. return `<div class="d-flex align-items-center">
  491. <i class="ki-duotone ${icon_name} fs-2x text-primary me-4">
  492. <i class="path1"></i>
  493. <i class="path2"></i>
  494. </i>
  495. <a href="${row['url']}" class="text-gray-800 text-hover-primary">${data}</a>
  496. </div>`
  497. }
  498. return data;
  499. }
  500. },
  501. {
  502. data: "size",
  503. render: function (data, type) {
  504. if (type === 'display') {
  505. if (data || data === 0){
  506. return fileSizeIEC(data);
  507. }
  508. return "";
  509. }
  510. return data;
  511. }
  512. },
  513. {
  514. data: "last_modified",
  515. render: function (data, type, row) {
  516. if (type === 'display') {
  517. if (data){
  518. return $.t('general.datetime', {
  519. val: new Date(data),
  520. formatParams: {
  521. val: { year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric' },
  522. }
  523. });
  524. }
  525. return ""
  526. }
  527. return data;
  528. }
  529. },
  530. {
  531. data: "edit_url",
  532. className: 'text-end',
  533. render: function (data, type, row) {
  534. if (type === 'display') {
  535. let previewDiv = "";
  536. if (row["type"] == "2") {
  537. let filename = escapeHTML(row["name"]);
  538. let extension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
  539. switch (extension) {
  540. case "jpeg":
  541. case "jpg":
  542. case "png":
  543. case "gif":
  544. case "webp":
  545. case "bmp":
  546. case "svg":
  547. case "ico":
  548. let desc = escapeHTML(filename).replace(/"/g, '&quot;');
  549. previewDiv = `<div class="ms-2" data-kt-filemanger-table="view_item">
  550. <a href="${row['url']}" data-gallery="gallery" data-glightbox="description: &lt;span class=&quot;fs-5 fw-bold&quot;&gt;${desc}&lt;/span&gt;" class="btn btn-sm btn-icon btn-light btn-active-light-primary glightbox">
  551. <i class="ki-duotone ki-eye fs-5 m-0">
  552. <span class="path1"></span>
  553. <span class="path2"></span>
  554. <span class="path3"></span>
  555. </i>
  556. </a>
  557. </div>`;
  558. break;
  559. case "mp4":
  560. case "mov":
  561. case "webm":
  562. case "ogv":
  563. case "ogg":
  564. case "mp3":
  565. case "wav":
  566. previewDiv = `<div class="ms-2" data-kt-filemanger-table="view_item">
  567. <a href="#" class="btn btn-sm btn-icon btn-light btn-active-light-primary" data-kt-filemanager-table-action="view_media">
  568. <i class="ki-duotone ki-eye fs-5 m-0">
  569. <span class="path1"></span>
  570. <span class="path2"></span>
  571. <span class="path3"></span>
  572. </i>
  573. </a>
  574. </div>`;
  575. break;
  576. case "pdf":
  577. if (PDFObject.supportsPDFs){
  578. let view_url = row['url'];
  579. view_url = view_url.replace('{{.FilesURL}}','{{.ViewPDFURL}}');
  580. previewDiv = `<div class="ms-2" data-kt-filemanger-table="view_item">
  581. <a href="${view_url}" target="_blank" class="btn btn-sm btn-icon btn-light btn-active-light-primary">
  582. <i class="ki-duotone ki-eye fs-5 m-0">
  583. <span class="path1"></span>
  584. <span class="path2"></span>
  585. <span class="path3"></span>
  586. </i>
  587. </a>
  588. </div>`;
  589. }
  590. break;
  591. default:
  592. //{{- if not .ShareUploadBaseURL}}
  593. if (data && (supportedEditExtensions.includes(extension) || supportedEditFilenames.includes(filename.toLowerCase()))){
  594. previewDiv = `<div class="ms-2" data-kt-filemanger-table="view_item">
  595. <a href="${data}" target="_blank" class="btn btn-sm btn-icon btn-light btn-active-light-primary">
  596. <i class="ki-duotone ki-eye fs-5 m-0">
  597. <span class="path1"></span>
  598. <span class="path2"></span>
  599. <span class="path3"></span>
  600. </i>
  601. </a>
  602. </div>`;
  603. }
  604. //{{- end}}
  605. }
  606. }
  607. let more = `{{- if not .ShareUploadBaseURL}}
  608. {{- if or .CanRename .CanAddFiles .CanShare .CanDelete }}
  609. <div class="ms-2">
  610. <button type="button" class="btn btn-sm btn-icon btn-light btn-active-light-primary" data-kt-menu-trigger="click" data-kt-menu-placement="bottom-end">
  611. <i class="ki-duotone ki-dots-square fs-5 m-0">
  612. <span class="path1"></span>
  613. <span class="path2"></span>
  614. <span class="path3"></span>
  615. <span class="path4"></span>
  616. </i>
  617. </button>
  618. <div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-700 menu-state-bg-light-primary fw-semibold fs-7 w-150px py-4" data-kt-menu="true">
  619. {{- if .CanRename}}
  620. <div class="menu-item px-3">
  621. <a data-i18n="general.rename" href="#" class="menu-link px-3" data-kt-filemanager-table-action="rename">Rename</a>
  622. </div>
  623. {{- end}}
  624. {{- if or .CanRename .CanAddFiles}}
  625. <div class="menu-item px-3">
  626. <a data-i18n="fs.move_copy" href="#" class="menu-link px-3" data-kt-filemanager-table-action="move_or_copy">Move or copy</a>
  627. </div>
  628. {{- end}}
  629. {{- if .CanShare}}
  630. <div class="menu-item px-3">
  631. <a data-i18n="fs.share" href="#" class="menu-link px-3" data-kt-filemanager-table-action="share">Share</a>
  632. </div>
  633. {{- end}}
  634. {{- if .CanDelete}}
  635. <div class="menu-item px-3">
  636. <a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-kt-filemanager-table-action="delete">Delete</a>
  637. </div>
  638. {{- end}}
  639. </div>
  640. </div>
  641. {{- end}}
  642. {{- end}}`;
  643. return `<div class="d-flex justify-content-end">
  644. ${previewDiv}
  645. ${more}
  646. </div>
  647. `;
  648. }
  649. return "";
  650. }
  651. }
  652. ],
  653. lengthChange: true,
  654. columnDefs: [
  655. {
  656. targets: 0,
  657. orderable: false,
  658. searchable: false,
  659. },
  660. {
  661. targets: 1,
  662. visible: false,
  663. searchable: false
  664. },
  665. {
  666. targets: 2,
  667. visible: true,
  668. searchable: true
  669. },
  670. {
  671. targets: [3,4],
  672. searchable: false
  673. },
  674. {
  675. targets: 5,
  676. orderable: false,
  677. searchable: false
  678. }
  679. ],
  680. language: {
  681. info: $.t('datatable.info'),
  682. infoEmpty: $.t('datatable.info_empty'),
  683. infoFiltered: $.t('datatable.info_filtered'),
  684. loadingRecords: "",
  685. processing: $.t('datatable.processing'),
  686. zeroRecords: "",
  687. emptyTable: $.t('fs.no_files_folders')
  688. },
  689. orderFixed: [1, 'asc'],
  690. order: [2, 'asc']
  691. });
  692. //table = dt.$;
  693. dt.on('draw', function () {
  694. lightbox.reload();
  695. KTMenu.createInstances();
  696. let targets = document.querySelectorAll('#file_manager_list_body tr');
  697. if (targets) {
  698. KTUtil.each(targets, function (target) {
  699. if (!target){
  700. return;
  701. }
  702. let checkbox = target.querySelector('.form-check-input');
  703. if (!checkbox) {
  704. return;
  705. }
  706. if (target.classList.contains('row-selected')) {
  707. checkbox.checked = true;
  708. } else {
  709. checkbox.checked = false;
  710. }
  711. });
  712. }
  713. toggleToolbars();
  714. handleRowActions();
  715. $('#file_manager_list_body').localize();
  716. });
  717. dt.on('user-select', function(e, dt, type, cell, originalEvent){
  718. let pageSelected = dt.rows({ selected: true, page: 'current' }).count();
  719. let totalSelected = dt.rows({ selected: true, search: 'applied' }).count();
  720. if (totalSelected > pageSelected){
  721. let currentIndexes = [];
  722. dt.rows({ page: 'current' }).every(function (rowIdx, tableLoop, rowLoop){
  723. currentIndexes.push(rowIdx);
  724. });
  725. dt.rows().deselect();
  726. currentIndexes.forEach((idx) => {
  727. dt.row(idx).select();
  728. });
  729. totalSelected = dt.rows({ selected: true, search: 'applied' }).count();
  730. }
  731. if ($(originalEvent.target).is(':checked')){
  732. pageSelected++;
  733. totalSelected++;
  734. } else {
  735. pageSelected--;
  736. totalSelected--;
  737. }
  738. handleToogleToolbar(pageSelected, totalSelected);
  739. });
  740. }
  741. function handleToogleToolbar(pageSelected, totalSelected) {
  742. const toolbarBase = document.querySelector('[data-kt-filemanager-table-toolbar="base"]');
  743. const toolbarSelected = document.querySelector('[data-kt-filemanager-table-toolbar="selected"]');
  744. const selectedCount = document.querySelector('[data-kt-filemanager-table-select="selected_count"]');
  745. const selectAllContainer = document.querySelector('[data-kt-filemanager-table-select="select_all_pages_container"]');
  746. const selectAllCheck = document.querySelector('[data-kt-filemanager-table-select="select_all_pages"]');
  747. if (pageSelected > 0) {
  748. let pageTotal = dt.rows({ page: 'current' }).count();
  749. if (pageSelected === pageTotal){
  750. selectAllContainer.classList.remove("d-none");
  751. $('#select_checkbox').prop("checked", true);
  752. } else {
  753. $('#select_checkbox').prop("checked", false);
  754. selectAllCheck.checked = false;
  755. selectAllContainer.classList.add('d-none');
  756. }
  757. if (selectedCount){
  758. selectedCount.innerHTML = $.t('general.selected_items', { count: totalSelected});
  759. }
  760. if (toolbarBase){
  761. toolbarBase.classList.add('d-none');
  762. }
  763. if (toolbarSelected){
  764. toolbarSelected.classList.remove('d-none');
  765. }
  766. } else {
  767. $('#select_checkbox').prop("checked", false);
  768. selectAllCheck.checked = false;
  769. if (toolbarBase) {
  770. toolbarBase.classList.remove('d-none');
  771. }
  772. if (toolbarBase) {
  773. toolbarSelected.classList.add('d-none');
  774. }
  775. }
  776. }
  777. function handleRowActions() {
  778. const renameButtons = document.querySelectorAll('[data-kt-filemanager-table-action="rename"]');
  779. renameButtons.forEach(d => {
  780. d.addEventListener("click", function(e){
  781. e.preventDefault();
  782. const parent = e.target.closest('tr');
  783. renameItem(dt.row(parent).data()["meta"]);
  784. });
  785. });
  786. const moveCopyButtons = document.querySelectorAll('[data-kt-filemanager-table-action="move_or_copy"]');
  787. moveCopyButtons.forEach(d => {
  788. d.addEventListener("click", function(e){
  789. e.preventDefault();
  790. const parent = e.target.closest('tr');
  791. moveOrCopyItem(dt.row(parent).data()["meta"]);
  792. });
  793. });
  794. const shareButtons = document.querySelectorAll('[data-kt-filemanager-table-action="share"]');
  795. shareButtons.forEach(d => {
  796. d.addEventListener("click", function(e){
  797. e.preventDefault();
  798. const parent = e.target.closest('tr');
  799. shareItem(dt.row(parent).data()["meta"]);
  800. });
  801. });
  802. const deleteButtons = document.querySelectorAll('[data-kt-filemanager-table-action="delete"]');
  803. deleteButtons.forEach(d => {
  804. d.addEventListener("click", function(e){
  805. e.preventDefault();
  806. const parent = e.target.closest('tr');
  807. deleteItem(dt.row(parent).data()["meta"]);
  808. });
  809. });
  810. const viewMediaLinks = document.querySelectorAll('[data-kt-filemanager-table-action="view_media"]');
  811. viewMediaLinks.forEach(d => {
  812. d.addEventListener("click", function(e){
  813. e.preventDefault();
  814. const parent = e.target.closest('tr');
  815. let rowData = dt.row(parent).data();
  816. openMediaPlayer(rowData["name"], rowData["url"]);
  817. });
  818. });
  819. }
  820. var initLightbox = function() {
  821. lightbox = GLightbox({
  822. slideHTML: `<div class="gslide">
  823. <div class="gslide-inner-content">
  824. <div class="ginner-container">
  825. <div class="gslide-media">
  826. </div>
  827. <div class="gslide-description gslide-description-bg">
  828. <div class="gdesc-inner">
  829. <h4 class="gslide-title"></h4>
  830. <div class="gslide-desc"></div>
  831. </div>
  832. </div>
  833. </div>
  834. </div>
  835. </div>`,
  836. preload: false,
  837. closeOnOutsideClick: false
  838. });
  839. }
  840. var handleSearchDatatable = function () {
  841. const filterSearch = document.querySelector('[data-kt-filemanager-table-filter="search"]');
  842. filterSearch.addEventListener('keyup', function (e) {
  843. dt.rows().deselect();
  844. dt.search(e.target.value, true, false).draw();
  845. });
  846. }
  847. var initToggleToolbar = function () {
  848. const selectAllCheck = document.querySelector('[data-kt-filemanager-table-select="select_all_pages"]');
  849. if (selectAllCheck){
  850. selectAllCheck.addEventListener('change', function(e){
  851. if (this.checked){
  852. dt.rows({ search: 'applied' }).select();
  853. } else {
  854. let selectedIndexes = [];
  855. dt.rows({ selected: true, page: 'current' }).every(function (rowIdx, tableLoop, rowLoop){
  856. selectedIndexes.push(rowIdx);
  857. });
  858. dt.rows().deselect();
  859. selectedIndexes.forEach((idx) => {
  860. dt.row(idx).select();
  861. });
  862. }
  863. toggleToolbars();
  864. })
  865. }
  866. const downloadButton = document.querySelector('[data-kt-filemanager-table-select="download_selected"]');
  867. if (downloadButton){
  868. downloadButton.addEventListener('click', function(e){
  869. let filesArray = [];
  870. dt.rows({ selected: true, search: 'applied' }).every(function (rowIdx, tableLoop, rowLoop){
  871. let row = dt.row(rowIdx);
  872. filesArray.push(getNameFromMeta(row.data()['meta']));
  873. });
  874. let token = "{{.CSRFToken}}";
  875. let downloadURL = '{{.DownloadURL}}';
  876. let currentDir = '{{.CurrentDir}}';
  877. let ts = new Date().getTime().toString();
  878. let files = JSON.stringify(filesArray);
  879. $(`<form method="post" action="${downloadURL}?path=${currentDir}&_=${ts}" target="_blank">
  880. <input type="hidden" name="_form_token" value="${token}">
  881. <textarea name="files" hidden>${files}</textarea>
  882. </form>`).appendTo('body').submit().remove();
  883. });
  884. }
  885. const moveOrCopyButton = document.querySelector('[data-kt-filemanager-table-select="move_or_copy_selected"]');
  886. if (moveOrCopyButton){
  887. moveOrCopyButton.addEventListener('click', function(e){
  888. $('#errorMsg').addClass("d-none");
  889. $('#move_copy_name_container').addClass("d-none");
  890. $('#move_copy_source').val("");
  891. $('#modal_move_or_copy').modal('show');
  892. KTDatatablesFoldersExplorer.init('{{.DirsURL}}?dirtree=1&path={{.CurrentDir}}', '{{.CurrentDir}}');
  893. });
  894. }
  895. const shareButton = document.querySelector('[data-kt-filemanager-table-select="share_selected"]');
  896. if (shareButton){
  897. shareButton.addEventListener('click', function(e){
  898. let filesArray = [];
  899. dt.rows({ selected: true, search: 'applied' }).every(function (rowIdx, tableLoop, rowLoop){
  900. let row = dt.row(rowIdx);
  901. filesArray.push(getNameFromMeta(row.data()['meta']));
  902. });
  903. let files = encodeURIComponent(JSON.stringify(filesArray));
  904. let shareURL = '{{.ShareURL}}';
  905. let currentDir = '{{.CurrentDir}}';
  906. let ts = new Date().getTime().toString();
  907. window.open(`${shareURL}?path=${currentDir}&files=${files}&_=${ts}`,'_blank');
  908. });
  909. }
  910. const deleteButton = document.querySelector('[data-kt-filemanager-table-select="delete_selected"]');
  911. if (deleteButton) {
  912. deleteButton.addEventListener('click', function(e){
  913. ModalAlert.fire({
  914. text: $.t('general.delete_multi_confirm'),
  915. icon: "warning",
  916. confirmButtonText: $.t('general.delete'),
  917. cancelButtonText: $.t('general.cancel'),
  918. customClass: {
  919. confirmButton: "btn btn-danger",
  920. cancelButton: 'btn btn-secondary'
  921. }
  922. }).then((result) => {
  923. if (result.isConfirmed){
  924. let hasError = false;
  925. let deleted = 0;
  926. let index = 0;
  927. let errDivEl = $('#errorMsg');
  928. let errTxtEl = $('#errorTxt');
  929. errDivEl.addClass("d-none");
  930. let selectedRowsIdx = [];
  931. dt.rows({ selected: true, search: 'applied' }).every(function (rowIdx, tableLoop, rowLoop){
  932. selectedRowsIdx.push(rowIdx);
  933. });
  934. if (selectedRowsIdx.length == 0){
  935. return;
  936. }
  937. keepAlive();
  938. let keepAliveTimer = setInterval(keepAlive, 300000);
  939. $('#loading_message').text("");
  940. KTApp.showPageLoading();
  941. function deleteSelected() {
  942. if (index >= selectedRowsIdx.length || hasError){
  943. clearInterval(keepAliveTimer);
  944. KTApp.hidePageLoading();
  945. if (!hasError){
  946. location.reload();
  947. }
  948. return;
  949. }
  950. let meta = dt.row(selectedRowsIdx[index]).data()['meta'];
  951. let attrs = getDeleteReqAttrs(meta);
  952. let deleteTxt = "";
  953. if (selectedRowsIdx.length > 1){
  954. deleteTxt = $.t('fs.deleting', {
  955. idx : index + 1,
  956. total: selectedRowsIdx.length,
  957. name: getNameFromMeta(meta)
  958. });
  959. }
  960. $('#loading_message').text(deleteTxt);
  961. axios.delete(attrs.path,{
  962. timeout: attrs.reqTimeout,
  963. headers: {
  964. 'X-CSRF-TOKEN': '{{.CSRFToken}}'
  965. },
  966. validateStatus: function (status) {
  967. return status == 200;
  968. }
  969. }).then(function(response){
  970. index++;
  971. deleted++;
  972. deleteSelected();
  973. }).catch(function(error){
  974. index++;
  975. hasError = true;
  976. let errorMessage;
  977. if (error && error.response) {
  978. switch (error.response.status) {
  979. case 403:
  980. if (deleted > 0){
  981. errorMessage = "fs.delete_multi.err_403_partial";
  982. } else {
  983. errorMessage = "fs.delete_multi.err_403";
  984. }
  985. break;
  986. case 429:
  987. if (deleted > 0){
  988. errorMessage = "fs.delete_multi.err_429_partial";
  989. } else {
  990. errorMessage = "fs.delete_multi.err_429";
  991. }
  992. break;
  993. }
  994. }
  995. if (!errorMessage){
  996. if (deleted > 0){
  997. errorMessage = "fs.delete_multi.err_generic_partial";
  998. } else {
  999. errorMessage = "fs.delete_multi.err_generic";
  1000. }
  1001. }
  1002. setI18NData(errTxtEl, errorMessage);
  1003. errDivEl.removeClass("d-none");
  1004. deleteSelected();
  1005. });
  1006. }
  1007. deleteSelected();
  1008. }
  1009. });
  1010. });
  1011. }
  1012. $('#select_checkbox').on("click", function(){
  1013. let targets = document.querySelectorAll('#file_manager_list_body .form-check-input');
  1014. if (!targets){
  1015. return;
  1016. }
  1017. dt.rows().deselect();
  1018. let isChecked = $(this).is(':checked');
  1019. if (isChecked){
  1020. dt.rows({page: 'current'}).select();
  1021. }
  1022. KTUtil.each(targets, function (target) {
  1023. if (target){
  1024. target.checked = isChecked;
  1025. }
  1026. });
  1027. toggleToolbars();
  1028. });
  1029. toggleToolbars();
  1030. }
  1031. var toggleToolbars = function () {
  1032. let pageSelected = dt.rows({ selected: true, page: 'current' }).count();
  1033. let totalSelected = dt.rows({ selected: true , search: 'applied'}).count();
  1034. handleToogleToolbar(pageSelected, totalSelected);
  1035. }
  1036. return {
  1037. init: function () {
  1038. initLightbox();
  1039. initDatatable();
  1040. handleSearchDatatable();
  1041. initToggleToolbar();
  1042. }
  1043. }
  1044. }();
  1045. function getNameFromMeta(meta) {
  1046. return meta.split('_').slice(1).join('_');
  1047. }
  1048. function getTypeFromMeta(meta) {
  1049. return meta.split('_')[0];
  1050. }
  1051. //{{- if not .ShareUploadBaseURL}}
  1052. function onDirBrowserClick(dirPath) {
  1053. KTDatatablesFoldersExplorer.init('{{.DirsURL}}?dirtree=1&path='+dirPath, dirPath);
  1054. }
  1055. function moveOrCopyItem(meta) {
  1056. $('#errorMsg').addClass("d-none");
  1057. $('#move_copy_name_container').removeClass("d-none");
  1058. $('#move_copy_source').val(meta);
  1059. $('#move_copy_name').val(getNameFromMeta(meta));
  1060. $('#modal_move_or_copy').modal('show');
  1061. KTDatatablesFoldersExplorer.init('{{.DirsURL}}?dirtree=1&path={{.CurrentDir}}', '{{.CurrentDir}}');
  1062. }
  1063. function getMoveOrCopyItems() {
  1064. let items = [];
  1065. let targetDir = $("#move_copy_folder").val();
  1066. if (targetDir != "/") {
  1067. targetDir = targetDir.endsWith('/') ? targetDir.slice(0, -1) : targetDir;
  1068. }
  1069. if (targetDir.trim() == ""){
  1070. targetDir = "{{.CurrentDir}}";
  1071. } else {
  1072. targetDir = encodeURIComponent(targetDir);
  1073. }
  1074. if ($('#move_copy_name_container').hasClass("d-none")){
  1075. let dt = $('#file_manager_list').DataTable();
  1076. dt.rows({ selected: true, search: 'applied' }).every(function (rowIdx, tableLoop, rowLoop){
  1077. let row = dt.row(rowIdx);
  1078. let sourceName = getNameFromMeta(row.data()['meta']);
  1079. items.push({
  1080. targetDir: targetDir,
  1081. sourceName: sourceName,
  1082. targetName: sourceName
  1083. });
  1084. });
  1085. } else {
  1086. let meta = $('#move_copy_source').val();
  1087. let sourceName = getNameFromMeta(meta);
  1088. items.push({
  1089. targetDir: targetDir,
  1090. sourceName: sourceName,
  1091. targetName: $("#move_copy_name").val()
  1092. });
  1093. }
  1094. return items;
  1095. }
  1096. function checkMoveCopyItems(items) {
  1097. let hasSlash = items.some(item => item.targetName.includes("/"));
  1098. if (hasSlash){
  1099. return [];
  1100. }
  1101. return items;
  1102. }
  1103. function doCopy() {
  1104. let items = getMoveOrCopyItems();
  1105. if (items.length == 0){
  1106. return;
  1107. }
  1108. let errDivEl = $('#errorMsg');
  1109. let errTxtEl = $('#errorTxt');
  1110. errDivEl.addClass("d-none");
  1111. items = checkMoveCopyItems(items)
  1112. if (items.length == 0){
  1113. setI18NData(errTxtEl, "fs.invalid_name");
  1114. errDivEl.removeClass("d-none");
  1115. return;
  1116. }
  1117. keepAlive();
  1118. let keepAliveTimer = setInterval(keepAlive, 300000);
  1119. let hasError = false;
  1120. let index = 0;
  1121. $('#loading_message').text("");
  1122. KTApp.showPageLoading();
  1123. function copyItem() {
  1124. if (index >= items.length || hasError){
  1125. clearInterval(keepAliveTimer);
  1126. KTApp.hidePageLoading();
  1127. if (!hasError){
  1128. location.reload();
  1129. }
  1130. return;
  1131. }
  1132. let item = items[index];
  1133. let sourcePath = decodeURIComponent('{{.CurrentDir}}');
  1134. if (!sourcePath.endsWith('/')){
  1135. sourcePath+="/";
  1136. }
  1137. sourcePath+=item.sourceName;
  1138. let targetPath = decodeURIComponent(item.targetDir);
  1139. if (!targetPath.endsWith('/')){
  1140. targetPath+="/";
  1141. }
  1142. targetPath+=item.targetName;
  1143. if (items.length > 1) {
  1144. msgTxt = $.t('fs.copying', {
  1145. idx: index + 1,
  1146. total: items.length,
  1147. name: `${sourcePath} => ${targetPath}`
  1148. });
  1149. $('#loading_message').text(msgTxt);
  1150. }
  1151. let path = '{{.FileActionsURL}}/copy';
  1152. path+='?path={{.CurrentDir}}'+encodeURIComponent("/"+item.sourceName)+'&target='+item.targetDir+encodeURIComponent("/"+item.targetName);
  1153. axios.post(path, null, {
  1154. timeout: 180000,
  1155. headers: {
  1156. 'X-CSRF-TOKEN': '{{.CSRFToken}}'
  1157. },
  1158. validateStatus: function (status) {
  1159. return status == 200;
  1160. }
  1161. }).then(function (response) {
  1162. index++;
  1163. copyItem();
  1164. }).catch(function (error) {
  1165. index++;
  1166. hasError = true;
  1167. let errorMessage = "";
  1168. if (error && error.response) {
  1169. switch (error.response.status) {
  1170. case 403:
  1171. errorMessage = "fs.copy.err_403";
  1172. break;
  1173. case 429:
  1174. errorMessage = "fs.copy.err_429";
  1175. break;
  1176. }
  1177. }
  1178. if (!errorMessage){
  1179. errorMessage = "fs.copy.err_generic";
  1180. }
  1181. setI18NData(errTxtEl, errorMessage);
  1182. errDivEl.removeClass("d-none");
  1183. copyItem();
  1184. });
  1185. }
  1186. copyItem();
  1187. }
  1188. function doMove() {
  1189. let items = getMoveOrCopyItems();
  1190. if (items.length == 0){
  1191. return;
  1192. }
  1193. let errDivEl = $('#errorMsg');
  1194. let errTxtEl = $('#errorTxt');
  1195. errDivEl.addClass("d-none");
  1196. items = checkMoveCopyItems(items)
  1197. if (items.length == 0){
  1198. setI18NData(errTxtEl, "fs.invalid_name");
  1199. errDivEl.removeClass("d-none");
  1200. return;
  1201. }
  1202. keepAlive();
  1203. let keepAliveTimer = setInterval(keepAlive, 300000);
  1204. let hasError = false;
  1205. let index = 0;
  1206. $('#loading_message').text("");
  1207. KTApp.showPageLoading();
  1208. function moveItem() {
  1209. if (index >= items.length || hasError){
  1210. clearInterval(keepAliveTimer);
  1211. KTApp.hidePageLoading();
  1212. if (!hasError){
  1213. location.reload();
  1214. }
  1215. return;
  1216. }
  1217. let item = items[index];
  1218. let sourcePath = decodeURIComponent('{{.CurrentDir}}');
  1219. if (!sourcePath.endsWith('/')){
  1220. sourcePath+="/";
  1221. }
  1222. sourcePath+=item.sourceName;
  1223. let targetPath = decodeURIComponent(item.targetDir);
  1224. if (!targetPath.endsWith('/')){
  1225. targetPath+="/";
  1226. }
  1227. targetPath+=item.targetName;
  1228. if (items.length > 1) {
  1229. msgTxt = $.t('fs.moving', {
  1230. idx: index + 1,
  1231. total: items.length,
  1232. name: `${sourcePath} => ${targetPath}`
  1233. });
  1234. $('#loading_message').text(msgTxt);
  1235. }
  1236. let path = '{{.FileActionsURL}}/move';
  1237. path+='?path={{.CurrentDir}}'+encodeURIComponent("/"+item.sourceName)+'&target='+item.targetDir+encodeURIComponent("/"+item.targetName);
  1238. axios.post(path, null, {
  1239. timeout: 180000,
  1240. headers: {
  1241. 'X-CSRF-TOKEN': '{{.CSRFToken}}'
  1242. },
  1243. validateStatus: function (status) {
  1244. return status == 200;
  1245. }
  1246. }).then(function (response) {
  1247. index++;
  1248. moveItem();
  1249. }).catch(function (error) {
  1250. index++;
  1251. hasError = true;
  1252. let errorMessage = "";
  1253. if (error && error.response) {
  1254. switch (error.response.status) {
  1255. case 403:
  1256. errorMessage = "fs.move.err_403";
  1257. break;
  1258. case 429:
  1259. errorMessage = "fs.move.err_429";
  1260. break;
  1261. }
  1262. }
  1263. if (!errorMessage){
  1264. errorMessage = "fs.move.err_generic";
  1265. }
  1266. setI18NData(errTxtEl, errorMessage);
  1267. errDivEl.removeClass("d-none");
  1268. moveItem();
  1269. });
  1270. }
  1271. moveItem();
  1272. }
  1273. function getDeleteReqAttrs(meta) {
  1274. let path;
  1275. let reqTimeout = 15000;
  1276. let itemType = getTypeFromMeta(meta);
  1277. let itemName = getNameFromMeta(meta);
  1278. if (itemType == "1"){
  1279. path = '{{.DirsURL}}';
  1280. reqTimeout = 120000
  1281. } else {
  1282. path = '{{.FilesURL}}';
  1283. }
  1284. path+='?path={{.CurrentDir}}'+encodeURIComponent("/"+itemName);
  1285. return { path, reqTimeout}
  1286. }
  1287. function deleteItem(meta) {
  1288. let errDivEl = $('#errorMsg');
  1289. let errTxtEl = $('#errorTxt');
  1290. errDivEl.addClass("d-none");
  1291. let itemName = getNameFromMeta(meta);
  1292. ModalAlert.fire({
  1293. text: $.t('general.delete_confirm', {name: itemName}),
  1294. icon: "warning",
  1295. confirmButtonText: $.t('general.delete'),
  1296. cancelButtonText: $.t('general.cancel'),
  1297. customClass: {
  1298. confirmButton: "btn btn-danger",
  1299. cancelButton: 'btn btn-secondary'
  1300. }
  1301. }).then((result) => {
  1302. if (result.isConfirmed){
  1303. $('#loading_message').text("");
  1304. KTApp.showPageLoading();
  1305. let attrs = getDeleteReqAttrs(meta);
  1306. axios.delete(attrs.path, {
  1307. timeout: attrs.reqTimeout,
  1308. headers: {
  1309. 'X-CSRF-TOKEN': '{{.CSRFToken}}'
  1310. },
  1311. validateStatus: function (status) {
  1312. return status == 200;
  1313. }
  1314. }).then(function(response){
  1315. location.reload();
  1316. }).catch(function(error){
  1317. KTApp.hidePageLoading();
  1318. let errorMessage;
  1319. if (error && error.response) {
  1320. switch (error.response.status) {
  1321. case 403:
  1322. errorMessage = "fs.delete.err_403";
  1323. break;
  1324. case 429:
  1325. errorMessage = "fs.delete.err_429";
  1326. break;
  1327. }
  1328. }
  1329. if (!errorMessage){
  1330. errorMessage = "fs.delete.err_generic";
  1331. }
  1332. errTxtEl.removeAttr("data-i18n")
  1333. errTxtEl.text($.t(errorMessage, {name: itemName}));
  1334. errDivEl.removeClass("d-none");
  1335. });
  1336. }
  1337. });
  1338. }
  1339. function shareItem(meta) {
  1340. let filesArray = [];
  1341. filesArray.push(getNameFromMeta(meta));
  1342. let files = encodeURIComponent(JSON.stringify(filesArray));
  1343. let shareURL = '{{.ShareURL}}';
  1344. let currentDir = '{{.CurrentDir}}';
  1345. let ts = new Date().getTime().toString();
  1346. window.open(`${shareURL}?path=${currentDir}&files=${files}&_=${ts}`,'_blank');
  1347. }
  1348. function renameItem(meta) {
  1349. $('#errorMsg').addClass("d-none");
  1350. let oldName = getNameFromMeta(meta);
  1351. $('#rename_old_name').val(meta);
  1352. $('#rename_new_name').val(oldName);
  1353. $('#rename_title').text(`Rename "${oldName}"`);
  1354. $('#modal_rename').modal('show');
  1355. }
  1356. function doRename() {
  1357. let meta = $('#rename_old_name').val();
  1358. let oldName = getNameFromMeta(meta);
  1359. let newName = $('#rename_new_name').val();
  1360. let errDivEl = $('#errorMsg');
  1361. let errTxtEl = $('#errorTxt');
  1362. if (!newName){
  1363. setI18NData(errTxtEl, "general.name_required");
  1364. errDivEl.removeClass("d-none");
  1365. return;
  1366. }
  1367. if (newName == oldName){
  1368. setI18NData(errTxtEl, "general.name_different");
  1369. errDivEl.removeClass("d-none");
  1370. return;
  1371. }
  1372. if (newName.includes("/")){
  1373. setI18NData(errTxtEl, "fs.invalid_name");
  1374. errDivEl.removeClass("d-none");
  1375. return;
  1376. }
  1377. let path = '{{.FileActionsURL}}/move';
  1378. path+='?path={{.CurrentDir}}'+encodeURIComponent("/"+oldName)+'&target={{.CurrentDir}}'+encodeURIComponent("/"+newName);
  1379. axios.post(path, null, {
  1380. timeout: 15000,
  1381. headers: {
  1382. 'X-CSRF-TOKEN': '{{.CSRFToken}}'
  1383. },
  1384. validateStatus: function (status) {
  1385. return status == 200;
  1386. }
  1387. }).then(function (response) {
  1388. location.reload();
  1389. }).catch(function (error) {
  1390. let errorMessage;
  1391. if (error && error.response) {
  1392. switch (error.response.status) {
  1393. case 403:
  1394. errorMessage = "fs.rename.err_403";
  1395. break;
  1396. case 429:
  1397. errorMessage = "fs.rename.err_429";
  1398. break;
  1399. }
  1400. }
  1401. if (!errorMessage){
  1402. errorMessage = "fs.rename.err_generic";
  1403. }
  1404. errTxtEl.removeAttr("data-i18n")
  1405. errTxtEl.text($.t(errorMessage, {name: oldName}));
  1406. errDivEl.removeClass("d-none");
  1407. });
  1408. }
  1409. function showCreateNewFolder(sender) {
  1410. if (sender == 0) {
  1411. $('#file_manager_new_folder').removeClass("d-none");
  1412. $('#errorMsg').addClass("d-none");
  1413. let el = $('#file_manager_new_folder_input');
  1414. el.val("");
  1415. el.focus();
  1416. return;
  1417. }
  1418. $('#dirsbrowser_new_folder').removeClass("d-none");
  1419. $('#errorModalMsg').addClass("d-none");
  1420. let el = $('#dirsbrowser_new_folder_input');
  1421. el.val("");
  1422. el.focus();
  1423. }
  1424. function hideCreateNewFolder(sender) {
  1425. if (sender == 0){
  1426. $('#file_manager_new_folder').addClass("d-none");
  1427. } else {
  1428. $('#dirsbrowser_new_folder').addClass("d-none");
  1429. }
  1430. }
  1431. function createNewFolder() {
  1432. let errDivEl = $('#errorMsg');
  1433. let errTxtEl = $('#errorTxt');
  1434. let dirName = $("#file_manager_new_folder_input").val();
  1435. let submitButton = document.querySelector('#file_manager_add_folder');
  1436. let cancelButton = document.querySelector('#file_manager_cancel_folder');
  1437. errDivEl.addClass("d-none");
  1438. if (!dirName){
  1439. setI18NData(errTxtEl, "fs.folder_name_required");
  1440. errDivEl.removeClass("d-none");
  1441. return;
  1442. }
  1443. if (dirName.includes("/")){
  1444. setI18NData(errTxtEl, "fs.invalid_name");
  1445. errDivEl.removeClass("d-none");
  1446. return;
  1447. }
  1448. let path = '{{.DirsURL}}?path={{.CurrentDir}}' + encodeURIComponent("/"+dirName);
  1449. submitButton.setAttribute('data-kt-indicator', 'on');
  1450. submitButton.disabled = true;
  1451. cancelButton.disabled = true;
  1452. axios.post(path, null, {
  1453. timeout: 15000,
  1454. headers: {
  1455. 'X-CSRF-TOKEN': '{{.CSRFToken}}'
  1456. },
  1457. validateStatus: function (status) {
  1458. return status == 201;
  1459. }
  1460. }).then(function (response) {
  1461. location.reload();
  1462. }).catch(function (error) {
  1463. let errorMessage = "";
  1464. if (error && error.response) {
  1465. switch (error.response.status) {
  1466. case 403:
  1467. errorMessage = "fs.create_dir.err_403";
  1468. break;
  1469. case 429:
  1470. errorMessage = "fs.create_dir.err_429";
  1471. break;
  1472. }
  1473. }
  1474. if (!errorMessage){
  1475. errorMessage = "fs.create_dir.err_generic";
  1476. }
  1477. setI18NData(errTxtEl, errorMessage);
  1478. errDivEl.removeClass("d-none");
  1479. submitButton.removeAttribute('data-kt-indicator');
  1480. submitButton.disabled = false;
  1481. cancelButton.disabled = false;
  1482. });
  1483. }
  1484. //{{- end}}
  1485. var playerKeepAlive;
  1486. function uploadFiles(files) {
  1487. keepAlive();
  1488. let keepAliveTimer = setInterval(keepAlive, 300000);
  1489. let has_errors = false;
  1490. let index = 0;
  1491. let success = 0;
  1492. $('#errorMsg').addClass("d-none");
  1493. $('#loading_message').text("");
  1494. KTApp.showPageLoading();
  1495. function uploadFile() {
  1496. if (index >= files.length || has_errors) {
  1497. //console.log("upload done, index: "+index+" has errors: "+has_errors+" ok: "+success);
  1498. clearInterval(keepAliveTimer);
  1499. KTApp.hidePageLoading();
  1500. if (!has_errors) {
  1501. location.reload();
  1502. }
  1503. return;
  1504. }
  1505. let f = files[index];
  1506. let uploadPath;
  1507. //{{- if .ShareUploadBaseURL}}
  1508. uploadPath = '{{.ShareUploadBaseURL}}' + encodeURIComponent("/" + f.name);
  1509. //{{- else}}
  1510. uploadPath = '{{.FileURL}}?path={{.CurrentDir}}' + encodeURIComponent("/" + f.name);
  1511. //{{- end}}
  1512. let lastModified;
  1513. try {
  1514. lastModified = f.lastModified;
  1515. } catch (e) {
  1516. console.error("unable to get last modified time from file: " + e.message);
  1517. lastModified = "";
  1518. }
  1519. let uploadTxt = f.name;
  1520. if (files.length > 1){
  1521. uploadTxt = $.t('fs.uploading', {
  1522. idx: index + 1,
  1523. total: files.length,
  1524. name: uploadTxt
  1525. });
  1526. }
  1527. $('#loading_message').text(uploadTxt);
  1528. axios.post(uploadPath, f, {
  1529. headers: {
  1530. 'X-SFTPGO-MTIME': lastModified,
  1531. 'X-CSRF-TOKEN': '{{.CSRFToken}}'
  1532. },
  1533. onUploadProgress: function (progressEvent) {
  1534. if (!progressEvent.total){
  1535. return;
  1536. }
  1537. const percentage = Math.round((100 * progressEvent.loaded) / progressEvent.total);
  1538. if (percentage > 0 && percentage < 100){
  1539. $('#loading_message').text(`${uploadTxt} ${percentage}%`);
  1540. }
  1541. },
  1542. validateStatus: function (status) {
  1543. return status == 201;
  1544. }
  1545. }).then(function (response) {
  1546. index++;
  1547. success++;
  1548. uploadFile();
  1549. }).catch(function (error) {
  1550. let errorMessage;
  1551. if (error && error.response) {
  1552. switch (error.response.status) {
  1553. case 403:
  1554. errorMessage = "fs.upload.err_403";
  1555. break;
  1556. case 429:
  1557. errorMessage = "fs.upload.err_429";
  1558. break;
  1559. }
  1560. }
  1561. if (!errorMessage){
  1562. errorMessage = "fs.upload.err_generic";
  1563. }
  1564. index++;
  1565. has_errors = true;
  1566. setI18NData($('#errorTxt'), errorMessage);
  1567. $('#errorMsg').removeClass("d-none");
  1568. uploadFile();
  1569. });
  1570. }
  1571. uploadFile();
  1572. }
  1573. function openMediaPlayer(name, url){
  1574. $("#video_title").text(name);
  1575. $("#video_player").attr("src", url);
  1576. $("#video_player").get(0).load();
  1577. $('#modal_video_player').modal('show');
  1578. keepAlive();
  1579. playerKeepAlive = setInterval(keepAlive, 300000);
  1580. }
  1581. $(document).on("i18nshow", function(){
  1582. KTDatatablesServerSide.init();
  1583. var dropzone = new Dropzone("#upload_files", {
  1584. url: "{{.FilesURL}}?path={{.CurrentDir}}",
  1585. paramName: "filenames",
  1586. maxFiles: 250,
  1587. maxFilesize: null,
  1588. autoQueue: false,
  1589. addRemoveLinks: true,
  1590. autoProcessQueue: false,
  1591. filesizeBase: 1000,
  1592. init: function() {
  1593. var dropzone = this;
  1594. $("#upload_files_button").click(function(){
  1595. uploadFiles(dropzone.getAcceptedFiles());
  1596. });
  1597. }
  1598. });
  1599. dropzone.on("addedfile", file => {
  1600. file.previewElement.querySelector(".dz-progress").style.display = 'none';
  1601. });
  1602. $('#modal_video_player').on('hide.bs.modal', function () {
  1603. $("#video_player").get(0).pause();
  1604. if (playerKeepAlive != null) {
  1605. clearInterval(playerKeepAlive);
  1606. playerKeepAlive = null;
  1607. }
  1608. });
  1609. //{{- if not .ShareUploadBaseURL}}
  1610. $('#modal_rename').on('shown.bs.modal', function () {
  1611. let newNameEl = $('#rename_new_name');
  1612. newNameEl.focus();
  1613. newNameEl.select();
  1614. });
  1615. //{{- end}}
  1616. // onclick handlers
  1617. var createDirBtn = $('#id_create_dir_button');
  1618. if (createDirBtn){
  1619. createDirBtn.on("click", function (){
  1620. showCreateNewFolder(0);
  1621. });
  1622. }
  1623. var dirBrowserCreateDir = $('#id_dir_browser_create_dir');
  1624. if (dirBrowserCreateDir){
  1625. dirBrowserCreateDir.on("click", function(){
  1626. showCreateNewFolder(1);
  1627. });
  1628. }
  1629. var fileManagerAddFolder = $('#file_manager_add_folder');
  1630. if (fileManagerAddFolder){
  1631. fileManagerAddFolder.on("click", function() {
  1632. createNewFolder();
  1633. });
  1634. }
  1635. var fileManagerCancelCreateFolder = $('#file_manager_cancel_folder');
  1636. if (fileManagerCancelCreateFolder){
  1637. fileManagerCancelCreateFolder.on("click", function(){
  1638. hideCreateNewFolder(0);
  1639. })
  1640. }
  1641. var dirBrowserCancelCreateFolder = $('#dirsbrowser_cancel_folder');
  1642. if (dirBrowserCancelCreateFolder){
  1643. dirBrowserCancelCreateFolder.on("click", function(){
  1644. hideCreateNewFolder(1);
  1645. });
  1646. }
  1647. var doRenameBtn = $('#id_do_rename_button');
  1648. if (doRenameBtn){
  1649. doRenameBtn.on("click", function() {
  1650. doRename();
  1651. });
  1652. }
  1653. var doCopyBtn = $('#id_copy_button');
  1654. if (doCopyBtn){
  1655. doCopyBtn.on("click", function(){
  1656. doCopy();
  1657. });
  1658. }
  1659. var doMoveBtn = $('#id_move_button');
  1660. if (doMoveBtn){
  1661. doMoveBtn.on("click", function(){
  1662. doMove();
  1663. });
  1664. }
  1665. var dismissErrorModalBtn = $('#id_dismiss_error_modal_msg');
  1666. if (dismissErrorModalBtn){
  1667. dismissErrorModalBtn.on("click",function(){
  1668. $('#errorModalMsg').addClass("d-none");
  1669. });
  1670. }
  1671. });
  1672. </script>
  1673. {{- end}}
  1674. {{- define "additionalnavitems"}}
  1675. {{- if .QuotaUsage.HasQuotaInfo}}
  1676. <div class="d-flex align-items-center ms-2 ms-lg-3">
  1677. <div class="btn btn-icon btn-active-light-primary position-relative w-35px h-35px w-md-40px h-md-40px" data-kt-menu-trigger="{default:'click', lg: 'hover'}" data-kt-menu-attach="parent" data-kt-menu-placement="bottom-end">
  1678. <i class="ki-duotone {{if .QuotaUsage.IsQuotaLow}}ki-information-5 text-warning{{else}}ki-information-2{{end}} fs-2">
  1679. <span class="path1"></span>
  1680. <span class="path2"></span>
  1681. <span class="path3"></span>
  1682. </i>
  1683. </div>
  1684. <div class="menu menu-sub menu-sub-dropdown menu-column w-375px" data-kt-menu="true">
  1685. <div class="card">
  1686. <div class="card-header">
  1687. <h3 class="card-title"><span data-i18n="fs.quota_usage.title" class="text-gray-700 fw-bold fs-6">Quota usage</span></h3>
  1688. </div>
  1689. <div class="card-body p-0">
  1690. {{- if .QuotaUsage.HasDiskQuota}}
  1691. {{- $size := .QuotaUsage.GetQuotaSize}}
  1692. {{- $files := .QuotaUsage.GetQuotaFiles}}
  1693. <div class="d-flex align-items-center bg-hover-lighten py-3 px-9">
  1694. <div class="symbol symbol-40px symbol-circle me-5">
  1695. <span class="symbol-label {{ if .QuotaUsage.IsDiskQuotaLow }}bg-light-warning{{end}}">
  1696. <i class="ki-duotone ki-external-drive {{ if .QuotaUsage.IsDiskQuotaLow }}text-warning{{end}} fs-1">
  1697. <span class="path1"></span>
  1698. <span class="path2"></span>
  1699. <span class="path3"></span>
  1700. <span class="path4"></span>
  1701. <span class="path5"></span>
  1702. </i>
  1703. </span>
  1704. </div>
  1705. <div class="mb-1 pe-3 flex-grow-1">
  1706. <span data-i18n="fs.quota_usage.disk" class="fs-6 text-gray-900 fw-semibold">Disk quota</span>
  1707. {{- if $size}}
  1708. {{- $percentage := .QuotaUsage.GetQuotaSizePercentage}}
  1709. <div class="{{if .QuotaUsage.IsQuotaSizeLow}}text-warning{{else}}text-gray-700{{end}} fw-semibold fs-6">
  1710. <span {{if gt $percentage 0}}data-i18n="fs.quota_usage.size_percentage" data-i18n-options='{ "val": "{{$size}}", "percentage": {{$percentage}} }'{{else}}data-i18n="fs.quota_usage.size" data-i18n-options='{ "val": "{{$size}}" }'{{end}}></span>
  1711. </div>
  1712. {{- end}}
  1713. {{- if $files}}
  1714. {{- $percentage := .QuotaUsage.GetQuotaFilesPercentage}}
  1715. <div class="{{if .QuotaUsage.IsQuotaFilesLow}}text-warning{{else}}text-gray-700{{end}} fw-semibold fs-6">
  1716. <span {{if gt $percentage 0}}data-i18n="fs.quota_usage.files_percentage" data-i18n-options='{ "val": "{{$files}}", "percentage": {{$percentage}} }'{{else}}data-i18n="fs.quota_usage.files" data-i18n-options='{ "val": "{{$files}}" }'{{end}}></span>
  1717. </div>
  1718. {{- end}}
  1719. </div>
  1720. </div>
  1721. {{- end}}
  1722. {{- if .QuotaUsage.HasTranferQuota}}
  1723. {{- $total := .QuotaUsage.GetTotalTransferQuota}}
  1724. {{- $upload := .QuotaUsage.GetUploadTransferQuota}}
  1725. {{- $download := .QuotaUsage.GetDownloadTransferQuota}}
  1726. <div class="d-flex align-items-center bg-hover-lighten py-3 px-9">
  1727. <div class="symbol symbol-40px symbol-circle me-5">
  1728. <span class="symbol-label {{ if .QuotaUsage.IsTransferQuotaLow }}bg-light-warning{{end}}">
  1729. <i class="ki-duotone ki-arrow-right-left {{ if .QuotaUsage.IsTransferQuotaLow }}text-warning{{end}} fs-1">
  1730. <span class="path1"></span>
  1731. <span class="path2"></span>
  1732. </i>
  1733. </span>
  1734. </div>
  1735. <div class="mb-1 pe-3 flex-grow-1">
  1736. <span data-i18n="fs.quota_usage.transfer" class="fs-6 text-gray-900 fw-semibold">Transfer quota</span>
  1737. {{- if $total}}
  1738. {{$percentage := .QuotaUsage.GetTotalTransferQuotaPercentage}}
  1739. <div class="{{if .QuotaUsage.IsTotalTransferQuotaLow}}text-warning{{else}}text-gray-700{{end}} fw-semibold fs-6">
  1740. <span {{if gt $percentage 0}}data-i18n="fs.quota_usage.total_percentage" data-i18n-options='{ "val": "{{$total}}", "percentage": {{$percentage}} }'{{else}}data-i18n="fs.quota_usage.total" data-i18n-options='{ "val": "{{$total}}" }'{{end}}>
  1741. </span>
  1742. </div>
  1743. {{- end}}
  1744. {{- if $download}}
  1745. {{$percentage := .QuotaUsage.GetDownloadTransferQuotaPercentage}}
  1746. <div class="{{if .QuotaUsage.IsDownloadTransferQuotaLow}}text-warning{{else}}text-gray-700{{end}} fw-semibold fs-6">
  1747. <span {{if gt $percentage 0}}data-i18n="fs.quota_usage.downloads_percentage" data-i18n-options='{ "val": "{{$download}}", "percentage": {{$percentage}} }'{{else}}data-i18n="fs.quota_usage.downloads" data-i18n-options='{ "val": "{{$download}}" }'{{end}}>
  1748. </span>
  1749. </div>
  1750. {{- end}}
  1751. {{- if $upload}}
  1752. {{$percentage := .QuotaUsage.GetUploadTransferQuotaPercentage}}
  1753. <div class="{{if .QuotaUsage.IsUploadTransferQuotaLow}}text-warning{{else}}text-gray-700{{end}} fw-semibold fs-6">
  1754. <span {{if gt $percentage 0}}data-i18n="fs.quota_usage.uploads_percentage" data-i18n-options='{ "val": "{{$upload}}", "percentage": {{$percentage}} }'{{else}}data-i18n="fs.quota_usage.uploads" data-i18n-options='{ "val": "{{$upload}}" }'{{end}}>
  1755. </span>
  1756. </div>
  1757. {{- end}}
  1758. </div>
  1759. </div>
  1760. {{- end}}
  1761. </div>
  1762. </div>
  1763. </div>
  1764. </div>
  1765. {{- end}}
  1766. {{- end}}
  1767. {{- define "modals"}}
  1768. <div class="modal fade" tabindex="-1" id="modal_upload">
  1769. <div class="modal-dialog modal-dialog-centered mw-600px">
  1770. <div class="modal-content">
  1771. <div class="modal-header border-0">
  1772. <h3 data-i18n="fs.upload.text" class="modal-title">Upload files</h3>
  1773. <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
  1774. <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
  1775. </div>
  1776. </div>
  1777. <div class="modal-body">
  1778. <form id="upload_files_form" action="{{.FilesURL}}?path={{.CurrentDir}}" method="POST" enctype="multipart/form-data">
  1779. <div class="fv-row">
  1780. <div class="dropzone" id="upload_files">
  1781. <div class="dz-message needsclick align-items-center">
  1782. <i class="ki-duotone ki-file-up fs-3x text-primary"><span class="path1"></span><span class="path2"></span></i>
  1783. <div class="ms-4">
  1784. <h3 data-i18n="fs.upload.message" class="fs-5 fw-bold text-gray-900 mb-1">Drop files here or click to upload.</h3>
  1785. <!-- <span class="fs-7 fw-semibold text-gray-500">Upload up to 30 files</span> -->
  1786. </div>
  1787. </div>
  1788. </div>
  1789. </div>
  1790. </form>
  1791. </div>
  1792. <div class="modal-footer border-0">
  1793. <button data-i18n="general.cancel" type="button" class="btn btn-light me-5" data-bs-dismiss="modal">Cancel</button>
  1794. <button data-i18n="general.submit" type="button" id="upload_files_button" class="btn btn-primary" data-bs-dismiss="modal">Submit</button>
  1795. </div>
  1796. </div>
  1797. </div>
  1798. </div>
  1799. <div class="modal fade" tabindex="-1" id="modal_video_player" data-bs-backdrop="static" data-bs-keyboard="false">
  1800. <div class="modal-dialog modal-dialog-centered modal-lg">
  1801. <div class="modal-content">
  1802. <div class="modal-header border-0">
  1803. <h5 class="modal-title">
  1804. <span id="video_title"></span>
  1805. </h5>
  1806. <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
  1807. <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
  1808. </div>
  1809. </div>
  1810. <div class="modal-body">
  1811. <video id="video_player" width="100%" height="auto" controls preload="metadata">
  1812. <span data-i18n="general.html5_media_not_supported"></span>
  1813. </video>
  1814. </div>
  1815. </div>
  1816. </div>
  1817. </div>
  1818. {{- if not .ShareUploadBaseURL}}
  1819. <div class="modal fade" tabindex="-1" id="modal_rename">
  1820. <div class="modal-dialog modal-dialog-centered">
  1821. <div class="modal-content">
  1822. <div class="modal-header border-0">
  1823. <h5 class="modal-title">
  1824. <span id="rename_title"></span>
  1825. </h5>
  1826. <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
  1827. <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
  1828. </div>
  1829. </div>
  1830. <div class="modal-body">
  1831. <input id="rename_old_name" type="text" class="d-none"/>
  1832. <div class="mb-10">
  1833. <label data-i18n="fs.rename.new_name" for="rename_new_name" class="form-label"></label>
  1834. <input id="rename_new_name" type="text" class="form-control"/>
  1835. </div>
  1836. </div>
  1837. <div class="modal-footer border-0">
  1838. <button data-i18n="general.cancel" type="button" class="btn btn-secondary me-5" data-bs-dismiss="modal">Cancel</button>
  1839. <button data-i18n="general.submit" id="id_do_rename_button" type="button" class="btn btn-primary" data-bs-dismiss="modal">Submit</button>
  1840. </div>
  1841. </div>
  1842. </div>
  1843. </div>
  1844. <div class="modal fade" tabindex="-1" id="modal_move_or_copy">
  1845. <div class="modal-dialog modal-dialog-centered modal-lg">
  1846. <div class="modal-content">
  1847. <div class="modal-header border-0">
  1848. <h3 data-i18n="general.choose_target_folder" class="modal-title">
  1849. Choose target folder
  1850. </h3>
  1851. <div data-i18n="[aria-label]general.close" class="btn btn-icon btn-sm btn-active-color-primary" data-bs-dismiss="modal" aria-label="Close">
  1852. <i class="ki-duotone ki-cross fs-1"><span class="path1"></span><span class="path2"></span></i>
  1853. </div>
  1854. </div>
  1855. <div class="modal-body">
  1856. <div id="errorModalMsg" class="d-none rounded border-warning border border-dashed bg-light-warning d-flex align-items-center p-5 mb-10">
  1857. <i class="ki-duotone ki-information fs-3x text-warning me-5"><span class="path1"></span><span class="path2"></span><span class="path3"></span></i>
  1858. <div class="text-gray-700 fw-bold fs-5 d-flex flex-column pe-0 pe-sm-10">
  1859. <span id="errorModalTxt"></span>
  1860. </div>
  1861. <button id="id_dismiss_error_modal_msg" type="button" class="position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 btn btn-icon btn-sm btn-active-light-primary ms-sm-auto">
  1862. <i class="ki-duotone ki-cross fs-2x text-primary"><span class="path1"></span><span class="path2"></span></i>
  1863. </button>
  1864. </div>
  1865. <div class="row">
  1866. <div class="col-md-9 align-items-center d-flex flex-stack">
  1867. <div class="badge badge-lg badge-light-primary">
  1868. <div id="dirs_browser_nav" class="d-flex align-items-center flex-wrap">
  1869. </div>
  1870. </div>
  1871. </div>
  1872. <div class="col-md-3 align-items-center d-flex justify-content-end">
  1873. <button id="id_dir_browser_create_dir" type="button" class="btn btn-flex btn-primary">
  1874. <i class="ki-duotone ki-add-folder fs-2">
  1875. <span class="path1"></span>
  1876. <span class="path2"></span>
  1877. </i>
  1878. <span data-i18n="general.add"></span>
  1879. </button>
  1880. </div>
  1881. </div>
  1882. <div id="dirsbrowser_new_folder" class="d-flex align-items-center py-7 d-none">
  1883. <span>
  1884. <i class="ki-duotone ki-folder fs-2x text-primary me-4">
  1885. <span class="path1"></span>
  1886. <span class="path2"></span>
  1887. </i>
  1888. </span>
  1889. <input data-i18n="[placeholder]fs.create_folder_msg" id="dirsbrowser_new_folder_input" type="text" name="new_folder_name" placeholder="Enter the new folder name" class="form-control mw-250px me-3" />
  1890. <button class="btn btn-icon btn-light-primary me-3" id="dirsbrowser_add_folder">
  1891. <span class="indicator-label">
  1892. <i class="ki-duotone ki-check fs-1"></i>
  1893. </span>
  1894. <span class="indicator-progress">
  1895. <span class="spinner-border spinner-border-sm align-middle"></span>
  1896. </span>
  1897. </button>
  1898. <button class="btn btn-icon btn-light-danger" id="dirsbrowser_cancel_folder">
  1899. <i class="ki-duotone ki-cross fs-1">
  1900. <span class="path1"></span>
  1901. <span class="path2"></span>
  1902. </i>
  1903. </button>
  1904. </div>
  1905. <table id="dirsbrowser_list" class="table align-middle table-row-dashed fs-6 gy-5">
  1906. <thead>
  1907. <tr class="text-start text-muted fw-bold fs-7 text-uppercase gs-0">
  1908. <th></th>
  1909. <th data-i18n="general.name" class="min-w-250px">Name</th>
  1910. </tr>
  1911. </thead>
  1912. <tbody id="dirsbrowser_list_body" class="text-gray-600 fw-semibold">
  1913. </tbody>
  1914. </table>
  1915. <div class="form-floating d-none">
  1916. <input type="text" class="form-control form-control-solid" id="move_copy_source"/>
  1917. <label data-i18n="general.source_name" for="move_copy_source">Source name</label>
  1918. </div>
  1919. <div class="form-floating mb-5 mt-7">
  1920. <input type="text" class="form-control form-control-solid" id="move_copy_folder"/>
  1921. <label data-i18n="general.target_folder" for="move_copy_folder">Target folder</label>
  1922. </div>
  1923. <div class="form-floating" id="move_copy_name_container">
  1924. <input type="text" class="form-control form-control-solid" id="move_copy_name"/>
  1925. <label data-i18n="general.dest_name" for="move_copy_name">Destination name</label>
  1926. </div>
  1927. </div>
  1928. <div class="modal-footer border-0">
  1929. {{- if .CanAddFiles }}
  1930. <button data-i18n="fs.copy.msg" id="id_copy_button" type="button" class="btn btn-light-primary me-5" data-bs-dismiss="modal">Copy</button>
  1931. {{- end}}
  1932. {{- if .CanRename }}
  1933. <button data-i18n="fs.move.msg" id="id_move_button" type="button" class="btn btn-primary" data-bs-dismiss="modal">Move</button>
  1934. {{- end}}
  1935. </div>
  1936. </div>
  1937. </div>
  1938. </div>
  1939. {{- end}}
  1940. {{- end}}