Skip to content

Commit

Permalink
Finalization of PR #20: bugfix and improvements to make it work for d…
Browse files Browse the repository at this point in the history
…ifferent cases and environments.

New "outsideServerRoot" option for capability to preview images and media files for the case when the root folder is located beyond server root directory.
  • Loading branch information
psolom committed Aug 6, 2016
1 parent c866e93 commit 8b52ab8
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 70 deletions.
13 changes: 12 additions & 1 deletion changelog
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,15 @@ Version 1.0.4
- Added new associations of some icons with file types for filetree and listview (docx, xlsx, odt, ods, odp, etc...)
- Fixed bug: multiple issues when filemanager is located and accessible not from root (nested) folder
- Fixed bug: error in PHP connector while file downloading
- Fixed bug: error in PHP connector while transliteration process
- Fixed bug: error in PHP connector while transliteration process


Version 1.0.5
---------------------------
- Improved path processing at the client-side. Absolute path used whenever it is possible. Normalization of the preview path
- New "outsideServerRoot" option which allows to preview images and media files for the case when the root folder is located beyond server root directory
- Improved a way of automatically determination of `baseUrl` based on page URL
- Fixed bug: Copy URL to clipboard does not work - #5
- Fixed bug: Path issue upon file selection in WYSIWYG editor
- Fixed bug: It is possible to select folders in WYSIWYG editor
- Fixed bug: Preview of media and pdf files is broken when the path contains special chars (fixed by path encoding)
45 changes: 33 additions & 12 deletions connectors/php/LocalFilemanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -536,10 +536,21 @@ public function savefile()

/**
* @inheritdoc
* Local connector is able to reach all files directly, so no need to implement readfile() method
* Local connector is able to reach all files directly, but it is used to preview files placed outside server root
* @see BaseFilemanager::readfile() to get purpose description
*/
public function readfile() {}
public function readfile()
{
$current_path = $this->getFullPath($this->get['path'], true);

header('Content-type: ' . mime_content_type($current_path));
header("Content-Transfer-Encoding: binary");
header("Content-length: " . filesize($current_path));
header('Content-Disposition: inline; filename="' . basename($current_path) . '"');

readfile($current_path);
exit();
}

/**
* @inheritdoc
Expand Down Expand Up @@ -824,30 +835,40 @@ protected function has_system_permission($filepath, $permissions)
protected function get_file_info($relative_path, $thumbnail = false)
{
$current_path = $this->getFullPath($relative_path);
$dynamic_path = $this->getDynamicPath($current_path);

$item = $this->defaultInfo;
$pathInfo = pathinfo($current_path);
$filemtime = filemtime($current_path);
$iconsFolder = $this->getFmUrl($this->config['icons']['path']);

$outsideRoot = $this->config['options']['outsideServerRoot'];
$getImageMode = $this->connector_script_url . '?mode=getimage&path=' . rawurlencode($relative_path) . '&time=' . time();
$readFileMode = $this->connector_script_url . '?mode=readfile&path=' . rawurlencode($relative_path) . '&time=' . time();

// check if file is writable and readable
$protected = $this->has_system_permission($current_path, array('w', 'r')) ? 0 : 1;

if(is_dir($current_path)) {
$fileType = self::FILE_TYPE_DIR;
$thumbPath = $iconsFolder . ($protected ? 'locked_' : '') . $this->config['icons']['directory'];
$imagePath = $iconsFolder . ($protected ? 'locked_' : '') . $this->config['icons']['directory'];
} else {
$fileType = $pathInfo['extension'];
if($protected == 1) {
$thumbPath = $iconsFolder . 'locked_' . $this->config['icons']['default'];
$imagePath = $iconsFolder . 'locked_' . $this->config['icons']['default'];
} else {
$thumbPath = $iconsFolder . $this->config['icons']['default'];
$imagePath = $iconsFolder . $this->config['icons']['default'];
$item['Properties']['Size'] = $this->get_real_filesize($current_path);

if($this->config['options']['showThumbs'] && in_array(strtolower($fileType), array_map('strtolower', $this->config['images']['imagesExt']))) {
if(in_array(strtolower($fileType), array_map('strtolower', $this->config['images']['imagesExt']))) {

// svg should not be previewed as raster formats images
if($thumbnail && $fileType !== 'svg'){
$thumbPath = $this->connector_script_url . '?mode=getimage&path=' . rawurlencode($relative_path) . '&time=' . time() . '&thumbnail=true' ;
if($fileType === 'svg') {
$imagePath = $outsideRoot ? $readFileMode : $dynamic_path;
} else if($thumbnail && $this->config['options']['showThumbs']) {
$imagePath = $getImageMode . '&thumbnail=true';
} else {
$imagePath = $outsideRoot ? $getImageMode : $dynamic_path;
}

if($item['Properties']['Size']) {
Expand All @@ -859,7 +880,7 @@ protected function get_file_info($relative_path, $thumbnail = false)
$item['Properties']['Height'] = $height;
$item['Properties']['Width'] = $width;
} else if(file_exists($this->fm_path . '/' . $this->config['icons']['path'] . strtolower($fileType) . '.png')) {
$thumbPath = $iconsFolder . strtolower($fileType) . '.png';
$imagePath = $iconsFolder . strtolower($fileType) . '.png';
}
}
}
Expand All @@ -868,8 +889,8 @@ protected function get_file_info($relative_path, $thumbnail = false)
$item['Filename'] = $pathInfo['basename'];
$item['File Type'] = $fileType;
$item['Protected'] = $protected;
$item['Thumbnail'] = $thumbPath;
$item['Preview'] = $this->getDynamicPath($current_path);
$item['Image Path'] = $imagePath;
$item['Dynamic Path'] = $outsideRoot ? $readFileMode : $dynamic_path;
$item['Properties']['Date Modified'] = $this->formatDate($filemtime);
//$item['Properties']['Date Created'] = $this->formatDate(filectime($current_path)); // PHP cannot get create timestamp
$item['Properties']['filemtime'] = $filemtime;
Expand Down Expand Up @@ -1264,4 +1285,4 @@ protected function createThumbnail($imagePath, $thumbnailPath)
}
}

}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rich-filemanager",
"version": "1.0.4",
"version": "1.0.5",
"description": "Highly customizable open-source file manager",
"main": "scripts/filemanager.js",
"repository": {
Expand Down
3 changes: 2 additions & 1 deletion scripts/filemanager.config.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"fileRoot": false,
"fileConnector": false,
"fileRootSizeLimit": false,
"outsideServerRoot": false,
"baseUrl": false,
"capabilities": ["select", "upload", "download", "rename", "move", "replace", "delete"],
"logger": false,
Expand Down Expand Up @@ -182,5 +183,5 @@
"default": "default.png"
},
"url": "https://github.com/servocoder/RichFilemanager",
"version": "1.0.4"
"version": "1.0.5"
}
98 changes: 45 additions & 53 deletions scripts/filemanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var normalizePath = function(path){
target.push(token);
}
}
return target.join(SLASH).replace(/[\/]{2, }/g, SLASH) || SLASH;
return target.join(SLASH).replace(/[\/]{2,}/g, SLASH) || SLASH;
};

/*---------------------------------------------------------
Expand Down Expand Up @@ -105,8 +105,12 @@ var HEAD_included_files = [];
// Default relative files root, may be changed with query params during initialization
var fileRoot = '/';

// Base URL to access the filemanager
var baseUrl;

// Sets paths to connectors based on language selection.
var fileConnector = config.options.fileConnector || config.globals.pluginPath + '/connectors/' + config.options.lang + '/filemanager.' + config.options.lang;
var langConnector = '/connectors/' + config.options.lang + '/filemanager.' + config.options.lang;
var fileConnector = config.options.fileConnector || config.globals.pluginPath + langConnector;

// Read capabilities from config files if exists else apply default settings
var capabilities = config.options.capabilities || ['upload', 'select', 'download', 'rename', 'move', 'delete', 'replace'];
Expand Down Expand Up @@ -282,29 +286,6 @@ var displayPath = function (path, reduce) {
}
};

/**
* Determine path when using baseUrl and setFileRoot connector
* function to give back a valid path on selectItem calls
*/
var smartPath = function(url, path) {
var a = url.split('/'),
separator = '/' + a[a.length-2] + '/',
position = path.indexOf(separator),
smart_path;

// separator is not found
// this can happen when not set dynamically with setFileRoot function - see : https://github.com/simogeo/Filemanager/issues/354
if(position == -1) {
smart_path = url + path;
} else {
smart_path = url + path.substring(position + separator.length);
}
if(config.options.logger) {
console.log("url : " + url + " - path : " + path + " - separator: " + separator + " - position: " + position + " - returned value : " + smart_path);
}
return smart_path;
};

