Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/storage.pl
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
storage_load_term/2, % +Hash, -Term

use_gitty_file/1, % +File
use_gitty_file/2 % +File, +Options
use_gitty_file/2, % +File, +Options
open_gittystore/1
]).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_parameters)).
Expand All @@ -71,7 +72,7 @@
:- use_module(library(dcg/basics)).
:- use_module(library(pcre)).
:- use_module(library(pengines_io)).

:- use_module(upload).
:- use_module(page).
:- use_module(gitty).
:- use_module(patch).
Expand Down
58 changes: 58 additions & 0 deletions lib/upload.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
:- module(upload, [handle_upload/1]).
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_header)).
:- use_module(library(http/http_multipart_plugin)).
:- use_module(library(http/http_client)).
:- use_module(library(http/html_write)).
:- use_module(library(option)).
:- use_module(library(debug)).
:- use_module(web_storage).
:- use_module(gitty).

:- http_handler(root(upload), handle_upload, [method(post)]).

handle_upload(Request) :-
multipart_post_request(Request), !,
http_read_data(Request, Parts, [on_filename(save_file)]),
memberchk(file=file(FileName, TempFile), Parts),
save_file_to_storage(FileName, TempFile, SavedPath),
format('Content-type: application/json~n~n'),
format('{"status":"success","filename":"~w","saved":"~w"}', [FileName, SavedPath]).
handle_upload(_Request) :-
throw(http_reply(bad_request(bad_file_upload))).

multipart_post_request(Request) :-
memberchk(method(post), Request),
memberchk(content_type(ContentType), Request),
http_parse_header_value(
content_type, ContentType,
media(multipart/'form-data', _)).

:- public save_file/3.

save_file(In, file(FileName, TempFile), Options) :-
option(filename(FileName), Options),
setup_call_cleanup(
tmp_file_stream(octet, TempFile, Out),
copy_stream_data(In, Out),
close(Out)).

save_file_to_storage(FileName, TempFile, SavedPath) :-
web_storage:open_gittystore(Dir),
setup_call_cleanup(
open(TempFile, read, In, [type(binary)]),
read_string(In, _, Data),
close(In)
),
Meta = _{author: 'user', public: true},
gitty:gitty_create(Dir, FileName, Data, Meta, _Commit),
directory_file_path(Dir, FileName, SavedPath).

% error message
:- multifile prolog:message//1.

prolog:message(bad_file_upload) -->
[ 'A file upload must be submitted as multipart/form-data using', nl,
'name=file and providing a file-name'
].
69 changes: 69 additions & 0 deletions web/js/jswish.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ preferences.setInform("preserve-state", ".unloadable");
"Download": glyph("floppy-save", function() {
menuBroadcast("download");
}),
"Upload ...": glyph("upload", function() {
$('#fileInput').trigger('click');
}),
"Print ...": glyph("print", function() {
menuBroadcast("print");
})
Expand Down Expand Up @@ -180,6 +183,72 @@ preferences.setInform("preserve-state", ".unloadable");
}
}; // defaults;

$(() => {
if ($('#fileInput').length === 0) {
$('body').append('<input type="file" id="fileInput" multiple style="display: none;" />');
}

$(document).on('change', '#fileInput', function(event) {
var files = event.target.files;
if (files.length > 0) {
var allowedExtensions = ['.swib', '.pl'];

var uploadFile = function(file, callback) {
var formData = new FormData();
formData.append('file', file);

$.ajax({
url: '/upload',
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
console.log('File uploaded successfully:', response);
callback(null, response.filename);
},
error: function(error) {
console.error('File upload failed:', error);
callback(error);
}
});
};

var lastUploadedFileName = null;

var uploadNextFile = function(index) {
if (index < files.length) {
var file = files[index];
var fileName = file.name;
var fileExtension = fileName.slice(fileName.lastIndexOf('.')).toLowerCase();

if (allowedExtensions.includes(fileExtension)) {
uploadFile(file, function(err, fileName) {
if (!err) {
lastUploadedFileName = fileName;
uploadNextFile(index + 1);
} else {
alert('File upload failed for file: ' + fileName);
}
});
} else {
alert('Invalid file type: ' + fileName + '. Only .swib and .pl files are allowed.');
uploadNextFile(index + 1);
}
} else {
// redirect to last file
if (lastUploadedFileName) {
window.location.href = '/p/' + lastUploadedFileName;
} else {
location.reload();
}
}
};

uploadNextFile(0);
}
});
});

/** @lends $.fn.swish */
var methods = {
Expand Down