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
- - php version?
- - cache?
- - thumbnails?
- - temp directory?
- - packaged downloads?
+ - php version?
+ - cache?
+ - thumbnails?
+ - temp directory?
+ - php tar and zip?
+ - shell tar?
+ - shell zip?
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
);
}