// Set the view buttons state
var setViewButtonsFor = function(viewMode) {
if (viewMode == 'grid') {
Expand Down Expand Up @@ -409,9 +390,7 @@ function has_capability(data, cap) {

// Test if file is authorized
var isAuthorizedFile = function(filename) {

var ext = getExtension(filename);

// no extension is allowed
if(ext == '' && config.security.allowNoExtension == true) return true;

Expand All @@ -421,7 +400,6 @@ var isAuthorizedFile = function(filename) {
if(config.security.uploadPolicy == 'ALLOW_ALL') {
if($.inArray(ext, config.security.uploadRestrictions) == -1) return true;
}

return false;
};

Expand All @@ -430,14 +408,18 @@ var isFile = function(path) {
return path.charAt(path.length - 1) != '/';
};

// Replace all leading or trailing slashes with an empty string
var trimSlashes = function(string) {
return string.replace(/^\/+|\/+$/g, '');
};

// from http://phpjs.org/functions/basename:360
var basename = function(path, suffix) {
var b = path.replace(/^.*[\/\\]/g, '');

if (typeof(suffix) == 'string' && b.substr(b.length-suffix.length) == suffix) {
b = b.substr(0, b.length-suffix.length);
}

return b;
};

Expand Down Expand Up @@ -538,41 +520,54 @@ var isDocumentFile = function(filename) {
};

// Build url to preview files
var createPreviewUrl = function(path) {
// already an absolute path
if(path.substr(0,4) === 'http' || path.substr(0,3) === 'ftp')
var createPreviewUrl = function(path, encode) {
encode = encode || false;
// already an absolute path or or a relative path to connector action
if(path.substr(0,4) === 'http' || path.substr(0,3) === 'ftp' || path.indexOf(langConnector) !== -1) {
return path;
}
path = trimSlashes(normalizePath(path));

return location.origin + normalizePath(location.pathname + path);
if(encode) {
var parts = [];
$.each(path.split('/'), function(i, part) {
parts.push(encodeURIComponent(part));
});
path = parts.join('/');
}
return baseUrl + path;
};

// Return HTML video player
var getVideoPlayer = function(data) {
var code = '<video src="' + createPreviewUrl(data['Preview']) + '" width=' + config.videos.videosPlayerWidth + ' height=' + config.videos.videosPlayerHeight + ' controls="controls"></video>';
var url = createPreviewUrl(data['Dynamic Path'], true);
var code = '<video src="' + url + '" width=' + config.videos.videosPlayerWidth + ' height=' + config.videos.videosPlayerHeight + ' controls="controls"></video>';

$fileinfo.find('img').remove();
$fileinfo.find('#preview #main-title').before(code);
};

// Return HTML audio player
var getAudioPlayer = function(data) {
var code = '<audio src="' + createPreviewUrl(data['Preview']) + '" controls="controls"></audio>';
var url = createPreviewUrl(data['Dynamic Path'], true);
var code = '<audio src="' + url + '" controls="controls"></audio>';

$fileinfo.find('img').remove();
$fileinfo.find('#preview #main-title').before(code);
};

// Return PDF Reader
var getPdfReader = function(data) {
var code = '<iframe id="fm-pdf-viewer" src="' + config.globals.pluginPath + '/scripts/ViewerJS/index.html#' + createPreviewUrl(data['Preview']) + '" width="' + config.pdfs.pdfsReaderWidth + '" height="' + config.pdfs.pdfsReaderHeight + '" allowfullscreen webkitallowfullscreen></iframe>';
var url = createPreviewUrl(data['Dynamic Path'], true);
var code = '<iframe id="fm-pdf-viewer" src="' + config.globals.pluginPath + '/scripts/ViewerJS/index.html#' + url + '" width="' + config.pdfs.pdfsReaderWidth + '" height="' + config.pdfs.pdfsReaderHeight + '" allowfullscreen webkitallowfullscreen></iframe>';

$fileinfo.find('img').remove();
$fileinfo.find('#preview #main-title').before(code);
};

// Return Google Viewer
var getGoogleViewer = function(data) {
var url = encodeURIComponent(createPreviewUrl(data['Preview']));
var url = encodeURIComponent(createPreviewUrl(data['Dynamic Path']));
var code = '<iframe id="fm-google-viewer" src="http://docs.google.com/viewer?url=' + url + '&embedded=true" width="' + config.docs.docsReaderWidth + '" height="' + config.docs.docsReaderHeight + '" allowfullscreen webkitallowfullscreen></iframe>';

$fileinfo.find('img').remove();
Expand Down Expand Up @@ -881,12 +876,7 @@ var createFileTree = function() {
// contextual menu option in list views.
// NOTE: closes the window when finished.
var selectItem = function(data) {
if(config.options.baseUrl !== false ) {
var url = smartPath(baseUrl, data['Preview'].replace(fileRoot, ""));
} else {
var url = createPreviewUrl(data['Preview']);
}

var url = createPreviewUrl(data['Dynamic Path']);
if(window.opener || window.tinyMCEPopup || $.urlParam('field_name') || $.urlParam('CKEditorCleanUpFuncNum') || $.urlParam('CKEditor') || $.urlParam('ImperaviElementId')) {
if(window.tinyMCEPopup) {
// use TinyMCE > 3.0 integration method
Expand Down Expand Up @@ -1863,7 +1853,7 @@ var getFileInfo = function(file) {

$fileinfo.find('#main-title > h1').text(data['Filename']).attr('title', file);

$fileinfo.find('img').attr('src', createPreviewUrl(data["Preview"]));
$fileinfo.find('img').attr('src', createPreviewUrl(data['Image Path']));
if(isVideoFile(data['Filename']) && config.videos.showVideoPlayer == true) {
getVideoPlayer(data);
}
Expand All @@ -1880,11 +1870,7 @@ var getFileInfo = function(file) {
editItem(data);
}

if(config.options.baseUrl !== false ) {
var url = smartPath(baseUrl, data['Preview'].replace(fileRoot, ""));
} else {
var url = createPreviewUrl(data['Preview']);
}
var url = createPreviewUrl(data['Dynamic Path']);
if(data['Protected']==0) {
$fileinfo.find('div#tools').append(' <a id="copy-button" data-clipboard-text="'+ url + '" title="' + lg.copy_to_clipboard + '" href="#"><span>' + lg.copy_to_clipboard + '</span></a>');

Expand Down Expand Up @@ -1921,7 +1907,7 @@ var getFileInfo = function(file) {
// Clean up unnecessary item data
var prepareItemInfo = function(item) {
var data = $.extend({}, item);
delete data['Thumbnail'];
delete data['Image Path'];
delete data['Error'];
delete data['Code'];
return data;
Expand Down Expand Up @@ -1981,7 +1967,7 @@ var getFolderInfo = function(path) {
'data-path': item['Path']
}).data('itemdata', prepareItemInfo(item));

node = '<div class="clip"><img src="' + item['Thumbnail'] + '" width="' + scaledWidth + '" alt="' + item['Path'] + '" /></div>';
node = '<div class="clip"><img src="' + createPreviewUrl(item['Image Path']) + '" width="' + scaledWidth + '" alt="' + item['Path'] + '" /></div>';
node += '<p>' + item['Filename'] + '</p>';
if(props['Width'] && props['Width'] != '') node += '<span class="meta dimensions">' + props['Width'] + 'x' + props['Height'] + '</span>';
if(props['Size'] && props['Size'] != '') node += '<span class="meta size">' + props['Size'] + '</span>';
Expand Down Expand Up @@ -2296,11 +2282,17 @@ $(function() {
loadJS('/scripts/CodeMirror/dynamic-mode.js');
}

// init baseUrl
if(config.options.baseUrl === false) {
baseUrl = window.location.protocol + "//" + window.location.host;
baseUrl = location.origin + location.pathname;
} else {
baseUrl = config.options.baseUrl;
}
// for url like http://site.com/index.html
if(getExtension(baseUrl).length > 0) {
baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
}
baseUrl = trimSlashes(baseUrl) + '/';

// changes files root to restrict the view to a given folder
if($.urlParam('exclusiveFolder') != 0) {
Expand Down Expand Up @@ -2903,4 +2895,4 @@ if (!window.location.origin) {

$(window).load(function() {
$('#fileinfo').css({'left':$('#splitter .vsplitbar').width() + $('#filetree').width()});
});
});
4 changes: 2 additions & 2 deletions scripts/filemanager.min.js

Large diffs are not rendered by default.

0 comments on commit 8b52ab8

Please sign in to comment.