first commit

main
Ben 1 year ago
commit 38cc8a171f

@ -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

@ -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;
}
}

@ -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 DirectoryPlease 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 failedPlease 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 = '<table class="cbi-section-table"><tbody>';
if (path !== '/') {
listHtml += '<tr class="cbi-section-table-row cbi-rowstyle-2"><td class="parent-icon" colspan="6"><strong>..</strong></td></tr>';
}
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 = '<button class="cbi-button cbi-button-install" style="visibility: hidden;">Install</button>';
var index= o.filename.lastIndexOf(".");
var ext = o.filename.substr(index+1);
if (ext === 'ipk') {
install_btn = '<button class="cbi-button cbi-button-install">Install</button>';
}
listHtml += '<tr class="cbi-section-table-row cbi-rowstyle-' + (1 + i%2)
+ '" data-filename="' + o.filename + '" data-isdir="' + Number(f[1][0] === 'd' || f[1][0] === 'z') + '"'
+ ((f[1][0] === 'z' || f[1][0] === 'l') ? (' data-linktarget="' + f[9].split(' -> ')[1]) : '')
+ '">'
+ '<td class="cbi-value-field ' + o.icon + '">'
+ '<strong>' + o.displayname + '</strong>'
+ '</td>'
+ '<td class="cbi-value-field cbi-value-owner">'+o.owner+'</td>'
+ '<td class="cbi-value-field cbi-value-date">'+o.date+'</td>'
+ '<td class="cbi-value-field cbi-value-size">'+o.size+'</td>'
+ '<td class="cbi-value-field cbi-value-perm">'+o.perms+'</td>'
+ '<td class="cbi-section-table-cell">\
<button class="cbi-button cbi-button-edit">Edit</button>\
<button class="cbi-button cbi-button-remove">Delete</button>'
+ install_btn
+ '</td>'
+ '</tr>';
}
}
}
listHtml += "</table>";
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});
});
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,232 @@
module("luci.controller.fileassistant", package.seeall)
function index()
entry({"admin", "nas"}, firstchild(), _("NAS") , 45).dependent = false
entry({"admin", "nas"}, firstchild(), "NAS", 44).dependent = false
local page
page = entry({"admin", "nas", "fileassistant"}, template("fileassistant"), _("File Assistant"), 1)
page.i18n = "base"
page.dependent = true
page.acl_depends = { "luci-app-fileassistant" }
page = entry({"admin", "nas", "fileassistant", "list"}, call("fileassistant_list"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "open"}, call("fileassistant_open"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "delete"}, call("fileassistant_delete"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "rename"}, call("fileassistant_rename"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "upload"}, call("fileassistant_upload"), nil)
page.leaf = true
page = entry({"admin", "nas", "fileassistant", "install"}, call("fileassistant_install"), nil)
page.leaf = true
end
function list_response(path, success)
luci.http.prepare_content("application/json")
local result
if success then
local rv = scandir(path)
result = {
ec = 0,
data = rv
}
else
result = {
ec = 1
}
end
luci.http.write_json(result)
end
function fileassistant_list()
local path = luci.http.formvalue("path")
list_response(path, true)
end
function fileassistant_open()
local path = luci.http.formvalue("path")
local filename = luci.http.formvalue("filename")
local io = require "io"
local mime = to_mime(filename)
file = path..filename
local download_fpi = io.open(file, "r")
luci.http.header('Content-Disposition', 'inline; filename="'..filename..'"' )
luci.http.prepare_content(mime)
luci.ltn12.pump.all(luci.ltn12.source.file(download_fpi), luci.http.write)
end
function fileassistant_delete()
local path = luci.http.formvalue("path")
local isdir = luci.http.formvalue("isdir")
path = path:gsub("<>", "/")
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

@ -0,0 +1,20 @@
<%+header%>
<link rel="stylesheet" href="/luci-static/resources/fileassistant/fb.css?v=@ver">
<h2 name="content">File Assistant</h2>
<fieldset class="cbi-section fb-container">
<input id="current-path" type="text" class="current-path cbi-input-text" value="/"/>
<div class="panel-container">
<div class="panel-title">File List</div>
<button id="upload-toggle" class="upload-toggle cbi-button cbi-button-edit">Upload</button>
</div>
<div class="upload-container" id="upload-container">
<input id="upload-file" name="upload-file" class="upload-file" type="file">
<button type="button" class="cbi-button cbi-input-apply">Click to Upload</button>
</div>
<div id="list-content"></div>
</fieldset>
<script src="/luci-static/resources/fileassistant/fb.js?v=@ver"></script>
<%+footer%>

@ -0,0 +1,11 @@
{
"luci-app-fileassistant": {
"description": "Grant UCI access for luci-app-fileassistant",
"read": {
"uci": [ "fileassistant" ]
},
"write": {
"uci": [ "fileassistant" ]
}
}
}
Loading…
Cancel
Save