Verified Commit 424a8eb0 authored by Luc Didry's avatar Luc Didry

Fix #130 — Allow to individually download files from zip

parent 28a4908a
Pipeline #128587 passed with stages
in 2 minutes and 35 seconds
......@@ -3,6 +3,7 @@ Revision history for Lufi
0.04.0 2019-??-??
- Allow to zip the files before upload
- Allow to see what's in zip file on download page
- Allow to individually download files from zip file (only if zip created by Lufi)
0.03.5 2018-12-06
- Fix CI
......
......@@ -133,6 +133,7 @@ sub upload {
->nbslices($json->{total})
->mod_token($c->shortener($c->config('token_length')))
->passwd($salted_pwd)
->zipped($json->{zipped})
->write;
}
......
......@@ -26,6 +26,7 @@ has 'slices' => sub {
};
has 'passwd';
has 'abuse';
has 'zipped' => 0;
has 'record' => 0;
has 'app';
......@@ -78,6 +79,8 @@ Have a look at Lufi::DB::File::SQLite's code: it's simple and may be more unders
=item B<abuse> : integer
=item B<zipped> : boolean
=item B<app> : a Mojolicious object
=back
......@@ -175,9 +178,9 @@ sub write {
my $c = shift;
if ($c->record) {
$c->app->dbi->db->query('UPDATE files SET short = ?, deleted = ?, mediatype = ?, filename = ?, filesize = ?, counter = ?, delete_at_first_view = ?, delete_at_day = ?, created_at = ?, created_by = ?, last_access_at = ?, mod_token = ?, nbslices = ?, complete = ?, passwd = ?, abuse = ? WHERE short = ?', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd, $c->abuse, $c->short);
$c->app->dbi->db->query('UPDATE files SET short = ?, deleted = ?, mediatype = ?, filename = ?, filesize = ?, counter = ?, delete_at_first_view = ?, delete_at_day = ?, created_at = ?, created_by = ?, last_access_at = ?, mod_token = ?, nbslices = ?, complete = ?, passwd = ?, abuse = ?, zipped = ? WHERE short = ?', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd, $c->abuse, $c->zipped, $c->short);
} else {
$c->app->dbi->db->query('INSERT INTO files (short, deleted, mediatype, filename, filesize, counter, delete_at_first_view, delete_at_day, created_at, created_by, last_access_at, mod_token, nbslices, complete, passwd, abuse) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd, $c->abuse);
$c->app->dbi->db->query('INSERT INTO files (short, deleted, mediatype, filename, filesize, counter, delete_at_first_view, delete_at_day, created_at, created_by, last_access_at, mod_token, nbslices, complete, passwd, abuse, zipped) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd, $c->abuse, $c->zipped);
$c->record(1);
}
......@@ -505,6 +508,7 @@ sub _slurp {
$c->complete($file->{complete});
$c->passwd($file->{passwd});
$c->abuse($file->{abuse});
$c->zipped($file->{zipped});
$c->record(1) unless $c->record;
}
......
......@@ -19,9 +19,9 @@ sub register {
# Database migration
my $migrations = Mojo::Pg::Migrations->new(pg => $app->dbi);
if ($app->mode eq 'development' && $ENV{LUFI_DEV}) {
$migrations->from_file('utilities/migrations/pg.sql')->migrate(0)->migrate(2);
$migrations->from_file('utilities/migrations/pg.sql')->migrate(0)->migrate(3);
} else {
$migrations->from_file('utilities/migrations/pg.sql')->migrate(2);
$migrations->from_file('utilities/migrations/pg.sql')->migrate(3);
}
} elsif ($app->config('dbtype') eq 'mysql') {
require Mojo::mysql;
......@@ -30,9 +30,9 @@ sub register {
# Database migration
my $migrations = Mojo::mysql::Migrations->new(mysql => $app->dbi);
if ($app->mode eq 'development' && $ENV{LUFI_DEV}) {
$migrations->from_file('utilities/migrations/mysql.sql')->migrate(0)->migrate(1);
$migrations->from_file('utilities/migrations/mysql.sql')->migrate(0)->migrate(2);
} else {
$migrations->from_file('utilities/migrations/mysql.sql')->migrate(1);
$migrations->from_file('utilities/migrations/mysql.sql')->migrate(2);
}
} elsif ($app->config('dbtype') eq 'sqlite') {
require Mojo::SQLite;
......@@ -43,9 +43,9 @@ sub register {
my $sql = $app->dbi;
my $migrations = $sql->migrations;
if ($app->mode eq 'development' && $ENV{LUFI_DEV}) {
$migrations->from_file('utilities/migrations/sqlite.sql')->migrate(0)->migrate(2);
$migrations->from_file('utilities/migrations/sqlite.sql')->migrate(0)->migrate(3);
} else {
$migrations->from_file('utilities/migrations/sqlite.sql')->migrate(2);
$migrations->from_file('utilities/migrations/sqlite.sql')->migrate(3);
}
# Check if passwd column is missing
......
......@@ -22,6 +22,7 @@ my $msg = to_json {
"delay" => "0",
"del_at_first_view" => 1,
"id" => undef,
"zipped" => 0,
"i" => 0
};
my $encrypted = '"{\\"iv\\":\\"2RGAviAeYybBqcLCmnqlgA==\\",\\"v\\":1,\\"iter\\":10000,\\"ks\\":128,\\"ts\\":64,\\"mode\\":\\"ccm\\",\\"adata\\":\\"\\",\\"cipher\\":\\"aes\\",\\"salt\\":\\"1dvKtbZ8hxA=\\",\\"ct\\":\\"w9wDZCwNSyH/yL7q1GW5fPSdi+w=\\"}"';
......
......@@ -98,15 +98,15 @@ msgstr ""
msgid "Copy to clipboard"
msgstr ""
#: lib/Lufi/Controller/Files.pm:460
#: lib/Lufi/Controller/Files.pm:461
msgid "Could not delete the file. You are not authenticated."
msgstr ""
#: lib/Lufi/Controller/Files.pm:442
#: lib/Lufi/Controller/Files.pm:443
msgid "Could not find the file. Are you sure of the URL and the token?"
msgstr ""
#: lib/Lufi/Controller/Files.pm:353
#: lib/Lufi/Controller/Files.pm:354
msgid "Could not find the file. Are you sure of the URL?"
msgstr ""
......@@ -170,15 +170,15 @@ msgstr ""
msgid "Encrypting part XX1 of XX2"
msgstr ""
#: lib/Lufi/Controller/Files.pm:242
#: lib/Lufi/Controller/Files.pm:243
msgid "Error: the file existed but was deleted."
msgstr ""
#: lib/Lufi/Controller/Files.pm:322
#: lib/Lufi/Controller/Files.pm:323
msgid "Error: the file has not been sent entirely."
msgstr ""
#: lib/Lufi/Controller/Files.pm:332
#: lib/Lufi/Controller/Files.pm:333
msgid "Error: unable to find the file. Are you sure of your URL?"
msgstr ""
......@@ -194,7 +194,7 @@ msgstr ""
msgid "Export localStorage data"
msgstr ""
#: lib/Lufi/Controller/Files.pm:424
#: lib/Lufi/Controller/Files.pm:425
msgid "File deleted"
msgstr ""
......@@ -397,7 +397,7 @@ msgstr ""
msgid "The email subject can't be empty."
msgstr ""
#: lib/Lufi/Controller/Files.pm:421
#: lib/Lufi/Controller/Files.pm:422
msgid "The file has already been deleted"
msgstr ""
......@@ -422,11 +422,11 @@ msgstr ""
msgid "The original (and only for now) author is <a href=\"https://fiat-tux.fr\" class=\"classic\">Luc Didry</a>."
msgstr ""
#: lib/Lufi/Controller/Files.pm:189
#: lib/Lufi/Controller/Files.pm:190
msgid "The server was unable to find the file record to add your file part to. Please, contact the administrator."
msgstr ""
#: lib/Lufi/Controller/Files.pm:248
#: lib/Lufi/Controller/Files.pm:249
msgid "This file has been deactivated by the admins. Contact them to know why."
msgstr ""
......@@ -439,17 +439,17 @@ msgid "Unable to copy the link(s) to your clipboard"
msgstr ""
#. ($short)
#: lib/Lufi/Controller/Files.pm:392
#: lib/Lufi/Controller/Files.pm:393
msgid "Unable to get counter for %1. The file does not exists. It will be removed from your localStorage."
msgstr ""
#. ($short)
#: lib/Lufi/Controller/Files.pm:382
#: lib/Lufi/Controller/Files.pm:383
msgid "Unable to get counter for %1. The token is invalid."
msgstr ""
#. ($short)
#: lib/Lufi/Controller/Files.pm:402
#: lib/Lufi/Controller/Files.pm:403
msgid "Unable to get counter for %1. You are not authenticated."
msgstr ""
......@@ -518,7 +518,7 @@ msgstr ""
msgid "Your file is too big: %1 (maximum size allowed: %2)"
msgstr ""
#: lib/Lufi/Controller/Files.pm:304
#: lib/Lufi/Controller/Files.pm:305
msgid "Your password is not valid. Please refresh the page to retry."
msgstr ""
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -106,7 +106,7 @@ function spawnWebsocket(pa) {
}
var innerHTML = ['<p><a href="', blobURL, '" class="btn btn-primary" download="', escapeHtml(data.name), '">', i18n.download, '</a></p>'];
var isZip = false;
var isZip = ($('#filesize').attr('data-zipped') === 'true');
if (data.type.match(/^image\//) !== null) {
innerHTML.push('<img id="render-image" class="responsive-img" alt="', escapeHtml(data.name), '" src="', blobURL, '">');
} else if (data.type.match(/^video\//) !== null) {
......@@ -117,9 +117,8 @@ function spawnWebsocket(pa) {
innerHTML.push('<audio class="responsive-video" controls>',
'<source src="', blobURL, '" type="', data.type, '">',
'</audio>');
} else if (data.type.match(/^application\/zip/) !== null) {
} else if (isZip) {
innerHTML.push('<p><a class="btn btn-primary" id="showZipContent">', i18n.showZipContent, '</a></p>');
isZip = true;
}
pbd.html(innerHTML.join(''));
......@@ -130,10 +129,28 @@ function spawnWebsocket(pa) {
.then(function (zip) {
var innerHTML = ['<h3>Zip content:</h3><ul>'];
zip.forEach(function (relativePath, zipEntry) {
innerHTML.push('<li>', zipEntry.name, ' (', filesize(zipEntry._data.uncompressedSize, {base: 10}), ')</li>');
innerHTML.push(
'<li>',
zipEntry.name,
' (', filesize(zipEntry._data.uncompressedSize, {base: 10}), ') ',
'<a href="#" download="', zipEntry.name, '" class="download-zip-content" title="', i18n.download, '">',
'<i class="mdi-file-file-download"></i>',
'</a>',
'</li>'
);
});
innerHTML.push('</ul>');
pbd.append(innerHTML.join(''));
$('.download-zip-content').click(function(e) {
e.preventDefault();
var t = $(this);
var filename = t.attr('download');
zip.files[filename].async('blob').then(function(blob) {
t.unbind('click');
t.attr('href', URL.createObjectURL(blob));
t[0].click();
});
})
$('#showZipContent').hide();
$('#showZipContent').unbind('click');
});
......
......@@ -113,7 +113,7 @@ function handleFiles(f) {
$('#zipping').show();
for (var i = 0; i < f.length; i++) {
var element = f.item(i);
zip.file(element.name, new Blob([element]));
zip.file(element.name, element);
}
zip.generateAsync({type:"blob"})
.then(function(zipFile) {
......@@ -222,6 +222,7 @@ function sliceAndUpload(randomkey, i, parts, j, delay, del_at_first_view, short)
type: file.type,
delay: delay,
del_at_first_view: del_at_first_view,
zipped: $('#zip-files').is(':checked'),
id: short,
// number of the sent file in the queue
i: i
......
......@@ -29,7 +29,7 @@
</div>
% }
<div class="file-progress col s12<%= (stash('file_pwd')) ? ' hide' : '' %>">
<p id="filesize" data-filesize="<%= stash('f')->filesize %>"></p>
<p id="filesize" data-filesize="<%= stash('f')->filesize %>" data-zipped="<%= (stash('f')->zipped) ? 'true' : 'false' %>"></p>
<p id="please-wait"><%= l('Please wait while we are getting your file. We first need to download and decrypt all parts before you can get it.') %></p>
<p id="loading"></p>
<div class="progress" id="pbd">
......
......@@ -27,3 +27,7 @@ CREATE TABLE IF NOT EXISTS slices (
-- 1 down
DROP TABLE slices;
DROP TABLE files;
-- 2 up
ALTER TABLE files ADD COLUMN zipped boolean default false;
-- 2 down
ALTER TABLE files DROP COLUMN zipped;
......@@ -30,3 +30,7 @@ DROP TABLE files;
ALTER TABLE files ADD COLUMN abuse integer;
-- 2 down
ALTER TABLE files DROP COLUMN abuse;
-- 3 up
ALTER TABLE files ADD COLUMN zipped boolean default false;
-- 3 down
ALTER TABLE files DROP COLUMN zipped;
......@@ -52,3 +52,29 @@ BEGIN TRANSACTION;
DROP TABLE files;
ALTER TABLE files_backup RENAME TO files;
COMMIT;
-- 3 up
ALTER TABLE files ADD COLUMN zipped INTEGER;
-- 3 down
BEGIN TRANSACTION;
CREATE TABLE files_backup (
short TEXT PRIMARY KEY,
deleted INTEGER,
mediatype TEXT,
filename TEXT,
filesize INTEGER,
counter INTEGER,
delete_at_first_view INTEGER,
delete_at_day INTEGER,
created_at INTEGER,
created_by TEXT,
last_access_at INTEGER,
mod_token TEXT,
nbslices INTEGER,
complete INTEGER,
passwd TEXT,
abuse INTEGER
);
INSERT INTO files_backup SELECT short, deleted, mediatype, filename, filesize, counter, delete_at_first_view, delete_at_day, created_at, created_by, last_access_at, mod_token, nbslices, complete, passwd, abuse FROM files;
DROP TABLE files;
ALTER TABLE files_backup RENAME TO files;
COMMIT;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment