Add pickexclude to file tree operations so folders can be excluded to minimize confusion.

This commit is contained in:
dlandon
2024-04-26 16:44:18 -05:00
parent f79f907923
commit ff2328ddca
4 changed files with 137 additions and 8 deletions

View File

@@ -116,6 +116,7 @@ $bgcolor = strstr('white,azure',$display['theme']) ? '#f2f2f2' : '#1c1c1c';
<input type="hidden" name="#cleanup" value="true">
<input type="hidden" name="DOCKER_CUSTOM_NETWORKS" value="<?=implode(' ',$unset)?> ">
<input type="hidden" name="DOCKER_IMAGE_FILE" value="<?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>">
_(Enable Docker)_:
: <select id="DOCKER_ENABLED" name="DOCKER_ENABLED">
<?=mk_option(_var($dockercfg,'DOCKER_ENABLED'), 'no', _('No'))?>
@@ -162,7 +163,7 @@ _(Docker vDisk size)_:
:docker_vdisk_size_help:
_(Docker vDisk location)_:
: <input type="text" id="DOCKER_IMAGE_FILE1" name="DOCKER_IMAGE_FILE1" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>" placeholder="_(e.g.)_ /mnt/user/system/docker.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickfolders="true" disabled required pattern="^[^\\]*(docker-xfs\.img|docker\.img)$">
: <input type="text" id="DOCKER_IMAGE_FILE1" name="DOCKER_IMAGE_FILE1" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>" placeholder="_(e.g.)_ /mnt/user/system/docker.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickexclude=".Recycle.Bin,RecycleBin,addons,disks,remotes,rootshare,user0" data-pickfolders="true" disabled required pattern="^[^\\]*(docker-xfs\.img|docker\.img)$">
<span class="deleteLabel"><label><input type="checkbox" class="deleteCheckbox"> _(Delete vDisk file)_</label></span>
<?if ($var['fsState'] != "Started"):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
<?elseif (!is_dir(dirname(_var($dockercfg,'DOCKER_IMAGE_FILE')))):?><span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
@@ -173,7 +174,7 @@ _(Docker vDisk location)_:
</div>
<div markdown="1" id="vdisk_dir" style="display:none">
_(Docker directory)_:
: <input type="text" id="DOCKER_IMAGE_FILE2" name="DOCKER_IMAGE_FILE2" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>" placeholder="_(e.g.)_ /mnt/user/system/docker" data-pickcloseonfile="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="/mnt" data-pickfolders="true" disabled required pattern="^[^\\]*/$">
: <input type="text" id="DOCKER_IMAGE_FILE2" name="DOCKER_IMAGE_FILE2" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_IMAGE_FILE')?>" placeholder="_(e.g.)_ /mnt/user/system/docker" data-pickcloseonfile="true" data-pickfilter="HIDE_FILES_FILTER" data-pickexclude=".Recycle.Bin,RecycleBin,addons,disks,remotes,rootshare,user0" data-pickroot="/mnt" data-pickfolders="true" disabled required pattern="^[^\\]*/$">
<span class="deleteLabel"><label><input type="checkbox" class="deleteCheckbox"> _(Delete directory)_</label></span>
<?if ($var['fsState'] != "Started"):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
<?elseif (!is_dir(dirname(_var($dockercfg,'DOCKER_IMAGE_FILE')))):?><span class="nonexist"><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
@@ -183,7 +184,7 @@ _(Docker directory)_:
</div>
_(Default appdata storage location)_:
: <input type="text" id="DOCKER_APP_CONFIG_PATH" name="DOCKER_APP_CONFIG_PATH" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_APP_CONFIG_PATH')?>" placeholder="_(e.g.)_ /mnt/user/appdata/" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="<?=is_dir('/mnt/user')?'/mnt/user':'/mnt'?>" data-pickfolders="true" pattern="^[^\\]*/$">
: <input type="text" id="DOCKER_APP_CONFIG_PATH" name="DOCKER_APP_CONFIG_PATH" autocomplete="off" spellcheck="false" value="<?=_var($dockercfg,'DOCKER_APP_CONFIG_PATH')?>" placeholder="_(e.g.)_ /mnt/user/appdata/" data-pickfilter="HIDE_FILES_FILTER" data-pickexclude=".Recycle.Bin,RecycleBin,addons,disks,remotes,rootshare,user0" data-pickroot="/mnt" data-pickfolders="true" pattern="^[^\\]*/$">
<?if ($var['fsState'] != "Started"):?>
<span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
<?elseif (!is_dir(_var($dockercfg,'DOCKER_APP_CONFIG_PATH'))):?>

