From 99ff869e771266eb4d2a6fe3dcba034b8a731d66 Mon Sep 17 00:00:00 2001 From: nao-pon Date: Mon, 17 Oct 2016 23:47:48 +0900 Subject: [PATCH] [core] fix #1706 configurable to limit max number of selectable items Added an option `maxTargets` of connector main option. --- js/elFinder.js | 49 ++++++++++++++++++++++++--------- js/i18n/elfinder.LANG.js | 3 +- js/i18n/elfinder.en.js | 3 +- js/i18n/elfinder.jp.js | 3 +- js/ui/cwd.js | 13 +++++++-- php/elFinder.class.php | 30 ++++++++++++-------- php/elFinderConnector.class.php | 6 ++++ 7 files changed, 77 insertions(+), 30 deletions(-) diff --git a/js/elFinder.js b/js/elFinder.js index 8c441e8689..10a5ca2e49 100644 --- a/js/elFinder.js +++ b/js/elFinder.js @@ -1542,6 +1542,10 @@ window.elFinder = function(node, opts) { self.netDrivers = response.netDrivers; } + if (response.maxTargets) { + self.maxTargets = response.maxTargets; + } + if (isOpen && !!data.init) { self.uplMaxSize = self.returnBytes(response.uplMaxSize); self.uplMaxFile = !!response.uplMaxFile? parseInt(response.uplMaxFile) : 20; @@ -1575,11 +1579,6 @@ window.elFinder = function(node, opts) { } }; - if (!cmd) { - return dfrd.reject('errCmdReq'); - } - - defdone && dfrd.done(done); dfrd.fail(function(error, xhr, response) { self.trigger(cmd + 'fail', response); if (error) { @@ -1588,6 +1587,17 @@ window.elFinder = function(node, opts) { syncOnFail && self.sync(); }) + if (!cmd) { + syncOnFail = false; + return dfrd.reject('errCmdReq'); + } + + if (self.maxTargets && data.targets && data.targets.length > self.maxTargets) { + syncOnFail = false; + return dfrd.reject(['errMaxTargets', self.maxTargets]); + } + + defdone && dfrd.done(done); if (notify.type && notify.cnt) { if (cancel) { notify.cancel = dfrd; @@ -2045,7 +2055,7 @@ window.elFinder = function(node, opts) { * @return Array */ this.clipboard = function(hashes, cut) { - var map = function() { return $.map(clipboard, function(f) { return f.hash }); } + var map = function() { return $.map(clipboard, function(f) { return f.hash }); }; if (hashes !== void(0)) { clipboard.length && this.trigger('unlockfiles', {files : map()}); @@ -2750,7 +2760,20 @@ window.elFinder = function(node, opts) { selected = []; }) .select(function(e) { - selected = $.map(e.data.selected || e.data.value|| [], function(hash) { return files[hash] ? hash : null; }); + var cnt = 0, + unselects = []; + selected = $.map(e.data.selected || e.data.value|| [], function(hash) { + if (unselects.length || (self.maxTargets && ++cnt > self.maxTargets)) { + unselects.push(hash); + return null; + } else { + return files[hash] ? hash : null; + } + }); + if (unselects.length) { + self.trigger('unselectfiles', {files: unselects, inselect: true}); + self.error(['errMaxTargets', self.maxTargets]); + } }) .error(function(e) { var opts = { @@ -5625,20 +5648,20 @@ elFinder.prototype = { for (i = start; i< arguments.length; i++) { m = arguments[i]; - if (typeof m == 'string') { - input.push(message(m)); - } else if ($.isArray(m)) { + if ($.isArray(m)) { for (j = 0; j < m.length; j++) { - if (typeof m[j] == 'string') { - input.push(message(m[j])); - } else if (m[j] instanceof jQuery) { + if (m[j] instanceof jQuery) { // jQuery object is HTML element input.push(m[j]); + } else if (typeof m[j] !== 'undefined'){ + input.push(message('' + m[j])); } } } else if (m instanceof jQuery) { // jQuery object is HTML element input.push(m[j]); + } else if (typeof m !== 'undefined'){ + input.push(message('' + m)); } } diff --git a/js/i18n/elfinder.LANG.js b/js/i18n/elfinder.LANG.js index 02b6912882..72f0db0fa7 100644 --- a/js/i18n/elfinder.LANG.js +++ b/js/i18n/elfinder.LANG.js @@ -104,7 +104,8 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'errConvUTF8' : 'Not convertible to UTF-8', // from v2.1 added 08.04.2014 'errFolderUpload' : 'Try Google Chrome, If you\'d like to upload the folder.', // from v2.1 added 26.6.2015 'errSearchTimeout' : 'Timed out while searching "$1". Search result is partial.', // from v2.1 added 12.1.2016 - 'errReauthRequire' : 'Re-authorization is required.', // from v2.1.10 added 3.24.2016 + 'errReauthRequire' : 'Re-authorization is required.', // from v2.1.10 added 24.3.2016 + 'errMaxTargets' : 'Max number of selectable items is $1.', // from v2.1.17 added 17.10.2016 /******************************* commands names ********************************/ 'cmdarchive' : 'Create archive', diff --git a/js/i18n/elfinder.en.js b/js/i18n/elfinder.en.js index c3c77afebb..8bbe68ffe5 100644 --- a/js/i18n/elfinder.en.js +++ b/js/i18n/elfinder.en.js @@ -97,7 +97,8 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'errConvUTF8' : 'Not convertible to UTF-8', // from v2.1 added 08.04.2014 'errFolderUpload' : 'Try Google Chrome, If you\'d like to upload the folder.', // from v2.1 added 26.6.2015 'errSearchTimeout' : 'Timed out while searching "$1". Search result is partial.', // from v2.1 added 12.1.2016 - 'errReauthRequire' : 'Re-authorization is required.', // from v2.1.10 added 3.24.2016 + 'errReauthRequire' : 'Re-authorization is required.', // from v2.1.10 added 24.3.2016 + 'errMaxTargets' : 'Max number of selectable items is $1.', // from v2.1.17 added 17.10.2016 /******************************* commands names ********************************/ 'cmdarchive' : 'Create archive', diff --git a/js/i18n/elfinder.jp.js b/js/i18n/elfinder.jp.js index bb9b72f2fa..3b01de195b 100644 --- a/js/i18n/elfinder.jp.js +++ b/js/i18n/elfinder.jp.js @@ -98,7 +98,8 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'errConvUTF8' : 'UTF-8 に変換できませんでした', // from v2.1 added 08.04.2014 'errFolderUpload' : 'フォルダをアップロードしたいのであれば、Google Chrome を使用してください', // from v2.1 added 26.6.2015 'errSearchTimeout' : '"$1"を検索中にタイムアウトしました。検索結果は部分的です。', // from v2.1 added 12.1.2016 - 'errReauthRequire' : '再認可が必要です', // from v2.1.10 added 3.24.2016 + 'errReauthRequire' : '再認可が必要です', // from v2.1.10 added 24.3.2016 + 'errMaxTargets' : '選択可能な最大アイテム数は $1 個です', // from v2.1.17 added 17.10.2016 /******************************* commands names ********************************/ 'cmdarchive' : 'アーカイブ作成', diff --git a/js/ui/cwd.js b/js/ui/cwd.js index 01c7bbd955..ae1740e221 100644 --- a/js/ui/cwd.js +++ b/js/ui/cwd.js @@ -387,8 +387,15 @@ $.fn.elfindercwd = function(fm, options) { selectCheckbox && selectAllCheckbox.find('input').prop('checked', true); fm.lazy(function() { + var files; cwd.find('[id]:not(.'+clSelected+'):not(.elfinder-cwd-parent)').trigger(evtSelect); - selectedFiles = (incHashes || cwdHashes).concat(); + if (fm.maxTargets && (incHashes || cwdHashes).length > fm.maxTargets) { + files = $.map(incHashes || cwdHashes, function(hash) { return fm.file(hash) || null }); + files = fm.sortFiles(files); + selectedFiles = $.map(files, function(f) { return f.hash; }); + } else { + selectedFiles = (incHashes || cwdHashes).concat(); + } trigger(); selectCheckbox && selectAllCheckbox.data('pending', false); cwd.addClass('elfinder-cwd-allselected'); @@ -2153,7 +2160,7 @@ $.fn.elfindercwd = function(fm, options) { } if (event === evtSelect || event === evtUnselect) { add = (event === evtSelect), - sels = selectedFiles.concat(); + sels = add? selectedFiles.concat() : selectedFiles; $.each(files, function(i, hash) { var idx = $.inArray(hash, sels); if (idx === -1) { @@ -2167,7 +2174,7 @@ $.fn.elfindercwd = function(fm, options) { while (l--) { $('#'+fm.cwdHash2Id(files[l])).trigger(event); } - trigger(); + ! e.data.inselect && trigger(); } if (wrapper.data('dropover') && parents.indexOf(wrapper.data('dropover')) !== -1) { ctr = e.type !== 'lockfiles'; diff --git a/php/elFinder.class.php b/php/elFinder.class.php index dd5bc3416b..88b88c37b4 100644 --- a/php/elFinder.class.php +++ b/php/elFinder.class.php @@ -226,6 +226,13 @@ class elFinder { **/ protected $uploadDebug = ''; + /** + * Max allowed numbar of @var targets (0 - no limit) + * + * @var integer + */ + public $maxTargets = 1000; + /** * Errors from PHP * @@ -302,18 +309,17 @@ class elFinder { const ERROR_NETMOUNT = 'errNetMount'; const ERROR_NETUNMOUNT = 'errNetUnMount'; const ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver'; - const ERROR_NETMOUNT_FAILED = 'errNetMountFailed'; - - const ERROR_SESSION_EXPIRES = 'errSessionExpires'; - - const ERROR_CREATING_TEMP_DIR = 'errCreatingTempDir'; - const ERROR_FTP_DOWNLOAD_FILE = 'errFtpDownloadFile'; - const ERROR_FTP_UPLOAD_FILE = 'errFtpUploadFile'; - const ERROR_FTP_MKDIR = 'errFtpMkdir'; - const ERROR_ARCHIVE_EXEC = 'errArchiveExec'; - const ERROR_EXTRACT_EXEC = 'errExtractExec'; + const ERROR_NETMOUNT_FAILED = 'errNetMountFailed'; + const ERROR_SESSION_EXPIRES = 'errSessionExpires'; + const ERROR_CREATING_TEMP_DIR = 'errCreatingTempDir'; + const ERROR_FTP_DOWNLOAD_FILE = 'errFtpDownloadFile'; + const ERROR_FTP_UPLOAD_FILE = 'errFtpUploadFile'; + const ERROR_FTP_MKDIR = 'errFtpMkdir'; + const ERROR_ARCHIVE_EXEC = 'errArchiveExec'; + const ERROR_EXTRACT_EXEC = 'errExtractExec'; const ERROR_SEARCH_TIMEOUT = 'errSearchTimeout'; // 'Timed out while searching "$1". Search result is partial.' - const ERROR_REAUTH_REQUIRE = 'errReauthRequire'; // 'Re-authorization is required.' + const ERROR_REAUTH_REQUIRE = 'errReauthRequire'; // 'Re-authorization is required.' + const ERROR_MAX_TARGTES = 'errMaxTargets'; // 'Max number of selectable items is $1.' /** * Constructor @@ -371,6 +377,7 @@ public function __construct($opts) { $this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0); $this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : ''); $this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : ''); + $this->maxTargets = (isset($opts['maxTargets']) ? intval($opts['maxTargets']) : $this->maxTargets); elFinder::$commonTempPath = (isset($opts['commonTempPath']) ? $opts['commonTempPath'] : './.tmp'); if (!is_writable(elFinder::$commonTempPath)) { elFinder::$commonTempPath = ''; @@ -1119,6 +1126,7 @@ protected function open($args) { $result['uplMaxSize'] = ini_get('upload_max_filesize'); $result['uplMaxFile'] = ini_get('max_file_uploads'); $result['netDrivers'] = array_keys(self::$netDrivers); + $result['maxTargets'] = $this->maxTargets; if ($volume) { $result['cwd']['root'] = $volume->root(); } diff --git a/php/elFinderConnector.class.php b/php/elFinderConnector.class.php index e7c6e416ad..d008d11319 100644 --- a/php/elFinderConnector.class.php +++ b/php/elFinderConnector.class.php @@ -87,6 +87,12 @@ public function run() { $_REQUEST = $this->input_filter(array_merge_recursive($src, $_REQUEST)); } } + + if (isset($src['targets']) && $this->elFinder->maxTargets && count($src['targets']) > $this->elFinder->maxTargets) { + $error = $this->elFinder->error(elFinder::ERROR_MAX_TARGTES); + $this->output(array('error' => $this->elFinder->error(elFinder::ERROR_MAX_TARGTES))); + } + $cmd = isset($src['cmd']) ? $src['cmd'] : ''; $args = array();