diff --git a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_jio_js.xml b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_jio_js.xml
index c5d6478ef5c6bdd4bfd0cca3fc0f703890b12589..76f1b6934248713f94a5afd226997d562b140b14 100644
--- a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_jio_js.xml
+++ b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_jio_js.xml
@@ -149,6 +149,10 @@
     .declareMethod(\'removeAttachment\', function () {\n
       var storage = this.state_parameter_dict.jio_storage;\n
       return storage.removeAttachment.apply(storage, arguments);\n
+    })\n
+    .declareMethod(\'repair\', function () {\n
+      var storage = this.state_parameter_dict.jio_storage;\n
+      return storage.repair.apply(storage, arguments);\n
     });\n
 \n
 }(window, rJS, jIO));</string> </value>
@@ -286,7 +290,7 @@
             </item>
             <item>
                 <key> <string>serial</string> </key>
-                <value> <string>941.55610.36294.45499</string> </value>
+                <value> <string>942.15696.23962.61542</string> </value>
             </item>
             <item>
                 <key> <string>state</string> </key>
@@ -304,7 +308,7 @@
                     </tuple>
                     <state>
                       <tuple>
-                        <float>1428655701.93</float>
+                        <float>1429085619.37</float>
                         <string>GMT</string>
                       </tuple>
                     </state>
diff --git a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_jio_js.xml b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_jio_js.xml
index abd3fad3237d2d4558f1b0f1841fb8f54e0f682b..0dc269b36a9e0cab6578ec0545687acc89bd70a3 100644
--- a/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_jio_js.xml
+++ b/bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_jio_js.xml
@@ -5678,7 +5678,8 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   function declareMethod(klass, name, precondition_function, post_function) {\n
     klass.prototype[name] = function () {\n
       var argument_list = arguments,\n
-        context = this;\n
+        context = this,\n
+        precondition_result;\n
 \n
       return new RSVP.Queue()\n
         .push(function () {\n
@@ -5689,8 +5690,9 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             );\n
           }\n
         })\n
-        .push(function () {\n
+        .push(function (result) {\n
           var storage_method = context.__storage[name];\n
+          precondition_result = result;\n
           if (storage_method === undefined) {\n
             throw new jIO.util.jIOError(\n
               "Capacity \'" + name + "\' is not implemented on \'" +\n
@@ -5708,7 +5710,8 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             return post_function.call(\n
               context,\n
               argument_list,\n
-              result\n
+              result,\n
+              precondition_result\n
             );\n
           }\n
           return result;\n
@@ -5787,6 +5790,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   declareMethod(JioProxyStorage, \'getAttachment\', function (argument_list,\n
                                                             storage,\n
                                                             method_name) {\n
+    var result = "blob";\n
 //     if (param.storage_spec.type !== "indexeddb" &&\n
 //         param.storage_spec.type !== "dav" &&\n
 //         (param.kwargs._start !== undefined\n
@@ -5800,8 +5804,15 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 //     }\n
     checkId(argument_list, storage, method_name);\n
     checkAttachmentId(argument_list, storage, method_name);\n
-  }, function (argument_list, result) {\n
-    if (!(result instanceof Blob)) {\n
+    // Drop optional parameters, which are only used in postfunction\n
+    if (argument_list[2] !== undefined) {\n
+      result = argument_list[2].format || result;\n
+      delete argument_list[2].format;\n
+    }\n
+    return result;\n
+  }, function (argument_list, blob, convert) {\n
+    var result;\n
+    if (!(blob instanceof Blob)) {\n
       throw new jIO.util.jIOError(\n
         "\'getAttachment\' (" + argument_list[0] + " , " +\n
           argument_list[1] + ") on \'" + this.__type +\n
@@ -5809,6 +5820,47 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         501\n
       );\n
     }\n
+    if (convert === "blob") {\n
+      result = blob;\n
+    } else if (convert === "data_url") {\n
+      result = new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.readBlobAsDataURL(blob);\n
+        })\n
+        .push(function (evt) {\n
+          return evt.target.result;\n
+        });\n
+    } else if (convert === "array_buffer") {\n
+      result = new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.readBlobAsArrayBuffer(blob);\n
+        })\n
+        .push(function (evt) {\n
+          return evt.target.result;\n
+        });\n
+    } else if (convert === "text") {\n
+      result = new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.readBlobAsText(blob);\n
+        })\n
+        .push(function (evt) {\n
+          return evt.target.result;\n
+        });\n
+    } else if (convert === "json") {\n
+      result = new RSVP.Queue()\n
+        .push(function () {\n
+          return jIO.util.readBlobAsText(blob);\n
+        })\n
+        .push(function (evt) {\n
+          return JSON.parse(evt.target.result);\n
+        });\n
+    } else {\n
+      throw new jIO.util.jIOError(\n
+        this.__type + ".getAttachment format: \'" + convert +\n
+          "\' is not supported",\n
+        400\n
+      );\n
+    }\n
     return result;\n
   });\n
 \n
@@ -5872,6 +5924,20 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   };\n
 \n
   declareMethod(JioProxyStorage, "allAttachments", checkId);\n
+  declareMethod(JioProxyStorage, "repair");\n
+\n
+  JioProxyStorage.prototype.repair = function () {\n
+    var context = this,\n
+      argument_list = arguments;\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        var storage_method = context.__storage.repair;\n
+        if (storage_method !== undefined) {\n
+          return context.__storage.repair.apply(context.__storage,\n
+                                                argument_list);\n
+        }\n
+      });\n
+  };\n
 \n
   /////////////////////////////////////////////////////////////////\n
   // Storage builder\n
@@ -5926,6 +5992,838 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   window.jIO = jIO;\n
 \n
 }(window, RSVP, Blob, QueryFactory, Query, FileReader));\n