View File

@@ -115,7 +115,7 @@ _(Libvirt vdisk size)_:
:vms_libvirt_vdisk_size_help:
_(Libvirt storage location)_:
: <input type="text" id="IMAGE_FILE" name="IMAGE_FILE" autocomplete="off" spellcheck="false" value="<?=htmlspecialchars($domain_cfg['IMAGE_FILE']);?>" placeholder="e.g. /mnt/user/system/libvirt/libvirt.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickfolders="true" required pattern="^[^\\]*libvirt\.img$">
: <input type="text" id="IMAGE_FILE" name="IMAGE_FILE" autocomplete="off" spellcheck="false" value="<?=htmlspecialchars($domain_cfg['IMAGE_FILE']);?>" placeholder="e.g. /mnt/user/system/libvirt/libvirt.img" data-pickcloseonfile="true" data-pickfilter="img" data-pickroot="/mnt" data-pickexclude=".Recycle.Bin,RecycleBin,addons,disks,remotes,rootshare,user0" data-pickfolders="true" required pattern="^[^\\]*libvirt\.img$">
<?if (file_exists($domain_cfg['IMAGE_FILE'])):?><span id="deletePanel"><label><input type="checkbox" id="deleteCheckbox" /> _(Delete Image File)_</label></span><?endif;?>
<?if (!$started):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
<?elseif (!is_dir(dirname($domain_cfg['IMAGE_FILE']))):?><span><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span>
@@ -125,14 +125,14 @@ _(Libvirt storage location)_:
<?endif;?>
_(Default VM storage path)_:
: <input type="text" id="domaindir" name="DOMAINDIR" autocomplete="off" spellcheck="false" data-pickfolders="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="<?=is_dir('/mnt/user')?'/mnt/user':'/mnt'?>" value="<?=htmlspecialchars($domain_cfg['DOMAINDIR'])?>" placeholder="_(Click to Select)_" pattern="^[^\\]*/$">
: <input type="text" id="domaindir" name="DOMAINDIR" autocomplete="off" spellcheck="false" data-pickfolders="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="/mnt" data-pickexclude=".Recycle.Bin,RecycleBin,addons,disks,remotes,rootshare,user0" value="<?=htmlspecialchars($domain_cfg['DOMAINDIR'])?>" placeholder="_(Click to Select)_" pattern="^[^\\]*/$">
<?if (!$started):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
<?elseif (!is_dir($domain_cfg['DOMAINDIR'])):?><span><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span><?endif;?>
:vms_libvirt_storage_help:
_(Default ISO storage path)_:
: <input type="text" id="mediadir" name="MEDIADIR" autocomplete="off" spellcheck="false" data-pickfolders="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="<?=is_dir('/mnt/user')?'/mnt/user':'/mnt'?>" value="<?=htmlspecialchars($domain_cfg['MEDIADIR'])?>" placeholder="_(Click to Select)_" pattern="^[^\\]*/$">
: <input type="text" id="mediadir" name="MEDIADIR" autocomplete="off" spellcheck="false" data-pickfolders="true" data-pickfilter="HIDE_FILES_FILTER" data-pickroot="/mnt" data-pickexclude=".Recycle.Bin,RecycleBin,addons,disks,remotes,rootshare,user0" value="<?=htmlspecialchars($domain_cfg['MEDIADIR'])?>" placeholder="_(Click to Select)_" pattern="^[^\\]*/$">
<?if (!$started):?><span><i class="fa fa-warning icon warning"></i> _(Modify with caution: unable to validate path until Array is Started)_</span>
<?elseif (!is_dir($domain_cfg['MEDIADIR'])):?><span><i class="fa fa-warning icon warning"></i> _(Path does not exist)_</span><?endif;?>

View File

