* @class elFinder command "netmount"
* Mount network volume with user credentials.
* @author Dmitry (dio) Levashov
elFinder.prototype.commands.netmount = function() {
this.alwaysEnabled = true;
this.updateOnSelect = false;
fm.one('open', function() {
self.drivers = fm.netDrivers;
if (self.drivers.length) {
$.each(self.drivers, function() {
var d = self.options[this];
fm.trigger('helpIntegration', Object.assign({cmd: 'netmount'}, d.integrateInfo));
this.getstate = function() {
return hasMenus ? 0 : -1;
var winFocus = function() {
inputs.protocol.trigger('change', 'winfocus');
protocol : $('<select></select>')
.on('change', function(e, data){
var protocol = this.value;
content.find('.elfinder-netmount-tr').hide();
content.find('.elfinder-netmount-tr-'+protocol).show();
dialogNode && dialogNode.children('.ui-dialog-buttonpane:first').find('button').show();
if (typeof o[protocol].select == 'function') {
o[protocol].select(fm, e, data);
.addClass('ui-corner-all')
title : fm.i18n('netMountDialogTitle'),
$(window).on('focus.'+fm.namespace, winFocus);
inputs.protocol.trigger('change');
dfrd.state() == 'pending' && dfrd.reject();
$(window).off('focus.'+fm.namespace, winFocus);
var protocol = inputs.protocol.val(),
data = {cmd : 'netmount', protocol: protocol},
$.each(content.find('input.elfinder-netmount-inputs-'+protocol), function(name, input) {
if (elm.is(':radio,:checkbox')) {
if (elm.is(':checked')) {
return fm.trigger('error', {error : 'errNetMountHostReq', opts : {modal: true}});
fm.request({data : data, notify : {type : 'netmount', cnt : 1, hideCnt : true}})
if (data.added && data.added.length) {
mnt2res && inputs.protocol.trigger('change', 'reset');
if (data.added[0].phash) {
if (pdir = fm.file(data.added[0].phash)) {
fm.change({ changed: [ pdir ] });
fm.one('netmountdone', function() {
fm.exec('open', data.added[0].hash);
if (cur.fail && typeof cur.fail == 'function') {
cur.fail(fm, fm.parseError(error));
self.dialog.elfinderdialog('close');
form = $('<form autocomplete="off"></form>').on('keydown', 'input', function(e) {
if (e.keyCode === $.ui.keyCode.ENTER) {
$.each(form.find('input:visible:not(.elfinder-input-optional)'), function() {
if ($(this).val() === '') {
hidden = $('<div></div>'),
content = $('<table class="elfinder-info-tb elfinder-netmount-tb"></table>')
.append($('<tr></tr>').append($('<td>'+fm.i18n('protocol')+'</td>')).append($('<td></td>').append(inputs.protocol)));
$.each(self.drivers, function(i, protocol) {
inputs.protocol.append('<option value="'+protocol+'">'+fm.i18n(o[protocol].name || protocol)+'</option>');
$.each(o[protocol].inputs, function(name, input) {
input.attr('name', name);
if (input.attr('type') != 'hidden') {
input.addClass('ui-corner-all elfinder-netmount-inputs-'+protocol);
content.append($('<tr></tr>').addClass('elfinder-netmount-tr elfinder-netmount-tr-'+protocol).append($('<td>'+fm.i18n(name)+'</td>')).append($('<td></td>').append(input)));
input.addClass('elfinder-netmount-inputs-'+protocol);
o[protocol].protocol = inputs.protocol;
content.find('.elfinder-netmount-tr').hide();
content.find('.elfinder-netmount-tr-' + self.drivers[0]).show();
opts.buttons[fm.i18n('btnMount')] = doMount;
opts.buttons[fm.i18n('btnCancel')] = function() {
self.dialog.elfinderdialog('close');
content.find('select,input').addClass('elfinder-tabstop');
dialog = self.fmDialog(form.append(content), opts).ready(function() {
inputs.protocol.trigger('change');
dialog.elfinderdialog('posInit');
dialogNode = dialog.closest('.ui-dialog');
self.dialog.elfinderdialog('open');
self.fm.bind('netmount', function(e) {
if (o[d.protocol] && typeof o[d.protocol].done == 'function') {
o[d.protocol].done(self.fm, d);
content.find('select,input').addClass('elfinder-tabstop');
self.dialog.elfinderdialog('tabstopsInit');
if (d.mode && d.mode === 'redirect') {
// To support of third-party cookie blocking (ITP) on CORS
// On iOS and iPadOS 13.4 and Safari 13.1 on macOS, the session cannot be continued when redirecting OAuth in CORS mode
data : {cmd : 'netmount', protocol : d.protocol, host: d.host, user : 'init', pass : 'return', options: d.options},
d = JSON.parse(data.body);
elFinder.prototype.commands.netunmount = function() {
this.alwaysEnabled = true;
this.updateOnSelect = false;
this.drivers = this.fm.netDrivers;
this.getstate = function(sel) {
return !!sel && this.drivers.length && !this._disabled && (file = fm.file(sel[0])) && file.netkey ? 0 : -1;
this.exec = function(hashes) {
error && fm.error(error);
drive = fm.file(hashes[0]),
childrenRoots = function(hash) {
$.each(fm.leafRoots, function(phash, hashes) {
var parents = fm.parents(phash),
if ((idx = $.inArray(hash, parents)) !== -1) {
idx = parents.length - idx;
$.each(hashes, function(i, h) {
work.push({i: idx, hash: h});
work.sort(function(a, b) { return a.i < b.i; });
$.each(work, function(i, o) {
if (dfrd.state() == 'pending') {
text : fm.i18n('confirmUnmount', drive.name),
roots = childrenRoots(target),
$.when(requests).done(function() {
data : {cmd : 'netmount', protocol : 'netunmount', host: drive.netkey, user : target, pass : 'dum'},
notify : {type : 'netunmount', cnt : 1, hideCnt : true},
drive.volumeid && delete fm.volumeExpires[drive.volumeid];
}).fail(function(error) {
fm.remove({ removed: removed });
var msgs = ['unmountChildren'];
$.each(roots, function(i, hash) {
msgs.push([fm.file(hash).name]);
$.each(roots, function(i, hash) {
requests.push(fm.request({
data : {cmd : 'netmount', protocol : 'netunmount', host: d.netkey, user : d.hash, pass : 'dum'},
notify : {type : 'netunmount', cnt : 1, hideCnt : true},
d.volumeid && delete fm.volumeExpires[d.volumeid];
removed = removed.concat(data.removed);
callback : function() { dfrd.reject(); }