Add binary field support
review1010002
This commit is contained in:
1 parent
dd0416c46a
commit
0cc196376b
4 files changed
+263
-3
No files matched your search
@@ -72,6 +72,16 @@
|
||||
return text;
|
||||
};
|
||||
|
||||
Sao.common.humanize = function(size) {
|
||||
var sizes = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
||||
for (var i =0, len = sizes.length; i < len; i++) {
|
||||
if (size < 1000) {
|
||||
return size.toPrecision(4) + ' ' + sizes[i];
|
||||
}
|
||||
size /= 1000;
|
||||
}
|
||||
};
|
||||
|
||||
Sao.common.EvalEnvironment = function(parent_, eval_type) {
|
||||
if (eval_type === undefined)
|
||||
eval_type = 'eval';
|
||||
|
||||
+39
-2
@@ -406,7 +406,7 @@
|
||||
n++;
|
||||
}
|
||||
}
|
||||
var context = this.get_context();
|
||||
var context = jQuery.extend({}, this.get_context());
|
||||
var fnames = [];
|
||||
if (loading == 'eager') {
|
||||
for (fname in this.model.fields) {
|
||||
@@ -436,7 +436,15 @@
|
||||
fnames_to_fetch.push('rec_name');
|
||||
}
|
||||
fnames_to_fetch.push('_timestamp');
|
||||
// TODO size of binary
|
||||
for (fname in this.model.fields) {
|
||||
if (!this.model.fields.hasOwnProperty(fname)) {
|
||||
continue;
|
||||
}
|
||||
if ((this.model.fields[fname].description.type == 'binary') &&
|
||||
~fnames_to_fetch.indexOf(fname, fnames_to_fetch)) {
|
||||
context[this.model.name + '.' + fname] = 'size';
|
||||
}
|
||||
}
|
||||
prm = this.model.execute('read', [Object.keys(id2record),
|
||||
fnames_to_fetch], context);
|
||||
var succeed = function(values) {
|
||||
@@ -842,6 +850,8 @@
|
||||
return Sao.field.One2Many;
|
||||
case 'many2many':
|
||||
return Sao.field.Many2Many;
|
||||
case 'binary':
|
||||
return Sao.field.Binary;
|
||||
default:
|
||||
return Sao.field.Char;
|
||||
}
|
||||
@@ -1609,4 +1619,31 @@
|
||||
return this.get_eval(record);
|
||||
}
|
||||
});
|
||||
|
||||
Sao.field.Binary = Sao.class_(Sao.field.Field, {
|
||||
_default: null,
|
||||
get_size: function(record) {
|
||||
var data = record._values[this.name] || 0;
|
||||
if (data instanceof Uint8Array) {
|
||||
return data.length;
|
||||
}
|
||||
return data;
|
||||
},
|
||||
get_data: function(record) {
|
||||
var prm = jQuery.when();
|
||||
var data = record._values[this.name] || 0;
|
||||
if (!(data instanceof Uint8Array)) {
|
||||
if (record.id < 0) {
|
||||
return prm;
|
||||
}
|
||||
var context = record.get_context();
|
||||
prm = record.model.execute('read', [[record.id], [this.name]],
|
||||
context);
|
||||
prm.done(function(data) {
|
||||
return data[0][this.name];
|
||||
}.bind(this));
|
||||
return prm;
|
||||
}
|
||||
}
|
||||
});
|
||||
}());
|
||||
+20
-1
@@ -81,7 +81,18 @@
|
||||
case 'time':
|
||||
throw new Error('Time support not implemented');
|
||||
case 'buffer':
|
||||
throw new Error('Buffer support not implemented');
|
||||
// javascript's atob does not understand linefeed
|
||||
// characters
|
||||
var byte_string = atob(value.base64.replace(/\s/g, ''));
|
||||
// javascript decodes base64 string as a "DOMString", we
|
||||
// need to convert it to an array of bytes
|
||||
var array_buffer = new ArrayBuffer(byte_string.length);
|
||||
var uint_array = new Uint8Array(array_buffer);
|
||||
for (var j=0; j < byte_string.length; j++) {
|
||||
uint_array[j] = byte_string.charCodeAt(j);
|
||||
}
|
||||
value = uint_array;
|
||||
break;
|
||||
case 'Decimal':
|
||||
value = new Sao.Decimal(value.decimal);
|
||||
break;
|
||||
@@ -137,6 +148,14 @@
|
||||
if (parent) {
|
||||
parent[index] = value;
|
||||
}
|
||||
} else if (value instanceof Uint8Array) {
|
||||
value = {
|
||||
'__class__': 'buffer',
|
||||
'base64': btoa(String.fromCharCode.apply(null, value))
|
||||
};
|
||||
if (parent) {
|
||||
parent[index] = value;
|
||||
}
|
||||
} else {
|
||||
for (var p in value) {
|
||||
Sao.rpc.prepareObject(value[p], p, value);
|
||||
|
||||
+194
@@ -960,6 +960,8 @@
|
||||
return Sao.View.Form.One2Many;
|
||||
case 'many2many':
|
||||
return Sao.View.Form.Many2Many;
|
||||
case 'binary':
|
||||
return Sao.View.Form.Binary;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1974,4 +1976,196 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Sao.View.Form.Binary = Sao.class_(Sao.View.Form.Widget, {
|
||||
class_: 'form-binary',
|
||||
blob_url: '',
|
||||
init: function(field_name, model, attributes) {
|
||||
Sao.View.Form.Binary._super.init.call(this, field_name, model,
|
||||
attributes);
|
||||
this.filename = attributes.filename || null;
|
||||
|
||||
this.el = jQuery('<div/>', {
|
||||
'class': this.class_
|
||||
});
|
||||
|
||||
if (this.filename && attributes.filename_visible) {
|
||||
this.text = jQuery('<input/>', {
|
||||
type: 'input'
|
||||
});
|
||||
this.text.change(this.focus_out.bind(this));
|
||||
this.text.on('keyup', this.key_press.bind(this));
|
||||
this.el.append(this.text);
|
||||
}
|
||||
this.size = jQuery('<input/>', {
|
||||
type: 'input'
|
||||
});
|
||||
this.el.append(this.size);
|
||||
|
||||
this.but_new = jQuery('<button/>').button({
|
||||
icons: {
|
||||
primary: 'ui-icon-document'
|
||||
},
|
||||
text: false
|
||||
});
|
||||
this.but_new.click(this.new_.bind(this));
|
||||
this.el.append(this.but_new);
|
||||
|
||||
if (this.filename) {
|
||||
this.but_open = jQuery('<a/>').button({
|
||||
icons: {
|
||||
primary: 'ui-icon-folder-open'
|
||||
},
|
||||
text: false
|
||||
});
|
||||
this.but_open.click(this.open.bind(this));
|
||||
this.el.append(this.but_open);
|
||||
}
|
||||
|
||||
this.but_save_as = jQuery('<button/>').button({
|
||||
icons: {
|
||||
primary: 'ui-icon-disk'
|
||||
},
|
||||
text: false
|
||||
});
|
||||
this.but_save_as.click(this.save_as.bind(this));
|
||||
this.el.append(this.but_save_as);
|
||||
|
||||
this.but_remove = jQuery('<button/>').button({
|
||||
icons: {
|
||||
primary: 'ui-icon-trash'
|
||||
},
|
||||
text: false
|
||||
});
|
||||
this.but_remove.click(this.remove.bind(this));
|
||||
this.el.append(this.but_remove);
|
||||
},
|
||||
filename_field: function() {
|
||||
var record = this.record();
|
||||
if (record) {
|
||||
return record.model.fields[this.filename];
|
||||
}
|
||||
},
|
||||
display: function(record, field) {
|
||||
Sao.View.Form.Binary._super.display.call(this, record, field);
|
||||
if (field === undefined) {
|
||||
this.size.val('');
|
||||
if (this.filename) {
|
||||
this.but_open.button('disable');
|
||||
}
|
||||
if (this.text) {
|
||||
this.text.val('');
|
||||
}
|
||||
this.but_save_as.button('disable');
|
||||
return;
|
||||
}
|
||||
var size = field.get_size(record);
|
||||
var button_sensitive;
|
||||
if (size) {
|
||||
button_sensitive = 'enable';
|
||||
} else {
|
||||
button_sensitive = 'disable';
|
||||
}
|
||||
|
||||
if (this.filename) {
|
||||
if (this.text) {
|
||||
this.text.val(this.filename_field().get(record) || '');
|
||||
}
|
||||
this.but_open.button(button_sensitive);
|
||||
}
|
||||
this.size.val(Sao.common.humanize(size));
|
||||
this.but_save_as.button(button_sensitive);
|
||||
},
|
||||
save_as: function(evt) {
|
||||
var field = this.field();
|
||||
var record = this.record();
|
||||
field.get_data(record).done(function(data) {
|
||||
var blob = new Blob([data],
|
||||
{type: 'application/octet-binary'});
|
||||
var blob_url = window.URL.createObjectURL(blob);
|
||||
if (this.blob_url) {
|
||||
window.URL.revokeObjectURL(this.blob_url);
|
||||
}
|
||||
this.blob_url = blob_url;
|
||||
window.open(blob_url);
|
||||
}.bind(this));
|
||||
},
|
||||
open: function(evt) {
|
||||
// TODO find a way to make the difference between downloading and
|
||||
// opening
|
||||
this.save_as(evt);
|
||||
},
|
||||
new_: function(evt) {
|
||||
var record = this.record();
|
||||
var file_dialog = jQuery('<div/>', {
|
||||
'class': 'file-dialog'
|
||||
});
|
||||
var file_selector = jQuery('<input/>', {
|
||||
type: 'file'
|
||||
});
|
||||
file_dialog.append(file_selector);
|
||||
var save_file = function() {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(evt) {
|
||||
var uint_array = new Uint8Array(reader.result);
|
||||
this.field().set_client(record, uint_array);
|
||||
}.bind(this);
|
||||
reader.onloadend = function(evt) {
|
||||
file_dialog.dialog('close');
|
||||
};
|
||||
var file = file_selector[0].files[0];
|
||||
reader.readAsArrayBuffer(file);
|
||||
if (this.filename) {
|
||||
this.filename_field().set_client(record, file.name);
|
||||
}
|
||||
};
|
||||
file_dialog.dialog({
|
||||
modal: true,
|
||||
title: 'Select a file', // TODO translation
|
||||
buttons: {
|
||||
Cancel: function() {
|
||||
$(this).dialog('close');
|
||||
},
|
||||
OK: save_file.bind(this)
|
||||
}
|
||||
});
|
||||
file_dialog.dialog('open');
|
||||
},
|
||||
remove: function(evt) {
|
||||
this.field().set_client(this.record(), null);
|
||||
},
|
||||
key_press: function(evt) {
|
||||
var editable = true; // TODO compute editable
|
||||
if (evt.which == Sao.common.F3_KEYCODE && editable) {
|
||||
this.new_();
|
||||
evt.preventDefault();
|
||||
} else if (evt.which == Sao.common.F2_KEYCODE) {
|
||||
this.open();
|
||||
evt.preventDefault();
|
||||
}
|
||||
},
|
||||
set_value: function(record, field) {
|
||||
if (this.text) {
|
||||
this.filename_field().set_client(record,
|
||||
this.text.val() || '');
|
||||
}
|
||||
},
|
||||
_get_color_el: function() {
|
||||
if (this.text) {
|
||||
return this.text;
|
||||
} else {
|
||||
return this.size;
|
||||
}
|
||||
},
|
||||
set_readonly: function(readonly) {
|
||||
if (readonly) {
|
||||
this.but_new.hide();
|
||||
this.but_remove.hide();
|
||||
|
||||
} else {
|
||||
this.but_new.show();
|
||||
this.but_remove.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}());
|
||||
Reference in new issue
Block a user