@@ -9,6 +9,7 @@
*
* History:
*
* 1.2.1 - pass in a list of folders to exclude from the dropdown list
* 1.2.0 - adapted by Bergware for use in Unraid - support UTF-8 encoding & hardening
* 1.1.1 - SECURITY: forcing root to prevent users from determining system's file structure (per DaveBrad)
* 1.1.0 - adding multiSelect (checkbox) support (08/22/2014)
@@ -48,6 +49,9 @@ $filters = (array)$_POST['filter'];
$match = $_POST['match'];
$checkbox = $_POST['multiSelect']=='true' ? "<input type='checkbox'>" : "";
/* Add the pickexclude functionality to exclude folders from the list. */
$excludedFolders = explode(",", $_POST['pickexclude']);
echo "<ul class='jqueryFileTree'>";
if ($_POST['show_parent']=='true' && is_top($rootdir)) echo "<li class='directory collapsed'>$checkbox<a href='#' rel=\"".htmlspecialchars(dirname($rootdir))."\">..</a></li>";
@@ -60,7 +64,10 @@ if (is_low($rootdir) && is_dir($rootdir)) {
$htmlRel = htmlspecialchars($rootdir.$name);
$htmlName = htmlspecialchars(mb_strlen($name)<=33 ? $name : mb_substr($name,0,30).'...');
if (is_dir($rootdir.$name)) {
if (empty($match)||preg_match("/$match/",$rootdir.$name)) echo "<li class='directory collapsed'>$checkbox<a href='#' rel=\"$htmlRel/\">$htmlName</a></li>";
/* Check if the directory should be excluded. */
if (!in_array($name, $excludedFolders) && (empty($match) || preg_match("/$match/",$rootdir.$name))) {
echo "<li class='directory collapsed'>$checkbox<a href='#' rel=\"$htmlRel/\">$htmlName</a></li>";
}
}
}
foreach ($files as $name) {

View File

@@ -1,2 +1,123 @@
/* jQuery File Tree. Authors - Cory S.N. LaViska & Dave Rogers. Copyright 2008 A Beautiful Site, LLC. - Adapted by Bergware for use in Unraid */
jQuery&&function(e){e.extend(e.fn,{fileTree:function(i,t,l){void 0===i.root&&(i.root="/mnt/"),void 0===i.top&&(i.top="/mnt/"),void 0===i.filter&&(i.filter=""),void 0===i.match&&(i.match=".*"),void 0===i.script&&(i.script="/webGui/include/FileTree.php"),void 0===i.folderEvent&&(i.folderEvent="click"),void 0===i.expandSpeed&&(i.expandSpeed=300),void 0===i.collapseSpeed&&(i.collapseSpeed=300),void 0===i.expandEasing&&(i.expandEasing=null),void 0===i.collapseEasing&&(i.collapseEasing=null),void 0===i.multiFolder&&(i.multiFolder=!1),void 0===i.loadMessage&&(i.loadMessage="Loading..."),void 0===i.multiSelect&&(i.multiSelect=!1),void 0===i.allowBrowsing&&(i.allowBrowsing=!1),e(this).each(function(){function s(r,n,o){e(r).addClass("wait"),e(".jqueryFileTree.start").remove(),e.post(i.script,{dir:n,root:i.top,multiSelect:i.multiSelect,filter:i.filter,match:i.match,show_parent:o}).done(function(o){var d;e(r).find(".start").html(""),e(r).removeClass("wait").append(o),i.root==n?e(r).find("UL:hidden").show():e(r).find("UL:hidden").slideDown({duration:i.expandSpeed,easing:i.expandEasing}),e(d=r).find("LI A").on(i.folderEvent,function(r){r.preventDefault();var n={};return n.li=e(this).closest("li"),n.type=n.li.hasClass("directory")?"directory":"file",n.value=e(this).text(),n.rel=e(this).prop("rel"),".."==e(this).text()?(i.root=n.rel,l&&l(e(this).attr("rel")),a(e(this),"filetreefolderclicked",n),root=e(this).closest("ul.jqueryFileTree"),root.html('<ul class="jqueryFileTree start"><li class="wait">'+i.loadMessage+"<li></ul>"),s(e(root),i.root,i.allowBrowsing)):e(this).parent().hasClass("directory")?(e(this).parent().hasClass("collapsed")?(a(e(this),"filetreeexpand",n),i.multiFolder||(e(this).parent().parent().find("UL").slideUp({duration:i.collapseSpeed,easing:i.collapseEasing}),e(this).parent().parent().find("LI.directory").removeClass("expanded").addClass("collapsed")),e(this).parent().removeClass("collapsed").addClass("expanded"),e(this).parent().find("UL").remove(),s(e(this).parent(),e(this).attr("rel").match(/.*\//)[0],!1)):(a(e(this),"filetreecollapse",n),e(this).parent().find("UL").slideUp({duration:i.collapseSpeed,easing:i.collapseEasing}),e(this).parent().removeClass("expanded").addClass("collapsed"),a(e(this),"filetreecollapsed",n)),l&&l(e(this).attr("rel")),a(e(this),"filetreefolderclicked",n)):(t&&t(e(this).attr("rel")),a(e(this),"filetreeclicked",n)),!1}),"click"!=i.folderEvent.toLowerCase&&e(d).find("LI A").on("click",function(e){return e.preventDefault(),!1}),a(e(this),"filetreeexpanded",o)}).fail(function(){e(r).find(".start").html(""),e(r).removeClass("wait").append("<li>Unable to get file tree information</li>")})}function a(e,i,t){t.trigger=i,e.trigger(i,t)}e(this).html('<ul class="jqueryFileTree start"><li class="wait">'+i.loadMessage+"<li></ul>"),s(e(this),i.root,i.allowBrowsing),e(this).on("change","input:checkbox",function(){var i={};i.li=e(this).closest("li"),i.type=i.li.hasClass("directory")?"directory":"file",i.value=i.li.children("a").text(),i.rel=i.li.children("a").prop("rel"),i.li.find("input:checkbox").prop("checked",e(this).prop("checked")),e(this).prop("checked")?a(e(this),"filetreechecked",i):a(e(this),"filetreeunchecked",i)})})},fileTreeAttach:function(i,t,l){var s={};e.isFunction(i)?(e.isFunction(t)&&(l=t),t=i):i&&e.extend(s,i),e(this).each(function(){var i=e(this),a=e.extend({},s,i.data()),r=i.next(".fileTree");0===r.length&&(e(document).mousedown(function(i){var t=e(".fileTree");t.is(i.target)||0!==t.has(i.target).length||t.slideUp("fast")}),r=e("<div>",{class:"textarea fileTree"}),i.after(r)),i.click(function(){r.is(":visible")?r.slideUp("fast"):(""===r.html()&&(r.html('<span style="padding-left: 20px"><img src="/webGui/images/spinner.gif"> Loading...</span>'),r.fileTree({root:a.pickroot,top:a.picktop,filter:(a.pickfilter||"").split(","),match:a.pickmatch||".*"},e.isFunction(t)?t:function(e){i.val(e).change(),a.hasOwnProperty("pickcloseonfile")&&r.slideUp("fast")},e.isFunction(l)?l:function(e){a.hasOwnProperty("pickfolders")&&i.val(e).change()})),r.offset({left:i.position().left}),r.slideDown("fast"))})})}})}(jQuery);
jQuery && function(e) {
e.extend(e.fn, {
fileTree: function(i, t, l) {
// Initialize pickexclude with an empty string if not provided
void 0 === i.root && (i.root = "/mnt/"),
void 0 === i.top && (i.top = "/mnt/"),
void 0 === i.filter && (i.filter = ""),
void 0 === i.match && (i.match = ".*"),
void 0 === i.script && (i.script = "/webGui/include/FileTree.php"),
void 0 === i.folderEvent && (i.folderEvent = "click"),
void 0 === i.expandSpeed && (i.expandSpeed = 300),
void 0 === i.collapseSpeed && (i.collapseSpeed = 300),
void 0 === i.expandEasing && (i.expandEasing = null),
void 0 === i.collapseEasing && (i.collapseEasing = null),
void 0 === i.multiFolder && (i.multiFolder = !1),
void 0 === i.loadMessage && (i.loadMessage = "Loading..."),
void 0 === i.multiSelect && (i.multiSelect = !1),
void 0 === i.allowBrowsing && (i.allowBrowsing = !1),
void 0 === i.pickexclude && (i.pickexclude = "");
e(this).each(function() {
function s(r, n, o) {
e(r).addClass("wait"),
e(".jqueryFileTree.start").remove(),
// Modify the post data to include pickexclude
e.post(i.script, {
dir: n,
root: i.top,
multiSelect: i.multiSelect,
filter: i.filter,
match: i.match,
show_parent: o,
// Add pickexclude parameter
pickexclude: i.pickexclude
}).done(function(o) {
var d;
e(r).find(".start").html(""),
e(r).removeClass("wait").append(o),
i.root == n ? e(r).find("UL:hidden").show() : e(r).find("UL:hidden").slideDown({
duration: i.expandSpeed,
easing: i.expandEasing
}),
e(d = r).find("LI A").on(i.folderEvent, function(r) {
r.preventDefault();
var n = {};
return n.li = e(this).closest("li"),
n.type = n.li.hasClass("directory") ? "directory" : "file",
n.value = e(this).text(),
n.rel = e(this).prop("rel"),
".." == e(this).text() ? (i.root = n.rel, l && l(e(this).attr("rel")), a(e(this), "filetreefolderclicked", n), root = e(this).closest("ul.jqueryFileTree"), root.html('<ul class="jqueryFileTree start"><li class="wait">' + i.loadMessage + "<li></ul>"), s(e(root), i.root, i.allowBrowsing)) : e(this).parent().hasClass("directory") ? (e(this).parent().hasClass("collapsed") ? (a(e(this), "filetreeexpand", n), i.multiFolder || (e(this).parent().parent().find("UL").slideUp({
duration: i.collapseSpeed,
easing: i.collapseEasing
}), e(this).parent().parent().find("LI.directory").removeClass("expanded").addClass("collapsed")), e(this).parent().removeClass("collapsed").addClass("expanded"), e(this).parent().find("UL").remove(), s(e(this).parent(), e(this).attr("rel").match(/.*\//)[0], !1)) : (a(e(this), "filetreecollapse", n), e(this).parent().find("UL").slideUp({
duration: i.collapseSpeed,
easing: i.collapseEasing
}), e(this).parent().removeClass("expanded").addClass("collapsed"), a(e(this), "filetreecollapsed", n)), l && l(e(this).attr("rel")), a(e(this), "filetreefolderclicked", n)) : (t && t(e(this).attr("rel")), a(e(this), "filetreeclicked", n)), !1
}),
"click" != i.folderEvent.toLowerCase && e(d).find("LI A").on("click", function(e) {
return e.preventDefault(), !1
}),
a(e(this), "filetreeexpanded", o)
}).fail(function() {
e(r).find(".start").html(""),
e(r).removeClass("wait").append("<li>Unable to get file tree information</li>")
})
}
function a(e, i, t) {
t.trigger = i,
e.trigger(i, t)
}
e(this).html('<ul class="jqueryFileTree start"><li class="wait">' + i.loadMessage + "<li></ul>"),
s(e(this), i.root, i.allowBrowsing),
e(this).on("change", "input:checkbox", function() {
var i = {};
i.li = e(this).closest("li"),
i.type = i.li.hasClass("directory") ? "directory" : "file",
i.value = i.li.children("a").text(),
i.rel = i.li.children("a").prop("rel"),
i.li.find("input:checkbox").prop("checked", e(this).prop("checked")),
e(this).prop("checked") ? a(e(this), "filetreechecked", i) : a(e(this), "filetreeunchecked", i)
})
})
},
fileTreeAttach: function(i, t, l) {
var s = {};
e.isFunction(i) ? (e.isFunction(t) && (l = t), t = i) : i && e.extend(s, i),
e(this).each(function() {
var i = e(this),
a = e.extend({}, s, i.data()),
r = i.next(".fileTree");
0 === r.length && (e(document).mousedown(function(i) {
var t = e(".fileTree");
t.is(i.target) || 0 !== t.has(i.target).length || t.slideUp("fast")
}),
r = e("<div>", {
class: "textarea fileTree"
}),
i.after(r)),
i.click(function() {
r.is(":visible") ? r.slideUp("fast") : ("" === r.html() && (r.html('<span style="padding-left: 20px"><img src="/webGui/images/spinner.gif"> Loading...</span>'),
r.fileTree({
root: a.pickroot,
top: a.picktop,
filter: (a.pickfilter || "").split(","),
match: a.pickmatch || ".*",
// Include pickexclude parameter in fileTreeAttach
pickexclude: a.pickexclude
}, e.isFunction(t) ? t : function(e) {
i.val(e).change(),
a.hasOwnProperty("pickcloseonfile") && r.slideUp("fast")
}, e.isFunction(l) ? l : function(e) {
a.hasOwnProperty("pickfolders") && i.val(e).change()
})),
r.offset({
left: i.position().left
}),
r.slideDown("fast"))
})
})
}
})
}(jQuery);