Skip to content

Commit

Permalink
Merge pull request openwrt#6553 from stokito/luc-mod-system_filemanager
Browse files Browse the repository at this point in the history
Add File Manager / File Browser based on form.FileUpload
  • Loading branch information
jow- authored Apr 9, 2024
2 parents b576339 + 6afea76 commit 1181915
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 12 deletions.
15 changes: 15 additions & 0 deletions applications/luci-app-filebrowser/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This is free software, licensed under the Apache License, Version 2.0 .

include $(TOPDIR)/rules.mk

LUCI_TITLE:=LuCI File Browser module
LUCI_DEPENDS:=+luci-base

PKG_LICENSE:=Apache-2.0
PKG_VERSION:=1.1.0
PKG_RELEASE:=1
PKG_MAINTAINER:=Sergey Ponomarev <[email protected]>

include ../../luci.mk

# call BuildPackage - OpenWrt buildroot signature
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';
'require view';
'require ui';
'require form';

var formData = {
files: {
root: null,
}
};

return view.extend({
render: function() {
var m, s, o;

m = new form.JSONMap(formData, _('File Browser'), '');

s = m.section(form.NamedSection, 'files', 'files');

o = s.option(form.FileUpload, 'root', '');
o.root_directory = '/';
o.browser = true;
o.show_hidden = true;
o.enable_upload = true;
o.enable_remove = true;
o.enable_download = true;

return m.render();
},

handleSave: null,
handleSaveApply: null,
handleReset: null
})
11 changes: 11 additions & 0 deletions applications/luci-app-filebrowser/po/templates/filebrowser.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"

#: applications/luci-app-filebrowser/htdocs/luci-static/resources/view/system/filebrowser.js:16
#: applications/luci-app-filebrowser/root/usr/share/luci/menu.d/luci-app-filebrowser.json:3
msgid "File Browser"
msgstr ""

