diff --git a/.gitattributes b/.gitattributes index ecd6b17..9b00969 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,9 +1,9 @@ -ifm.php -diff -ifm.min.php -diff -build/* -diff +dist/ifm.php -diff +dist/ifm.min.php -diff +dist/* -diff src/includes/ace.js -diff -src export-ignore -compiler.php export-ignore -.gitignore export-ignore -.gitattributes export-ignore +src export-ignore +compiler.php export-ignore +.gitignore export-ignore +.gitattributes export-ignore diff --git a/.gitignore b/.gitignore index 75a763a..f3b0a71 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.zip misc src/includes/ace/* +sftp-config.json +dist/* \ No newline at end of file diff --git a/compiler.php b/compiler.php index cdf17c2..7b71dba 100755 --- a/compiler.php +++ b/compiler.php @@ -8,6 +8,20 @@ chdir( realpath( dirname( __FILE__ ) ) ); +// output files and common attrs +define( "IFM_CDN", true ); +define( "IFM_VERSION", "v2.6.1" ); +define( "IFM_RELEASE_DIR", "dist/"); +define( "IFM_STANDALONE", "ifm.php" ); +define( "IFM_STANDALONE_GZ", "ifm.min.php" ); +define( "IFM_LIB", "libifm.php" ); + +if( IFM_CDN ){ + $IFM_ASSETS = "src/assets.cdn.part"; +} else { + $IFM_ASSETS = "src/assets.part"; +} + // php source files $IFM_SRC_PHP = array( 0 => "src/main.php", @@ -15,16 +29,11 @@ $IFM_SRC_PHP = array( 2 => "src/htpasswd.php" ); -// output files -define( "IFM_STANDALONE", "ifm.php" ); -define( "IFM_STANDALONE_GZ", "build/ifm.min.php" ); -define( "IFM_LIB", "build/libifm.php" ); - // get options $options = getopt( null, array( "language::" ) ); // process languages -$vars['languages'] = isset( $options['language'] ) ? explode( ',', $options['language'] ) : array( "en" ); +$vars['languages'] = isset( $options['language'] ) ? explode( ',', $options['language'] ) : array( "en", "ru" ); $vars['defaultlanguage'] = $vars['languages'][0]; $vars['languageincludes'] = ""; foreach( $vars['languages'] as $l ) { @@ -49,6 +58,7 @@ foreach( $IFM_SRC_PHP as $phpfile ) { } $compiled = join( $compiled ); +$compiled = str_replace( "IFM_ASSETS", file_get_contents( $IFM_ASSETS ), $compiled ); /** * Process file includes */ @@ -82,12 +92,15 @@ preg_match_all( "/\@\@\@vars:([^\@]+)\@\@\@/", $compiled, $includes, PREG_SET_OR foreach( $includes as $var ) $compiled = str_replace( $var[0], $vars[$var[1]], $compiled ); -/** - * Build versions - */ +$compiled = str_replace( 'IFM_VERSION', IFM_VERSION, $compiled ); + +if (!is_dir(IFM_RELEASE_DIR)){ + mkdir(IFM_RELEASE_DIR); +} + // build standalone ifm -file_put_contents( IFM_STANDALONE, $compiled ); -file_put_contents( IFM_STANDALONE, ' +file_put_contents( IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : 'simple.') . IFM_STANDALONE, $compiled ); +file_put_contents( IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : 'simple.') . IFM_STANDALONE, ' /** * start IFM */ @@ -95,13 +108,11 @@ $ifm = new IFM(); $ifm->run(); ', FILE_APPEND ); -/* // build compressed ifm +// build compressed ifm file_put_contents( - IFM_STANDALONE_GZ, + IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : 'simple.') . IFM_STANDALONE_GZ, '' - . gzencode( file_get_contents( "ifm.php", false, null, 5 ) ) + . gzencode( file_get_contents( IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : 'simple.') .IFM_STANDALONE, false, null, 5 ) ) ); - */ - // build lib -file_put_contents( IFM_LIB, $compiled ); +file_put_contents( IFM_RELEASE_DIR . (IFM_CDN ? 'cdn.' : 'simple.') . IFM_LIB, $compiled ); diff --git a/.dockerignore b/docker/.dockerignore similarity index 100% rename from .dockerignore rename to docker/.dockerignore diff --git a/Dockerfile b/docker/Dockerfile similarity index 100% rename from Dockerfile rename to docker/Dockerfile diff --git a/docker-startup.sh b/docker/docker-startup.sh old mode 100755 new mode 100644 similarity index 100% rename from docker-startup.sh rename to docker/docker-startup.sh diff --git a/src/assets.cdn.part b/src/assets.cdn.part new file mode 100644 index 0000000..636d29c --- /dev/null +++ b/src/assets.cdn.part @@ -0,0 +1,32 @@ + public function getCSS() { + print ' + + + + + + + '; + } + + public function getJS() { + print ' + + + + + + + + + + + '; + echo <<<'f00bar' + +f00bar; + } \ No newline at end of file diff --git a/src/assets.part b/src/assets.part new file mode 100644 index 0000000..8fdd7a3 --- /dev/null +++ b/src/assets.part @@ -0,0 +1,31 @@ + public function getCSS() { + print ' + + + + + + + '; + } + + public function getJS() { + echo <<<'f00bar' + +f00bar; + } \ No newline at end of file diff --git a/src/i18n/de.json b/src/i18n/de.json index 7d2593c..d7c84b3 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -30,7 +30,6 @@ "file_new": "Neue Datei", "file_load_error": "Der Inhalt der Datei konnte nicht geladen werden", "file_open_error": "Die Datei konnte nicht geöffnet werden.", - "file_new": "Neue Datei", "file_no_permission": "Sie haben keine Berechtigung diese Datei zu erstellen/bearbeiten.", "file_not_found": "Die Datei wurde nicht gefunden, oder kann nicht geöffnet werden.", "file_rename": "Datei umbenennen", @@ -98,7 +97,7 @@ "soft_tabs": "Leichte Tabulatoren", "tab_size": "Tabulatoren Größe", "tasks": "Aufgaben", - "remaining_tasks": "Es gibt noch laufende Prozesse. Wollen Sie wirklich neu laden?", + "remaining_tasks": "Es gibt noch laufende Prozesse. Wollen Sie wirklich neu laden?", "toggle_nav": "Navigation umschalten", "upload": "Hochladen", "upload_drop": "Dateien zum hochladen hier ablegen", diff --git a/src/i18n/en.json b/src/i18n/en.json index c82fffb..dc8dc0d 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1,15 +1,15 @@ { "ajax_request": "AJAX request", - "archivename": "Name of the archive", - "archive_create_success": "Archive successfully created.", "archive_create_error": "Could not create archive.", + "archive_create_success": "Archive successfully created.", "archive_invalid_format": "Invalid archive format given.", + "archivename": "Name of the archive", "cancel": "Cancel", "close": "Close", "copy": "Copy", - "copylink": "Copy link", "copy_error": "The following files could not be copied:", "copy_success": "File(s) copied successfully.", + "copylink": "Copy link", "create_archive": "Create archive", "data": "Data", "delete": "Delete", @@ -27,19 +27,19 @@ "file_delete_error": "File(s) could not be deleted.", "file_delete_success": "File(s) successfully deleted.", "file_display_error": "This file can not be displayed or edited.", - "file_new": "New File", "file_load_error": "Content could not be loaded.", - "file_open_error": "Could not open the file.", + "file_new": "New File", "file_new": "New file", "file_no_permission": "No permission to edit/create file.", "file_not_found": "File was not found or could not be opened.", + "file_open_error": "Could not open the file.", "file_rename": "Rename File", "file_rename_error": "File could not be renamed: ", "file_rename_success": "File successfully renamed.", - "file_save_error": "File could not be saved.", - "file_save_success": "File was saved successfully.", "file_save_confirm": "Do you want to save the following file -", "file_save_error": "File could not be edited or created: ", + "file_save_error": "File could not be saved.", + "file_save_success": "File was saved successfully.", "file_upload_error": "File could not be uploaded.", "file_upload_success": "File successfully uploaded.", "filename": "Filename", @@ -85,6 +85,7 @@ "permission_parse_error": "Permissions could not be parsed correctly.", "permissions": "Permissions", "refresh": "Refresh", + "remaining_tasks": "There are remaining tasks. Do you really want to reload?", "rename": "Rename", "rename_filename": "Rename file -", "request": "Request", @@ -98,13 +99,12 @@ "soft_tabs": "Soft Tabs", "tab_size": "Tab Size", "tasks": "Tasks", - "remaining_tasks": "There are remaining tasks. Do you really want to reload?", "toggle_nav": "Toggle navigation", "upload": "Upload", "upload_drop": "Drop files to upload", "upload_file": "Upload File", "upload_remote": "Remote Upload", "upload_remote_url": "Remote Upload URL", - "username": "username", + "username": "Username", "word_wrap": "Word Wrap" } diff --git a/src/i18n/fr.json b/src/i18n/fr.json index db7381b..f5e67c7 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -30,7 +30,6 @@ "file_new": "Nouveau fichier", "file_load_error": "Le contenu n'a pas pu être chargé.", "file_open_error": "Impossible d'ouvrir le fichier.", - "file_new": "Nouveau fichier", "file_no_permission": "Permissions insuffisantes pour éditer ou créer le fichier.", "file_not_found": "Le fichier n'a pas été trouvé ou n'a pas pu être ouvert.", "file_rename": "Renommer fichier", @@ -105,6 +104,6 @@ "upload_file": "Envoyer fichier", "upload_remote": "Envoi distant", "upload_remote_url": "URL pour l'envoi distant", - "username": "nom d'utilisateur", + "username": "Nom d'utilisateur", "word_wrap": "Revenir à la ligne" } diff --git a/src/i18n/pt-br.json b/src/i18n/pt-br.json index b9bb519..e727bd0 100644 --- a/src/i18n/pt-br.json +++ b/src/i18n/pt-br.json @@ -30,7 +30,6 @@ "file_new": "Novo Arquivo", "file_load_error": "Conteúdo não pôde ser carregado.", "file_open_error": "Não foi possível abrir o arquivo.", - "file_new": "Novo Arquivo", "file_no_permission": "Sem permissão para editar/criar o arquivo.", "file_not_found": "Arquivo não foi encontrado ou não pôde ser aberto.", "file_rename": "Renomear arquivo", @@ -39,7 +38,6 @@ "file_save_error": "Não foi possível salvar o arquivo.", "file_save_success": "Arquivo salvo com sucesso.", "file_save_confirm": "Você quer salvar o seguinte arquivo -", - "file_save_error": "Não foi possível salvar o arquivo.", "file_save_error": "Não foi possível criar ou editar o arquivo: ", "file_upload_error": "Não foi possível enviar o arquivo.", "file_upload_success": "Arquivo enviado com sucesso.", @@ -99,13 +97,13 @@ "soft_tabs": "Tabulação suave", "tab_size": "Tamanho da tabulação", "tasks": "Tarefas", - "remaining_tasks": "Existem tarefas remanescentes. Você realmente deseja recarregar?", + "remaining_tasks": "Existem tarefas remanescentes. Você realmente deseja recarregar?", "toggle_nav": "Ativar/Desativar Navegação", "upload": "Enviar", "upload_drop": "Solte arquivo aqui para enviar", "upload_file": "Enviar Arquivo", "upload_remote": "Envio Remoto", "upload_remote_url": "URL para Envio Remoto", - "username": "nome de usuário", + "username": "Nome de usuário", "word_wrap": "Quebra de Linha" } diff --git a/src/i18n/ru.json b/src/i18n/ru.json new file mode 100644 index 0000000..f32ef50 --- /dev/null +++ b/src/i18n/ru.json @@ -0,0 +1,109 @@ +{ + "ajax_request": "AJAX запрос", + "archivename": "Название архива", + "archive_create_success": "Архив успешно создан.", + "archive_create_error": "Не удалось создать архив.", + "archive_invalid_format": "Указан неверный формат архива.", + "cancel": "Отмена", + "close": "Закрыть", + "copy": "Копировать", + "copylink": "Копировать ссылку", + "copy_error": "Не удалось скопировать следующие файлы:", + "copy_success": "Файл(ы) успешно скопированы.", + "create_archive": "Создать архив", + "data": "Данные", + "delete": "Удалить", + "directoryname": "Название каталога", + "download": "Скачать", + "edit": "Редактировать", + "editor_options": "Параметры редактора", + "error": "Ошибка:", + "extract": "извлечь", + "extract_error": "Не удалось извлечь архив.", + "extract_filename": "Извлечь файл - ", + "extract_success": "Архив извлечен успешно.", + "file_copy_to": "в", + "file_delete_confirm": "Вы действительно хотите удалить следующий файл - ", + "file_delete_error": "Файл(ы) не может быть удалены.", + "file_delete_success": "Файл(ы) успешно удалены.", + "file_display_error": "Файл не может быть отображен или отредактирован.", + "file_new": "Новый файл", + "file_load_error": "Не удалось загрузить содержимое.", + "file_open_error": "Не удалось открыть файл.", + "file_no_permission": "Нет прав на редактирование/создание файла.", + "file_not_found": "Файл не найден или не может быть открыт.", + "file_rename": "Переименовать файл", + "file_rename_error": "Файл не может быть переименован: ", + "file_rename_success": "Файл успешно переименован.", + "file_save_error": "Не удалось сохранить файл.", + "file_save_success": "Файл успешно сохранен.", + "file_save_confirm": "Хотите сохранить следующий файл -", + "file_save_error": "Файл не может быть отредактирован или создан: ", + "file_upload_error": "Не удалось загрузить файл.", + "file_upload_success": "Файл успешно загружен.", + "filename": "Имя файла", + "filename_new": "Новое имя файла", + "filename_slashes": "Имя файла не должно содержать косую черту.", + "filter": "Фильтр", + "folder_create_error": "Не удалось создать каталог: ", + "folder_create_success": "Каталог успешно сохранен.", + "folder_new": "Новый каталог", + "folder_not_found": "Каталог не найден.", + "folder_tree_load_error": "Ошибка при получении дерева папок.", + "footer": "IFM - improved file manager | ifm.php hidden |", + "general_error": "Ошибка: Отсутствующий или неверный ответ.", + "github": "Страница проекта на GitHub", + "group": "Группа", + "invalid_action": "Неверное действие.", + "invalid_archive_format": "Неверный формат архива. Допустимые форматы: zip, tar, tar.gz или tar.bz2.", + "invalid_data": "Неверные данные от сервера.", + "invalid_dir": "Неверный каталог.", + "invalid_filename": "Неверное имя файла.", + "invalid_params": "Неверный параметр.", + "invalid_url": "Неверный URL.", + "json_encode_error": "Не удалось представить ответ в виде JSON:", + "last_modified": "Изменено", + "load_config_error": "Ошибка загрузки конфигурации.", + "load_template_error": "Ошибка загрузки шаблона.", + "load_text_error": "Ошибка загрузки текста.", + "login": "Вход", + "login_failed": "Ошибка входа.", + "logout": "Выйти", + "method": "Метод", + "move": "Переместить", + "move_error": "Следующие файлы не могут быть перемещены:", + "move_success": "Файл(ы) успешно перемещены.", + "nopermissions": "Нет прав.", + "options": "Опции", + "owner": "Владелец", + "password": "Пароль", + "path_content": "Содержимое", + "pattern_error_slashes": "Шаблон не должен содержать косую черту.", + "permission_change_error": "Права не могут быть изменены: ", + "permission_change_success": "Права успешно изменены.", + "permission_parse_error": "Не удалось разобрать права.", + "permissions": "Права", + "refresh": "Обновить", + "rename": "Переименовать", + "rename_filename": "Переименовать файл -", + "request": "Запрос", + "response": "Ответ", + "save": "Сохранить и выйти", + "save_wo_close": "Сохранить", + "search": "Поиск", + "search_pattern": "Шаблон", + "select_destination": "Выберите назначение", + "size": "Размер", + "soft_tabs": "Пробелы вместо табуляции", + "tab_size": "Размер табов", + "tasks": "Задачи", + "remaining_tasks": "Есть оставшиеся задачи. Вы действительно хотите перезагрузить?", + "toggle_nav": "Переключить вид", + "upload": "Загрузить", + "upload_drop": "Перетащите файлы для загрузки", + "upload_file": "Загрузить файла", + "upload_remote": "Удаленная загрузка", + "upload_remote_url": "Удаленная загрузка по URL", + "username": "Имя пользователя", + "word_wrap": "Перенос строк" +} \ No newline at end of file diff --git a/src/ifm.js b/src/ifm.js index 7576d49..04f4ac7 100644 --- a/src/ifm.js +++ b/src/ifm.js @@ -134,7 +134,7 @@ function IFM(params) { item.download.icon = "icon icon-download"; } if( item.icon.indexOf( 'file-image' ) !== -1 ) { - item.tooltip = 'data-toggle="tooltip"'; + item.popover = 'data-toggle="popover"'; } if( self.config.extract && self.inArray( item.ext, ["zip","tar","tgz","tar.gz","tar.xz","tar.bz2"] ) ) { item.eaction = "extract"; @@ -270,19 +270,20 @@ function IFM(params) { } }); // has to be jquery, since this is a bootstrap feature - $( 'a[data-toggle="tooltip"]' ).tooltip({ - title: function() { + $( 'a[data-toggle="popover"]' ).popover({ + content: function() { var item = self.fileCache.find( x => x.guid == $(this).attr('id') ); - var tooltip = document.createElement( 'img' ); + var popover = document.createElement( 'img' ); if( self.config.isDocroot ) - tooltip.src = encodeURI( self.pathCombine( self.currentDir, item.name ) ).replace( '#', '%23' ).replace( '?', '%3F' ); + popover.src = encodeURI( self.pathCombine( self.currentDir, item.name ) ).replace( '#', '%23' ).replace( '?', '%3F' ); else - tooltip.src = self.api + "?api=proxy&dir=" + encodeURIComponent( self.currentDir ) + "&filename=" + encodeURIComponent( item.name ); - tooltip.classList.add( 'imgpreview' ); - return tooltip; + popover.src = self.api + "?api=proxy&dir=" + encodeURIComponent( self.currentDir ) + "&filename=" + encodeURIComponent( item.name ); + popover.classList.add( 'imgpreview' ); + return popover; }, animated: 'fade', - placement: 'right', + placement: 'bottom', + trigger: 'hover', html: true }); @@ -479,8 +480,8 @@ function IFM(params) { title: self.i18n.options, content: function() { // see https://github.com/twbs/bootstrap/issues/12571 - var ihatethisfuckingpopoverworkaround = $('#editoroptions').data('bs.popover'); - ihatethisfuckingpopoverworkaround.$tip.find( '.popover-content' ).empty(); + // var ihatethisfuckingpopoverworkaround = $('#editoroptions').data('bs.popover'); + // $(ihatethisfuckingpopoverworkaround.tip).find( '.popover-body' ).empty(); var aceSession = self.editor.getSession(); var content = self.getNodesFromString( @@ -519,7 +520,8 @@ function IFM(params) { self.editor.getSession().setMode( e.target.value ); }); }); - return content; + return $(content); + } }); @@ -1130,7 +1132,7 @@ function IFM(params) { searchresults.tBodies[0].addEventListener( 'click', function( e ) { if( e.target.classList.contains( 'searchitem' ) || e.target.parentElement.classList.contains( 'searchitem' ) ) { e.preventDefault(); - self.changeDirectory(self.pathCombine(self.search.data.currentDir, e.target.dataset.folder || e.target.parentElement.dataset.folder), {absolute: true}); + self.changeDirectory( self.pathCombine( self.search.data.currentDir, e.target.dataset.folder || e.target.parentElement.dataset.folder ), { absolute: true }); self.hideModal(); } }); @@ -1328,7 +1330,7 @@ function IFM(params) { this.formatDate = function( timestamp ) { var d = new Date( timestamp * 1000 ); - return d.toLocaleString(); + return d.toLocaleString(self.config.dateLocale); }; this.getClipboardLink = function( relpath ) { diff --git a/src/includes/BootstrapMenu.min.js b/src/includes/BootstrapMenu.min.js index 4934a19..145ef7a 100644 --- a/src/includes/BootstrapMenu.min.js +++ b/src/includes/BootstrapMenu.min.js @@ -1,16 +1,9 @@ -!function(t){function n(e){if(o[e])return o[e].exports;var i=o[e]={exports:{},id:e,loaded:!1};return t[e].call(i.exports,i,i.exports,n),i.loaded=!0,i.exports}var o={};return n.m=t,n.c=o,n.p="",n(0)}([function(t,n,o){window.BootstrapMenu=o(1)},function(t,n,o){"use strict";function e(t){var n=f('
'),o=f(' '),e=[];e[0]=[],p.each(t.options.actionsGroups,function(t,n){e[n+1]=[]});var i=!1;p.each(t.options.actions,function(n,o){var r=!1;p.each(t.options.actionsGroups,function(t,n){p.contains(t,o)&&(e[n+1].push(o),r=!0)}),r===!1&&e[0].push(o),"undefined"!=typeof n.iconClass&&(i=!0)});var r=!0;return p.each(e,function(n){0!=n.length&&(r===!1&&o.append(''),r=!1,p.each(n,function(n){var e=t.options.actions[n];i===!0?o.append('