ImageUpload: local storage erste Version
authorSven Schöling <s.schoeling@googlemail.com>
Fri, 30 Apr 2021 08:54:25 +0000 (10:54 +0200)
committerSven Schöling <s.schoeling@googlemail.com>
Fri, 25 Jun 2021 13:51:32 +0000 (15:51 +0200)
SL/Controller/ImageUpload.pm
js/kivi.FileDB.js [new file with mode: 0644]
js/kivi.ImageUpload.js [new file with mode: 0644]
js/kivi.Materialize.js
package.json [new file with mode: 0644]
templates/mobile_webpages/image_upload/local_list.html [new file with mode: 0644]

index 173c5da..a93c853 100644 (file)
@@ -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 (file)
index 0000000..3edb45b
--- /dev/null
@@ -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 (file)
index 0000000..d711299
--- /dev/null
@@ -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 = $("<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);
index 56c5bbd..006f8ac 100644 (file)
@@ -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>');
-      $div.attr('id', id)
+      $div.attr('id', id);
       $div.addClass("modal");
       let $modal_content = $('<div>');
       $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 (file)
index 0000000..84f664a
--- /dev/null
@@ -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 (file)
index 0000000..35ba71e
--- /dev/null
@@ -0,0 +1,47 @@
+[%- 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>