diff --git a/README.md b/README.md index cc9d9074..74adca9c 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,9 @@ h5ai is provided under the terms of the [MIT License](http://github.com/lrsjng/h * adds an info page at `/_h5ai` * sort order is preserved while browsing * removes PHP error messages on thumbnail generation -* fixes PHP problems with zipped download -* changes crumb image for folders with an index file +* fixes PHP some problems with packed download * adds support for tarred downloads +* changes crumb image for folders with an index file * adds `index.php` to use h5ai in non-Apache environments * switches from [Datejs](http://www.datejs.com) to [Moment.js](http://momentjs.com) * adds [underscore.js](http://underscorejs.org) diff --git a/build.properties b/build.properties index 1b874df3..20c056d4 100644 --- a/build.properties +++ b/build.properties @@ -3,7 +3,7 @@ custom = true # project project.name = h5ai -project.version = 0.19-pre +project.version = 0.19 # src diff --git a/src/_h5ai/cache/readme.md b/src/_h5ai/cache/README.md similarity index 100% rename from src/_h5ai/cache/readme.md rename to src/_h5ai/cache/README.md diff --git a/src/_h5ai/config.js b/src/_h5ai/config.js index 54e2c479..002184a2 100644 --- a/src/_h5ai/config.js +++ b/src/_h5ai/config.js @@ -69,11 +69,16 @@ var H5AI_CONFIG = { }, /* + * EXPLICITLY: USE "shell" ON YOUR OWN RISK. + * * Requires PHP on the server. - * Enable packaged download of selected entries. Supported formats: "tar", "zip". + * Enable packaged download of selected entries. + * Execution: "php", "shell". + * Supported formats: "tar", "zip". */ "download": { "enabled": true, + "execution": "shell", "format": "zip" }, @@ -108,16 +113,11 @@ var H5AI_CONFIG = { * * Optionally try to use browser language, falls back to previous * specified language. - * - * Date format in detailed view, for example: "YYYY-MM-DD HH:mm:ss" - * Syntax as specified by Moment.js (http://momentjs.com) - * This might be overidden by "dateFormat" in a lang specification. */ "l10n": { "enabled": true, "lang": "en", - "useBrowserLang": true, - "defaultDateFormat": "YYYY-MM-DD HH:mm" + "useBrowserLang": true }, /* @@ -136,7 +136,7 @@ var H5AI_CONFIG = { }, /* - * Make entries selectable. At the moment only needed for zipped download. + * Make entries selectable. At the moment only needed for packaged download. */ "select": { "enabled": true @@ -265,6 +265,9 @@ var H5AI_CONFIG = { /* * Available translations. "en" in first place as a reference, otherwise in alphabetical order. + * + * Date format is used in detailed view, for example: "YYYY-MM-DD HH:mm:ss" + * Syntax as specified by Moment.js (http://momentjs.com) */ "langs": { @@ -280,7 +283,8 @@ var H5AI_CONFIG = { "folders": "folders", "files": "files", "download": "download", - "noMatch": "no match" + "noMatch": "no match", + "dateFormat": "YYYY-MM-DD HH:mm" }, "bg": { @@ -293,9 +297,7 @@ var H5AI_CONFIG = { "parentDirectory": "Предходна директория", "empty": "празно", "folders": "папки", - "files": "файлове", - "download": "download", - "noMatch": "no match" + "files": "файлове" }, "cs": { @@ -308,9 +310,7 @@ var H5AI_CONFIG = { "parentDirectory": "Nadřazený adresář", "empty": "prázdný", "folders": "složek", - "files": "souborů", - "download": "download", - "noMatch": "no match" + "files": "souborů" }, "de": { @@ -340,8 +340,7 @@ var H5AI_CONFIG = { "empty": "vacío", "folders": "Directorios", "files": "Archivos", - "download": "Descargar", - "noMatch": "no match" + "download": "Descargar" }, "fr": { @@ -384,9 +383,7 @@ var H5AI_CONFIG = { "parentDirectory": "Cartella Superiore", "empty": "vuota", "folders": "cartelle", - "files": "file", - "download": "download", - "noMatch": "no match" + "files": "file" }, "ja": { @@ -442,11 +439,7 @@ var H5AI_CONFIG = { "lastModified": "Laatste wijziging", "size": "Grootte", "parentDirectory": "Bovenliggende map", - "empty": "lege", - "folders": "folders", - "files": "files", - "download": "download", - "noMatch": "no match" + "empty": "lege" }, "pl": { @@ -459,9 +452,7 @@ var H5AI_CONFIG = { "parentDirectory": "Katalog nadrzędny", "empty": "pusty", "folders": "foldery", - "files": "pliki", - "download": "download", - "noMatch": "no match" + "files": "pliki" }, "pt": { @@ -474,9 +465,7 @@ var H5AI_CONFIG = { "parentDirectory": "Diretório superior", "empty": "vazio", "folders": "pastas", - "files": "arquivos", - "download": "download", - "noMatch": "no match" + "files": "arquivos" }, "ro": { @@ -504,9 +493,7 @@ var H5AI_CONFIG = { "parentDirectory": "Главная директория", "empty": "пусто", "folders": "папки", - "files": "файлы", - "download": "download", - "noMatch": "no match" + "files": "файлы" }, "sk": { @@ -519,9 +506,7 @@ var H5AI_CONFIG = { "parentDirectory": "Nadriadený priečinok", "empty": "prázdny", "folders": "priečinkov", - "files": "súborov", - "download": "download", - "noMatch": "no match" + "files": "súborov" }, "sr": { @@ -547,11 +532,7 @@ var H5AI_CONFIG = { "lastModified": "Senast ändrad", "size": "Filstorlek", "parentDirectory": "Till överordnad mapp", - "empty": "tom", - "folders": "folders", - "files": "files", - "download": "download", - "noMatch": "no match" + "empty": "tom" }, "tr": { @@ -565,8 +546,7 @@ var H5AI_CONFIG = { "empty": "boş", "folders": "klasörler", "files": "dosyalar", - "download": "indir", - "noMatch": "no match" + "download": "indir" }, "zh-cn": { @@ -580,8 +560,7 @@ var H5AI_CONFIG = { "empty": "空文件夹", "folders": "文件夹", "files": "文件", - "download": "下载", - "noMatch": "no match" + "download": "下载" }, "zh-tw": { @@ -595,8 +574,7 @@ var H5AI_CONFIG = { "empty": "空資料夾", "folders": "資料夾", "files": "檔案", - "download": "下載", - "noMatch": "no match" + "download": "下載" } } }; diff --git a/src/_h5ai/css/inc/apache-autoindex-table.less b/src/_h5ai/css/inc/apache-autoindex-table.less index 957e54c2..5fcd86a1 100644 --- a/src/_h5ai/css/inc/apache-autoindex-table.less +++ b/src/_h5ai/css/inc/apache-autoindex-table.less @@ -36,6 +36,7 @@ border-right: none; overflow: hidden; white-space: nowrap; + font-size: 16px; a, a:active, a:visited { display: block; diff --git a/src/_h5ai/css/inc/bottombar.less b/src/_h5ai/css/inc/bottombar.less index 432e0466..d902d81c 100644 --- a/src/_h5ai/css/inc/bottombar.less +++ b/src/_h5ai/css/inc/bottombar.less @@ -10,7 +10,6 @@ border-top: 1px solid rgb(210,210,210); color: #999; - font-size: @smaller-font; text-align: center; a, a:active, a:visited { diff --git a/src/_h5ai/css/inc/content.less b/src/_h5ai/css/inc/content.less index 202d2084..3c17fbfe 100644 --- a/src/_h5ai/css/inc/content.less +++ b/src/_h5ai/css/inc/content.less @@ -2,6 +2,7 @@ #content { max-width: 960px; margin: 50px auto; + font-size: 16px; } diff --git a/src/_h5ai/css/inc/download.less b/src/_h5ai/css/inc/download.less new file mode 100644 index 00000000..d56119ec --- /dev/null +++ b/src/_h5ai/css/inc/download.less @@ -0,0 +1,30 @@ + +#download { + display: none; + .topbar-right; + .transition(all 0.2s ease-in-out); + + &.failed { + background-color: rgba(255,0,0,0.5); + } +} + +#download-auth { + display: none; + position: fixed; + z-index: 5; + left: 0; + top: 0; + .vert-gradient(rgb(241,241,241), rgb(228,228,228)); + border: 1px solid rgb(210,210,210); + + input { + display: block; + margin: 4px 6px; + border: 1px solid rgb(210,210,210); + font-family: Ubuntu, sans-serif; + color: #555; + background-color: rgba(255,255,255,1); + width: 100px; + } +} diff --git a/src/_h5ai/css/inc/extended.less b/src/_h5ai/css/inc/extended.less index 3fbb7810..32e9ba23 100644 --- a/src/_h5ai/css/inc/extended.less +++ b/src/_h5ai/css/inc/extended.less @@ -13,6 +13,8 @@ clear: both; &.header { + font-size: 13px; + a, a:active, a:visited { padding-bottom: 18px; color: #555; @@ -42,7 +44,6 @@ } } &.entry { - a, a:active, a:visited { display: block; color: #555; diff --git a/src/_h5ai/css/inc/filter.less b/src/_h5ai/css/inc/filter.less new file mode 100644 index 00000000..8ae0ae6d --- /dev/null +++ b/src/_h5ai/css/inc/filter.less @@ -0,0 +1,19 @@ + +#filter { + .topbar-right; + + input { + display: none; + border: none; + font-family: Ubuntu, sans-serif; + color: #555; + background-color: rgba(0,0,0,0); + width: 100px; + } + + &.current { + input { + display: inline; + } + } +} diff --git a/src/_h5ai/css/inc/h5ai-info.less b/src/_h5ai/css/inc/h5ai-info.less index e63d8664..dc65b089 100644 --- a/src/_h5ai/css/inc/h5ai-info.less +++ b/src/_h5ai/css/inc/h5ai-info.less @@ -56,4 +56,8 @@ body#h5ai-info { } } } + + #bottombar { + font-size: 13px; + } } diff --git a/src/_h5ai/css/inc/topbar.less b/src/_h5ai/css/inc/topbar.less index 231fa4ee..9b5577a7 100644 --- a/src/_h5ai/css/inc/topbar.less +++ b/src/_h5ai/css/inc/topbar.less @@ -5,32 +5,31 @@ width: 100%; left: 0; top: 0; - font-size: @smaller-font; .vert-gradient(rgb(241,241,241), rgb(228,228,228)); border-bottom: 1px solid rgb(210,210,210); } -.nav-highlight { +.topbar-highlight { background-color: rgba(255,255,255,0.5); opacity: 1.0; } -.nav-hover { - .nav-highlight; +.topbar-hover { + .topbar-highlight; color: #e80; } -@nav-sep-border: 1px solid rgba(0,0,0,0.05); +@topbar-sep-border: 1px solid rgba(0,0,0,0.05); -.nav-left { +.topbar-left { float: left; - border-right: @nav-sep-border; + border-right: @topbar-sep-border; } -.nav-right { +.topbar-right { float: right; - border-left: @nav-sep-border; + border-left: @topbar-sep-border; } @@ -53,12 +52,12 @@ padding: 0 10px; &:hover, &.hover { - .nav-hover; + .topbar-hover; } } .current { a, span.element { - .nav-highlight; + .topbar-highlight; } } img { @@ -71,7 +70,7 @@ margin-left: 6px; } .crumb { - .nav-left; + .topbar-left; .hint { margin-left: 8px; font-style: italic; @@ -84,58 +83,6 @@ } } .view { - .nav-right; - } -} - - -#filter { - .nav-right; - - input { - display: none; - border: none; - font-family: Ubuntu, sans-serif; - color: #555; - background-color: rgba(0,0,0,0); - width: 100px; - } - - &.current { - input { - display: inline; - } - } -} - - -#download { - display: none; - .nav-right; - .transition(all 0.2s ease-in-out); - - &.failed { - background-color: rgba(255,0,0,0.5); - } -} - -#download-auth { - display: none; - position: fixed; - z-index: 5; - left: 0; - top: 0; - font-size: @smaller-font; - .vert-gradient(rgb(241,241,241), rgb(228,228,228)); - border: 1px solid rgb(210,210,210); - - input { - display: block; - margin: 4px 6px; - border: 1px solid rgb(210,210,210); - font-family: Ubuntu, sans-serif; - color: #555; - background-color: rgba(255,255,255,1); - width: 100px; + .topbar-right; } } diff --git a/src/_h5ai/css/inc/tree.less b/src/_h5ai/css/inc/tree.less index 2c96fe3f..1462593b 100644 --- a/src/_h5ai/css/inc/tree.less +++ b/src/_h5ai/css/inc/tree.less @@ -7,7 +7,6 @@ height: 100%; z-index: 3; overflow: auto; - font-size: @smaller-font; padding: 8px; background-color: rgb(241,241,241); border-right: 2px solid rgb(221,221,221); diff --git a/src/_h5ai/css/styles.less b/src/_h5ai/css/styles.less index 40923bd8..2cc2b6c9 100644 --- a/src/_h5ai/css/styles.less +++ b/src/_h5ai/css/styles.less @@ -7,25 +7,31 @@ body { font-family: Ubuntu, sans-serif; - font-size: 16px; + font-size: 13px; color: #555; background-color: #fff; margin: 30px; } -@smaller-font: 15px; - @import "inc/topbar"; +@import "inc/download"; +@import "inc/filter"; + @import "inc/content"; @import "inc/extended"; + @import "inc/bottombar"; @import "inc/l10n"; + @import "inc/tree"; @import "inc/context"; @import "inc/apache-autoindex-table"; + @import "inc/responsive"; +@import "inc/h5ai-info"; + html.js .hideOnJs, html.no-js .hideOnNoJs { display: none; @@ -39,5 +45,3 @@ html.oldie { } } - -@import "inc/h5ai-info"; diff --git a/src/_h5ai/index.html b/src/_h5ai/index.html index 4a9c3c33..5bb60c83 100644 --- a/src/_h5ai/index.html +++ b/src/_h5ai/index.html @@ -22,11 +22,13 @@ version %BUILD_VERSION%

server supports

diff --git a/src/_h5ai/js/inc/core/settings.js b/src/_h5ai/js/inc/core/settings.js index 22d5974e..f81d3e27 100644 --- a/src/_h5ai/js/inc/core/settings.js +++ b/src/_h5ai/js/inc/core/settings.js @@ -3,7 +3,7 @@ module.define('core/settings', [H5AI_CONFIG], function (config) { var defaults = { rootAbsHref: '/', - h5aiAbsHref: '/_h5ai/', + h5aiAbsHref: '/_h5ai/' }; return _.extend({}, defaults, config.options); @@ -31,7 +31,7 @@ module.define('core/types', [H5AI_CONFIG], function (config) { } else { fileNames[match] = type; } - }) + }); }); }, @@ -61,5 +61,33 @@ module.define('core/types', [H5AI_CONFIG], function (config) { module.define('core/langs', [H5AI_CONFIG], function (config) { - return _.extend({}, config.langs); + var defaults = { + lang: 'unknown', + details: 'details', + icons: 'icons', + name: 'Name', + lastModified: 'Last modified', + size: 'Size', + parentDirectory: 'Parent Directory', + empty: 'empty', + folders: 'folders', + files: 'files', + download: 'download', + noMatch: 'no match', + dateFormat: 'YYYY-MM-DD HH:mm' + }, + + translations = {}, + + parse = function (langs) { + + _.each(langs, function (trans, lang) { + + translations[lang] = _.extend({}, defaults, trans); + }); + }; + + parse(_.extend({}, config.langs)); + + return translations; }); diff --git a/src/_h5ai/js/inc/ext/download.js b/src/_h5ai/js/inc/ext/download.js index a02a6c7e..3fcc7c10 100644 --- a/src/_h5ai/js/inc/ext/download.js +++ b/src/_h5ai/js/inc/ext/download.js @@ -3,10 +3,11 @@ module.define('ext/download', [jQuery, 'core/settings', 'core/resource', 'core/e var defaults = { enabled: false, - format: 'tar' + execution: 'php', + format: 'zip' }, - settings = _.extend({}, defaults, allsettings['download']), + settings = _.extend({}, defaults, allsettings.download), formats = ['tar', 'zip'], @@ -38,7 +39,7 @@ module.define('ext/download', [jQuery, 'core/settings', 'core/resource', 'core/e $img.attr('src', resource.image('download')); if (response) { - if (response.status === 'ok') { + if (response.code === 0) { setTimeout(function () { // wait here so the img above can be updated in time window.location = resource.api() + '?action=getarchive&id=' + response.id + '&as=h5ai-selection.' + settings.format; @@ -68,6 +69,7 @@ module.define('ext/download', [jQuery, 'core/settings', 'core/resource', 'core/e url: resource.api(), data: { action: 'archive', + execution: settings.execution, format: settings.format, hrefs: hrefsStr }, @@ -95,15 +97,13 @@ module.define('ext/download', [jQuery, 'core/settings', 'core/resource', 'core/e onSelection = function (entries) { - var $download = $('#download').appendTo('#navbar'); - selectedHrefsStr = ''; if (entries.length) { selectedHrefsStr = _.map(entries, function (entry) { return entry.absHref; }).join(':'); - $download.show(); + $download.appendTo('#navbar').show(); } else { $download.hide(); $downloadAuth.hide(); @@ -117,13 +117,13 @@ module.define('ext/download', [jQuery, 'core/settings', 'core/resource', 'core/e } $download = $(downloadBtnTemplate) - .appendTo('#navbar') .find('a').on('click', function (event) { event.preventDefault(); $downloadAuth.hide(); requestArchive(selectedHrefsStr); - }); + }).end() + .appendTo('#navbar'); $img = $download.find('img'); $downloadAuth = $(authTemplate).appendTo('body'); diff --git a/src/_h5ai/js/inc/ext/l10n.js b/src/_h5ai/js/inc/ext/l10n.js index da4a3d77..f3c650e7 100644 --- a/src/_h5ai/js/inc/ext/l10n.js +++ b/src/_h5ai/js/inc/ext/l10n.js @@ -4,8 +4,7 @@ module.define('ext/l10n', [jQuery, 'core/settings', 'core/langs', 'core/format', var defaults = { enabled: true, lang: 'en', - useBrowserLang: true, - defaultDateFormat: 'YYYY-MM-DD HH:mm' + useBrowserLang: true }, settings = _.extend({}, defaults, allsettings.l10n), @@ -52,7 +51,7 @@ module.define('ext/l10n', [jQuery, 'core/settings', 'core/langs', 'core/format', $('.langOption.' + lang).addClass('current'); } - format.setDefaultDateFormat(currentLang.dateFormat || settings.defaultDateFormat); + format.setDefaultDateFormat(currentLang.dateFormat); $('#extended .entry .date').each(function () { diff --git a/src/_h5ai/js/inc/ext/select.js b/src/_h5ai/js/inc/ext/select.js index 70a984f2..ed067e2d 100644 --- a/src/_h5ai/js/inc/ext/select.js +++ b/src/_h5ai/js/inc/ext/select.js @@ -7,8 +7,9 @@ module.define('ext/select', [jQuery, 'core/settings', 'core/event'], function ($ settings = _.extend({}, defaults, allsettings.select), - x = 0, - y = 0, + x = 0, y = 0, + l = 0, t = 0, w = 0, h = 0, + shrink = 1/3, $document = $(document), $selectionRect = $('
'), @@ -24,16 +25,17 @@ module.define('ext/select', [jQuery, 'core/settings', 'core/event'], function ($ selectionUpdate = function (event) { - var l = Math.min(x, event.pageX), - t = Math.min(y, event.pageY), - w = Math.abs(x - event.pageX), - h = Math.abs(y - event.pageY), - selRect; + l = Math.min(x, event.pageX); + t = Math.min(y, event.pageY); + w = Math.abs(x - event.pageX); + h = Math.abs(y - event.pageY); event.preventDefault(); - $selectionRect.css({left: l, top: t, width: w, height: h}); + $selectionRect + .stop(true, true) + .css({left: l, top: t, width: w, height: h}); - selRect = $selectionRect.fracs('rect'); + var selRect = $selectionRect.fracs('rect'); $('#extended .entry').removeClass('selecting').each(function () { var $entry = $(this), @@ -49,10 +51,13 @@ module.define('ext/select', [jQuery, 'core/settings', 'core/event'], function ($ event.preventDefault(); $document.off('mousemove', selectionUpdate); - $selectionRect.fadeOut(300); $('#extended .entry.selecting.selected').removeClass('selecting').removeClass('selected'); $('#extended .entry.selecting').removeClass('selecting').addClass('selected'); publish(); + + $selectionRect + .stop(true, true) + .animate({left: l + w * 0.5 * shrink, top: t + h * 0.5 * shrink, width: w * (1 - shrink), height: h * (1 - shrink), opacity: 0}, 300); }, selectionStart = function (event) { @@ -61,8 +66,13 @@ module.define('ext/select', [jQuery, 'core/settings', 'core/event'], function ($ x = event.pageX; y = event.pageY; + l = x; + t = y; + w = 0; + h = 0; + // only on left button and don't block the scrollbars - if (event.button !== 0 || x >= view.right || y >= view.bottom) { + if (event.button !== 0 || l >= view.right || t >= view.bottom) { return; } @@ -72,7 +82,10 @@ module.define('ext/select', [jQuery, 'core/settings', 'core/event'], function ($ $('#extended .entry').removeClass('selected'); publish(); } - $selectionRect.show().css({left: x, top: y, width: 0, height: 0}); + $selectionRect + .stop(true, true) + .css({left: l, top: t, width: w, height: h, opacity: 1}) + .show(); $document .on('mousemove', selectionUpdate) diff --git a/src/_h5ai/js/inc/ext/thumbnails.js b/src/_h5ai/js/inc/ext/thumbnails.js index 2e37619f..7c4d8466 100644 --- a/src/_h5ai/js/inc/ext/thumbnails.js +++ b/src/_h5ai/js/inc/ext/thumbnails.js @@ -27,14 +27,14 @@ module.define('ext/thumbnails', [jQuery, 'core/settings', 'core/resource', 'core var $imgBig = entry.$extended.find('.icon.big img'); requestThumb($imgSmall, { - action: 'thumbsrc', + action: 'getthumbsrc', href: entry.absHref, width: 16, height: 16, mode: 'square' }); requestThumb($imgBig, { - action: 'thumbsrc', + action: 'getthumbsrc', href: entry.absHref, width: 100, height: 48, diff --git a/src/_h5ai/js/inc/h5ai-info.js b/src/_h5ai/js/inc/h5ai-info.js index 17897dd4..9ad9bd48 100644 --- a/src/_h5ai/js/inc/h5ai-info.js +++ b/src/_h5ai/js/inc/h5ai-info.js @@ -14,10 +14,10 @@ module.define('h5ai-info', [jQuery, 'core/resource'], function ($, resource) { handleChecksResponse = function (response) { - _.each(['php', 'cache', 'thumbs', 'temp', 'download'], function (test) { + $('.test').each(function () { - setCheckResult('#test-' + test, response && response[test]); - }) + setCheckResult(this, response && response[$(this).data('id')]); + }); }, checks = function () { @@ -25,7 +25,7 @@ module.define('h5ai-info', [jQuery, 'core/resource'], function ($, resource) { $.ajax({ url: resource.api(), data: { - action: 'checks' + action: 'getchecks' }, type: 'POST', dataType: 'json', diff --git a/src/_h5ai/php/api.php b/src/_h5ai/php/api.php index 5f4b8ce0..b0dc6939 100644 --- a/src/_h5ai/php/api.php +++ b/src/_h5ai/php/api.php @@ -7,85 +7,41 @@ $h5ai = new H5ai(); $options = $h5ai->getOptions(); -function fail($code, $msg, $cond = true) { +function json_exit($obj) { + + $obj["code"] = 0; + echo json_encode($obj); + exit; +} + +function json_fail($code, $msg = "", $cond = true) { + if ($cond) { - echo "$code: $msg"; + echo json_encode(array("code" => $code, "msg" => $msg)); exit; } } -function checkKeys($keys) { +function check_keys($keys) { $values = array(); foreach ($keys as $key) { - fail(1, "parameter '$key' is missing", !array_key_exists($key, $_REQUEST)); + json_fail(101, "parameter '$key' is missing", !array_key_exists($key, $_REQUEST)); $values[] = $_REQUEST[$key]; } return $values; } -list($action) = checkKeys(array("action")); +list($action) = check_keys(array("action")); -if ($action === "httpcodes") { - - list($hrefs) = checkKeys(array("hrefs")); - - function getHttpCodes($h5ai, $hrefs) { - - $codes = array(); - foreach ($hrefs as $href) { - $href = trim($href); - if (strlen($href) > 0) { - $codes[$href] = $h5ai->getHttpCode($href); - } - } - return $codes; - } - - $hrefs = preg_split("/;/", $hrefs); - $codes = getHttpCodes($h5ai, $hrefs); - - echo count($codes) === 0 ? "{}" : json_encode($codes); -} - - -else if ($action === "thumb") { - - fail(0, "thumbs are disabled", !$options["thumbnails"]["enabled"]); - list($srcAbsHref, $width, $height, $mode) = checkKeys(array("href", "width", "height", "mode")); - - H5ai::req_once("/php/inc/Thumbnail.php"); - H5ai::req_once("/php/inc/Image.php"); - - $srcAbsPath = $h5ai->getRootAbsPath() . rawurldecode($srcAbsHref); - - if (!Thumbnail::isUsable()) { - Image::showImage($srcAbsPath); - exit; - } - - $thumbnail = new Thumbnail($h5ai, $srcAbsHref, $mode, $width, $height); - $thumbnail->create(1); - if (file_exists($thumbnail->getPath())) { - Image::showImage($thumbnail->getPath()); - } else { - $image = new Image(); - $image->setSource($srcAbsPath); - $image->thumb($mode, $width, $height); - $image->showDest(); - } -} - - -else if ($action === "thumbsrc") { +if ($action === "getthumbsrc") { if (!$options["thumbnails"]["enabled"]) { - echo json_encode(array("code" => 1, "absHref" => null)); - exit; + json_fail(1, "thumbnails disabled"); } - list($srcAbsHref, $width, $height, $mode) = checkKeys(array("href", "width", "height", "mode")); + list($srcAbsHref, $width, $height, $mode) = check_keys(array("href", "width", "height", "mode")); H5ai::req_once("/php/inc/Thumbnail.php"); H5ai::req_once("/php/inc/Image.php"); @@ -93,75 +49,77 @@ else if ($action === "thumbsrc") { $srcAbsPath = $h5ai->getRootAbsPath() . rawurldecode($srcAbsHref); if (!Thumbnail::isUsable()) { - echo json_encode(array("code" => 2, "absHref" => null)); - exit; + json_fail(2, "thumbnails not supported"); } $thumbnail = new Thumbnail($h5ai, $srcAbsHref, $mode, $width, $height); $thumbnail->create(1); if (!file_exists($thumbnail->getPath())) { - echo json_encode(array("code" => 3, "absHref" => null)); - exit; + json_fail(3, "thumbnail creation failed"); } - echo json_encode(array("code" => 0, "absHref" => $thumbnail->getHref())); + json_exit(array("absHref" => $thumbnail->getHref())); } else if ($action === "archive") { - fail(0, "download is disabled", !$options["download"]["enabled"]); + json_fail(1, "downloads disabled", !$options["download"]["enabled"]); - list($format, $hrefs) = checkKeys(array("format", "hrefs")); + list($execution, $format, $hrefs) = check_keys(array("execution", "format", "hrefs")); H5ai::req_once("/php/inc/Archive.php"); $archive = new Archive($h5ai); $hrefs = explode(":", trim($hrefs)); - $target = $archive->create($format, $hrefs); + $target = $archive->create($execution, $format, $hrefs); - if (is_string($target)) { - $response = array('status' => 'ok', 'id' => basename($target), 'size' => filesize($target)); - } else { - $response = array('status' => 'failed', 'code' => $target); + if (!is_string($target)) { + json_fail($target, "package creation failed"); } - echo json_encode($response); + + json_exit(array("id" => basename($target), "size" => filesize($target))); } else if ($action === "getarchive") { - fail(0, "download is disabled", !$options["download"]["enabled"]); + json_fail(1, "downloads disabled", !$options["download"]["enabled"]); - list($id,$as) = checkKeys(array("id", "as")); - fail(1, "file not found: " . $id, !preg_match("/^h5ai-selection-/", $id)); + list($id, $as) = check_keys(array("id", "as")); + json_fail(2, "file not found", !preg_match("/^h5ai-selection-/", $id)); $target = H5ai::normalize_path(sys_get_temp_dir(), true) . $id; - fail(2, "file not found: " . $id, !file_exists($target)); + json_fail(3, "file not found", !file_exists($target)); - header("Content-Disposition: attachment; filename=\"$as\""); header("Content-Type: application/octet-stream"); header("Content-Length: " . filesize($target)); + header("Content-Disposition: attachment; filename=\"$as\""); header("Connection: close"); readfile($target); } -else if ($action === "checks") { +else if ($action === "getchecks") { - $response = array( - 'php' => $h5ai->checks["php"], - 'cache' => $h5ai->checks["php"] && $h5ai->checks["cache"], - 'thumbs' => $h5ai->checks["php"] && $h5ai->checks["cache"] && $h5ai->checks["gd"], - 'temp' => $h5ai->checks["php"] && $h5ai->checks["temp"], - 'download' => $h5ai->checks["php"] && $h5ai->checks["temp"] && $h5ai->checks["archive"] - ); - echo json_encode($response); + $php = $h5ai->checks["php"]; + $cache = $php && $h5ai->checks["cache"]; + $temp = $php && $h5ai->checks["temp"]; + + json_exit(array( + "php" => $php, + "cache" => $cache, + "thumbs" => $cache && $h5ai->checks["gd"], + "temp" => $temp, + "archive" => $temp && $h5ai->checks["archive"], + "tar" => $temp && $h5ai->checks["tar"], + "zip" => $temp && $h5ai->checks["zip"] + )); } else { - fail(1, "unsupported 'action' specified"); + json_fail(100, "unsupported action"); } diff --git a/src/_h5ai/php/inc/Archive.php b/src/_h5ai/php/inc/Archive.php index dcf81c79..49dcbdce 100644 --- a/src/_h5ai/php/inc/Archive.php +++ b/src/_h5ai/php/inc/Archive.php @@ -2,7 +2,11 @@ class Archive { - private $h5ai; + private static $TAR_CMD = "$(cd [ROOTDIR] && tar --no-recursion -cf [TARGET] [DIRS] [FILES])"; + private static $ZIP_CMD = "$(cd [ROOTDIR] && zip [TARGET] [FILES])"; + + + private $h5ai, $dirs, $files, $sc401; public function __construct($h5ai) { @@ -11,66 +15,116 @@ class Archive { } - public function create($format, $hrefs) { + public function create($execution, $format, $hrefs) { - $target = H5ai::normalize_path(sys_get_temp_dir(), true) . "h5ai-selection-" . microtime(true) . "-" . rand() . "." . $format; - $archive = new PharData($target); + $this->dirs = array(); + $this->files = array(); + $this->sc401 = false; + + $this->addHrefs($hrefs); + + if ($this->sc401) { + return 401; + } else if (count($this->dirs) === 0 && count($this->files) === 0) { + return 404; + } + + $target = H5ai::normalize_path(sys_get_temp_dir(), true) . "h5ai-selection-" . microtime(true) . rand() . "." . $format; + + try { + if ($execution === "shell") { + + if ($format === "tar") { + $cmd = Archive::$TAR_CMD; + } else if ($format === "zip") { + $cmd = Archive::$ZIP_CMD; + } else { + return null; + } + $cmd = str_replace("[ROOTDIR]", "\"" . $this->h5ai->getRootAbsPath() . "\"", $cmd); + $cmd = str_replace("[TARGET]", "\"" . $target . "\"", $cmd); + $cmd = str_replace("[DIRS]", count($this->dirs) ? "\"" . implode("\" \"", array_values($this->dirs)) . "\"" : "", $cmd); + $cmd = str_replace("[FILES]", count($this->files) ? "\"" . implode("\" \"", array_values($this->files)) . "\"" : "", $cmd); + + `$cmd`; + + } else if ($execution === "php") { + + $archive = new PharData($target); + foreach ($this->dirs as $archivedDir) { + $archive->addEmptyDir($archivedDir); + } + foreach ($this->files as $realFile => $archivedFile) { + $archive->addFile($realFile, $archivedFile); // very, very slow :/ + } + + } + } catch (Exeption $err) { + return 500; + } + + return @filesize($target) ? $target : null; + } + + + private function addHrefs($hrefs) { foreach ($hrefs as $href) { + $d = H5ai::normalize_path(dirname($href), true); $n = basename($href); + $code = $this->h5ai->getHttpCode($d); if ($code == 401) { - return $code; + $this->sc401 = true; } if ($code == "h5ai" && !$this->h5ai->ignoreThisFile($n)) { + $realFile = $this->h5ai->getAbsPath($href); - $archivedFile = preg_replace("!^" . $this->h5ai->getRootAbsPath() . "!", "", $realFile); + $archivedFile = preg_replace("!^" . H5ai::normalize_path($this->h5ai->getRootAbsPath(), true) . "!", "", $realFile); + if (is_dir($realFile)) { - $rcode = $this->addDir($archive, $realFile, $archivedFile); - if ($rcode == 401) { - return $rcode; - } + $this->addDir($realFile, $archivedFile); } else { - $this->addFile($archive, $realFile, $archivedFile); + $this->addFile($realFile, $archivedFile); } } } - - return filesize($target) ? $target : null; } - private function addFile($archive, $realFile, $archivedFile) { + private function addFile($realFile, $archivedFile) { if (is_readable($realFile)) { - $archive->addFile($realFile, $archivedFile); + $this->files[$realFile] = $archivedFile; } } - private function addDir($archive, $realDir, $archivedDir) { + private function addDir($realDir, $archivedDir) { $code = $this->h5ai->getHttpCode($this->h5ai->getAbsHref($realDir)); + if ($code == 401) { + $this->sc401 = true; + } if ($code == "h5ai") { - $archive->addEmptyDir($archivedDir); + $this->dirs[] = $archivedDir; + $files = $this->h5ai->readDir($realDir); foreach ($files as $file) { + $realFile = $realDir . "/" . $file; $archivedFile = $archivedDir . "/" . $file; + if (is_dir($realFile)) { - $rcode = $this->addDir($archive, $realFile, $archivedFile); - if ($rcode == 401) { - return $rcode; - } + $this->addDir($realFile, $archivedFile); } else { - $this->addFile($archive, $realFile, $archivedFile); + $this->addFile($realFile, $archivedFile); } } } - return $code; } } diff --git a/src/_h5ai/php/inc/H5ai.php b/src/_h5ai/php/inc/H5ai.php index 748538a4..51c19b71 100644 --- a/src/_h5ai/php/inc/H5ai.php +++ b/src/_h5ai/php/inc/H5ai.php @@ -56,9 +56,11 @@ class H5ai { private $h5aiAbsPath, $rootAbsPath, $ignore, $ignoreRE, $config, $options, - $cache, $rootAbsHref, $h5aiAbsHref, - $absHref, $absPath; + $absHref, $absPath, + $cache; + + public $checks; public function __construct() { @@ -86,7 +88,9 @@ class H5ai { "archive" => class_exists("PharData"), "gd" => GD_VERSION != "GD_VERSION", "cache" => is_writable($this->h5aiAbsPath . "/cache"), - "temp" => is_writable(sys_get_temp_dir()) + "temp" => is_writable(sys_get_temp_dir()), + "tar" => preg_match("/tar$/", `which tar`) > 0, + "zip" => preg_match("/zip$/", `which zip`) > 0 ); }