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 ###############
--- /dev/null
+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);
+ };
+});
--- /dev/null
+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 = $("<div>").addClass("row image-upload-row");
+ let $button = $("<a>")
+ .addClass("btn-floating btn-large waves-effect waves-light red")
+ .click((event) => ns.remove_image(event, file.name))
+ .append($("<i>delete</i>").addClass("material-icons"));
+ $row.append($("<div>").addClass("col s3").append($button));
+
+ let $image = $('<img>').attr("src", file_url).addClass("materialboxed responsive-img");
+ $row.append($("<div>").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);
ns.init = function() {
ns.reinit_widgets();
- }
+ };
- ns.build_i18n = function(locale) {
+ ns.build_i18n = function() {
return {
months: [
kivi.t8('January'),
// Accessibility labels
labelMonthNext: kivi.t8('Next month'),
labelMonthPrev: kivi.t8('Previous month')
- }
- }
+ };
+ };
ns.reinit_widgets = function() {
$('.sidenav').sidenav();
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;
// 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
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;
if (params.html) {
$div = $('<div>');
- $div.attr('id', id)
+ $div.attr('id', id);
$div.addClass("modal");
let $modal_content = $('<div>');
$modal_content.addClass('modal-content');
$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 {
$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));
+ },
+
+ };
- }
});
--- /dev/null
+{
+ "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"
+}
--- /dev/null
+[%- USE LxERP -%] [%- USE L %]
+[%- USE HTML %]
+[%- USE P %]
+[%- USE T8 %]
+
+<h4>[% source.title | html %]</h4>
+
+ <div id="updoad_result"></div>
+
+ <div id="stored-images" class="container">
+ </div>
+
+
+ <div class="container">
+ <div class="row">
+
+ <div class="file-field input-field col">
+ <div class="btn m3 s12">
+ <span>[% 'Upload Image' | $T8 %]</span>
+ <input
+ name="uploadfiles[]" type="file" [% IF multiple %]multiple[% END %]
+ id="upload_files" accept="[% SELF.accept_types %]" capture="camera"
+ onchange="kivi.ImageUpload.add_files(this)">
+ </div>
+ <div class="file-path-wrapper m9 s12">
+ <input class="file-path validate" type="hidden">
+ </div>
+ </div>
+
+ </div>
+
+ <div class="row">
+ [% P.M.input_tag("object_number", "", label=LxERP.t8("Number")) %]
+ [% P.M.button_tag("submit", LxERP.t8("Upload")) %]
+ </div>
+
+
+ </div>
+ </div>
+
+<div id="upload_modal" class="modal">
+ <div class="modal-content">
+ <h4>Uploading</h4>
+
+ <p>A bunch of text</p>
+ </div>
+</div>