#: applications/luci-app-filebrowser/root/usr/share/rpcd/acl.d/luci-app-filebrowser.json:3
msgid "Grant access to File Browser"
msgstr ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"admin/system/filebrowser": {
"title": "File Browser",
"order": 80,
"action": {
"type": "view",
"path": "system/filebrowser"
},
"depends": {
"acl": [ "luci-app-filebrowser" ]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"luci-app-filebrowser": {
"description": "Grant access to File Browser",
"write": {
"cgi-io": [ "upload", "download" ],
"ubus": {
"file": [ "*" ]
},
"file": {
"/*": [ "list", "read", "write" ]
}
}
}
}
21 changes: 21 additions & 0 deletions modules/luci-base/htdocs/luci-static/resources/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -4543,12 +4543,23 @@ var CBIFileUpload = CBIValue.extend(/** @lends LuCI.form.FileUpload.prototype */
__init__: function(/* ... */) {
this.super('__init__', arguments);

this.browser = false;
this.show_hidden = false;
this.enable_upload = true;
this.enable_remove = true;
this.enable_download = false;
this.root_directory = '/etc/luci-uploads';
},


/**
* Open in a file browser mode instead of selecting for a file
*
* @name LuCI.form.FileUpload.prototype#browser
* @type boolean
* @default false
*/

/**
* Toggle display of hidden files.
*
Expand Down Expand Up @@ -4593,6 +4604,14 @@ var CBIFileUpload = CBIValue.extend(/** @lends LuCI.form.FileUpload.prototype */
* @default true
*/

/**
* Toggle download file functionality.
*
* @name LuCI.form.FileUpload.prototype#enable_download
* @type boolean
* @default false
*/

/**
* Specify the root directory for file browsing.
*
Expand All @@ -4614,9 +4633,11 @@ var CBIFileUpload = CBIValue.extend(/** @lends LuCI.form.FileUpload.prototype */
var browserEl = new ui.FileUpload((cfgvalue != null) ? cfgvalue : this.default, {
id: this.cbid(section_id),
name: this.cbid(section_id),
browser: this.browser,
show_hidden: this.show_hidden,
enable_upload: this.enable_upload,
enable_remove: this.enable_remove,
enable_download: this.enable_download,
root_directory: this.root_directory,
disabled: (this.readonly != null) ? this.readonly : this.map.readonly
});
Expand Down
62 changes: 50 additions & 12 deletions modules/luci-base/htdocs/luci-static/resources/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -2613,6 +2613,9 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
* @typedef {LuCI.ui.AbstractElement.InitOptions} InitOptions
* @memberof LuCI.ui.FileUpload
*
* @property {boolean} [browser=false]
* Use a file browser mode.
*
* @property {boolean} [show_hidden=false]
* Specifies whether hidden files should be displayed when browsing remote
* files. Note that this is not a security feature, hidden files are always
Expand All @@ -2633,6 +2636,9 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
* remotely depends on the ACL setup for the current session. This option
* merely controls whether the file remove controls are rendered or not.
*
* @property {boolean} [enable_download=false]
* Specifies whether the widget allows the user to download files.
*
* @property {string} [root_directory=/etc/luci-uploads]
* Specifies the remote directory the upload and file browsing actions take
* place in. Browsing to directories outside the root directory is
Expand All @@ -2643,9 +2649,11 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
__init__: function(value, options) {
this.value = value;
this.options = Object.assign({
browser: false,
show_hidden: false,
enable_upload: true,
enable_remove: true,
enable_download: false,
root_directory: '/etc/luci-uploads'
}, options);
},
Expand All @@ -2664,7 +2672,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {

/** @override */
render: function() {
return L.resolveDefault(this.value != null ? fs.stat(this.value) : null).then(L.bind(function(stat) {
var renderFileBrowser = L.resolveDefault(this.value != null ? fs.stat(this.value) : null).then(L.bind(function(stat) {
var label;

if (L.isObject(stat) && stat.type != 'directory')
Expand All @@ -2676,13 +2684,13 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
label = [ this.iconForType('file'), ' %s (%s)'.format(this.truncatePath(this.value), _('File not accessible')) ];
else
label = [ _('Select file…') ];

return this.bind(E('div', { 'id': this.options.id }, [
E('button', {
'class': 'btn',
'click': UI.prototype.createHandlerFn(this, 'handleFileBrowser'),
'disabled': this.options.disabled ? '' : null
}, label),
let btnOpenFileBrowser = E('button', {
'class': 'btn open-file-browser',
'click': UI.prototype.createHandlerFn(this, 'handleFileBrowser'),
'disabled': this.options.disabled ? '' : null
}, label);
var fileBrowserEl = E('div', { 'id': this.options.id }, [
btnOpenFileBrowser,
E('div', {
'class': 'cbi-filebrowser'
}),
Expand All @@ -2691,8 +2699,18 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
'name': this.options.name,
'value': this.value
})
]));
]);
return this.bind(fileBrowserEl);
}, this));
// in a browser mode open dir listing after render by clicking on a Select button
if (this.options.browser) {
return renderFileBrowser.then(function (fileBrowserEl) {
var btnOpenFileBrowser = fileBrowserEl.getElementsByClassName('open-file-browser').item(0);
btnOpenFileBrowser.click();
return fileBrowserEl;
});
}
return renderFileBrowser
},

/** @private */
Expand Down Expand Up @@ -2917,6 +2935,10 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
'class': 'btn',
'click': UI.prototype.createHandlerFn(this, 'handleReset')
}, [ _('Deselect') ]) : '',
this.options.enable_download && list[i].type == 'file' ? E('button', {
'class': 'btn',
'click': UI.prototype.createHandlerFn(this, 'handleDownload', entrypath, list[i])
}, [ _('Download') ]) : '',
this.options.enable_remove ? E('button', {
'class': 'btn cbi-button-negative',
'click': UI.prototype.createHandlerFn(this, 'handleDelete', entrypath, list[i])
Expand Down Expand Up @@ -2947,11 +2969,11 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
rows,
E('div', { 'class': 'right' }, [
this.renderUpload(path, list),
E('a', {
!this.options.browser ? E('a', {
'href': '#',
'class': 'btn',
'click': UI.prototype.createHandlerFn(this, 'handleCancel')
}, _('Cancel'))
}, _('Cancel')) : ''
]),
]);
},
Expand Down Expand Up @@ -2980,6 +3002,22 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
this.handleCancel(ev);
},

/** @private */
handleDownload: function(path, fileStat, ev) {
fs.read_direct(path, 'blob').then(function (blob) {
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = fileStat.name;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
}).catch(function(err) {
alert(_('Download failed: %s').format(err.message));
});
},

/** @private */
handleSelect: function(path, fileStat, ev) {
var browser = dom.parent(ev.target, '.cbi-filebrowser'),
Expand All @@ -2989,7 +3027,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
dom.content(ul, E('em', { 'class': 'spinning' }, _('Loading directory contents…')));
L.resolveDefault(fs.list(path), []).then(L.bind(this.renderListing, this, browser, path));
}
else {
else if (!this.options.browser) {
var button = this.node.firstElementChild,
hidden = this.node.lastElementChild;

Expand Down

0 comments on commit 1181915

Please sign in to comment.