From ecc4b0c2ef75ebb81706fe00e61956e602ac13c4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sven=20Sch=C3=B6ling?= Date: Fri, 30 Apr 2021 10:54:25 +0200 Subject: [PATCH] ImageUpload: local storage erste Version --- SL/Controller/ImageUpload.pm | 4 +- js/kivi.FileDB.js | 87 ++++++++++++++ js/kivi.ImageUpload.js | 108 ++++++++++++++++++ js/kivi.Materialize.js | 51 +++++++-- package.json | 22 ++++ .../image_upload/local_list.html | 47 ++++++++ 6 files changed, 306 insertions(+), 13 deletions(-) create mode 100644 js/kivi.FileDB.js create mode 100644 js/kivi.ImageUpload.js create mode 100644 package.json create mode 100644 templates/mobile_webpages/image_upload/local_list.html diff --git a/SL/Controller/ImageUpload.pm b/SL/Controller/ImageUpload.pm index 173c5daf1..a93c85347 100644 --- a/SL/Controller/ImageUpload.pm +++ b/SL/Controller/ImageUpload.pm @@ -28,8 +28,10 @@ sub action_upload_image { my ($self) = @_; $::request->layout->add_javascripts('kivi.File.js'); + $::request->layout->add_javascripts('kivi.FileDB.js'); + $::request->layout->add_javascripts('kivi.ImageUpload.js'); - $self->render('image_upload/form'); + $self->render('image_upload/local_list'); } ################# internal ############### diff --git a/js/kivi.FileDB.js b/js/kivi.FileDB.js new file mode 100644 index 000000000..3edb45b8b --- /dev/null +++ b/js/kivi.FileDB.js @@ -0,0 +1,87 @@ +namespace("kivi.FileDB", function(ns) { + "use strict"; + + const database = 'kivi'; + const store = 'files'; + const db_version = 1; + + // IndexedDB + const indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB; + + // Create/open database + let db; + let request = indexedDB.open(database, db_version); + request.onupgradeneeded = (event) => { + ns.create_image_store(event.target.result); + }; + request.onerror = ns.onerror; + request.onsuccess = () => { + db = request.result; + + db.onerror = (event) => { + console.error("Error creating/accessing IndexedDB database"); + console.error(event); + }; + + // Interim solution for Google Chrome to create an objectStore. Will be deprecated + if (db.setVersion) { + if (db.version != db_version) { + let setVersion = db.setVersion(db_version); + setVersion.onsuccess = () => { + ns.create_image_store(db); + }; + } + } + }; + + ns.create_image_store = function (db) { + db.createObjectStore(store, { autoIncrement : true }); + }; + + ns.store_image = function (blob, filename, success) { + let put_request = ns.open_store("readwrite").add(blob, filename); + + put_request.onsuccess = success; + put_request.on_error = ns.onerror; + }; + + ns.retrieve_image = function(key, success) { + let get_request = ns.open_store().objectStore(store).get(key); + + get_request.onsuccess = success; + get_request.onerror = request.onerror; + }; + + ns.retrieve_all = function(success) { + let request = ns.open_store().getAll(); + request.onsuccess = (event) => { success(event.target.result); }; + request.onerror = ns.error; + }; + + ns.retrieve_all_keys = function(success) { + let request = ns.open_store().getAllKeys(); + request.onsuccess = (event) => { success(event.target.result); }; + request.onerror = ns.error; + }; + + ns.delete_all= function() { + ns.retrieve_all_keys((keys) => { + keys.forEach((key) => ns.delete_key(key)); + }); + }; + + ns.delete_key= function(key, success) { + let request = ns.open_store("readwrite").delete(key); + request.onsuccess = (event) => { if (success) success(event.target.result); }; + request.onerror = ns.error; + }; + + ns.open_store = function(mode = "readonly") { + return db.transaction([store], mode).objectStore(store); + }; + + ns.onerror = (event) => { + console.error("Error creating/accessing IndexedDB database"); + console.error(event.errorState); + }; +}); diff --git a/js/kivi.ImageUpload.js b/js/kivi.ImageUpload.js new file mode 100644 index 000000000..d71129924 --- /dev/null +++ b/js/kivi.ImageUpload.js @@ -0,0 +1,108 @@ +namespace("kivi.ImageUpload", function(ns) { + "use strict"; + + ns.add_files = function(target) { + let files = []; + for (var i = 0; i < target.files.length; i++) { + files.push(target.files.item(i)); + } + + kivi.FileDB.store_image(files[0], files[0].name, () => { + ns.reload_images(); + target.value = null; + }); + }; + + ns.reload_images = function() { + kivi.FileDB.retrieve_all((data) => { + $('#stored-images').empty(); + data.forEach(ns.create_thumb_row); + }); + }; + + ns.create_thumb_row = function(file) { + let URL = window.URL || window.webkitURL; + let file_url = URL.createObjectURL(file); + + let $row = $("
").addClass("row image-upload-row"); + let $button = $("") + .addClass("btn-floating btn-large waves-effect waves-light red") + .click((event) => ns.remove_image(event, file.name)) + .append($("delete").addClass("material-icons")); + $row.append($("
").addClass("col s3").append($button)); + + let $image = $('').attr("src", file_url).addClass("materialboxed responsive-img"); + $row.append($("
").addClass("col s9").append($image)); + + $("#stored-images").append($row); + }; + + ns.remove_image = function(event, key) { + let $row = $(event.target).closest(".image-upload-row"); + kivi.FileDB.delete_key(key, () => { + $row.remove(); + }); + }; + + ns.upload_selected_files = function(id,type,filetype,maxsize) { + kivi.FileDB.retrieve_all((myfiles) => { + let filesize = 0; + myfiles.forEach(file => { + filesize += file.size; + if (filesize > maxsize) { + $("#upload_result").html(kivi.t8("filesize too big: ") + filesize+ kivi.t8(" bytes, max=") + maxsize ); + return; + } + + let data = new FormData(); + data.append(file); + data.append("action", "File/ajax_files_uploaded"); + data.append("json", "1"); + data.append("object_type", type); + data.append("object_id", id); + data.append("file_type", filetype); + + $("#upload_result").html(kivi.t8("start upload")); + + $.ajax({ + url: "controller.pl", + data: data, + success: ns.attSuccess, + progress: ns.attProgress, + error: ns.attFailes, + abort: ns.attCanceled + }); + }); + }); + }; + + ns.attProgress = function(event) { + if (event.lengthComputable) { + var percentComplete = (event.loaded / event.total) * 100; + $("#upload_result").html(percentComplete+" % "+ kivi.t8("uploaded")); + } + }; + + ns.attFailed = function() { + $('#upload_modal').modal('close'); + $("#upload_result").html(kivi.t8("An error occurred while transferring the file.")); + }; + + ns.attCanceled = function() { + $('#upload_modal').modal('close'); + $("#upload_result").html(kivi.t8("The transfer has been canceled by the user.")); + }; + + ns.attSuccess = function() { + $('#upload_modal').modal('close'); + $("#upload_result").html(kivi.t8("Files have been uploaded successfully.")); + }; + + ns.init = function() { + ns.reload_images(); + }; + + +}); + +$(kivi.ImageUpload.init); diff --git a/js/kivi.Materialize.js b/js/kivi.Materialize.js index 56c5bbd5e..006f8acb2 100644 --- a/js/kivi.Materialize.js +++ b/js/kivi.Materialize.js @@ -3,9 +3,9 @@ namespace("kivi.Materialize", function(ns) { ns.init = function() { ns.reinit_widgets(); - } + }; - ns.build_i18n = function(locale) { + ns.build_i18n = function() { return { months: [ kivi.t8('January'), @@ -63,8 +63,8 @@ namespace("kivi.Materialize", function(ns) { // Accessibility labels labelMonthNext: kivi.t8('Next month'), labelMonthPrev: kivi.t8('Previous month') - } - } + }; + }; ns.reinit_widgets = function() { $('.sidenav').sidenav(); @@ -76,15 +76,15 @@ namespace("kivi.Materialize", function(ns) { i18n: ns.build_i18n() }); $('.modal').modal(); + $('.materialboxed').materialbox(); M.updateTextFields(); - } + }; // alternative for kivi.popup_dialog. // opens materialize modal instead. // // differences: M.modal can not load external content, so it needs to be fetched manually and inserted into the DOM. ns.popup_dialog = function(params) { - console.log(params); params = params || { }; let id = params.id || 'jqueryui_popup_dialog'; let $div; @@ -94,7 +94,7 @@ namespace("kivi.Materialize", function(ns) { // unlike classic layout, there is not fixed size, and M.modal is always... modal onCloseStart: custom_close }, - // User supplied options: + // User supplied options: params.dialog || { }, { // Options that must not be changed: // close options already work @@ -110,7 +110,7 @@ namespace("kivi.Materialize", function(ns) { params.data = undefined; ns.popup_dialog(params); }, - error: function(x, status, error) { console.log(error); }, + error: function(x, status, error) { console.error(error); }, dataType: 'text', }); return 1; @@ -118,7 +118,7 @@ namespace("kivi.Materialize", function(ns) { if (params.html) { $div = $('
'); - $div.attr('id', id) + $div.attr('id', id); $div.addClass("modal"); let $modal_content = $('
'); $modal_content.addClass('modal-content'); @@ -126,10 +126,9 @@ namespace("kivi.Materialize", function(ns) { $div.append($modal_content); $('body').append($div); kivi.reinit_widgets(); - dialog_params.onCloseEnd = function() { $div.remove(); } + dialog_params.onCloseEnd = function() { $div.remove(); }; $div.modal(dialog_params); - } else if(params.id) { $div = $('#' + params.id); } else { @@ -140,6 +139,34 @@ namespace("kivi.Materialize", function(ns) { $div.modal('open'); return true; + }; + + /** + * upload file to local storage for later sync + * + * should be used with P.M.file_upload(..., local=>1) + */ + ns.LocalFileUpload = function(options) { + this.storage_token = options.storage_token; // used in localstorage to retrieve the file + this.dom_selector = options.dom_selector; // file inputs to listen on + + this.init(); + }; + + ns.LocalFileUpload.prototype = { + init: function() { + $(this.dom_selector).change(this.handle_file_upload); + }, + handle_file_upload: function() { + + }, + load_files: function() { + return JSON.parse(localStorage.getImte(this.storage_token)); + }, + save_files: function() { + return JSON.parse(localStorage.getImte(this.storage_token)); + }, + + }; - } }); diff --git a/package.json b/package.json new file mode 100644 index 000000000..84f664ad4 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "dev", + "version": "1.0.0", + "description": "", + "main": "index.js", + "directories": { + "doc": "doc" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/kivitendo/kivitendo-erp.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/kivitendo/kivitendo-erp/issues" + }, + "homepage": "https://github.com/kivitendo/kivitendo-erp#readme" +} diff --git a/templates/mobile_webpages/image_upload/local_list.html b/templates/mobile_webpages/image_upload/local_list.html new file mode 100644 index 000000000..35ba71eb3 --- /dev/null +++ b/templates/mobile_webpages/image_upload/local_list.html @@ -0,0 +1,47 @@ +[%- USE LxERP -%] [%- USE L %] +[%- USE HTML %] +[%- USE P %] +[%- USE T8 %] + +

[% source.title | html %]

+ +
+ +
+
+ + +
+
+ +
+
+ [% 'Upload Image' | $T8 %] + +
+
+ +
+
+ +
+ +
+ [% P.M.input_tag("object_number", "", label=LxERP.t8("Number")) %] + [% P.M.button_tag("submit", LxERP.t8("Upload")) %] +
+ + +
+
+ + -- 2.20.1