+;/*\n
+ * Rusha, a JavaScript implementation of the Secure Hash Algorithm, SHA-1,\n
+ * as defined in FIPS PUB 180-1, tuned for high performance with large inputs.\n
+ * (http://github.com/srijs/rusha)\n
+ *\n
+ * Inspired by Paul Johnstons implementation (http://pajhome.org.uk/crypt/md5).\n
+ *\n
+ * Copyright (c) 2013 Sam Rijs (http://awesam.de).\n
+ * Released under the terms of the MIT license as follows:\n
+ *\n
+ * Permission is hereby granted, free of charge, to any person obtaining a\n
+ * copy of this software and associated documentation files (the "Software"),\n
+ * to deal in the Software without restriction, including without limitation\n
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,\n
+ * and/or sell copies of the Software, and to permit persons to whom the\n
+ * Software is furnished to do so, subject to the following conditions:\n
+ *\n
+ * The above copyright notice and this permission notice shall be included in\n
+ * all copies or substantial portions of the Software.\n
+ *\n
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n
+ * IN THE SOFTWARE.\n
+ */\n
+(function () {\n
+    // If we\'e running in Node.JS, export a module.\n
+    if (typeof module !== \'undefined\') {\n
+        module.exports = Rusha;\n
+    } else if (typeof window !== \'undefined\') {\n
+        window.Rusha = Rusha;\n
+    }\n
+    // If we\'re running in a webworker, accept\n
+    // messages containing a jobid and a buffer\n
+    // or blob object, and return the hash result.\n
+    if (typeof FileReaderSync !== \'undefined\') {\n
+        var reader = new FileReaderSync(), hasher = new Rusha(4 * 1024 * 1024);\n
+        self.onmessage = function onMessage(event) {\n
+            var hash, data = event.data.data;\n
+            try {\n
+                hash = hasher.digest(data);\n
+                self.postMessage({\n
+                    id: event.data.id,\n
+                    hash: hash\n
+                });\n
+            } catch (e) {\n
+                self.postMessage({\n
+                    id: event.data.id,\n
+                    error: e.name\n
+                });\n
+            }\n
+        };\n
+    }\n
+    var util = {\n
+            getDataType: function (data) {\n
+                if (typeof data === \'string\') {\n
+                    return \'string\';\n
+                }\n
+                if (data instanceof Array) {\n
+                    return \'array\';\n
+                }\n
+                if (typeof global !== \'undefined\' && global.Buffer && global.Buffer.isBuffer(data)) {\n
+                    return \'buffer\';\n
+                }\n
+                if (data instanceof ArrayBuffer) {\n
+                    return \'arraybuffer\';\n
+                }\n
+                if (data.buffer instanceof ArrayBuffer) {\n
+                    return \'view\';\n
+                }\n
+                if (data instanceof Blob) {\n
+                    return \'blob\';\n
+                }\n
+                throw new Error(\'Unsupported data type.\');\n
+            }\n
+        };\n
+    // The Rusha object is a wrapper around the low-level RushaCore.\n
+    // It provides means of converting different inputs to the\n
+    // format accepted by RushaCore as well as other utility methods.\n
+    function Rusha(chunkSize) {\n
+        \'use strict\';\n
+        // Private object structure.\n
+        var self$2 = { fill: 0 };\n
+        // Calculate the length of buffer that the sha1 routine uses\n
+        // including the padding.\n
+        var padlen = function (len) {\n
+            for (len += 9; len % 64 > 0; len += 1);\n
+            return len;\n
+        };\n
+        var padZeroes = function (bin, len) {\n
+            for (var i = len >> 2; i < bin.length; i++)\n
+                bin[i] = 0;\n
+        };\n
+        var padData = function (bin, chunkLen, msgLen) {\n
+            bin[chunkLen >> 2] |= 128 << 24 - (chunkLen % 4 << 3);\n
+            bin[((chunkLen >> 2) + 2 & ~15) + 14] = msgLen >> 29;\n
+            bin[((chunkLen >> 2) + 2 & ~15) + 15] = msgLen << 3;\n
+        };\n
+        // Convert a binary string and write it to the heap.\n
+        // A binary string is expected to only contain char codes < 256.\n
+        var convStr = function (H8, H32, start, len, off) {\n
+            var str = this, i, om = off % 4, lm = len % 4, j = len - lm;\n
+            if (j > 0) {\n
+                switch (om) {\n
+                case 0:\n
+                    H8[off + 3 | 0] = str.charCodeAt(start);\n
+                case 1:\n
+                    H8[off + 2 | 0] = str.charCodeAt(start + 1);\n
+                case 2:\n
+                    H8[off + 1 | 0] = str.charCodeAt(start + 2);\n
+                case 3:\n
+                    H8[off | 0] = str.charCodeAt(start + 3);\n
+                }\n
+            }\n
+            for (i = om; i < j; i = i + 4 | 0) {\n
+                H32[off + i >> 2] = str.charCodeAt(start + i) << 24 | str.charCodeAt(start + i + 1) << 16 | str.charCodeAt(start + i + 2) << 8 | str.charCodeAt(start + i + 3);\n
+            }\n
+            switch (lm) {\n
+            case 3:\n
+                H8[off + j + 1 | 0] = str.charCodeAt(start + j + 2);\n
+            case 2:\n
+                H8[off + j + 2 | 0] = str.charCodeAt(start + j + 1);\n
+            case 1:\n
+                H8[off + j + 3 | 0] = str.charCodeAt(start + j);\n
+            }\n
+        };\n
+        // Convert a buffer or array and write it to the heap.\n
+        // The buffer or array is expected to only contain elements < 256.\n
+        var convBuf = function (H8, H32, start, len, off) {\n
+            var buf = this, i, om = off % 4, lm = len % 4, j = len - lm;\n
+            if (j > 0) {\n
+                switch (om) {\n
+                case 0:\n
+                    H8[off + 3 | 0] = buf[start];\n
+                case 1:\n
+                    H8[off + 2 | 0] = buf[start + 1];\n
+                case 2:\n
+                    H8[off + 1 | 0] = buf[start + 2];\n
+                case 3:\n
+                    H8[off | 0] = buf[start + 3];\n
+                }\n
+            }\n
+            for (i = 4 - om; i < j; i = i += 4 | 0) {\n
+                H32[off + i >> 2] = buf[start + i] << 24 | buf[start + i + 1] << 16 | buf[start + i + 2] << 8 | buf[start + i + 3];\n
+            }\n
+            switch (lm) {\n
+            case 3:\n
+                H8[off + j + 1 | 0] = buf[start + j + 2];\n
+            case 2:\n
+                H8[off + j + 2 | 0] = buf[start + j + 1];\n
+            case 1:\n
+                H8[off + j + 3 | 0] = buf[start + j];\n
+            }\n
+        };\n
+        var convBlob = function (H8, H32, start, len, off) {\n
+            var blob = this, i, om = off % 4, lm = len % 4, j = len - lm;\n
+            var buf = new Uint8Array(reader.readAsArrayBuffer(blob.slice(start, start + len)));\n
+            if (j > 0) {\n
+                switch (om) {\n
+                case 0:\n
+                    H8[off + 3 | 0] = buf[0];\n
+                case 1:\n
+                    H8[off + 2 | 0] = buf[1];\n
+                case 2:\n
+                    H8[off + 1 | 0] = buf[2];\n
+                case 3:\n
+                    H8[off | 0] = buf[3];\n
+                }\n
+            }\n
+            for (i = 4 - om; i < j; i = i += 4 | 0) {\n
+                H32[off + i >> 2] = buf[i] << 24 | buf[i + 1] << 16 | buf[i + 2] << 8 | buf[i + 3];\n
+            }\n
+            switch (lm) {\n
+            case 3:\n
+                H8[off + j + 1 | 0] = buf[j + 2];\n
+            case 2:\n
+                H8[off + j + 2 | 0] = buf[j + 1];\n
+            case 1:\n
+                H8[off + j + 3 | 0] = buf[j];\n
+            }\n
+        };\n
+        var convFn = function (data) {\n
+            switch (util.getDataType(data)) {\n
+            case \'string\':\n
+                return convStr.bind(data);\n
+            case \'array\':\n
+                return convBuf.bind(data);\n
+            case \'buffer\':\n
+                return convBuf.bind(data);\n
+            case \'arraybuffer\':\n
+                return convBuf.bind(new Uint8Array(data));\n
+            case \'view\':\n
+                return convBuf.bind(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));\n
+            case \'blob\':\n
+                return convBlob.bind(data);\n
+            }\n
+        };\n
+        var slice = function (data, offset) {\n
+            switch (util.getDataType(data)) {\n
+            case \'string\':\n
+                return data.slice(offset);\n
+            case \'array\':\n
+                return data.slice(offset);\n
+            case \'buffer\':\n
+                return data.slice(offset);\n
+            case \'arraybuffer\':\n
+                return data.slice(offset);\n
+            case \'view\':\n
+                return data.buffer.slice(offset);\n
+            }\n
+        };\n
+        // Convert an ArrayBuffer into its hexadecimal string representation.\n
+        var hex = function (arrayBuffer) {\n
+            var i, x, hex_tab = \'0123456789abcdef\', res = [], binarray = new Uint8Array(arrayBuffer);\n
+            for (i = 0; i < binarray.length; i++) {\n
+                x = binarray[i];\n
+                res[i] = hex_tab.charAt(x >> 4 & 15) + hex_tab.charAt(x >> 0 & 15);\n
+            }\n
+            return res.join(\'\');\n
+        };\n
+        var ceilHeapSize = function (v) {\n
+            // The asm.js spec says:\n
+            // The heap object\'s byteLength must be either\n
+            // 2^n for n in [12, 24) or 2^24 * n for n ≥ 1.\n
+            // Also, byteLengths smaller than 2^16 are deprecated.\n
+            var p;\n
+            // If v is smaller than 2^16, the smallest possible solution\n
+            // is 2^16.\n
+            if (v <= 65536)\n
+                return 65536;\n
+            // If v < 2^24, we round up to 2^n,\n
+            // otherwise we round up to 2^24 * n.\n
+            if (v < 16777216) {\n
+                for (p = 1; p < v; p = p << 1);\n
+            } else {\n
+                for (p = 16777216; p < v; p += 16777216);\n
+            }\n
+            return p;\n
+        };\n
+        // Initialize the internal data structures to a new capacity.\n
+        var init = function (size) {\n
+            if (size % 64 > 0) {\n
+                throw new Error(\'Chunk size must be a multiple of 128 bit\');\n
+            }\n
+            self$2.maxChunkLen = size;\n
+            self$2.padMaxChunkLen = padlen(size);\n
+            // The size of the heap is the sum of:\n
+            // 1. The padded input message size\n
+            // 2. The extended space the algorithm needs (320 byte)\n
+            // 3. The 160 bit state the algoritm uses\n
+            self$2.heap = new ArrayBuffer(ceilHeapSize(self$2.padMaxChunkLen + 320 + 20));\n
+            self$2.h32 = new Int32Array(self$2.heap);\n
+            self$2.h8 = new Int8Array(self$2.heap);\n
+            self$2.core = RushaCore({\n
+                Int32Array: Int32Array,\n
+                DataView: DataView\n
+            }, {}, self$2.heap);\n
+            self$2.buffer = null;\n
+        };\n
+        // Iinitializethe datastructures according\n
+        // to a chunk siyze.\n
+        init(chunkSize || 64 * 1024);\n
+        var initState = function (heap, padMsgLen) {\n
+            var io = new Int32Array(heap, padMsgLen + 320, 5);\n
+            io[0] = 1732584193;\n
+            io[1] = -271733879;\n
+            io[2] = -1732584194;\n
+            io[3] = 271733878;\n
+            io[4] = -1009589776;\n
+        };\n
+        var padChunk = function (chunkLen, msgLen) {\n
+            var padChunkLen = padlen(chunkLen);\n
+            var view = new Int32Array(self$2.heap, 0, padChunkLen >> 2);\n
+            padZeroes(view, chunkLen);\n
+            padData(view, chunkLen, msgLen);\n
+            return padChunkLen;\n
+        };\n
+        // Write data to the heap.\n
+        var write = function (data, chunkOffset, chunkLen) {\n
+            convFn(data)(self$2.h8, self$2.h32, chunkOffset, chunkLen, 0);\n
+        };\n
+        // Initialize and call the RushaCore,\n
+        // assuming an input buffer of length len * 4.\n
+        var coreCall = function (data, chunkOffset, chunkLen, msgLen, finalize) {\n
+            var padChunkLen = chunkLen;\n
+            if (finalize) {\n
+                padChunkLen = padChunk(chunkLen, msgLen);\n
+            }\n
+            write(data, chunkOffset, chunkLen);\n
+            self$2.core.hash(padChunkLen, self$2.padMaxChunkLen);\n
+        };\n
+        var getRawDigest = function (heap, padMaxChunkLen) {\n
+            var io = new Int32Array(heap, padMaxChunkLen + 320, 5);\n
+            var out = new Int32Array(5);\n
+            var arr = new DataView(out.buffer);\n
+            arr.setInt32(0, io[0], false);\n
+            arr.setInt32(4, io[1], false);\n
+            arr.setInt32(8, io[2], false);\n
+            arr.setInt32(12, io[3], false);\n
+            arr.setInt32(16, io[4], false);\n
+            return out;\n
+        };\n
+        // Calculate the hash digest as an array of 5 32bit integers.\n
+        var rawDigest = this.rawDigest = function (str) {\n
+                var msgLen = str.byteLength || str.length || str.size || 0;\n
+                initState(self$2.heap, self$2.padMaxChunkLen);\n
+                var chunkOffset = 0, chunkLen = self$2.maxChunkLen, last;\n
+                for (chunkOffset = 0; msgLen > chunkOffset + chunkLen; chunkOffset += chunkLen) {\n
+                    coreCall(str, chunkOffset, chunkLen, msgLen, false);\n
+                }\n
+                coreCall(str, chunkOffset, msgLen - chunkOffset, msgLen, true);\n
+                return getRawDigest(self$2.heap, self$2.padMaxChunkLen);\n
+            };\n
+        // The digest and digestFrom* interface returns the hash digest\n
+        // as a hex string.\n
+        this.digest = this.digestFromString = this.digestFromBuffer = this.digestFromArrayBuffer = function (str) {\n
+            return hex(rawDigest(str).buffer);\n
+        };\n
+    }\n
+    ;\n
+    // The low-level RushCore module provides the heart of Rusha,\n
+    // a high-speed sha1 implementation working on an Int32Array heap.\n
+    // At first glance, the implementation seems complicated, however\n
+    // with the SHA1 spec at hand, it is obvious this almost a textbook\n
+    // implementation that has a few functions hand-inlined and a few loops\n
+    // hand-unrolled.\n
+    function RushaCore(stdlib, foreign, heap) {\n
+        \'use asm\';\n
+        var H = new stdlib.Int32Array(heap);\n
+        function hash(k, x) {\n
+            // k in bytes\n
+            k = k | 0;\n
+            x = x | 0;\n
+            var i = 0, j = 0, y0 = 0, z0 = 0, y1 = 0, z1 = 0, y2 = 0, z2 = 0, y3 = 0, z3 = 0, y4 = 0, z4 = 0, t0 = 0, t1 = 0;\n
+            y0 = H[x + 320 >> 2] | 0;\n
+            y1 = H[x + 324 >> 2] | 0;\n
+            y2 = H[x + 328 >> 2] | 0;\n
+            y3 = H[x + 332 >> 2] | 0;\n
+            y4 = H[x + 336 >> 2] | 0;\n
+            for (i = 0; (i | 0) < (k | 0); i = i + 64 | 0) {\n
+                z0 = y0;\n
+                z1 = y1;\n
+                z2 = y2;\n
+                z3 = y3;\n
+                z4 = y4;\n
+                for (j = 0; (j | 0) < 64; j = j + 4 | 0) {\n
+                    t1 = H[i + j >> 2] | 0;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 & y2 | ~y1 & y3) | 0) + ((t1 + y4 | 0) + 1518500249 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[k + j >> 2] = t1;\n
+                }\n
+                for (j = k + 64 | 0; (j | 0) < (k + 80 | 0); j = j + 4 | 0) {\n
+                    t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 & y2 | ~y1 & y3) | 0) + ((t1 + y4 | 0) + 1518500249 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[j >> 2] = t1;\n
+                }\n
+                for (j = k + 80 | 0; (j | 0) < (k + 160 | 0); j = j + 4 | 0) {\n
+                    t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 ^ y2 ^ y3) | 0) + ((t1 + y4 | 0) + 1859775393 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[j >> 2] = t1;\n
+                }\n
+                for (j = k + 160 | 0; (j | 0) < (k + 240 | 0); j = j + 4 | 0) {\n
+                    t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 & y2 | y1 & y3 | y2 & y3) | 0) + ((t1 + y4 | 0) - 1894007588 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[j >> 2] = t1;\n
+                }\n
+                for (j = k + 240 | 0; (j | 0) < (k + 320 | 0); j = j + 4 | 0) {\n
+                    t1 = (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) << 1 | (H[j - 12 >> 2] ^ H[j - 32 >> 2] ^ H[j - 56 >> 2] ^ H[j - 64 >> 2]) >>> 31;\n
+                    t0 = ((y0 << 5 | y0 >>> 27) + (y1 ^ y2 ^ y3) | 0) + ((t1 + y4 | 0) - 899497514 | 0) | 0;\n
+                    y4 = y3;\n
+                    y3 = y2;\n
+                    y2 = y1 << 30 | y1 >>> 2;\n
+                    y1 = y0;\n
+                    y0 = t0;\n
+                    ;\n
+                    H[j >> 2] = t1;\n
+                }\n
+                y0 = y0 + z0 | 0;\n
+                y1 = y1 + z1 | 0;\n
+                y2 = y2 + z2 | 0;\n
+                y3 = y3 + z3 | 0;\n
+                y4 = y4 + z4 | 0;\n
+            }\n
+            H[x + 320 >> 2] = y0;\n
+            H[x + 324 >> 2] = y1;\n
+            H[x + 328 >> 2] = y2;\n
+            H[x + 332 >> 2] = y3;\n
+            H[x + 336 >> 2] = y4;\n
+        }\n
+        return { hash: hash };\n
+    }\n
+}());;/*\n
+ * JIO extension for resource replication.\n
+ * Copyright (C) 2013, 2015  Nexedi SA\n
+ *\n
+ *   This library is free software: you can redistribute it and/or modify\n
+ *   it under the terms of the GNU Lesser General Public License as published by\n
+ *   the Free Software Foundation, either version 3 of the License, or\n
+ *   (at your option) any later version.\n
+ *\n
+ *   This library is distributed in the hope that it will be useful,\n
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of\n
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n
+ *   GNU Lesser General Public License for more details.\n
+ *\n
+ *   You should have received a copy of the GNU Lesser General Public License\n
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.\n
+ */\n
+\n
+/*jslint nomen: true*/\n
+/*global jIO, RSVP, Rusha*/\n
+\n
+(function (jIO, RSVP, Rusha) {\n
+  "use strict";\n
+\n
+  var rusha = new Rusha();\n
+\n
+  /****************************************************\n
+   Use a local jIO to read/write/search documents\n
+   Synchronize in background those document with a remote jIO.\n
+   Synchronization status is stored for each document as an local attachment.\n
+  ****************************************************/\n
+\n
+  function generateHash(content) {\n
+    // XXX Improve performance by moving calculation to WebWorker\n
+    return rusha.digestFromString(content);\n
+  }\n
+\n
+  function ReplicateStorage(spec) {\n
+    this._query_options = spec.query || {};\n
+\n
+    this._local_sub_storage = jIO.createJIO(spec.local_sub_storage);\n
+    this._remote_sub_storage = jIO.createJIO(spec.remote_sub_storage);\n
+\n
+    this._signature_hash = "_replicate_" + generateHash(\n
+      JSON.stringify(spec.local_sub_storage) +\n
+        JSON.stringify(spec.remote_sub_storage) +\n
+        JSON.stringify(this._query_options)\n
+    );\n
+    this._signature_sub_storage = jIO.createJIO({\n
+      type: "document",\n
+      document_id: this._signature_hash,\n
+      sub_storage: spec.local_sub_storage\n
+    });\n
+\n
+    this._use_remote_post = spec.use_remote_post || false;\n
+  }\n
+\n
+  ReplicateStorage.prototype.remove = function (id) {\n
+    if (id === this._signature_hash) {\n
+      throw new jIO.util.jIOError(this._signature_hash + " is frozen",\n
+                                  403);\n
+    }\n
+    return this._local_sub_storage.remove.apply(this._local_sub_storage,\n
+                                                arguments);\n
+  };\n
+  ReplicateStorage.prototype.post = function () {\n
+    return this._local_sub_storage.post.apply(this._local_sub_storage,\n
+                                              arguments);\n
+  };\n
+  ReplicateStorage.prototype.put = function (id) {\n
+    if (id === this._signature_hash) {\n
+      throw new jIO.util.jIOError(this._signature_hash + " is frozen",\n
+                                  403);\n
+    }\n
+    return this._local_sub_storage.put.apply(this._local_sub_storage,\n
+                                             arguments);\n
+  };\n
+  ReplicateStorage.prototype.get = function () {\n
+    return this._local_sub_storage.get.apply(this._local_sub_storage,\n
+                                             arguments);\n
+  };\n
+  ReplicateStorage.prototype.hasCapacity = function () {\n
+    return this._local_sub_storage.hasCapacity.apply(this._local_sub_storage,\n
+                                                     arguments);\n
+  };\n
+  ReplicateStorage.prototype.buildQuery = function () {\n
+    // XXX Remove signature document?\n
+    return this._local_sub_storage.buildQuery.apply(this._local_sub_storage,\n
+                                                    arguments);\n
+  };\n
+\n
+  ReplicateStorage.prototype.repair = function () {\n
+    var context = this,\n
+      argument_list = arguments,\n
+      skip_document_dict = {};\n
+\n
+    // Do not sync the signature document\n
+    skip_document_dict[context._signature_hash] = null;\n
+\n
+    function propagateModification(destination, doc, hash, id, options) {\n
+      var result,\n
+        to_skip = true;\n
+      if (options === undefined) {\n
+        options = {};\n
+      }\n
+      if (options.use_post) {\n
+        result = destination.post(doc)\n
+          .push(function () {\n
+            to_skip = false;\n
+          });\n
+      } else {\n
+        result = destination.put(id, doc);\n
+      }\n
+      return result\n
+        .push(function () {\n
+          return context._signature_sub_storage.put(id, {\n
+            "hash": hash\n
+          });\n
+        })\n
+        .push(function () {\n
+          if (to_skip) {\n
+            skip_document_dict[id] = null;\n
+          }\n
+        });\n
+    }\n
+\n
+    function checkLocalCreation(queue, source, destination, id, options) {\n
+      var remote_doc;\n
+      queue\n
+        .push(function () {\n
+          return destination.get(id);\n
+        })\n
+        .push(function (doc) {\n
+          remote_doc = doc;\n
+        }, function (error) {\n
+          if ((error instanceof jIO.util.jIOError) &&\n
+              (error.status_code === 404)) {\n
+            // This document was never synced.\n
+            // Push it to the remote storage and store sync information\n
+            return;\n
+          }\n
+          throw error;\n
+        })\n
+        .push(function () {\n
+          // This document was never synced.\n
+          // Push it to the remote storage and store sync information\n
+          return source.get(id);\n
+        })\n
+        .push(function (doc) {\n
+          var local_hash = generateHash(JSON.stringify(doc)),\n
+            remote_hash;\n
+          if (remote_doc === undefined) {\n
+            return propagateModification(destination, doc, local_hash, id,\n
+                                         options);\n
+          }\n
+\n
+          remote_hash = generateHash(JSON.stringify(remote_doc));\n
+          if (local_hash === remote_hash) {\n
+            // Same document\n
+            return context._signature_sub_storage.put(id, {\n
+              "hash": local_hash\n
+            })\n
+              .push(function () {\n
+                skip_document_dict[id] = null;\n
+              });\n
+          }\n
+          // Already exists on destination\n
+          throw new jIO.util.jIOError("Conflict on \'" + id + "\'",\n
+                                      409);\n
+        });\n
+    }\n
+\n
+    function checkLocalDeletion(queue, destination, id, source) {\n
+      var status_hash;\n
+      queue\n
+        .push(function () {\n
+          return context._signature_sub_storage.get(id);\n
+        })\n
+        .push(function (result) {\n
+          status_hash = result.hash;\n
+          return destination.get(id)\n
+            .push(function (doc) {\n
+              var remote_hash = generateHash(JSON.stringify(doc));\n
+              if (remote_hash === status_hash) {\n
+                return destination.remove(id)\n
+                  .push(function () {\n
+                    return context._signature_sub_storage.remove(id);\n
+                  })\n
+                  .push(function () {\n
+                    skip_document_dict[id] = null;\n
+                  });\n
+              }\n
+              // Modifications on remote side\n
+              // Push them locally\n
+              return propagateModification(source, doc, remote_hash, id);\n
+            }, function (error) {\n
+              if ((error instanceof jIO.util.jIOError) &&\n
+                  (error.status_code === 404)) {\n
+                return context._signature_sub_storage.remove(id)\n
+                  .push(function () {\n
+                    skip_document_dict[id] = null;\n
+                  });\n
+              }\n
+              throw error;\n
+            });\n
+        });\n
+    }\n
+\n
+    function checkSignatureDifference(queue, source, destination, id) {\n
+      queue\n
+        .push(function () {\n
+          return RSVP.all([\n
+            source.get(id),\n
+            context._signature_sub_storage.get(id)\n
+          ]);\n
+        })\n
+        .push(function (result_list) {\n
+          var doc = result_list[0],\n
+            local_hash = generateHash(JSON.stringify(doc)),\n
+            status_hash = result_list[1].hash;\n
+\n
+          if (local_hash !== status_hash) {\n
+            // Local modifications\n
+            return destination.get(id)\n
+              .push(function (remote_doc) {\n
+                var remote_hash = generateHash(JSON.stringify(remote_doc));\n
+                if (remote_hash !== status_hash) {\n
+                  // Modifications on both sides\n
+                  if (local_hash === remote_hash) {\n
+                    // Same modifications on both side \\o/\n
+                    return context._signature_sub_storage.put(id, {\n
+                      "hash": local_hash\n
+                    })\n
+                      .push(function () {\n
+                        skip_document_dict[id] = null;\n
+                      });\n
+                  }\n
+                  throw new jIO.util.jIOError("Conflict on \'" + id + "\'",\n
+                                              409);\n
+                }\n
+                return propagateModification(destination, doc, local_hash, id);\n
+              }, function (error) {\n
+                if ((error instanceof jIO.util.jIOError) &&\n
+                    (error.status_code === 404)) {\n
+                  // Document has been deleted remotely\n
+                  return propagateModification(destination, doc, local_hash,\n
+                                               id);\n
+                }\n
+                throw error;\n
+              });\n
+          }\n
+        });\n
+    }\n
+\n
+    function pushStorage(source, destination, options) {\n
+      var queue = new RSVP.Queue();\n
+      if (!options.hasOwnProperty("use_post")) {\n
+        options.use_post = false;\n
+      }\n
+      return queue\n
+        .push(function () {\n
+          return RSVP.all([\n
+            source.allDocs(context._query_options),\n
+            context._signature_sub_storage.allDocs()\n
+          ]);\n
+        })\n
+        .push(function (result_list) {\n
+          var i,\n
+            local_dict = {},\n
+            signature_dict = {},\n
+            key;\n
+          for (i = 0; i < result_list[0].data.total_rows; i += 1) {\n
+            if (!skip_document_dict.hasOwnProperty(\n
+                result_list[0].data.rows[i].id\n
+              )) {\n
+              local_dict[result_list[0].data.rows[i].id] = i;\n
+            }\n
+          }\n
+          for (i = 0; i < result_list[1].data.total_rows; i += 1) {\n
+            if (!skip_document_dict.hasOwnProperty(\n
+                result_list[1].data.rows[i].id\n
+              )) {\n
+              signature_dict[result_list[1].data.rows[i].id] = i;\n
+            }\n
+          }\n
+          for (key in local_dict) {\n
+            if (local_dict.hasOwnProperty(key)) {\n
+              if (!signature_dict.hasOwnProperty(key)) {\n
+                checkLocalCreation(queue, source, destination, key, options);\n
+              }\n
+            }\n
+          }\n
+          for (key in signature_dict) {\n
+            if (signature_dict.hasOwnProperty(key)) {\n
+              if (local_dict.hasOwnProperty(key)) {\n
+                checkSignatureDifference(queue, source, destination, key);\n
+              } else {\n
+                checkLocalDeletion(queue, destination, key, source);\n
+              }\n
+            }\n
+          }\n
+        });\n
+    }\n
+\n
+    return new RSVP.Queue()\n
+      .push(function () {\n
+        // Ensure that the document storage is usable\n
+        return context._signature_sub_storage.__storage._sub_storage.get(\n
+          context._signature_hash\n
+        );\n
+      })\n
+      .push(undefined, function (error) {\n
+        if ((error instanceof jIO.util.jIOError) &&\n
+            (error.status_code === 404)) {\n
+          return context._signature_sub_storage.__storage._sub_storage.put(\n
+            context._signature_hash,\n
+            {}\n
+          );\n
+        }\n
+        throw error;\n
+      })\n
+\n
+      .push(function () {\n
+        return RSVP.all([\n
+// Don\'t repair local_sub_storage twice\n
+//           context._signature_sub_storage.repair.apply(\n
+//             context._signature_sub_storage,\n
+//             argument_list\n
+//           ),\n
+          context._local_sub_storage.repair.apply(\n
+            context._local_sub_storage,\n
+            argument_list\n
+          ),\n
+          context._remote_sub_storage.repair.apply(\n
+            context._remote_sub_storage,\n
+            argument_list\n
+          )\n
+        ]);\n
+      })\n
+\n
+      .push(function () {\n
+        return pushStorage(context._local_sub_storage,\n
+                           context._remote_sub_storage,\n
+                           {use_post: context._use_remote_post});\n
+      })\n
+      .push(function () {\n
+        return pushStorage(context._remote_sub_storage,\n
+                           context._local_sub_storage, {});\n
+      });\n
+  };\n
+\n
+  jIO.addStorage(\'replicate\', ReplicateStorage);\n
+\n
+}(jIO, RSVP, Rusha));\n
+;/*\n
+ * Copyright 2015, Nexedi SA\n
+ * Released under the LGPL license.\n
+ * http://www.gnu.org/licenses/lgpl.html\n
+ */\n
+\n
+/*jslint nomen: true*/\n
+/*global Rusha*/\n
+\n
+/**\n
+ * JIO Sha Storage. Type = \'sha\'.\n
+ */\n
+\n
+(function (Rusha) {\n
+  "use strict";\n
+\n
+  var rusha = new Rusha();\n
+\n
+  function ShaStorage(spec) {\n
+    this._sub_storage = jIO.createJIO(spec.sub_storage);\n
+  }\n
+\n
+  ShaStorage.prototype.post = function (param) {\n
+    return this._sub_storage.put(\n
+      rusha.digestFromString(JSON.stringify(param)),\n
+      param\n
+    );\n
+  };\n
+\n
+  ShaStorage.prototype.get = function () {\n
+    return this._sub_storage.get.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.remove = function () {\n
+    return this._sub_storage.remove.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.hasCapacity = function () {\n
+    return this._sub_storage.hasCapacity.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.buildQuery = function () {\n
+    return this._sub_storage.buildQuery.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.getAttachment = function () {\n
+    return this._sub_storage.getAttachment.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.putAttachment = function () {\n
+    return this._sub_storage.putAttachment.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.removeAttachment = function () {\n
+    return this._sub_storage.removeAttachment.apply(this._sub_storage,\n
+                                                    arguments);\n
+  };\n
+  ShaStorage.prototype.allAttachments = function () {\n
+    return this._sub_storage.allAttachments.apply(this._sub_storage, arguments);\n
+  };\n
+  ShaStorage.prototype.repair = function () {\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments);\n
+  };\n
+\n
+  jIO.addStorage(\'sha\', ShaStorage);\n
+\n
+}(Rusha));\n
 ;/*jslint nomen: true*/\n
 (function (jIO) {\n
   "use strict";\n
@@ -5978,6 +6876,9 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
     return this._sub_storage.removeAttachment.apply(this._sub_storage,\n
                                                     arguments);\n
   };\n
+  UUIDStorage.prototype.repair = function () {\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments);\n
+  };\n
   UUIDStorage.prototype.hasCapacity = function (name) {\n
     return this._sub_storage.hasCapacity(name);\n
   };\n
@@ -6754,6 +7655,48 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
     }\n
     return false;\n
   };\n
+\n
+  UnionStorage.prototype.repair = function () {\n
+    var i,\n
+      promise_list = [];\n
+    for (i = 0; i < this._storage_list.length; i += 1) {\n
+      promise_list.push(this._storage_list[i].repair.apply(\n
+        this._storage_list[i],\n
+        arguments\n
+      ));\n
+    }\n
+    return RSVP.all(promise_list);\n
+  };\n
+\n
+  UnionStorage.prototype.getAttachment = function () {\n
+    var argument_list = arguments,\n
+      context = this;\n
+    return this._getWithStorageIndex.apply(this, arguments)\n
+      .push(function (result) {\n
+        var sub_storage = context._storage_list[result[0]];\n
+        return sub_storage.getAttachment.apply(sub_storage, argument_list);\n
+      });\n
+  };\n
+\n
+  UnionStorage.prototype.putAttachment = function () {\n
+    var argument_list = arguments,\n
+      context = this;\n
+    return this._getWithStorageIndex.apply(this, arguments)\n
+      .push(function (result) {\n
+        var sub_storage = context._storage_list[result[0]];\n
+        return sub_storage.putAttachment.apply(sub_storage, argument_list);\n
+      });\n
+  };\n
+\n
+  UnionStorage.prototype.removeAttachment = function () {\n
+    var argument_list = arguments,\n
+      context = this;\n
+    return this._getWithStorageIndex.apply(this, arguments)\n
+      .push(function (result) {\n
+        var sub_storage = context._storage_list[result[0]];\n
+        return sub_storage.removeAttachment.apply(sub_storage, argument_list);\n
+      });\n
+  };\n
 \n
   jIO.addStorage(\'union\', UnionStorage);\n
 \n
@@ -6798,17 +7741,80 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
     return getSiteDocument(storage)\n
       .push(function (site_hal) {\n
         // XXX need to get modified metadata\n
-        return jIO.util.ajax({\n
-          "type": "GET",\n
-          "url": UriTemplate.parse(site_hal._links.traverse.href)\n
-                            .expand({\n
-              relative_url: id,\n
-              view: options._view\n
-            }),\n
-          "xhrFields": {\n
-            withCredentials: true\n
+        return new RSVP.Queue()\n
+          .push(function () {\n
+            return jIO.util.ajax({\n
+              "type": "GET",\n
+              "url": UriTemplate.parse(site_hal._links.traverse.href)\n
+                                .expand({\n
+                  relative_url: id,\n
+                  view: options._view\n
+                }),\n
+              "xhrFields": {\n
+                withCredentials: true\n
+              }\n
+            });\n
+          })\n
+          .push(undefined, function (error) {\n
+            if ((error.target !== undefined) &&\n
+                (error.target.status === 404)) {\n
+              throw new jIO.util.jIOError("Cannot find document: " + id, 404);\n
+            }\n
+            throw error;\n
+          });\n
+      });\n
+  }\n
+\n
+  var allowed_field_dict = {\n
+    "StringField": null,\n
+    "IntegerField": null,\n
+    "FloatField": null,\n
+    "TextAreaField": null\n
+  };\n
+\n
+  function extractPropertyFromForm(context, id) {\n
+    return context.getAttachment(id, "view")\n
+      .push(function (blob) {\n
+        return jIO.util.readBlobAsText(blob);\n
+      })\n
+      .push(function (evt) {\n
+        return JSON.parse(evt.target.result);\n
+      })\n
+      .push(function (json) {\n
+        var form = json._embedded._view,\n
+          converted_json = {\n
+            portal_type: json.portal_type\n
+          },\n
+          form_data_json = {},\n
+          field,\n
+          key;\n
+\n
+        form_data_json.form_id = {\n
+          "key": [form.form_id.key],\n
+          "default": form.form_id["default"]\n
+        };\n
+        // XXX How to store datetime\n
+        for (key in form) {\n
+          if (form.hasOwnProperty(key)) {\n
+            field = form[key];\n
+            if ((key.indexOf(\'my_\') === 0) &&\n
+                (field.editable) &&\n
+                (allowed_field_dict.hasOwnProperty(field.type))) {\n
+\n
+              form_data_json[key.substring(3)] = {\n
+                "default": field["default"],\n
+                "key": field.key\n
+              };\n
+              converted_json[key.substring(3)] = field["default"];\n
+            }\n
           }\n
-        });\n
+        }\n
+\n
+        return {\n
+          action_href: form._actions.put.href,\n
+          data: converted_json,\n
+          form_data: form_data_json\n
+        };\n
       });\n
   }\n
 \n
@@ -6823,29 +7829,98 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   }\n
 \n
   ERP5Storage.prototype.get = function (id) {\n
-    return getDocumentAndHateoas(this, id)\n
-      .push(function (response) {\n
-        var result = JSON.parse(response.target.responseText),\n
-          key;\n
-          // action_type;\n
-        result.portal_type = result._links.type.name;\n
-\n
+    return extractPropertyFromForm(this, id)\n
+      .push(function (result) {\n
+        var key;\n
+        result = result.data;\n
         // Remove all ERP5 hateoas links / convert them into jIO ID\n
         for (key in result) {\n
           if (result.hasOwnProperty(key)) {\n
-            if (key.indexOf("_") === 0) {\n
+            if (!result[key]) {\n
               delete result[key];\n
             }\n
           }\n
         }\n
-\n
         return result;\n
       });\n
   };\n
+\n
+  ERP5Storage.prototype.post = function (data) {\n
+    var context = this,\n
+      new_id;\n
+\n
+    return getSiteDocument(this)\n
+      .push(function (site_hal) {\n
+        var form_data = new FormData();\n
+        form_data.append("portal_type", data.portal_type);\n
+        form_data.append("parent_relative_url", data.parent_relative_url);\n
+        return jIO.util.ajax({\n
+          type: "POST",\n
+          url: site_hal._actions.add.href,\n
+          data: form_data,\n
+          xhrFields: {\n
+            withCredentials: true\n
+          }\n
+        });\n
+      })\n
+      .push(function (evt) {\n
+        var location = evt.target.getResponseHeader("X-Location"),\n
+          uri = new URI(location);\n
+        new_id = uri.segment(2);\n
+        return context.put(new_id, data);\n
+      })\n
+      .push(function () {\n
+        return new_id;\n
+      });\n
+  };\n
+\n
+  ERP5Storage.prototype.put = function (id, data) {\n
+    var context = this;\n
+\n
+    return extractPropertyFromForm(context, id)\n
+      .push(function (result) {\n
+        var key,\n
+          json = result.form_data,\n
+          form_data = {};\n
+        form_data[json.form_id.key] = json.form_id["default"];\n
+\n
+        // XXX How to store datetime:!!!!!\n
+        for (key in data) {\n
+          if (data.hasOwnProperty(key)) {\n
+            if (key === "form_id") {\n
+              throw new jIO.util.jIOError(\n
+                "ERP5: forbidden property: " + key,\n
+                400\n
+              );\n
+            }\n
+            if ((key !== "portal_type") && (key !== "parent_relative_url")) {\n
+              if (!json.hasOwnProperty(key)) {\n
+                throw new jIO.util.jIOError(\n
+                  "ERP5: can not store property: " + key,\n
+                  400\n
+                );\n
+              }\n
+              form_data[json[key].key] = data[key];\n
+            }\n
+          }\n
+        }\n
+        return context.putAttachment(\n
+          id,\n
+          result.action_href,\n
+          new Blob([JSON.stringify(form_data)], {type: "application/json"})\n
+        );\n
+      });\n
+  };\n
 \n
   ERP5Storage.prototype.allAttachments = function (id) {\n
+    var context = this;\n
     return getDocumentAndHateoas(this, id)\n
       .push(function () {\n
+        if (context._default_view_reference === undefined) {\n
+          return {\n
+            links: {}\n
+          };\n
+        }\n
         return {\n
           view: {},\n
           links: {}\n
@@ -6856,6 +7931,12 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   ERP5Storage.prototype.getAttachment = function (id, action) {\n
 \n
     if (action === "view") {\n
+      if (this._default_view_reference === undefined) {\n
+        throw new jIO.util.jIOError(\n
+          "Cannot find attachment view for: " + id,\n
+          404\n
+        );\n
+      }\n
       return getDocumentAndHateoas(this, id,\n
                                    {"_view": this._default_view_reference})\n
         .push(function (response) {\n
@@ -7032,6 +8113,9 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
     return this._sub_storage.removeAttachment.apply(this._sub_storage,\n
                                                     arguments);\n
   };\n
+  QueryStorage.prototype.repair = function () {\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments);\n
+  };\n
 \n
   QueryStorage.prototype.hasCapacity = function (name) {\n
     if (name === "list") {\n
@@ -7208,10 +8292,12 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
     this._sub_storage = jIO.createJIO(spec.sub_storage);\n
   }\n
   var DOCUMENT_EXTENSION = ".json",\n
-    DOCUMENT_REGEXP = new RegExp("^([\\\\w=]+)" +\n
-                                 DOCUMENT_EXTENSION + "$"),\n
     DOCUMENT_KEY = "/.jio_documents/",\n
     ROOT = "/";\n
+\n
+  function endsWith(str, suffix) {\n
+    return str.indexOf(suffix, str.length - suffix.length) !== -1;\n
+  }\n
 \n
   FileSystemBridgeStorage.prototype.get = function (id) {\n
     var context = this;\n
@@ -7223,18 +8309,11 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         // First get the document itself if it exists\n
         return context._sub_storage.getAttachment(\n
           DOCUMENT_KEY,\n
-          id + DOCUMENT_EXTENSION\n
+          id + DOCUMENT_EXTENSION,\n
+          {format: "json"}\n
         );\n
       })\n
-      .push(function (blob) {\n
-        return new RSVP.Queue()\n
-          .push(function () {\n
-            return jIO.util.readBlobAsText(blob);\n
-          })\n
-          .push(function (text) {\n
-            return JSON.parse(text.target.result);\n
-          });\n
-      }, function (error) {\n
+      .push(undefined, function (error) {\n
         if ((error instanceof jIO.util.jIOError) &&\n
             (error.status_code === 404)) {\n
 \n
@@ -7368,8 +8447,11 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         var key;\n
         for (key in result) {\n
           if (result.hasOwnProperty(key)) {\n
-            if (DOCUMENT_REGEXP.test(key)) {\n
-              result_dict[DOCUMENT_REGEXP.exec(key)[1]] = null;\n
+            if (endsWith(key, DOCUMENT_EXTENSION)) {\n
+              result_dict[key.substring(\n
+                0,\n
+                key.length - DOCUMENT_EXTENSION.length\n
+              )] = null;\n
             }\n
           }\n
         }\n
@@ -7443,6 +8525,10 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
     return this._sub_storage.removeAttachment(ROOT, id);\n
   };\n
+\n
+  FileSystemBridgeStorage.prototype.repair = function () {\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments);\n
+  };\n
 \n
   jIO.addStorage(\'drivetojiomapping\', FileSystemBridgeStorage);\n
 \n
@@ -7478,14 +8564,9 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
   DocumentStorage.prototype.get = function (id) {\n
     return this._sub_storage.getAttachment(\n
       this._document_id,\n
-      getSubAttachmentIdFromParam(id)\n
-    )\n
-      .push(function (blob) {\n
-        return jIO.util.readBlobAsText(blob);\n
-      })\n
-      .push(function (text) {\n
-        return JSON.parse(text.target.result);\n
-      });\n
+      getSubAttachmentIdFromParam(id),\n
+      {format: "json"}\n
+    );\n
   };\n
 \n
   DocumentStorage.prototype.allAttachments = function (id) {\n
@@ -7536,6 +8617,10 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         return id;\n
       });\n
   };\n
+\n
+  DocumentStorage.prototype.repair = function () {\n
+    return this._sub_storage.repair.apply(this._sub_storage, arguments);\n
+  };\n
 \n
   DocumentStorage.prototype.hasCapacity = function (capacity) {\n
     return (capacity === "list");\n
@@ -7877,6 +8962,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
 \n
   IndexedDBStorage.prototype.getAttachment = function (id, name, options) {\n
     var transaction,\n
+      type,\n
       start,\n
       end;\n
     if (options === undefined) {\n
@@ -7897,6 +8983,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
           start_index,\n
           end_index;\n
 \n
+        type = attachment.info.content_type;\n
         start = options.start || 0;\n
         end = options.end || total_length;\n
         if (end > total_length) {\n
@@ -7934,8 +9021,11 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
         for (i = 0; i < len; i += 1) {\n
           array_buffer_list.push(result_list[i].blob);\n
         }\n
+        if ((options.start === undefined) && (options.end === undefined)) {\n
+          return new Blob(array_buffer_list, {type: type});\n
+        }\n
         blob = new Blob(array_buffer_list, {type: "application/octet-stream"});\n
-        return blob.slice(start, end);\n
+        return blob.slice(start, end, "application/octet-stream");\n
       });\n
   };\n
 \n
@@ -8162,7 +9252,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
             </item>
             <item>
                 <key> <string>serial</string> </key>
-                <value> <string>941.55617.298.51882</string> </value>
+                <value> <string>942.40553.20339.32529</string> </value>
             </item>
             <item>
                 <key> <string>state</string> </key>
@@ -8180,7 +9270,7 @@ Query.searchTextToRegExp = searchTextToRegExp;\n
                     </tuple>
                     <state>
                       <tuple>
-                        <float>1428655194.19</float>
+                        <float>1430410922.96</float>
                         <string>GMT</string>
                       </tuple>
                     </state>