From 38cc8a171f2315885ed2a64e7524ade682e8d1f0 Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 2 Sep 2023 22:25:45 +0000 Subject: [PATCH] first commit --- Makefile | 12 + README.md | 0 .../resources/fileassistant/fb.css | 68 +++++ .../luci-static/resources/fileassistant/fb.js | 288 ++++++++++++++++++ .../resources/fileassistant/file-icon.png | Bin 0 -> 1098 bytes .../resources/fileassistant/folder-icon.png | Bin 0 -> 1292 bytes .../resources/fileassistant/link-icon.png | Bin 0 -> 1622 bytes luasrc/controller/fileassistant.lua | 232 ++++++++++++++ luasrc/view/fileassistant.htm | 20 ++ .../rpcd/acl.d/luci-app-fileassistant.json | 11 + 10 files changed, 631 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 htdocs/luci-static/resources/fileassistant/fb.css create mode 100644 htdocs/luci-static/resources/fileassistant/fb.js create mode 100644 htdocs/luci-static/resources/fileassistant/file-icon.png create mode 100644 htdocs/luci-static/resources/fileassistant/folder-icon.png create mode 100644 htdocs/luci-static/resources/fileassistant/link-icon.png create mode 100644 luasrc/controller/fileassistant.lua create mode 100644 luasrc/view/fileassistant.htm create mode 100644 root/usr/share/rpcd/acl.d/luci-app-fileassistant.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a383ac8 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +# From https://github.com/DarkDean89/luci-app-filebrowser +# From https://github.com/stuarthua/oh-my-openwrt/tree/master/stuart/luci-app-fileassistant +# This is free software, licensed under the Apache License, Version 2.0 . + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI support for Fileassistant +LUCI_PKGARCH:=all + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/htdocs/luci-static/resources/fileassistant/fb.css b/htdocs/luci-static/resources/fileassistant/fb.css new file mode 100644 index 0000000..96cbbb3 --- /dev/null +++ b/htdocs/luci-static/resources/fileassistant/fb.css @@ -0,0 +1,68 @@ +.fb-container { + margin-top: 1rem; +} +.fb-container .cbi-button { + height: 1.8rem; +} +.fb-container .cbi-input-text { + margin-bottom: 1rem; + width: 100%; +} +.fb-container .panel-title { + padding-bottom: 0; + width: 50%; + border-bottom: none; +} +.fb-container .panel-container { + display: flex; + align-items: center; + justify-content: space-between; + padding-bottom: 1rem; + border-bottom: 1px solid #eee; +} +.fb-container .upload-container { + display: none; + margin: 1rem 0; +} +.fb-container .upload-file { + margin-right: 2rem; +} +.fb-container .cbi-value-field { + text-align: left; +} +.fb-container .parent-icon strong { + margin-left: 1rem; +} +.fb-container td[class$="-icon"] { + cursor: pointer; +} +.fb-container .file-icon, .fb-container .folder-icon, .fb-container .link-icon { + position: relative; +} +.fb-container .file-icon:before, .fb-container .folder-icon:before, .fb-container .link-icon:before { + display: inline-block; + width: 1.5rem; + height: 1.5rem; + content: ''; + background-size: contain; + margin: 0 0.5rem 0 1rem; + vertical-align: middle; +} +.fb-container .file-icon:before { + background-image: url(file-icon.png); +} +.fb-container .folder-icon:before { + background-image: url(folder-icon.png); +} +.fb-container .link-icon:before { + background-image: url(link-icon.png); +} +@media screen and (max-width: 480px) { + .fb-container .upload-file { + width: 14.6rem; + } + .fb-container .cbi-value-owner, + .fb-container .cbi-value-perm { + display: none; + } +} diff --git a/htdocs/luci-static/resources/fileassistant/fb.js b/htdocs/luci-static/resources/fileassistant/fb.js new file mode 100644 index 0000000..10c0d47 --- /dev/null +++ b/htdocs/luci-static/resources/fileassistant/fb.js @@ -0,0 +1,288 @@ +String.prototype.replaceAll = function(search, replacement) { + var target = this; + return target.replace(new RegExp(search, 'g'), replacement); +}; +(function () { + var iwxhr = new XHR(); + var listElem = document.getElementById("list-content"); + listElem.onclick = handleClick; + var currentPath; + var pathElem = document.getElementById("current-path"); + pathElem.onblur = function () { + update_list(this.value.trim()); + }; + pathElem.onkeyup = function (evt) { + if (evt.keyCode == 13) { + this.blur(); + } + }; + function removePath(filename, isdir) { + var c = confirm('Are you sure you want to delete ' + filename + ' ?'); + if (c) { + iwxhr.get('/cgi-bin/luci/admin/nas/fileassistant/delete', + { + path: concatPath(currentPath, filename), + isdir: isdir + }, + function (x, res) { + if (res.ec === 0) { + refresh_list(res.data, currentPath); + } + }); + } + } + + function installPath(filename, isdir) { + if (isdir === "1") { + alert('This is a Directory,Please Choose ipk file to install!'); + return; + } + var isipk = isIPK(filename); + if (isipk === 0) { + alert('Only allow installation ipk format!'); + return; + } + var c = confirm('Are you sure you want to install ' + filename + ' ?'); + if (c) { + iwxhr.get('/cgi-bin/luci/admin/nas/fileassistant/install', + { + filepath: concatPath(currentPath, filename), + isdir: isdir + }, + function (x, res) { + if (res.ec === 0) { + location.reload(); + alert('Successful installation!'); + } else { + alert('installation failed,Please check the file format!'); + } + }); + } + } + + function isIPK(filename) { + var index= filename.lastIndexOf("."); + var ext = filename.substr(index+1); + if (ext === 'ipk') { + return 1; + } else { + return 0; + } + } + + function renamePath(filename) { + var newname = prompt('Please enter a new filename:', filename); + if (newname) { + newname = newname.trim(); + if (newname != filename) { + var newpath = concatPath(currentPath, newname); + iwxhr.get('/cgi-bin/luci/admin/nas/fileassistant/rename', + { + filepath: concatPath(currentPath, filename), + newpath: newpath + }, + function (x, res) { + if (res.ec === 0) { + refresh_list(res.data, currentPath); + } + } + ); + } + } + } + + function openpath(filename, dirname) { + dirname = dirname || currentPath; + window.open('/cgi-bin/luci/admin/nas/fileassistant/open?path=' + + encodeURIComponent(dirname) + '&filename=' + + encodeURIComponent(filename)); + } + + function getFileElem(elem) { + if (elem.className.indexOf('-icon') > -1) { + return elem; + } + else if (elem.parentNode.className.indexOf('-icon') > -1) { + return elem.parentNode; + } + } + + function concatPath(path, filename) { + if (path === '/') { + return path + filename; + } + else { + return path.replace(/\/$/, '') + '/' + filename; + } + } + + function handleClick(evt) { + var targetElem = evt.target; + var infoElem; + if (targetElem.className.indexOf('cbi-button-remove') > -1) { + infoElem = targetElem.parentNode.parentNode; + removePath(infoElem.dataset['filename'] , infoElem.dataset['isdir']) + } + else if (targetElem.className.indexOf('cbi-button-install') > -1) { + infoElem = targetElem.parentNode.parentNode; + installPath(infoElem.dataset['filename'] , infoElem.dataset['isdir']) + } + else if (targetElem.className.indexOf('cbi-button-edit') > -1) { + renamePath(targetElem.parentNode.parentNode.dataset['filename']); + } + else if (targetElem = getFileElem(targetElem)) { + if (targetElem.className.indexOf('parent-icon') > -1) { + update_list(currentPath.replace(/\/[^/]+($|\/$)/, '')); + } + else if (targetElem.className.indexOf('file-icon') > -1) { + openpath(targetElem.parentNode.dataset['filename']); + } + else if (targetElem.className.indexOf('link-icon') > -1) { + infoElem = targetElem.parentNode; + var filepath = infoElem.dataset['linktarget']; + if (filepath) { + if (infoElem.dataset['isdir'] === "1") { + update_list(filepath); + } + else { + var lastSlash = filepath.lastIndexOf('/'); + openpath(filepath.substring(lastSlash + 1), filepath.substring(0, lastSlash)); + } + } + } + else if (targetElem.className.indexOf('folder-icon') > -1) { + update_list(concatPath(currentPath, targetElem.parentNode.dataset['filename'])) + } + } + } + function refresh_list(filenames, path) { + var listHtml = ''; + if (path !== '/') { + listHtml += ''; + } + if (filenames) { + for (var i = 0; i < filenames.length; i++) { + var line = filenames[i]; + if (line) { + var f = line.match(/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+([\S\s]+)/); + var isLink = f[1][0] === 'z' || f[1][0] === 'l' || f[1][0] === 'x'; + var o = { + displayname: f[9], + filename: isLink ? f[9].split(' -> ')[0] : f[9], + perms: f[1], + date: f[7] + ' ' + f[6] + ' ' + f[8], + size: f[5], + owner: f[3], + icon: (f[1][0] === 'd') ? "folder-icon" : (isLink ? "link-icon" : "file-icon") + }; + + var install_btn = ''; + var index= o.filename.lastIndexOf("."); + var ext = o.filename.substr(index+1); + if (ext === 'ipk') { + install_btn = ''; + } + + listHtml += '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + } + } + } + listHtml += "
..
' + + '' + o.displayname + '' + + ''+o.owner+''+o.date+''+o.size+''+o.perms+'\ + \ + ' + + install_btn + + '
"; + listElem.innerHTML = listHtml; + } + function update_list(path, opt) { + opt = opt || {}; + path = concatPath(path, ''); + if (currentPath != path) { + iwxhr.get('/cgi-bin/luci/admin/nas/fileassistant/list', + {path: path}, + function (x, res) { + if (res.ec === 0) { + refresh_list(res.data, path); + } + else { + refresh_list([], path); + } + } + ); + if (!opt.popState) { + history.pushState({path: path}, null, '?path=' + path); + } + currentPath = path; + pathElem.value = currentPath; + } + }; + + var uploadToggle = document.getElementById('upload-toggle'); + var uploadContainer = document.getElementById('upload-container'); + var isUploadHide = true; + uploadToggle.onclick = function() { + if (isUploadHide) { + uploadContainer.style.display = 'inline-flex'; + } + else { + uploadContainer.style.display = 'none'; + } + isUploadHide = !isUploadHide; + }; + var uploadBtn = uploadContainer.getElementsByClassName('cbi-input-apply')[0]; + uploadBtn.onclick = function (evt) { + var uploadinput = document.getElementById('upload-file'); + var fullPath = uploadinput.value; + if (!fullPath) { + evt.preventDefault(); + } + else { + var formData = new FormData(); + var startIndex = (fullPath.indexOf('\\') >= 0 ? fullPath.lastIndexOf('\\') : fullPath.lastIndexOf('/')); + formData.append('upload-filename', fullPath.substring(startIndex + 1)); + formData.append('upload-dir', concatPath(currentPath, '')); + formData.append('upload-file', uploadinput.files[0]); + var xhr = new XMLHttpRequest(); + xhr.open("POST", "/cgi-bin/luci/admin/nas/fileassistant/upload", true); + xhr.onload = function() { + if (xhr.status == 200) { + var res = JSON.parse(xhr.responseText); + refresh_list(res.data, currentPath); + uploadinput.value = ''; + } + else { + alert('Upload failed, please try again later...'); + } + }; + xhr.send(formData); + } + }; + + document.addEventListener('DOMContentLoaded', function(evt) { + var initPath = '/'; + if (/path=([/\w]+)/.test(location.search)) { + initPath = RegExp.$1; + } + update_list(initPath, {popState: true}); + }); + window.addEventListener('popstate', function (evt) { + var path = '/'; + if (evt.state && evt.state.path) { + path = evt.state.path; + } + update_list(path, {popState: true}); + }); + +})(); diff --git a/htdocs/luci-static/resources/fileassistant/file-icon.png b/htdocs/luci-static/resources/fileassistant/file-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f156dc1c7ce823b64401a62e249a377a52b20518 GIT binary patch literal 1098 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD~={fiXV7C&U%V9R;)t0gq*U=0MX+ zN`m}?85o(ESyC4w|-+%u4`_J-}sTczTlbWZCV@SoVw>LwB4?74P_~>1@e8=wH z&fU=qckC8l&g%8~Z~JzK84Nd5-}BA*yTAO~_e*zul#cA)^xP@%bNkV}@@1=Sm_nnE zsJyDOVK&^ans!Nao=4-k^E=Evcdue#I_C3e(OiQAVx|XrN*fszgEv38bVX+ZBLk<7 zgTLLX>}*a2RcCnDxd+V)Vr>uTRp^EDZ{YS5zRLWD zX+K+oZ^7&}j0wVDUNLYaA7pxy&DgP{)_cNgRs|`Y0}Vb07X9Dsw#Gcb+lVpm)>U4M z$qW{)40rHDoarZKhfLm@t>etnLD$M)VPGsasI53}8!m{U0}y(IC{&iZ7U@Z(z4w(uZ zdD~j}0zR}v9-Sy-)WBcf^Xq4217k{qqRfJRo(bPOX3BrNq`T>@q1(e(-elb6Mw<&;$T^(=;mp literal 0 HcmV?d00001 diff --git a/htdocs/luci-static/resources/fileassistant/folder-icon.png b/htdocs/luci-static/resources/fileassistant/folder-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1370df3ad554fcb4d11aa0a72510dd3011cb816b GIT binary patch literal 1292 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD~={fpJ5CPlzj!I|_zS2>kviS_E`% ze@T#EFaskKGYcy_2PYRdFCV{vkf@Z5tfH!#x`w8fw!Wc}v8kDbjjg?lo4bdnkDq@~ zNLWNlrlO|7@I&aaqIS-d-oqce)9C$^A|5)y?Oio-m(|)^6tj@ffbcxT$swdA#S9dDM(%YwA z=;`}t7X5o_cb4ab+U_%F*2&+yf4uTu`aSv6Ja0aq(Nkn#*l^0gUv=J|pA)vGDNXH| z5%v0TKlc`43d@{VRt#TOUdn344I z@hKL)O`5@Fmn5gzE%#Z_?rWK(J~!oMUO$Ulc&~%n!wIve^Uc&^UG?bc&k5U^ru=(j z@`QzLrH*H?*MqJt41c#ox+Tos!|->br&GfhO@k9=UK}j*vdPWQ;8$FBYF>a>4u(+{}*QfsHlKk>Zp>C@i+ z=-vG8o4>hkyxc=p^WV?EPtm_nYx(`x^Fv|LOMcFc{wo~4#ynGFUisp>jo<(Kx0>Jm zv2ORj?3$<7m1}nIe$U6#pOIMcz^`O^KR5$71pQ;2AX!`B8su&V67Y2Ob6Mw<&;$S{ CYKQCq literal 0 HcmV?d00001 diff --git a/htdocs/luci-static/resources/fileassistant/link-icon.png b/htdocs/luci-static/resources/fileassistant/link-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..03cc82cdfabae20c1036959e6244ba5039f74697 GIT binary patch literal 1622 zcmV-c2C4apP) zTWB0r7{`CvG(@YF+Ik_0m)2IR7C|eeQV{DEMU;wAE9isZi=a^9kCznM9+=gj%O9~|iA?Ch8G|LvKX zvojMgeDf4=0GI-r5w@eiYrtSt1E87hgb!HSz5)jOD;Wlz+y%TJx`-UC0475hkrF!y z|EbQ~jx;J63Y}aZx_|<#Gy(i^GIRk&FaiAWLFfXCUp~Y|grgsLkMIX~ z;?`HLNi`mIEI~P-w`uS~5@_sDlIxvK}}VL6M_I9?R1z#|E36NA7Z}R<1N{Sh4(!8SZE_Zv$+?YKISw53yv=Fgxz?YfS}b~!eD<6M zUIvzgC^i9NnF#<`1RT!sqAzf}3!@y@68?A#fUP-Rup1cgJrW8T0e&Wu8Q@D{=g)An zg;9=J!fyf$;||)obvywqt6(s+5mWe0fM_pZ#`C~v zpbp_T8^|u;oGv&BT8Vzz&#)Uq{8MH4XMJzb1n|o+)rEhkwO#LbAWQZYKCXyKonflm z=l{g{FW-EBi)aFPpjY8%_>{l|C`qrw&+sLI2~dJi;b-`ezyv5r=qGj$KYod<9td;+`)+|mA@Ccs=$3co8R zz=fy^A54ID)P)ZwK!(cj!2|%s;e!dFI(*PAfco%3uK*T)3ylI;_-$wtpvv$^sag1- zOMoFP{5fb5z`~yg4FXvB3y``17Je5}7Qn(6kg5O{zK|3Ju<&z`ngAAlPErye_V5=2 zdw{79+n)kH0xl}~9=SFI|p1p)eSSG!gf{{8p~KT1*%U@6r+$1wTuAq@fk!ri&!6BXe@8Ui$d&A4Cm>;pC7 zLmC1Ai~;vjUIJC&LplOLc?r~o4`~Si#U)S~KBOlAiUmAeTUO_>isu&|L!c z;X~R2!2A+e_$^Ww0M1J=*82N5Sor^=<(EK?0c&wz$9jL}ex1=U17tL@@aLdO0HXsY zfO1R#<(L4P>u0=T5&T2kj}++9rF zkmTW&Ey!+=+&13^jILY^9HaXf|B6f!uUrnC6BKs_+0Bi{T;4$TA=?qEz)yt*{s1fq zRAXJ)jQlqAdji$S!@>faz+UXMP|9P#zsR3K(DUg(qYwDLOM&fe9Jn(`rS)I{compN zKBilPRNBTS@;x?vm_@O**oj05{{=dgd%", "/") + path = path:gsub(" ", "\ ") + local success + if isdir then + success = os.execute('rm -r "'..path..'"') + else + success = os.remove(path) + end + list_response(nixio.fs.dirname(path), success) +end + +function fileassistant_rename() + local filepath = luci.http.formvalue("filepath") + local newpath = luci.http.formvalue("newpath") + local success = os.execute('mv "'..filepath..'" "'..newpath..'"') + list_response(nixio.fs.dirname(filepath), success) +end + +function fileassistant_install() + local filepath = luci.http.formvalue("filepath") + local isdir = luci.http.formvalue("isdir") + local ext = filepath:match(".+%.(%w+)$") + filepath = filepath:gsub("<>", "/") + filepath = filepath:gsub(" ", "\ ") + local success + if isdir == "1" then + success = false + elseif ext == "ipk" then + success = installIPK(filepath) + else + success = false + end + list_response(nixio.fs.dirname(filepath), success) +end + +function installIPK(filepath) + luci.sys.exec('opkg --force-depends install "'..filepath..'"') + luci.sys.exec('rm -rf /tmp/luci-*') + return true; +end + +function fileassistant_upload() + local filecontent = luci.http.formvalue("upload-file") + local filename = luci.http.formvalue("upload-filename") + local uploaddir = luci.http.formvalue("upload-dir") + local filepath = uploaddir..filename + + local fp + luci.http.setfilehandler( + function(meta, chunk, eof) + if not fp and meta and meta.name == "upload-file" then + fp = io.open(filepath, "w") + end + if fp and chunk then + fp:write(chunk) + end + if fp and eof then + fp:close() + end + end + ) + + list_response(uploaddir, true) +end + +function scandir(directory) + local i, t, popen = 0, {}, io.popen + + local pfile = popen("ls -lh \""..directory.."\" | egrep '^d' ; ls -lh \""..directory.."\" | egrep -v '^d|^l'") + for fileinfo in pfile:lines() do + i = i + 1 + t[i] = fileinfo + end + pfile:close() + pfile = popen("ls -lh \""..directory.."\" | egrep '^l' ;") + for fileinfo in pfile:lines() do + i = i + 1 + linkindex, _, linkpath = string.find(fileinfo, "->%s+(.+)$") + local finalpath; + if string.sub(linkpath, 1, 1) == "/" then + finalpath = linkpath + else + finalpath = nixio.fs.realpath(directory..linkpath) + end + local linktype; + if not finalpath then + finalpath = linkpath; + linktype = 'x' + elseif nixio.fs.stat(finalpath, "type") == "dir" then + linktype = 'z' + else + linktype = 'l' + end + fileinfo = string.sub(fileinfo, 2, linkindex - 1) + fileinfo = linktype..fileinfo.."-> "..finalpath + t[i] = fileinfo + end + pfile:close() + return t +end + +MIME_TYPES = { + ["txt"] = "text/plain"; + ["conf"] = "text/plain"; + ["ovpn"] = "text/plain"; + ["log"] = "text/plain"; + ["js"] = "text/javascript"; + ["json"] = "application/json"; + ["css"] = "text/css"; + ["htm"] = "text/html"; + ["html"] = "text/html"; + ["patch"] = "text/x-patch"; + ["c"] = "text/x-csrc"; + ["h"] = "text/x-chdr"; + ["o"] = "text/x-object"; + ["ko"] = "text/x-object"; + + ["bmp"] = "image/bmp"; + ["gif"] = "image/gif"; + ["png"] = "image/png"; + ["jpg"] = "image/jpeg"; + ["jpeg"] = "image/jpeg"; + ["svg"] = "image/svg+xml"; + + ["zip"] = "application/zip"; + ["pdf"] = "application/pdf"; + ["xml"] = "application/xml"; + ["xsl"] = "application/xml"; + ["doc"] = "application/msword"; + ["ppt"] = "application/vnd.ms-powerpoint"; + ["xls"] = "application/vnd.ms-excel"; + ["odt"] = "application/vnd.oasis.opendocument.text"; + ["odp"] = "application/vnd.oasis.opendocument.presentation"; + ["pl"] = "application/x-perl"; + ["sh"] = "application/x-shellscript"; + ["php"] = "application/x-php"; + ["deb"] = "application/x-deb"; + ["iso"] = "application/x-cd-image"; + ["tgz"] = "application/x-compressed-tar"; + + ["mp3"] = "audio/mpeg"; + ["ogg"] = "audio/x-vorbis+ogg"; + ["wav"] = "audio/x-wav"; + + ["mpg"] = "video/mpeg"; + ["mpeg"] = "video/mpeg"; + ["avi"] = "video/x-msvideo"; +} + +function to_mime(filename) + if type(filename) == "string" then + local ext = filename:match("[^%.]+$") + + if ext and MIME_TYPES[ext:lower()] then + return MIME_TYPES[ext:lower()] + end + end + + return "application/octet-stream" +end diff --git a/luasrc/view/fileassistant.htm b/luasrc/view/fileassistant.htm new file mode 100644 index 0000000..2211bc3 --- /dev/null +++ b/luasrc/view/fileassistant.htm @@ -0,0 +1,20 @@ +<%+header%> + + +

File Assistant

+
+ +
+
File List
+ +
+
+ + +
+
+
+ + + +<%+footer%> diff --git a/root/usr/share/rpcd/acl.d/luci-app-fileassistant.json b/root/usr/share/rpcd/acl.d/luci-app-fileassistant.json new file mode 100644 index 0000000..8db4ea3 --- /dev/null +++ b/root/usr/share/rpcd/acl.d/luci-app-fileassistant.json @@ -0,0 +1,11 @@ +{ + "luci-app-fileassistant": { + "description": "Grant UCI access for luci-app-fileassistant", + "read": { + "uci": [ "fileassistant" ] + }, + "write": { + "uci": [ "fileassistant" ] + } + } +}