background: #7c7bad;
color: #eeeeee;
}
+.openerp .oe_form_field_radio.oe_horizontal {
+ white-space: nowrap;
+}
+.openerp .oe_form_field_radio.oe_horizontal label {
+ display: inline-block;
+ text-align: center;
+ height: 16px;
+}
+.openerp .oe_form_field_radio.oe_vertical label {
+ margin-left: 4px;
+}
+.openerp .oe_form_field_radio.oe_form_required .oe_radio_input {
+ border: 2px solid transparent;
+ display: inline-block;
+ height: 12px;
+ width: 12px;
+ vertical-align: bottom;
+ border-radius: 10px;
+ margin: 1px 0;
+}
+.openerp .oe_form_field_radio.oe_form_required.oe_form_invalid .oe_radio_input {
+ border-color: red;
+}
.openerp .oe_tags {
margin-bottom: 1px;
}
display: block;
color: #4c4c4c;
text-decoration: none;
- width: 200px;
- text-overflow: ellipsis;
- overflow: hidden;
}
.openerp .oe_dropdown_menu > li > a:hover {
text-decoration: none;
display: table-row;
height: inherit;
}
- .openerp .oe_view_manager .oe_view_manager_view_kanban {
+ .openerp .oe_view_manager .oe_view_manager_view_kanban:not(:empty) {
height: inherit;
}
.openerp .oe_view_manager table.oe_view_manager_header {
.openerp .oe_form_invisible {
display: none !important;
}
+.openerp .oe_form_editable .oe_read_only {
+ display: none !important;
+}
.openerp .oe_form_readonly .oe_edit_only, .openerp .oe_form_readonly .oe_form_field:empty {
display: none !important;
}
background: white;
min-width: 60px;
color: #1f1f1f;
+ font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
}
.openerp .oe_form input[readonly], .openerp .oe_form select[readonly], .openerp .oe_form textarea[readonly], .openerp .oe_form input[disabled], .openerp .oe_form select[disabled] {
background: #e5e5e5 !important;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
+ color: #4c4c4c;
}
.openerp .oe_form textarea.oe_inline[disabled] {
border-left: 8px solid #eeeeee;
white-space: nowrap;
}
.openerp .oe_form .oe_form_field_boolean {
- padding-top: 4px;
width: auto;
}
.openerp .oe_form .oe_datepicker_container {
color: #333333;
}
+@-moz-document url-prefix() {
+ .openerp .oe_view_manager .oe_view_manager_switch li {
+ line-height: 21px;
+ }
+ .openerp .oe_searchview .oe_searchview_search {
+ top: -1px;
+ }
+ .openerp .oe_form_field_many2one .oe_m2o_cm_button {
+ line-height: 18px;
+ }
+ .openerp .oe_secondary_submenu {
+ line-height: 14px;
+ }
+ .openerp .oe_webclient .oe_star_on, .openerp .oe_webclient .oe_star_off {
+ top: 0px;
+ }
+}
+
.kitten-mode-activated {
background-size: cover;
background-attachment: fixed;
opacity: 0.7;
}
+.loading-kitten {
+ -moz-border-radius: 15px;
+ -webkit-border-radius: 15px;
+ border-radius: 15px;
+ -moz-box-shadow: 0 0 5px 5px #999999;
+ -webkit-box-shadow: 0 0 5px 5px #999999;
+ box-shadow: 0 0 5px 5px #999999;
+}
+
div.ui-widget-overlay {
background: black;
filter: alpha(opacity=30);
.oe_tag_dark
background: $tag-bg-dark
color: #eee
-
+ .oe_form_field_radio
+ &.oe_horizontal
+ white-space: nowrap
+ label
+ display: inline-block
+ text-align: center
+ height: 16px
+ &.oe_vertical
+ label
+ margin-left: 4px
+ &.oe_form_required
+ .oe_radio_input
+ border: 2px solid transparent
+ display: inline-block
+ height: 12px
+ width: 12px
+ vertical-align: bottom
+ border-radius: 10px
+ margin: 1px 0
+ &.oe_form_invalid
+ .oe_radio_input
+ border-color: red
.oe_tags
&.oe_inline
min-width: 250px
display: block
color: #4c4c4c
text-decoration: none
- width: 200px
- text-overflow: ellipsis
- overflow: hidden
&:hover
text-decoration: none
.oe_dropdown_arrow:after
.oe_view_manager_body
display: table-row
height: inherit
- .oe_view_manager_view_kanban
+ .oe_view_manager_view_kanban:not(:empty)
height: inherit
table.oe_view_manager_header
@include box-shadow((0 1px 2px rgba(0, 0, 0, .1), 0 1px 1px rgba(255, 255, 255, .8) inset))
.oe_form_invisible
display: none !important
+ .oe_form_editable
+ .oe_read_only
+ display: none !important
.oe_form_readonly
.oe_edit_only, .oe_form_field:empty
display: none !important
background: white
min-width: 60px
color: #1f1f1f
+ font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
input[readonly], select[readonly], textarea[readonly], input[disabled], select[disabled]
background: #E5E5E5 !important
color: #666
padding-left: 8px
@include box-shadow(none)
@include radius(0px)
+ color: #4c4c4c
textarea.oe_inline[disabled]
border-left: 8px solid #eee
.oe_form_field_url button img
.oe_form_field_datetime
white-space: nowrap
.oe_form_field_boolean
- padding-top: 4px
width: auto
.oe_datepicker_container
display: none
float: right
color: #333
// }}}
+@-moz-document url-prefix()
+ .openerp
+ .oe_view_manager .oe_view_manager_switch li
+ line-height: 21px
+ .oe_searchview .oe_searchview_search
+ top: -1px
+ .oe_form_field_many2one .oe_m2o_cm_button
+ line-height: 18px
+ .oe_secondary_submenu
+ line-height: 14px
+ .oe_webclient
+ .oe_star_on, .oe_star_off
+ top: 0px
+
// Kitten Mode {{{
.kitten-mode-activated
background-size: cover
background-attachment: fixed
>*
opacity: 0.70
+.loading-kitten
+ @include radius(15px)
+ @include box-shadow(0 0 5px 5px #999)
// }}}
// jQueryUI top level {{{
* @param {Boolean} [options.disable_custom_filters=false] do not load custom filters from ir.filters
*/
init: function(parent, dataset, view_id, defaults, options) {
- // Backward compatibility - Can be removed when forward porting
- if (Object(options) !== options) {
- options = {
- hidden: !!options
- };
- }
- // End of Backward compatibility
this.options = _.defaults(options || {}, {
hidden: false,
disable_custom_filters: false,
context: context
}).then(function (results) {
if (_.isEmpty(results)) { return null; }
- return [{label: _.escape(self.attrs.string)}].concat(
+ return [{label: self.attrs.string}].concat(
_(results).map(function (result) {
return {
label: _.escape(result[1]),
var $name = this.$('input:first');
var private_filter = !this.$('#oe_searchview_custom_public').prop('checked');
var set_as_default = this.$('#oe_searchview_custom_default').prop('checked');
-
+ if (_.isEmpty($name.val())){
+ this.do_warn(_t("Error"), _t("Filter name is required."));
+ return false;
+ }
var search = this.view.build_search_data();
instance.web.pyeval.eval_domains_and_contexts({
domains: search.domains,
}
});
+instance.web.form.FieldRadio = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
+ template: 'FieldRadio',
+ events: {
+ 'click input': 'click_change_value'
+ },
+ init: function(field_manager, node) {
+ /* Radio button widget: Attributes options:
+ * - "horizontal" to display in column
+ * - "no_radiolabel" don't display text values
+ */
+ this._super(field_manager, node);
+ this.selection = _.clone(this.field.selection) || [];
+ this.domain = false;
+ },
+ initialize_content: function () {
+ this.uniqueId = _.uniqueId("radio");
+ this.on("change:effective_readonly", this, this.render_value);
+ this.field_manager.on("view_content_has_changed", this, this.get_selection);
+ this.get_selection();
+ },
+ click_change_value: function (event) {
+ var val = $(event.target).val();
+ val = this.field.type == "selection" ? val : +val;
+ if (val == this.get_value()) {
+ this.set_value(false);
+ } else {
+ this.set_value(val);
+ }
+ },
+ /** Get the selection and render it
+ * selection: [[identifier, value_to_display], ...]
+ * For selection fields: this is directly given by this.field.selection
+ * For many2one fields: perform a search on the relation of the many2one field
+ */
+ get_selection: function() {
+ var self = this;
+ var selection = [];
+ var def = $.Deferred();
+ if (self.field.type == "many2one") {
+ var domain = instance.web.pyeval.eval('domain', this.build_domain()) || [];
+ if (! _.isEqual(self.domain, domain)) {
+ self.domain = domain;
+ var ds = new instance.web.DataSetStatic(self, self.field.relation, self.build_context());
+ ds.call('search', [self.domain])
+ .then(function (records) {
+ ds.name_get(records).then(function (records) {
+ selection = records;
+ def.resolve();
+ });
+ });
+ } else {
+ selection = self.selection;
+ def.resolve();
+ }
+ }
+ else if (self.field.type == "selection") {
+ selection = self.field.selection || [];
+ def.resolve();
+ }
+ return def.then(function () {
+ if (! _.isEqual(selection, self.selection)) {
+ self.selection = _.clone(selection);
+ self.renderElement();
+ self.render_value();
+ }
+ });
+ },
+ set_value: function (value_) {
+ if (value_) {
+ if (this.field.type == "selection") {
+ value_ = _.find(this.field.selection, function (sel) { return sel[0] == value_});
+ }
+ else if (!this.selection.length) {
+ this.selection = [value_];
+ }
+ }
+ this._super(value_);
+ },
+ get_value: function () {
+ var value = this.get('value');
+ return value instanceof Array ? value[0] : value;
+ },
+ render_value: function () {
+ var self = this;
+ this.$el.toggleClass("oe_readonly", this.get('effective_readonly'));
+ this.$("input:checked").prop("checked", false);
+ if (this.get_value()) {
+ this.$("input").filter(function () {return this.value == self.get_value()}).prop("checked", true);
+ this.$(".oe_radio_readonly").text(this.get('value') ? this.get('value')[1] : "");
+ }
+ }
+});
+
// jquery autocomplete tweak to allow html and classnames
(function() {
var proto = $.ui.autocomplete.prototype,
values.push({
label: _t("Search More..."),
action: function() {
- dataset.name_search(search_val, self.build_domain(), 'ilike', false).done(function(data) {
+ dataset.name_search(search_val, self.build_domain(), 'ilike', 160).done(function(data) {
self._search_create_popup("search", data);
});
},
instance.web.form.CompletionFieldMixin.init.call(this);
this.set({'value': false});
this.display_value = {};
+ this.display_value_backup = {};
this.last_search = [];
this.floating = false;
this.current_display = null;
);
pop.on('write_completed', self, function(){
self.display_value = {};
+ self.display_value_backup = {};
self.render_value();
self.focus();
self.view.do_onchange(self);
this.$input.keydown(input_changed);
this.$input.change(input_changed);
this.$drop_down.click(function() {
+ self.$input.focus();
if (self.$input.autocomplete("widget").is(":visible")) {
- self.$input.autocomplete("close");
- self.$input.focus();
+ self.$input.autocomplete("close");
} else {
if (self.get("value") && ! self.floating) {
self.$input.autocomplete("search", "");
if (self.last_search.length > 0) {
if (self.last_search[0][0] != self.get("value")) {
self.display_value = {};
+ self.display_value_backup = {};
self.display_value["" + self.last_search[0][0]] = self.last_search[0][1];
self.reinit_value(self.last_search[0][0]);
} else {
var item = ui.item;
if (item.id) {
self.display_value = {};
+ self.display_value_backup = {};
self.display_value["" + item.id] = item.name;
self.reinit_value(item.id);
} else if (item.action) {
this.alive(dataset.name_get([self.get("value")])).done(function(data) {
self.display_value["" + self.get("value")] = data[0][1];
self.render_value(true);
+ }).fail( function (data, event) {
+ // avoid displaying crash errors as many2One should be name_get compliant
+ event.preventDefault();
+ self.display_value["" + self.get("value")] = self.display_value_backup["" + self.get("value")];
+ self.render_value(true);
});
}
},
var self = this;
if (value_ instanceof Array) {
this.display_value = {};
+ this.display_value_backup = {}
if (! this.options.always_reload) {
this.display_value["" + value_[0]] = value_[1];
}
+ else {
+ this.display_value_backup["" + value_[0]] = value_[1];
+ }
value_ = value_[0];
}
value_ = value_ || false;
},
add_id: function(id) {
this.display_value = {};
+ this.display_value_backup = {};
this.reinit_value(id);
},
is_false: function() {
_.extend(view.options, {
addable: null,
selectable: self.multi_selection,
- sortable: false,
+ sortable: true,
import_enabled: false,
deletable: true
});
else
return $.when();
}).done(function () {
- self.handle_button(name, id, callback);
+ if (!self.o2m.options.reload_on_button) {
+ self.handle_button(name, id, callback);
+ }else {
+ self.handle_button(name, id, function(){
+ self.o2m.view.reload();
+ });
+ }
});
},
var input = this.$text && this.$text[0];
return input ? input.focus() : false;
},
+ set_dimensions: function (height, width) {
+ this._super(height, width);
+ this.$("textarea").css({
+ width: width,
+ minHeight: height
+ });
+ },
});
/**
});
/**
- * Widget for (one2many field) to upload one or more file in same time and display in list.
+ * Widget for (many2many field) to upload one or more file in same time and display in list.
* The user can delete his files.
* Options on attribute ; "blockui" {Boolean} block the UI or not
* during the file is uploading
if(this.field.type != "many2many" || this.field.relation != 'ir.attachment') {
throw _.str.sprintf(_t("The type of the field '%s' must be a many2many field with a relation to 'ir.attachment' model."), this.field.string);
}
+ this.data = {};
+ this.set_value([]);
this.ds_file = new instance.web.DataSetSearch(this, 'ir.attachment');
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
$(window).on(this.fileupload_id, _.bind(this.on_file_loaded, this));
this.$el.on('change', 'input.oe_form_binary_file', this.on_file_change );
},
set_value: function(value_) {
- var value_ = value_ || [];
- var self = this;
- var ids = [];
- _.each(value_, function(command) {
- if (isNaN(command) && command.id == undefined) {
- switch (command[0]) {
- case commands.CREATE:
- ids = ids.concat(command[2]);
- return;
- case commands.REPLACE_WITH:
- ids = ids.concat(command[2]);
- return;
- case commands.UPDATE:
- ids = ids.concat(command[2]);
- return;
- case commands.LINK_TO:
- ids = ids.concat(command[1]);
- return;
- case commands.DELETE:
- ids = _.filter(ids, function (id) { return id != command[1];});
- return;
- case commands.DELETE_ALL:
- ids = [];
- return;
- }
- } else {
- ids.push(command);
- }
- });
- this._super( ids );
+ value_ = value_ || [];
+ if (value_.length >= 1 && value_[0] instanceof Array) {
+ value_ = value_[0][2];
+ }
+ this._super(value_);
},
get_value: function() {
- return _.map(this.get('value'), function (value) { return commands.link_to( isNaN(value) ? value.id : value ); });
+ var tmp = [commands.replace_with(this.get("value"))];
+ return tmp;
},
get_file_url: function (attachment) {
return this.session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'datas_fname', id: attachment['id']});
},
read_name_values : function () {
var self = this;
- // select the list of id for a get_name
- var values = [];
- _.each(this.get('value'), function (val) {
- if (typeof val != 'object') {
- values.push(val);
- }
- });
+ // don't reset know values
+ var _value = _.filter(this.get('value'), function (id) { return typeof self.data[id] == 'undefined'; } );
// send request for get_name
- if (values.length) {
- return this.ds_file.call('read', [values, ['id', 'name', 'datas_fname']]).done(function (datas) {
+ if (_value.length) {
+ return this.ds_file.call('read', [_value, ['id', 'name', 'datas_fname']]).done(function (datas) {
_.each(datas, function (data) {
data.no_unlink = true;
data.url = self.session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'datas_fname', id: data.id});
-
- _.each(self.get('value'), function (val, key) {
- if(val == data.id) {
- self.get('value')[key] = data;
- }
- });
+ self.data[data.id] = data;
});
});
} else {
- return $.when(this.get('value'));
+ return $.when();
}
},
render_value: function () {
var self = this;
- this.read_name_values().then(function (datas) {
+ this.read_name_values().then(function () {
var render = $(instance.web.qweb.render('FieldBinaryFileUploader.files', {'widget': self}));
render.on('click', '.oe_delete', _.bind(self.on_file_delete, self));
var self = this;
var $target = $(event.target);
if ($target.val() !== '') {
-
var filename = $target.val().replace(/.*[\\\/]/,'');
-
- // if the files is currently uploded, don't send again
- if( !isNaN(_.find(this.get('value'), function (file) { return (file.filename || file.name) == filename && file.upload; } )) ) {
+ // don't uplode more of one file in same time
+ if (self.data[0] && self.data[0].upload ) {
return false;
}
+ for (var id in this.get('value')) {
+ // if the files exits, delete the file before upload (if it's a new file)
+ if (self.data[id] && (self.data[id].filename || self.data[id].name) == filename && !self.data[id].no_unlink ) {
+ self.ds_file.unlink([id]);
+ }
+ }
// block UI or not
if(this.node.attrs.blockui>0) {
instance.web.blockUI();
}
- // if the files exits for this answer, delete the file before upload
- var files = _.filter(this.get('value'), function (file) {
- if((file.filename || file.name) == filename) {
- self.ds_file.unlink([file.id]);
- return false;
- } else {
- return true;
- }
- });
-
// TODO : unactivate send on wizard and form
// submit file
this.$('form.oe_form_binary_form').submit();
this.$(".oe_fileupload").hide();
-
- // add file on result
- files.push({
+ // add file on data result
+ this.data[0] = {
'id': 0,
'name': filename,
'filename': filename,
'url': '',
'upload': true
- });
-
- this.set({'value': files});
+ };
}
},
on_file_loaded: function (event, result) {
instance.web.unblockUI();
}
- // TODO : activate send on wizard and form
-
if (result.error || !result.id ) {
this.do_warn( _t('Uploading Error'), result.error);
- files = _.filter(files, function (val) { return !val.upload; });
+ delete this.data[0];
} else {
- for(var i in files){
- if(files[i].filename == result.filename && files[i].upload) {
- files[i] = {
- 'id': result.id,
- 'name': result.name,
- 'filename': result.filename,
- 'url': this.get_file_url(result)
- };
- }
+ if (this.data[0] && this.data[0].filename == result.filename && this.data[0].upload) {
+ delete this.data[0];
+ this.data[result.id] = {
+ 'id': result.id,
+ 'name': result.name,
+ 'filename': result.filename,
+ 'url': this.get_file_url(result)
+ };
+ } else {
+ this.data[result.id] = {
+ 'id': result.id,
+ 'name': result.name,
+ 'filename': result.filename,
+ 'url': this.get_file_url(result)
+ };
}
+ var values = _.clone(this.get('value'));
+ values.push(result.id);
+ this.set({'value': values});
}
-
- this.set({'value': files});
this.render_value()
},
on_file_delete: function (event) {
event.stopPropagation();
var file_id=$(event.target).data("id");
if (file_id) {
- var files=[];
- for(var i in this.get('value')){
- if(file_id != this.get('value')[i].id){
- files.push(this.get('value')[i]);
- }
- else if(!this.get('value')[i].no_unlink) {
- this.ds_file.unlink([file_id]);
- }
+ var files = _.filter(this.get('value'), function (id) {return id != file_id;});
+ if(!this.data[file_id].no_unlink) {
+ this.ds_file.unlink([file_id]);
}
this.set({'value': files});
}
var self = this;
var content = QWeb.render("FieldStatus.content", {widget: self});
self.$el.html(content);
- var colors = JSON.parse((self.node.attrs || {}).statusbar_colors || "{}");
- var color = colors[self.get('value')];
- if (color) {
- self.$("oe_active").css("color", color);
- }
},
calc_domain: function() {
var d = instance.web.pyeval.eval('domain', this.build_domain());
'date' : 'instance.web.form.FieldDate',
'datetime' : 'instance.web.form.FieldDatetime',
'selection' : 'instance.web.form.FieldSelection',
+ 'radio' : 'instance.web.form.FieldRadio',
'many2one' : 'instance.web.form.FieldMany2One',
'many2onebutton' : 'instance.web.form.Many2OneButton',
'many2many' : 'instance.web.form.FieldMany2Many',
<td>
<p>
<t t-js="d">
- var message = d.message ? d.message : d.error.data.fault_code;
+ var message = d.message ? d.message : d.error.data.message;
d.html_error = context.engine.tools.html_escape(message)
.replace(/\n/g, '<br/>');
</t>
<tr t-name="TreeView.rows"
t-foreach="records" t-as="record"
t-att-id="'treerow_' + record.id"
- t-att-data-id="record.id" t-att-data-level="level + 1">
+ t-att-data-id="record.id" t-att-data-level="level + 1"
+ t-att-data-row-parent-id="row_parent_id">
<t t-set="children" t-value="record[children_field]"/>
<t t-set="class" t-value="children and children.length ? 'treeview-tr' : 'treeview-td'"/>
<t t-set="rank" t-value="'oe-treeview-first'"/>
</select>
</span>
</t>
+<t t-name="FieldRadio">
+ <span t-attf-class="oe_form_field oe_form_field_radio #{widget.options.horizontal ? 'oe_horizontal' : 'oe_vertical'}" t-att-style="widget.node.attrs.style">
+ <span t-if="!widget.get('effective_readonly')">
+ <t t-if="widget.options.horizontal">
+ <t t-set="width" t-value="Math.floor(100 / widget.selection.length)"/>
+ <t t-if="!widget.options.no_radiolabel">
+ <t t-foreach="widget.selection" t-as="selection">
+ <label t-att-for="widget.uniqueId + '_' + selection[0]" t-att-style="'width: ' + width + '%;'"><t t-esc="selection[1]"/></label>
+ </t>
+ <br/>
+ </t>
+ <t t-foreach="widget.selection" t-as="selection">
+ <div t-att-style="'width: ' + width + '%;'">
+ <span class="oe_radio_input"><input type="radio" t-att-name="widget.uniqueId" t-att-id="widget.uniqueId + '_' + selection[0]" t-att-value="selection[0]"/></span>
+ </div>
+ </t>
+ </t>
+ <t t-if="!widget.options.horizontal">
+ <t t-foreach="widget.selection" t-as="selection">
+ <div>
+ <span class="oe_radio_input"><input type="radio" t-att-id="widget.uniqueId + '_' + selection[0]" t-att-name="widget.uniqueId" t-att-value="selection[0]"/></span><label t-if="!widget.options.no_radiolabel" t-att-for="widget.uniqueId + '_' + selection[0]"><t t-esc="selection[1]"/></label>
+ </div>
+ </t>
+ </t>
+ </span>
+ <span t-if="widget.get('effective_readonly')" class="oe_radio_readonly"><t t-esc="widget.get('value')[1]"/></span>
+ </span>
+</t>
<t t-name="FieldMany2One">
<span class="oe_form_field oe_form_field_many2one oe_form_field_with_button" t-att-style="widget.node.attrs.style">
<t t-if="widget.get('effective_readonly')">
</t>
<t t-name="FieldBinaryFileUploader.files">
<div class="oe_attachments">
- <t t-if="widget.get('value')">
- <t t-if="!widget.get('effective_readonly')" t-foreach="widget.get('value')" t-as="file">
+ <t t-if="!widget.get('effective_readonly')">
+ <t t-foreach="widget.get('value')" t-as="id">
+ <t t-set="file" t-value="widget.data[id]"/>
<div class="oe_attachment">
<span t-if="(file.upload or file.percent_loaded<100)" t-attf-title="{(file.name || file.filename) + (file.date?' \n('+file.date+')':'' )}" t-attf-name="{file.name || file.filename}">
<span class="oe_fileuploader_in_process">...Upload in progress...</span>
</t>
</div>
</t>
- <t t-if="widget.get('effective_readonly')" t-foreach="widget.get('value')" t-as="file">
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <t t-foreach="widget.get('value')" t-as="id">
+ <t t-set="file" t-value="widget.data[id]"/>
<div>
<a t-att-href="file.url" t-attf-title="{(file.name || file.filename) + (file.date?' \n('+file.date+')':'' )}">
<t t-raw="file.name || file.filename"/>
<ul class="oe_searchview_custom_list"/>
<div class="oe_searchview_custom">
<h4>Save current filter</h4>
- <form>
- <p><input id="oe_searchview_custom_input" placeholder="Filter name"/></p>
+ <form class="oe_form">
+ <p class="oe_form_required"><input id="oe_searchview_custom_input" placeholder="Filter name"/></p>
<p>
<input id="oe_searchview_custom_public" type="checkbox"/>
<label for="oe_searchview_custom_public">Share with all users</label>
@charset "utf-8";
.openerp .oe_kanban_view {
background: white;
- height: inherit;
-}
-.openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_dummy_cell {
- background: url(/web/static/src/img/form_sheetbg.png);
- width: 100%;
-}
-.openerp .oe_kanban_view .oe_kanban_group_length {
- text-align: center;
- display: none;
-}
-.openerp .oe_kanban_view .oe_kanban_group_length .oe_tag {
- position: relative;
- top: 8px;
- font-weight: bold;
-}
-.openerp .oe_kanban_view .oe_kanban_header:hover .oe_kanban_group_length {
- display: none;
-}
-.openerp .oe_kanban_view .ui-sortable-placeholder {
- border: 1px solid rgba(0, 0, 0, 0.1);
- visibility: visible !important;
-}
-.openerp .oe_kanban_view .ui-sortable-helper {
- -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
- -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
- -box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
- -moz-transform: rotate(3deg);
- -webkit-transform: rotate(3deg);
- -o-transform: rotate(3deg);
- -ms-transform: rotate(3deg);
- -webkit-transition: -webkit-transform 100ms linear;
- -moz-transition: -moz-transform 100ms linear;
- transition: transform 100ms linear;
-}
-.openerp .oe_kanban_view .oe_kanban_left {
- float: left;
-}
-.openerp .oe_kanban_view .oe_kanban_right {
- float: right;
-}
-.openerp .oe_kanban_view .oe_kanban_clear {
- clear: both;
-}
-.openerp .oe_kanban_view .oe_kanban_content {
- word-wrap: break-word;
-}
-.openerp .oe_kanban_view .oe_kanban_content .oe_star_on, .openerp .oe_kanban_view .oe_kanban_content .oe_star_off {
- color: #cccccc;
- text-shadow: 0 0 2px black;
- vertical-align: top;
- position: relative;
- top: -5px;
-}
-.openerp .oe_kanban_view .oe_kanban_content .oe_star_on:hover, .openerp .oe_kanban_view .oe_kanban_content .oe_star_off:hover {
- text-decoration: none;
-}
-.openerp .oe_kanban_view .oe_kanban_content .oe_star_on {
- color: gold;
-}
-.openerp .oe_kanban_view .oe_kanban_content div:first-child {
- margin-right: 16px;
-}
-.openerp .oe_kanban_view .oe_kanban_button_new {
- color: white;
- background: #dc5f59;
-}
-.openerp .oe_kanban_view .oe_kanban_groups {
- height: inherit;
-}
-.openerp .oe_kanban_view.oe_kanban_ungrouped .oe_kanban_groups {
- width: 100%;
-}
-.openerp .oe_kanban_view.oe_kanban_grouped_by_m2o .oe_kanban_group_title {
- cursor: move;
-}
-.openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_kanban {
- float: right;
-}
-.openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_kanban > span {
- visibility: hidden;
-}
-.openerp .oe_kanban_view .oe_kanban_header:hover .oe_dropdown_kanban > span {
- visibility: visible;
-}
-.openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_menu {
- font-weight: normal;
- font-size: 13px;
-}
-.openerp .oe_kanban_view .oe_kanban_group_title {
- position: relative;
- font-size: 16px;
- font-weight: bold;
- color: #333333;
- text-shadow: 0 1px 0 white;
- margin-right: 30px;
- width: 200px;
-}
-.openerp .oe_kanban_view .oe_kanban_group_title .oe_kanban_group_title_text {
- margin-right: 4px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-.openerp .oe_kanban_view .oe_fold_column .oe_kanban_group_length {
- position: absolute;
- top: -1px;
- right: -14px;
- float: right;
- display: block;
-}
-.openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_column, .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_group_header {
- width: 185px;
- min-width: 185px;
-}
-.openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_column.oe_kanban_group_folded, .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_group_header.oe_kanban_group_folded {
- width: auto;
- min-width: 30px;
-}
-.openerp .oe_kanban_view .oe_kanban_column, .openerp .oe_kanban_view .oe_kanban_group_header {
- vertical-align: top;
- padding: 5px 5px 5px 4px;
-}
-.openerp .oe_kanban_view .oe_kanban_column ul, .openerp .oe_kanban_view .oe_kanban_column li, .openerp .oe_kanban_view .oe_kanban_group_header ul, .openerp .oe_kanban_view .oe_kanban_group_header li {
- margin: 0;
- padding: 0;
- list-style-type: none;
-}
-.openerp .oe_kanban_view .oe_kanban_group_header.oe_kanban_no_group {
- padding: 0px;
-}
-.openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_column, .openerp .oe_kanban_view .oe_kanban_group_header {
- background: #f0eeee;
- border-left: 1px solid #f0f8f8;
- border-right: 1px solid #b9b9b9;
-}
-.openerp .oe_kanban_view .oe_form .oe_kanban_column {
- padding: 0px;
- background: white;
-}
-.openerp .oe_kanban_view .oe_kanban_column, .openerp .oe_kanban_view .oe_kanban_column_cards {
- height: 100%;
-}
-.openerp .oe_kanban_view .oe_kanban_aggregates {
- padding: 0;
- margin: 0px;
-}
-.openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_title, .openerp .oe_kanban_view .oe_kanban_group_folded.oe_kanban_column *, .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_aggregates, .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_add {
- display: none;
-}
-.openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_title_vertical, .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_length {
- display: block;
-}
-.openerp .oe_kanban_view .oe_kanban_group_folded .oe_dropdown_kanban {
- left: -5px;
-}
-.openerp .oe_kanban_view .oe_kanban_group_title_undefined {
- color: #666666;
-}
-.openerp .oe_kanban_view .oe_kanban_group_title_vertical {
- writing-mode: tb-rl;
- -webkit-transform: rotate(90deg);
- -moz-transform: rotate(90deg);
- -o-transform: rotate(90deg);
- -ms-transform: rotate(90deg);
- transform: rotate(90deg);
- width: 30px;
- font-size: 24px;
- white-space: nowrap;
- display: none;
- position: relative;
- opacity: 0.75;
- top: 26px;
-}
-.openerp .oe_kanban_view .oe_kanban_add, .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_toggle {
- margin-left: 4px;
- cursor: pointer;
- position: relative;
-}
-.openerp .oe_kanban_view .oe_kanban_add {
- top: -8px;
-}
-.openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_toggle {
- top: -2px;
- height: 14px;
-}
-.openerp .oe_kanban_view .oe_kanban_card, .openerp .oe_kanban_view .oe_dropdown_toggle {
- cursor: pointer;
- display: inline-block;
-}
-.openerp .oe_kanban_view .oe_kanban_add {
- float: right;
-}
-.openerp .oe_kanban_view .oe_kanban_quick_create_buttons {
- margin: 4px 0;
-}
-.openerp .oe_kanban_view .oe_kanban_no_group .oe_kanban_quick_create {
- width: 185px;
- padding: 10px;
-}
-.openerp .oe_kanban_view .oe_kanban_quick_create input {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- outline: none;
- border: 1px solid transparent;
- display: block;
- margin-bottom: 8px;
- font-size: 13px;
- width: 100%;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -box-shadow: none;
-}
-.openerp .oe_kanban_view .oe_kanban_quick_create input:focus {
- border: 1px solid #a6a6fe;
- -moz-box-shadow: 0px 0px 7px rgba(0, 133, 255, 0.3) inset;
- -webkit-box-shadow: 0px 0px 7px rgba(0, 133, 255, 0.3) inset;
- -box-shadow: 0px 0px 7px rgba(0, 133, 255, 0.3) inset;
-}
-.openerp .oe_kanban_view .oe_kanban_vignette {
- padding: 8px;
- min-height: 100px;
-}
-.openerp .oe_kanban_view .oe_kanban_image {
- display: inline-block;
- vertical-align: top;
- width: 64px;
- height: 64px;
- text-align: center;
- overflow: hidden;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
-}
-.openerp .oe_kanban_view .oe_kanban_details {
- display: inline-block;
- vertical-align: top;
- width: 240px;
- font-size: 13px;
- padding: 0 5px;
- color: #4c4c4c;
-}
-.openerp .oe_kanban_view .oe_kanban_details h4 {
- margin: 0 0 4px 0;
-}
-.openerp .oe_kanban_view .oe_kanban_details .oe_tag {
- display: inline-block;
- margin: 0 2px 2px 0;
-}
-.openerp .oe_kanban_view .oe_kanban_record {
- position: relative;
- display: block;
- min-height: 20px;
- margin: 0;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
-}
-.openerp .oe_kanban_view .oe_kanban_record:last-child {
- margin-bottom: 0;
-}
-.openerp .oe_kanban_view .oe_kanban_record .oe_kanban_title {
- font-weight: bold;
- margin: 2px 4px;
-}
-.openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_record {
- margin-bottom: 4px;
-}
-.openerp .oe_kanban_view .oe_kanban_avatar_smallbox {
- height: 40px;
- width: 40px;
- border: 1px solid;
- border-color: #e5e5e5 #dbdbdb #d2d2d2;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
- -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-}
-.openerp .oe_kanban_view .oe_kanban_box {
- background: white;
- border: 2px solid #cccccc;
- border-radius: 4px;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- margin-bottom: 5px;
-}
-.openerp .oe_kanban_view .oe_kanban_box_header {
- border-bottom: 1px solid #cccccc;
-}
-.openerp .oe_kanban_view .oe_kanban_title {
- font-size: 95%;
- font-weight: bold;
- padding: 0 4px 0 4px;
-}
-.openerp .oe_kanban_view .oe_kanban_small {
- font-size: 80%;
- font-weight: normal;
-}
-.openerp .oe_kanban_view .oe_kanban_show_more {
- clear: both;
- text-align: center;
-}
-.openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_show_more .oe_button {
- width: 100%;
-}
-.openerp .oe_kanban_view.oe_kanban_ungrouped .oe_kanban_column .oe_kanban_record {
- display: inline-block;
- padding: 2px;
- vertical-align: top;
- box-sizing: border-box;
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
-}
-.openerp .oe_kanban_view .oe_kanban_action_button {
- height: 22px;
- margin: 0;
-}
-.openerp .oe_kanban_view .oe_kanban_action_a {
- text-decoration: none;
-}
-.openerp .oe_kanban_view .oe_kanban_action_a:hover {
- text-decoration: none;
-}
-.openerp .oe_kanban_view .oe_kanban_table {
- width: 100%;
- border: none;
- border-collapse: collapse;
- margin: 0;
- padding: 0;
-}
-.openerp .oe_kanban_view .oe_kanban_table tr td {
- padding: 0;
-}
-.openerp .oe_kanban_view .oe_kanban_table tr td.oe_kanban_title {
- padding: 2px;
-}
-.openerp .oe_kanban_view .oe_kanban_box_content {
- padding: 4px;
- font-size: 90%;
-}
-.openerp .oe_kanban_view .oe_kanban_button {
- border: 1px solid #8ec1da;
- background-color: #ddeef6;
- border-radius: 3px;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- color: black;
- text-shadow: 0 1px white;
- padding: 0 4px;
- font-size: 85%;
- margin: 1px;
-}
-.openerp .oe_kanban_view a.oe_kanban_button:hover, .openerp .oe_kanban_view .openerp button.oe_kanban_button:hover {
- background-color: #eeddf6;
-}
-.openerp .oe_kanban_view .oe_kanban_buttons_set {
- border-top: 1px dotted;
- white-space: nowrap;
- padding-top: 2px;
- position: relative;
- clear: both;
-}
-.openerp .oe_kanban_view .oe_kanban_buttons_set a {
- padding: 2px;
-}
-.openerp .oe_kanban_view .oe_kanban_box_show_onclick {
- display: none;
-}
-.openerp .oe_kanban_view .oe_kanban_draghandle {
- cursor: move;
-}
-.openerp .oe_kanban_view .oe_kanban_color_border {
- border-color: #cccccc;
-}
-.openerp .oe_kanban_view .oe_kanban_color_border {
- border-color: #cccccc;
-}
-.openerp .oe_kanban_view .oe_kanban_tooltip ul, .openerp .oe_kanban_view ul.oe_kanban_tooltip {
- padding: 0 0 4px 0;
- margin: 5px 0 0 15px;
- list-style: circle;
-}
-.openerp .oe_kanban_view .oe_kanban_highlight {
- border-radius: 2px;
- -moz-border-radius: 2px;
- -webkit-border-radius: 2px;
- padding: 1px 5px;
- margin: 1px 4px;
- white-space: nowrap;
- display: inline-block;
- line-height: 1em;
-}
-.openerp .oe_kanban_view .oe_kanban_card, .openerp .oe_kanban_view .oe_kanban_quick_create {
- margin-bottom: 4px;
- position: relative;
- display: block;
- background: white;
- border: 1px solid rgba(0, 0, 0, 0.16);
- border-bottom-color: rgba(0, 0, 0, 0.3);
- padding: 5px;
- display: block;
- -webkit-transition: -webkit-transform, -webkit-box-shadow, border 200ms linear;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
-}
-.openerp .oe_kanban_view .oe_kanban_card:not(.ui-sortable-helper):hover, .openerp .oe_kanban_view .oe_kanban_quick_create:not(.ui-sortable-helper):hover {
- border: 1px solid #7c7bad;
- -moz-box-shadow: 0 0 4px #7c7bad;
- -webkit-box-shadow: 0 0 4px #7c7bad;
- -box-shadow: 0 0 4px #7c7bad;
-}
-.openerp .oe_kanban_view .oe_kanban_card:not(.ui-sortable-helper):hover .oe_dropdown_kanban > span, .openerp .oe_kanban_view .oe_kanban_quick_create:not(.ui-sortable-helper):hover .oe_dropdown_kanban > span {
- visibility: visible;
-}
-.openerp .oe_kanban_view .oe_kanban_card h3, .openerp .oe_kanban_view .oe_kanban_quick_create h3 {
- margin: 0 16px 0 0;
- color: #4c4c4c;
- text-decoration: none;
-}
-.openerp .oe_kanban_view .oe_kanban_card h3:hover, .openerp .oe_kanban_view .oe_kanban_quick_create h3:hover {
- text-decoration: none;
-}
-.openerp .oe_kanban_view .oe_kanban_card .oe_dropdown_kanban .oe_kanban_project_times li, .openerp .oe_kanban_view .oe_kanban_quick_create .oe_dropdown_kanban .oe_kanban_project_times li {
- float: left;
-}
-.openerp .oe_kanban_view .oe_kanban_star {
- float: left;
- position: inline-block;
- margin: 0 4px 0 0;
-}
-.openerp .oe_kanban_view .oe_kanban_avatar {
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
- -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-}
-.openerp .oe_kanban_view .oe_kanban_footer_left {
- margin-top: 2px;
-}
-.openerp .oe_kanban_view .oe_kanban_footer_left > span {
- margin-top: 2px;
- display: inline-block;
- background: #e6e6e6;
- border: 1px solid #b9b9b9;
- color: #666666;
- padding: 0 2px;
- line-height: 16px;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
-}
-.openerp .oe_kanban_view .oe_kanban_footer_left > span .oe_e {
- line-height: 12px;
- font-size: 22px;
-}
-.openerp .oe_kanban_view .oe_kanban_footer_left .oe_tags {
- margin-right: 0;
-}
-.openerp .oe_kanban_view .oe_kanban_footer_left .oe_tags .oe_tag {
- display: inline-block;
- padding: 0 2px;
- line-height: 14px;
-}
-.openerp .oe_kanban_view .oe_kanban_footer_left .oe_kanban_mail_new {
- line-height: 18px;
- background-color: #8a89ba;
- color: white;
- font-weight: bold;
- position: relative;
- top: -1px;
-}
-.openerp .oe_kanban_view .oe_kanban_bottom_right {
- float: right;
- position: relative;
- top: 2px;
-}
-.openerp .oe_kanban_view .oe_kanban_status {
- position: relative;
- top: 4px;
- display: inline-block;
- height: 12px;
- width: 12px;
- -moz-border-radius: 6px;
- -webkit-border-radius: 6px;
- border-radius: 6px;
- background-position: center center;
- background-image: -webkit-radial-gradient(circle, #eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
- background-image: -moz-radial-gradient(#eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
- background-image: -ms-radial-gradient(#eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
- background-image: radial-gradient(#eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
-}
-.openerp .oe_kanban_view .oe_kanban_status_green {
- background: green;
- background-position: center center;
- background-image: -webkit-radial-gradient(circle, #55dd55 0%, #44aa44 40%, #339933 100%);
- background-image: -moz-radial-gradient(#55dd55 0%, #44aa44 40%, #339933 100%);
- background-image: -ms-radial-gradient(#55dd55 0%, #44aa44 40%, #339933 100%);
- background-image: radial-gradient(#55dd55 0%, #44aa44 40%, #339933 100%);
-}
-.openerp .oe_kanban_view .oe_kanban_status_red {
- background: red;
- background-position: center center;
- background-image: -webkit-radial-gradient(circle, #ee7777 0%, #cc3333 40%, #bb0808 100%);
- background-image: -moz-radial-gradient(#ee7777 0%, #cc3333 40%, #bb0808 100%);
- background-image: -ms-radial-gradient(#ee7777 0%, #cc3333 40%, #bb0808 100%);
- background-image: radial-gradient(#ee7777 0%, #cc3333 40%, #bb0808 100%);
-}
-.openerp .oe_kanban_view .oe_kanban_text_red {
- color: #a61300;
- font-weight: bold;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
-}
-.openerp .oe_kanban_view .oe_kanban_ellipsis {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-.openerp .oe_kanban_view .oe_dropdown_kanban {
- float: right;
- cursor: pointer;
- margin-top: -6px;
-}
-.openerp .oe_kanban_view .oe_dropdown_kanban:hover {
- text-decoration: none;
-}
-.openerp .oe_kanban_view .oe_dropdown_kanban .oe_dropdown_menu {
- left: 0;
- top: 28px;
- min-width: 160px;
- padding: 2px;
-}
-.openerp .oe_kanban_view .oe_dropdown_kanban .oe_dropdown_menu > li {
- padding: 3px;
-}
-.openerp .oe_kanban_view .oe_dropdown_kanban.oe_opened > span {
- visibility: visible;
-}
-.openerp .oe_kanban_view .oe_dropdown_kanban > span {
- visibility: hidden;
-}
-.openerp .oe_kanban_view .oe_kanban_colorpicker {
- white-space: nowrap;
-}
-.openerp .oe_kanban_view .oe_kanban_colorpicker li {
- float: left;
- margin: 0;
- padding: 0;
-}
-.openerp .oe_kanban_view .oe_kanban_colorpicker li a {
- display: inline-block;
- width: 16px;
- height: 16px;
- border: 1px solid white;
-}
-.openerp .oe_kanban_view .oe_kanban_colorpicker li a:hover {
- border: 1px solid gray !important;
-}
-.openerp .oe_kanban_view .oe_kanban_colorpicker li:first-child a {
- border: 1px solid #cccccc;
-}
-.openerp .oe_kanban_view .oe_kanban_color_0 {
- background-color: white;
- color: #5a5a5a;
-}
-.openerp .oe_kanban_view .oe_kanban_color_1 {
- background-color: #cccccc;
- color: #424242;
-}
-.openerp .oe_kanban_view .oe_kanban_color_2 {
- background-color: #ffc7c7;
- color: #7a3737;
-}
-.openerp .oe_kanban_view .oe_kanban_color_3 {
- background-color: #fff1c7;
- color: #756832;
-}
-.openerp .oe_kanban_view .oe_kanban_color_4 {
- background-color: #e3ffc7;
- color: #5d6937;
-}
-.openerp .oe_kanban_view .oe_kanban_color_5 {
- background-color: #c7ffd5;
- color: #1a7759;
-}
-.openerp .oe_kanban_view .oe_kanban_color_6 {
- background-color: #c7ffff;
- color: #1a5d83;
-}
-.openerp .oe_kanban_view .oe_kanban_color_7 {
- background-color: #c7d5ff;
- color: #3b3e75;
-}
-.openerp .oe_kanban_view .oe_kanban_color_8 {
- background-color: #e3c7ff;
- color: #4c3668;
-}
-.openerp .oe_kanban_view .oe_kanban_color_9 {
- background-color: #ffc7f1;
- color: #6d2c70;
-}
+ height: inherit; }
+ .openerp .oe_kanban_view .oe_view_nocontent {
+ position: relative;
+ z-index: 1;
+ max-width: none;
+ height: 100%; }
+ .openerp .oe_kanban_view .oe_view_nocontent .oe_view_nocontent_content {
+ margin-left: 90px;
+ margin-top: 5px;
+ max-width: 700px; }
+ .openerp .oe_kanban_view .oe_view_nocontent .oe_view_nocontent_bg {
+ background: #eeeeee;
+ opacity: 0.7;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: -1; }
+ .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_dummy_cell {
+ background: url(/web/static/src/img/form_sheetbg.png);
+ width: 100%; }
+ .openerp .oe_kanban_view .oe_kanban_group_length {
+ text-align: center;
+ display: none; }
+ .openerp .oe_kanban_view .oe_kanban_group_length .oe_tag {
+ position: relative;
+ top: 8px;
+ font-weight: bold; }
+ .openerp .oe_kanban_view .oe_kanban_header:hover .oe_kanban_group_length {
+ display: none; }
+ .openerp .oe_kanban_view .ui-sortable-placeholder {
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ visibility: visible !important; }
+ .openerp .oe_kanban_view .ui-sortable-helper {
+ -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
+ -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
+ -box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
+ -moz-transform: rotate(3deg);
+ -webkit-transform: rotate(3deg);
+ -o-transform: rotate(3deg);
+ -ms-transform: rotate(3deg);
+ -webkit-transition: -webkit-transform 100ms linear;
+ -moz-transition: -moz-transform 100ms linear;
+ transition: transform 100ms linear; }
+ .openerp .oe_kanban_view .oe_kanban_left {
+ float: left; }
+ .openerp .oe_kanban_view .oe_kanban_right {
+ float: right; }
+ .openerp .oe_kanban_view .oe_kanban_clear {
+ clear: both; }
+ .openerp .oe_kanban_view .oe_kanban_content {
+ word-wrap: break-word; }
+ .openerp .oe_kanban_view .oe_kanban_content .oe_star_on, .openerp .oe_kanban_view .oe_kanban_content .oe_star_off {
+ color: #cccccc;
+ text-shadow: 0 0 2px black;
+ vertical-align: top;
+ position: relative;
+ top: -5px; }
+ .openerp .oe_kanban_view .oe_kanban_content .oe_star_on:hover, .openerp .oe_kanban_view .oe_kanban_content .oe_star_off:hover {
+ text-decoration: none; }
+ .openerp .oe_kanban_view .oe_kanban_content .oe_star_on {
+ color: gold; }
+ .openerp .oe_kanban_view .oe_kanban_content div:first-child {
+ margin-right: 16px; }
+ .openerp .oe_kanban_view .oe_kanban_button_new {
+ color: white;
+ background: #dc5f59; }
+ .openerp .oe_kanban_view .oe_kanban_groups {
+ height: inherit; }
+ .openerp .oe_kanban_view.oe_kanban_ungrouped .oe_kanban_groups {
+ width: 100%; }
+ .openerp .oe_kanban_view.oe_kanban_grouped_by_m2o .oe_kanban_group_title {
+ cursor: move; }
+ .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_kanban {
+ float: right; }
+ .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_kanban > span {
+ visibility: hidden; }
+ .openerp .oe_kanban_view .oe_kanban_header:hover .oe_dropdown_kanban > span {
+ visibility: visible; }
+ .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_menu {
+ font-weight: normal;
+ font-size: 13px; }
+ .openerp .oe_kanban_view .oe_kanban_group_title {
+ position: relative;
+ font-size: 16px;
+ font-weight: bold;
+ color: #333333;
+ text-shadow: 0 1px 0 white;
+ margin-right: 30px;
+ width: 200px; }
+ .openerp .oe_kanban_view .oe_kanban_group_title .oe_kanban_group_title_text {
+ margin-right: 4px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis; }
+ .openerp .oe_kanban_view .oe_fold_column .oe_kanban_group_length {
+ position: absolute;
+ top: -1px;
+ right: -14px;
+ float: right;
+ display: block; }
+ .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_column, .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_group_header {
+ width: 185px;
+ min-width: 185px; }
+ .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_column.oe_kanban_group_folded, .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_group_header.oe_kanban_group_folded {
+ width: auto;
+ min-width: 30px; }
+ .openerp .oe_kanban_view .oe_kanban_column, .openerp .oe_kanban_view .oe_kanban_group_header {
+ vertical-align: top;
+ padding: 5px 5px 5px 4px; }
+ .openerp .oe_kanban_view .oe_kanban_column ul, .openerp .oe_kanban_view .oe_kanban_column li, .openerp .oe_kanban_view .oe_kanban_group_header ul, .openerp .oe_kanban_view .oe_kanban_group_header li {
+ margin: 0;
+ padding: 0;
+ list-style-type: none; }
+ .openerp .oe_kanban_view .oe_kanban_group_header.oe_kanban_no_group {
+ padding: 0px; }
+ .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_column, .openerp .oe_kanban_view .oe_kanban_group_header {
+ background: #f0eeee;
+ border-left: 1px solid #f0f8f8;
+ border-right: 1px solid #b9b9b9; }
+ .openerp .oe_kanban_view .oe_form .oe_kanban_column {
+ padding: 0px;
+ background: white; }
+ .openerp .oe_kanban_view .oe_kanban_column, .openerp .oe_kanban_view .oe_kanban_column_cards {
+ height: 100%; }
+ .openerp .oe_kanban_view .oe_kanban_aggregates {
+ padding: 0;
+ margin: 0px; }
+ .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_title, .openerp .oe_kanban_view .oe_kanban_group_folded.oe_kanban_column *, .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_aggregates, .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_add {
+ display: none; }
+ .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_title_vertical, .openerp .oe_kanban_view .oe_kanban_group_folded .oe_kanban_group_length {
+ display: block; }
+ .openerp .oe_kanban_view .oe_kanban_group_folded .oe_dropdown_kanban {
+ left: -5px; }
+ .openerp .oe_kanban_view .oe_kanban_group_title_undefined {
+ color: #666666; }
+ .openerp .oe_kanban_view .oe_kanban_group_title_vertical {
+ writing-mode: tb-rl;
+ -webkit-transform: rotate(90deg);
+ -moz-transform: rotate(90deg);
+ -o-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+ width: 30px;
+ font-size: 24px;
+ white-space: nowrap;
+ display: none;
+ position: relative;
+ opacity: 0.75;
+ top: 26px; }
+ .openerp .oe_kanban_view .oe_kanban_add, .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_toggle {
+ margin-left: 4px;
+ cursor: pointer;
+ position: relative; }
+ .openerp .oe_kanban_view .oe_kanban_add {
+ top: -8px;
+ z-index: 2; }
+ .openerp .oe_kanban_view .oe_kanban_header .oe_dropdown_toggle {
+ top: -2px;
+ height: 14px; }
+ .openerp .oe_kanban_view .oe_kanban_card, .openerp .oe_kanban_view .oe_dropdown_toggle {
+ cursor: pointer;
+ display: inline-block; }
+ .openerp .oe_kanban_view .oe_kanban_add {
+ float: right; }
+ .openerp .oe_kanban_view .oe_kanban_quick_create_buttons {
+ margin: 4px 0; }
+ .openerp .oe_kanban_view .oe_kanban_no_group .oe_kanban_quick_create {
+ width: 185px;
+ padding: 10px; }
+ .openerp .oe_kanban_view .oe_kanban_quick_create {
+ z-index: 2; }
+ .openerp .oe_kanban_view .oe_kanban_quick_create input {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ outline: none;
+ border: 1px solid transparent;
+ display: block;
+ margin-bottom: 8px;
+ font-size: 13px;
+ width: 100%;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ -box-shadow: none; }
+ .openerp .oe_kanban_view .oe_kanban_quick_create input:focus {
+ border: 1px solid #a6a6fe;
+ -moz-box-shadow: 0px 0px 7px rgba(0, 133, 255, 0.3) inset;
+ -webkit-box-shadow: 0px 0px 7px rgba(0, 133, 255, 0.3) inset;
+ -box-shadow: 0px 0px 7px rgba(0, 133, 255, 0.3) inset; }
+ .openerp .oe_kanban_view .oe_kanban_vignette {
+ padding: 8px;
+ min-height: 100px; }
+ .openerp .oe_kanban_view .oe_kanban_image {
+ display: inline-block;
+ vertical-align: top;
+ width: 64px;
+ height: 64px;
+ text-align: center;
+ overflow: hidden;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
+ -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); }
+ .openerp .oe_kanban_view .oe_kanban_details {
+ display: inline-block;
+ vertical-align: top;
+ width: 240px;
+ font-size: 13px;
+ padding: 0 5px;
+ color: #4c4c4c; }
+ .openerp .oe_kanban_view .oe_kanban_details h4 {
+ margin: 0 0 4px 0; }
+ .openerp .oe_kanban_view .oe_kanban_details .oe_tag {
+ display: inline-block;
+ margin: 0 2px 2px 0; }
+ .openerp .oe_kanban_view .oe_kanban_record {
+ position: relative;
+ display: block;
+ min-height: 20px;
+ margin: 0;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px; }
+ .openerp .oe_kanban_view .oe_kanban_record:last-child {
+ margin-bottom: 0; }
+ .openerp .oe_kanban_view .oe_kanban_record .oe_kanban_title {
+ font-weight: bold;
+ margin: 2px 4px; }
+ .openerp .oe_kanban_view .oe_kanban_record .oe_kanban_alias {
+ margin: 0px 0 8px 0; }
+ .openerp .oe_kanban_view .oe_kanban_record .oe_kanban_alias .oe_e {
+ font-size: 30px;
+ line-height: 6px;
+ vertical-align: top;
+ margin-right: 3px;
+ color: white;
+ text-shadow: 0px 0px 2px black;
+ float: left; }
+ .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_record {
+ margin-bottom: 4px; }
+ .openerp .oe_kanban_view .oe_kanban_avatar_smallbox {
+ height: 40px;
+ width: 40px;
+ border: 1px solid;
+ border-color: #e5e5e5 #dbdbdb #d2d2d2;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); }
+ .openerp .oe_kanban_view .oe_kanban_box {
+ background: white;
+ border: 2px solid #cccccc;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ margin-bottom: 5px; }
+ .openerp .oe_kanban_view .oe_kanban_box_header {
+ border-bottom: 1px solid #cccccc; }
+ .openerp .oe_kanban_view .oe_kanban_title {
+ font-size: 95%;
+ font-weight: bold;
+ padding: 0 4px 0 4px; }
+ .openerp .oe_kanban_view .oe_kanban_small {
+ font-size: 80%;
+ font-weight: normal; }
+ .openerp .oe_kanban_view .oe_kanban_show_more {
+ clear: both;
+ text-align: center; }
+ .openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_show_more .oe_button {
+ width: 100%; }
+ .openerp .oe_kanban_view.oe_kanban_ungrouped .oe_kanban_column .oe_kanban_record {
+ display: inline-block;
+ padding: 2px;
+ vertical-align: top;
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box; }
+ .openerp .oe_kanban_view .oe_kanban_action_button {
+ height: 22px;
+ margin: 0; }
+ .openerp .oe_kanban_view .oe_kanban_action_a {
+ text-decoration: none; }
+ .openerp .oe_kanban_view .oe_kanban_action_a:hover {
+ text-decoration: none; }
+ .openerp .oe_kanban_view .oe_kanban_table {
+ width: 100%;
+ border: none;
+ border-collapse: collapse;
+ margin: 0;
+ padding: 0; }
+ .openerp .oe_kanban_view .oe_kanban_table tr td {
+ padding: 0; }
+ .openerp .oe_kanban_view .oe_kanban_table tr td.oe_kanban_title {
+ padding: 2px; }
+ .openerp .oe_kanban_view .oe_kanban_box_content {
+ padding: 4px;
+ font-size: 90%; }
+ .openerp .oe_kanban_view .oe_kanban_button {
+ border: 1px solid #8ec1da;
+ background-color: #ddeef6;
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ color: black;
+ text-shadow: 0 1px white;
+ padding: 0 4px;
+ font-size: 85%;
+ margin: 1px; }
+ .openerp .oe_kanban_view a.oe_kanban_button:hover, .openerp .oe_kanban_view .openerp button.oe_kanban_button:hover {
+ background-color: #eeddf6; }
+ .openerp .oe_kanban_view .oe_kanban_buttons_set {
+ border-top: 1px dotted;
+ white-space: nowrap;
+ padding-top: 2px;
+ position: relative;
+ clear: both; }
+ .openerp .oe_kanban_view .oe_kanban_buttons_set a {
+ padding: 2px; }
+ .openerp .oe_kanban_view .oe_kanban_box_show_onclick {
+ display: none; }
+ .openerp .oe_kanban_view .oe_kanban_draghandle {
+ cursor: move; }
+ .openerp .oe_kanban_view .oe_kanban_color_border {
+ border-color: #cccccc; }
+ .openerp .oe_kanban_view .oe_kanban_color_border {
+ border-color: #cccccc; }
+ .openerp .oe_kanban_view .oe_kanban_tooltip ul, .openerp .oe_kanban_view ul.oe_kanban_tooltip {
+ padding: 0 0 4px 0;
+ margin: 5px 0 0 15px;
+ list-style: circle; }
+ .openerp .oe_kanban_view .oe_kanban_highlight {
+ border-radius: 2px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ padding: 1px 5px;
+ margin: 1px 4px;
+ white-space: nowrap;
+ display: inline-block;
+ line-height: 1em; }
+ .openerp .oe_kanban_view .oe_kanban_card, .openerp .oe_kanban_view .oe_kanban_quick_create {
+ margin-bottom: 4px;
+ position: relative;
+ display: block;
+ background: white;
+ border: 1px solid rgba(0, 0, 0, 0.16);
+ border-bottom-color: rgba(0, 0, 0, 0.3);
+ padding: 5px;
+ display: block;
+ -webkit-transition: -webkit-transform, -webkit-box-shadow, border 200ms linear;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px; }
+ .openerp .oe_kanban_view .oe_kanban_card:not(.ui-sortable-helper):hover, .openerp .oe_kanban_view .oe_kanban_quick_create:not(.ui-sortable-helper):hover {
+ border: 1px solid #7c7bad;
+ -moz-box-shadow: 0 0 4px #7c7bad;
+ -webkit-box-shadow: 0 0 4px #7c7bad;
+ -box-shadow: 0 0 4px #7c7bad; }
+ .openerp .oe_kanban_view .oe_kanban_card:not(.ui-sortable-helper):hover .oe_dropdown_kanban > span, .openerp .oe_kanban_view .oe_kanban_quick_create:not(.ui-sortable-helper):hover .oe_dropdown_kanban > span {
+ visibility: visible; }
+ .openerp .oe_kanban_view .oe_kanban_card h3, .openerp .oe_kanban_view .oe_kanban_quick_create h3 {
+ margin: 0 16px 0 0;
+ color: #4c4c4c;
+ text-decoration: none; }
+ .openerp .oe_kanban_view .oe_kanban_card h3:hover, .openerp .oe_kanban_view .oe_kanban_quick_create h3:hover {
+ text-decoration: none; }
+ .openerp .oe_kanban_view .oe_kanban_card .oe_dropdown_kanban .oe_kanban_project_times li, .openerp .oe_kanban_view .oe_kanban_quick_create .oe_dropdown_kanban .oe_kanban_project_times li {
+ float: left; }
+ .openerp .oe_kanban_view .oe_kanban_star {
+ float: left;
+ position: inline-block;
+ margin: 0 4px 0 0; }
+ .openerp .oe_kanban_view .oe_kanban_avatar {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); }
+ .openerp .oe_kanban_view .oe_kanban_footer_left {
+ margin-top: 2px; }
+ .openerp .oe_kanban_view .oe_kanban_footer_left > span {
+ margin-top: 2px;
+ display: inline-block;
+ background: #e6e6e6;
+ border: 1px solid #b9b9b9;
+ color: #666666;
+ padding: 0 2px;
+ line-height: 16px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px; }
+ .openerp .oe_kanban_view .oe_kanban_footer_left > span .oe_e {
+ line-height: 12px;
+ font-size: 22px; }
+ .openerp .oe_kanban_view .oe_kanban_footer_left .oe_tags {
+ margin-right: 0; }
+ .openerp .oe_kanban_view .oe_kanban_footer_left .oe_tags .oe_tag {
+ display: inline-block;
+ padding: 0 2px;
+ line-height: 14px; }
+ .openerp .oe_kanban_view .oe_kanban_footer_left .oe_kanban_mail_new {
+ line-height: 18px;
+ background-color: #8a89ba;
+ color: white;
+ font-weight: bold;
+ position: relative;
+ top: -1px; }
+ .openerp .oe_kanban_view .oe_kanban_bottom_right {
+ float: right;
+ position: relative;
+ top: 2px; }
+ .openerp .oe_kanban_view .oe_kanban_status {
+ position: relative;
+ top: 4px;
+ display: inline-block;
+ height: 12px;
+ width: 12px;
+ -moz-border-radius: 6px;
+ -webkit-border-radius: 6px;
+ border-radius: 6px;
+ background-position: center center;
+ background-image: -webkit-radial-gradient(circle, #eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
+ background-image: -moz-radial-gradient(#eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
+ background-image: -ms-radial-gradient(#eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
+ background-image: radial-gradient(#eeeeee 0%, #cccccc 40%, #bbbbbb 100%); }
+ .openerp .oe_kanban_view .oe_kanban_status_green {
+ background: green;
+ background-position: center center;
+ background-image: -webkit-radial-gradient(circle, #55dd55 0%, #44aa44 40%, #339933 100%);
+ background-image: -moz-radial-gradient(#55dd55 0%, #44aa44 40%, #339933 100%);
+ background-image: -ms-radial-gradient(#55dd55 0%, #44aa44 40%, #339933 100%);
+ background-image: radial-gradient(#55dd55 0%, #44aa44 40%, #339933 100%); }
+ .openerp .oe_kanban_view .oe_kanban_status_red {
+ background: red;
+ background-position: center center;
+ background-image: -webkit-radial-gradient(circle, #ee7777 0%, #cc3333 40%, #bb0808 100%);
+ background-image: -moz-radial-gradient(#ee7777 0%, #cc3333 40%, #bb0808 100%);
+ background-image: -ms-radial-gradient(#ee7777 0%, #cc3333 40%, #bb0808 100%);
+ background-image: radial-gradient(#ee7777 0%, #cc3333 40%, #bb0808 100%); }
+ .openerp .oe_kanban_view .oe_kanban_text_red {
+ color: #a61300;
+ font-weight: bold;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px; }
+ .openerp .oe_kanban_view .oe_kanban_ellipsis {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap; }
+ .openerp .oe_kanban_view .oe_dropdown_kanban {
+ float: right;
+ cursor: pointer;
+ margin-top: -6px; }
+ .openerp .oe_kanban_view .oe_dropdown_kanban:hover {
+ text-decoration: none; }
+ .openerp .oe_kanban_view .oe_dropdown_kanban .oe_dropdown_menu {
+ left: 0;
+ top: 28px;
+ min-width: 160px;
+ padding: 2px; }
+ .openerp .oe_kanban_view .oe_dropdown_kanban .oe_dropdown_menu > li {
+ padding: 3px; }
+ .openerp .oe_kanban_view .oe_dropdown_kanban.oe_opened > span {
+ visibility: visible; }
+ .openerp .oe_kanban_view .oe_dropdown_kanban > span {
+ visibility: hidden; }
+ .openerp .oe_kanban_view .oe_kanban_colorpicker {
+ white-space: nowrap; }
+ .openerp .oe_kanban_view .oe_kanban_colorpicker li {
+ float: left;
+ margin: 0;
+ padding: 0; }
+ .openerp .oe_kanban_view .oe_kanban_colorpicker li a {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ border: 1px solid white; }
+ .openerp .oe_kanban_view .oe_kanban_colorpicker li a:hover {
+ border: 1px solid gray !important; }
+ .openerp .oe_kanban_view .oe_kanban_colorpicker li:first-child a {
+ border: 1px solid #cccccc; }
+ .openerp .oe_kanban_view .oe_kanban_color_0 {
+ background-color: white;
+ color: #5a5a5a; }
+ .openerp .oe_kanban_view .oe_kanban_color_1 {
+ background-color: #cccccc;
+ color: #424242; }
+ .openerp .oe_kanban_view .oe_kanban_color_2 {
+ background-color: #ffc7c7;
+ color: #7a3737; }
+ .openerp .oe_kanban_view .oe_kanban_color_3 {
+ background-color: #fff1c7;
+ color: #756832; }
+ .openerp .oe_kanban_view .oe_kanban_color_4 {
+ background-color: #e3ffc7;
+ color: #5d6937; }
+ .openerp .oe_kanban_view .oe_kanban_color_5 {
+ background-color: #c7ffd5;
+ color: #1a7759; }
+ .openerp .oe_kanban_view .oe_kanban_color_6 {
+ background-color: #c7ffff;
+ color: #1a5d83; }
+ .openerp .oe_kanban_view .oe_kanban_color_7 {
+ background-color: #c7d5ff;
+ color: #3b3e75; }
+ .openerp .oe_kanban_view .oe_kanban_color_8 {
+ background-color: #e3c7ff;
+ color: #4c3668; }
+ .openerp .oe_kanban_view .oe_kanban_color_9 {
+ background-color: #ffc7f1;
+ color: #6d2c70; }
.openerp .oe_form .oe_kanban_view .oe_kanban_column, .openerp .oe_form .oe_kanban_view .oe_kanban_group_header {
padding: 0px;
- background: white;
-}
+ background: white; }
.openerp .oe_popup_form .oe_kanban_buttons .oe_highlight {
color: #404040;
- background: none;
-}
+ background: none; }
.openerp .oe_popup_form .oe_kanban_buttons button.oe_highlight {
background-color: #efefef;
background-image: -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#d8d8d8));
background-image: linear-gradient(to bottom, #efefef, #d8d8d8);
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
-}
+ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; }
.openerp .oe_popup_form .oe_kanban_buttons button.oe_highlight:active {
background-color: #e3e3e3;
background-image: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6));
background-image: linear-gradient(to bottom, #e3e3e3, #f6f6f6);
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
-}
+ -box-shadow: none; }
.openerp .oe_popup_form .oe_kanban_buttons button.oe_highlight:hover {
background-color: #f6f6f6;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
background-image: linear-gradient(to bottom, #f6f6f6, #e3e3e3);
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
-}
+ -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset; }
.openerp_ie .oe_kanban_view .oe_kanban_group_header .oe_kanban_group_title_vertical {
- display: none !important;
-}
+ display: none !important; }
.openerp_ie .oe_kanban_view .oe_kanban_group_header.oe_kanban_group_folded .oe_kanban_group_title_vertical {
- display: inline-block !important;
-}
+ display: inline-block !important; }
.openerp_ie .oe_kanban_view .oe_kanban_group_title_vertical {
-ms-writing-mode: lr-tb !important;
-- background: #f0eeee;
- top: -5px !important; }
-}
++ background: #f0eeee;}
++
.openerp_ie .oe_kanban_view.oe_kanban_grouped .oe_kanban_group_header {
- height: 1%;
-}
+ height: 1%; }
@media print {
.openerp .oe_kanban_groups button {
- visibility: hidden;
- }
+ visibility: hidden; }
.openerp .oe_kanban_groups a[data-type=object], .openerp .oe_kanban_groups a[data-type=delete] {
- visibility: hidden;
- }
+ visibility: hidden; }
.openerp .oe_kanban_view .oe_kanban_group_title {
- text-shadow: none !important;
- }
-}
+ text-shadow: none !important; } }
//background: url(data:image/pngbase64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAKElEQVQIHWP8DwTv379nAAFBQUEGhnfv3oHEwADEZgJLIRGMIClkLQCr3x2Htp/lLwAAAABJRU5ErkJggg==)
background: white
height: inherit
- &.oe_kanban_grouped .oe_kanban_dummy_cell
- background: url(/web/static/src/img/form_sheetbg.png)
- width: 100%
+ .oe_view_nocontent
+ position: relative
+ z-index: 1
+ max-width: none
+ height: 100%
+ .oe_view_nocontent_content
+ margin-left: 90px
+ margin-top: 5px
+ max-width: 700px
+ .oe_view_nocontent_bg
+ background: #eee
+ opacity: 0.7
+ position: absolute
+ top: 0
+ bottom: 0
+ left: 0
+ right: 0
+ z-index: -1
+ &.oe_kanban_grouped
+ .oe_kanban_dummy_cell
+ background: url(/web/static/src/img/form_sheetbg.png)
+ width: 100%
.oe_kanban_group_length
text-align: center
display: none
position: relative
.oe_kanban_add
top: -8px
+ z-index: 2
.oe_kanban_header .oe_dropdown_toggle
top: -2px
height: 14px
.oe_kanban_no_group .oe_kanban_quick_create
width: 185px
padding: 10px
+ .oe_kanban_quick_create
+ z-index: 2
.oe_kanban_quick_create input
@include box-sizing(border-box)
outline: none
.oe_kanban_title
font-weight: bold
margin: 2px 4px
+ .oe_kanban_alias
+ margin: 0px 0 8px 0
+ .oe_e
+ font-size: 30px
+ line-height: 6px
+ vertical-align: top
+ margin-right: 3px
+ color: white
+ text-shadow: 0px 0px 2px rgba(0, 0, 0, 1)
+ float: left
&.oe_kanban_grouped
.oe_kanban_record
margin-bottom: 4px
.oe_kanban_group_title_vertical
-ms-writing-mode: lr-tb !important
background: rgb(240, 238, 238)
- top: -5px !important
&.oe_kanban_grouped
.oe_kanban_group_header
height: 1%
},
do_search: function(domain, context, group_by) {
var self = this;
- this.$el.find('.oe_view_nocontent').remove();
this.search_domain = domain;
this.search_context = context;
this.search_group_by = group_by;
self.$buttons.find('.oe_alternative').toggle(self.grouped_by_m2o);
self.$el.toggleClass('oe_kanban_grouped_by_m2o', self.grouped_by_m2o);
var grouping_fields = self.group_by ? [self.group_by].concat(_.keys(self.aggregates)) : undefined;
- var grouping = new instance.web.Model(self.dataset.model, context, domain).query().group_by(grouping_fields);
+ if (!_.isEmpty(grouping_fields)) {
+ // ensure group_by fields are read.
+ self.fields_keys = _.unique(self.fields_keys.concat(grouping_fields));
+ }
+ var grouping = new instance.web.Model(self.dataset.model, context, domain).query(self.fields_keys).group_by(grouping_fields);
return self.alive($.when(grouping)).done(function(groups) {
+ self.remove_no_result();
if (groups) {
self.do_process_groups(groups);
} else {
},
do_process_groups: function(groups) {
var self = this;
+ this.$el.find('table:first').show();
this.$el.removeClass('oe_kanban_ungrouped').addClass('oe_kanban_grouped');
this.add_group_mutex.exec(function() {
self.do_clear_groups();
self.no_result();
return false;
}
+ self.nb_records = 0;
var remaining = groups.length - 1,
groups_array = [];
return $.when.apply(null, _.map(groups, function (group, index) {
def = dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit });
}
return def.then(function(records) {
+ self.nb_records += records.length;
self.dataset.ids.push.apply(self.dataset.ids, dataset.ids);
groups_array[index] = new instance.web_kanban.KanbanGroup(self, records, group, dataset);
if (!remaining--) {
return self.do_add_groups(groups_array);
}
});
- }));
+ })).then(function () {
+ if(!self.nb_records) {
+ self.no_result();
+ }
+ });
});
},
do_process_dataset: function() {
var self = this;
+ this.$el.find('table:first').show();
this.$el.removeClass('oe_kanban_grouped').addClass('oe_kanban_ungrouped');
this.add_group_mutex.exec(function() {
var def = $.Deferred();
var $last_td = self.$el.find('.oe_kanban_groups_headers td:last');
var groups_started = _.map(this.groups, function(group) {
if (!group.is_started) {
+ group.on("add_record", self, function () {
+ self.remove_no_result();
+ });
return group.insertBefore($last_td);
}
});
new_group.do_save_sequences();
}).fail(function(error, evt) {
evt.preventDefault();
- alert(_t("An error has occured while moving the record to this group: ") + data.fault_code);
+ alert(_t("An error has occured while moving the record to this group: ") + data.message);
self.do_reload(); // TODO: use draggable + sortable in order to cancel the dragging when the rcp fails
});
}
}
},
no_result: function() {
+ var self = this;
if (this.groups.group_by
|| !this.options.action
- || !this.options.action.help) {
+ || (!this.options.action.help && !this.options.action.get_empty_list_help)) {
return;
}
- this.$el.find('.oe_view_nocontent').remove();
- this.$el.prepend(
- $('<div class="oe_view_nocontent">').html(this.options.action.help)
- );
- var create_nocontent = this.$buttons;
+ this.$el.find('table:first').css("position", "absolute");
+ $(QWeb.render('KanbanView.nocontent', { content : this.options.action.get_empty_list_help || this.options.action.help})).insertAfter(this.$('table:first'));
this.$el.find('.oe_view_nocontent').click(function() {
- create_nocontent.openerpBounce();
+ self.$buttons.openerpBounce();
});
},
+ remove_no_result: function() {
+ this.$el.find('table:first').css("position", false);
+ this.$el.find('.oe_view_nocontent').remove();
+ },
/*
* postprocessing of fields type many2many
});
this.$el.find('.oe_kanban_add').click(function () {
+ if (self.view.quick) {
+ self.view.quick.trigger('close');
+ }
if (self.quick) {
- return self.quick.trigger('close');
+ return false;
}
+ self.view.$el.find('.oe_view_nocontent').hide();
var ctx = {};
ctx['default_' + self.view.group_by] = self.value;
self.quick = new (get_class(self.view.quick_create_class))(this, self.dataset, ctx, true)
.on('added', self, self.proxy('quick_created'))
.on('close', self, function() {
+ self.view.$el.find('.oe_view_nocontent').show();
this.quick.destroy();
+ delete self.view.quick;
delete this.quick;
});
self.quick.appendTo($(".oe_kanban_group_list_header", self.$records));
self.quick.focus();
+ self.view.quick = self.quick;
});
// Add bounce effect on image '+' of kanban header when click on empty space of kanban grouped column.
this.$records.on('click', '.oe_kanban_show_more', this.do_show_more);
*/
quick_created: function (record) {
var id = record, self = this;
+ self.view.remove_no_result();
+ self.trigger("add_record");
this.dataset.read_ids([id], this.view.fields_keys)
.done(function (records) {
self.view.dataset.ids.push(id);
#
##############################################################################
-#.apidoc title: Object Relational Mapping
-#.apidoc module-mods: member-order: bysource
"""
Object relational mapping to database (postgresql) module
import fields
import openerp
-import openerp.netsvc as netsvc
import openerp.tools as tools
from openerp.tools.config import config
from openerp.tools.misc import CountingStream
for field_name, field_column in fields_to_fetch:
if field_column._type == 'many2one':
if result_line[field_name]:
- obj = self._table.pool.get(field_column._obj)
+ obj = self._table.pool[field_column._obj]
if isinstance(result_line[field_name], (list, tuple)):
value = result_line[field_name][0]
else:
else:
new_data[field_name] = browse_null()
elif field_column._type in ('one2many', 'many2many') and len(result_line[field_name]):
- new_data[field_name] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool.get(field_column._obj), self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in result_line[field_name]], self._context)
+ new_data[field_name] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool[field_column._obj], self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in result_line[field_name]], self._context)
elif field_column._type == 'reference':
if result_line[field_name]:
if isinstance(result_line[field_name], browse_record):
ref_obj, ref_id = result_line[field_name].split(',')
ref_id = long(ref_id)
if ref_id:
- obj = self._table.pool.get(ref_obj)
+ obj = self._table.pool[ref_obj]
new_data[field_name] = browse_record(self._cr, self._uid, ref_id, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process)
else:
new_data[field_name] = browse_null()
try:
return self[name]
except KeyError, e:
- raise AttributeError(e)
+ import sys
+ exc_info = sys.exc_info()
+ raise AttributeError, "Got %r while trying to get attribute %s on a %s record." % (e, name, self._table._name), exc_info[2]
def __contains__(self, name):
return (name in self._table._columns) or (name in self._table._inherit_fields) or hasattr(self._table, name)
raise TypeError('_name is mandatory in case of multiple inheritance')
for parent_name in ((type(parent_names)==list) and parent_names or [parent_names]):
- parent_model = pool.get(parent_name)
- if not parent_model:
+ if parent_name not in pool:
raise TypeError('The model "%s" specifies an unexisting parent class "%s"\n'
'You may need to add a dependency on the parent class\' module.' % (name, parent_name))
+ parent_model = pool[parent_name]
if not getattr(cls, '_original_module', None) and name == parent_model._name:
cls._original_module = parent_model._original_module
parent_class = parent_model.__class__
'ondelete': field['on_delete'],
'translate': (field['translate']),
'manual': True,
+ '_prefetch': False,
#'select': int(field['select_level'])
}
return ''
def selection_field(in_field):
- col_obj = self.pool.get(in_field.keys()[0])
+ col_obj = self.pool[in_field.keys()[0]]
if f[i] in col_obj._columns.keys():
return col_obj._columns[f[i]]
elif f[i] in col_obj._inherits.keys():
if not data[fpos]:
dt = ''
for rr in r:
- name_relation = self.pool.get(rr._table_name)._rec_name
+ name_relation = self.pool[rr._table_name]._rec_name
if isinstance(rr[name_relation], browse_record):
rr = rr[name_relation]
- rr_name = self.pool.get(rr._table_name).name_get(cr, uid, [rr.id], context=context)
+ rr_name = self.pool[rr._table_name].name_get(cr, uid, [rr.id], context=context)
rr_name = rr_name and rr_name[0] and rr_name[0][1] or ''
dt += tools.ustr(rr_name or '') + ','
data[fpos] = dt[:-1]
i += 1
if i == len(f):
if isinstance(r, browse_record):
- r = self.pool.get(r._table_name).name_get(cr, uid, [r.id], context=context)
+ r = self.pool[r._table_name].name_get(cr, uid, [r.id], context=context)
r = r and r[0] and r[0][1] or ''
data[fpos] = tools.ustr(r or '')
return [data] + lines
# get the default values for the inherited fields
for t in self._inherits.keys():
- defaults.update(self.pool.get(t).default_get(cr, uid, fields_list,
- context))
+ defaults.update(self.pool[t].default_get(cr, uid, fields_list, context))
# get the default values defined in the object
for f in fields_list:
if field in fields_list:
fld_def = (field in self._columns) and self._columns[field] or self._inherit_fields[field][2]
if fld_def._type == 'many2one':
- obj = self.pool.get(fld_def._obj)
+ obj = self.pool[fld_def._obj]
if not obj.search(cr, uid, [('id', '=', field_value or False)]):
continue
if fld_def._type == 'many2many':
- obj = self.pool.get(fld_def._obj)
+ obj = self.pool[fld_def._obj]
field_value2 = []
for i in range(len(field_value or [])):
if not obj.search(cr, uid, [('id', '=',
field_value2.append(field_value[i])
field_value = field_value2
if fld_def._type == 'one2many':
- obj = self.pool.get(fld_def._obj)
+ obj = self.pool[fld_def._obj]
field_value2 = []
for i in range(len(field_value or [])):
field_value2.append({})
for field2 in field_value[i]:
if field2 in obj._columns.keys() and obj._columns[field2]._type == 'many2one':
- obj2 = self.pool.get(obj._columns[field2]._obj)
+ obj2 = self.pool[obj._columns[field2]._obj]
if not obj2.search(cr, uid,
[('id', '=', field_value[i][field2])]):
continue
elif field2 in obj._inherit_fields.keys() and obj._inherit_fields[field2][2]._type == 'many2one':
- obj2 = self.pool.get(obj._inherit_fields[field2][2]._obj)
+ obj2 = self.pool[obj._inherit_fields[field2][2]._obj]
if not obj2.search(cr, uid,
[('id', '=', field_value[i][field2])]):
continue
# TODO I believe this loop can be replace by
# res.extend(self._inherit_fields.key())
for parent in self._inherits:
- res.extend(self.pool.get(parent).fields_get_keys(cr, user, context))
+ res.extend(self.pool[parent].fields_get_keys(cr, user, context))
return res
def _rec_name_fallback(self, cr, uid, context=None):
new_xml = etree.fromstring(encode(xml))
ctx = context.copy()
ctx['base_model_name'] = self._name
- xarch, xfields = self.pool.get(node.get('object')).__view_look_dom_arch(cr, user, new_xml, view_id, ctx)
+ xarch, xfields = self.pool[node.get('object')].__view_look_dom_arch(cr, user, new_xml, view_id, ctx)
views['form'] = {
'arch': xarch,
'fields': xfields
column = False
if column:
- relation = self.pool.get(column._obj)
+ relation = self.pool[column._obj] if column._obj else None
children = False
views = {}
fields = {}
if node.tag == 'diagram':
if node.getchildren()[0].tag == 'node':
- node_model = self.pool.get(node.getchildren()[0].get('object'))
+ node_model = self.pool[node.getchildren()[0].get('object')]
node_fields = node_model.fields_get(cr, user, None, context)
fields.update(node_fields)
if not node.get("create") and not node_model.check_access_rights(cr, user, 'create', raise_exception=False):
node.set("create", 'false')
if node.getchildren()[1].tag == 'arrow':
- arrow_fields = self.pool.get(node.getchildren()[1].get('object')).fields_get(cr, user, None, context)
+ arrow_fields = self.pool[node.getchildren()[1].get('object')].fields_get(cr, user, None, context)
fields.update(arrow_fields)
else:
fields = self.fields_get(cr, user, None, context)
if view_type == 'tree' or not action[2].get('multi')]
resprint = [clean(print_) for print_ in resprint
if view_type == 'tree' or not print_[2].get('multi')]
- #When multi="True" set it will display only in More of the list view
+ #When multi="True" set it will display only in More of the list view
resrelate = [clean(action) for action in resrelate
if (action[2].get('multi') and view_type == 'tree') or (not action[2].get('multi') and view_type == 'form')]
res[lang][f] = self._columns[f].string
for table in self._inherits:
cols = intersect(self._inherit_fields.keys(), fields)
- res2 = self.pool.get(table).read_string(cr, uid, id, langs, cols, context)
+ res2 = self.pool[table].read_string(cr, uid, id, langs, cols, context)
for lang in res2:
if lang in res:
res[lang]['code'] = lang
for table in self._inherits:
cols = intersect(self._inherit_fields.keys(), vals)
if cols:
- self.pool.get(table).write_string(cr, uid, id, langs, vals, context)
+ self.pool[table].write_string(cr, uid, id, langs, vals, context)
return True
def _add_missing_default_values(self, cr, uid, values, context=None):
order = orderby or groupby
data_ids = self.search(cr, uid, [('id', 'in', alldata.keys())], order=order, context=context)
-
+
# the IDs of records that have groupby field value = False or '' should be included too
data_ids += set(alldata.keys()).difference(data_ids)
-
- if groupby:
+
+ if groupby:
data = self.read(cr, uid, data_ids, [groupby], context=context)
# restore order of the search as read() uses the default _order (this is only for groups, so the footprint of data should be small):
data_dict = dict((d['id'], d[groupby] ) for d in data)
result = [{'id': i, groupby: data_dict[i]} for i in data_ids]
else:
- result = [{'id': i} for i in data_ids]
+ result = [{'id': i} for i in data_ids]
for d in result:
if groupby:
:param query: query object on which the JOIN should be added
"""
inherits_field = current_model._inherits[parent_model_name]
- parent_model = self.pool.get(parent_model_name)
+ parent_model = self.pool[parent_model_name]
parent_alias, parent_alias_statement = query.add_join((current_model._table, parent_model._table, inherits_field, 'id', inherits_field), implicit=True)
return parent_alias
parent_alias = '"%s"' % current_table._table
while field in current_table._inherit_fields and not field in current_table._columns:
parent_model_name = current_table._inherit_fields[field][0]
- parent_table = self.pool.get(parent_model_name)
+ parent_table = self.pool[parent_model_name]
parent_alias = self._inherits_join_add(current_table, parent_model_name, query)
current_table = parent_table
return '%s."%s"' % (parent_alias, field)
return
_logger.info('Computing parent left and right for table %s...', self._table)
def browse_rec(root, pos=0):
-# TODO: set order
+ # TODO: set order
where = self._parent_name+'='+str(root)
if not root:
where = self._parent_name+' IS NULL'
_schema.debug(msg, self._table, k, f._type)
if isinstance(f, fields.many2one):
- dest_model = self.pool.get(f._obj)
+ dest_model = self.pool[f._obj]
if dest_model._table != 'ir_actions':
self._m2o_fix_foreign_key(cr, self._table, k, dest_model, f.ondelete)
# and add constraints if needed
if isinstance(f, fields.many2one):
- if not self.pool.get(f._obj):
+ if f._obj not in self.pool:
raise except_orm('Programming Error', 'There is no reference available for %s' % (f._obj,))
- dest_model = self.pool.get(f._obj)
+ dest_model = self.pool[f._obj]
ref = dest_model._table
# ir_actions is inherited so foreign key doesn't work on it
if ref != 'ir_actions':
def _o2m_raise_on_missing_reference(self, cr, f):
# TODO this check should be a method on fields.one2many.
-
- other = self.pool.get(f._obj)
- if other:
+ if f._obj in self.pool:
+ other = self.pool[f._obj]
# TODO the condition could use fields_get_keys().
if f._fields_id not in other._columns.keys():
if f._fields_id not in other._inherit_fields.keys():
self._save_relation_table(cr, m2m_tbl)
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (m2m_tbl,))
if not cr.dictfetchall():
- if not self.pool.get(f._obj):
+ if f._obj not in self.pool:
raise except_orm('Programming Error', 'Many2Many destination model does not exist: `%s`' % (f._obj,))
- dest_model = self.pool.get(f._obj)
+ dest_model = self.pool[f._obj]
ref = dest_model._table
cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s"))' % (m2m_tbl, col1, col2, col1, col2))
# create foreign key references with ondelete=cascade, unless the targets are SQL views
"""
res = {}
for table in self._inherits:
- other = self.pool.get(table)
+ other = self.pool[table]
for col in other._columns.keys():
res[col] = (table, self._inherits[table], other._columns[col], table)
for col in other._inherit_fields.keys():
translation_obj = self.pool.get('ir.translation')
for parent in self._inherits:
- res.update(self.pool.get(parent).fields_get(cr, user, allfields, context))
+ res.update(self.pool[parent].fields_get(cr, user, allfields, context))
for f, field in self._columns.iteritems():
if (allfields and f not in allfields) or \
return res
+ def get_empty_list_help(self, cr, user, help, context=None):
+ """ Generic method giving the help message displayed when having
+ no result to display in a list or kanban view. By default it returns
+ the help given in parameter that is generally the help message
+ defined in the action.
+ """
+ return help
+
def check_field_access_rights(self, cr, user, operation, fields, context=None):
"""
Check the user access rights on the given fields. This raises Access
"""
- if not context:
- context = {}
self.check_access_rights(cr, user, 'read')
fields = self.check_field_access_rights(cr, user, 'read', fields)
if isinstance(ids, (int, long)):
select = map(lambda x: isinstance(x, dict) and x['id'] or x, select)
result = self._read_flat(cr, user, select, fields, context, load)
- for r in result:
- for key, v in r.items():
- if v is None:
- r[key] = False
-
- if isinstance(ids, (int, long, dict)):
+ if isinstance(ids, (int, long)):
return result and result[0] or False
return result
cols = [x for x in intersect(self._inherit_fields.keys(), fields_to_read) if x not in self._columns.keys()]
if not cols:
continue
- res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
+ res2 = self.pool[table].read(cr, user, [x[col] for x in res], cols, context, load)
res3 = {}
for r in res2:
if field in self._columns:
fobj = self._columns[field]
- if not fobj:
- continue
- groups = fobj.read
- if groups:
- edit = False
- for group in groups:
- module = group.split(".")[0]
- grp = group.split(".")[1]
- cr.execute("select count(*) from res_groups_users_rel where gid IN (select res_id from ir_model_data where name=%s and module=%s and model=%s) and uid=%s", \
- (grp, module, 'res.groups', user))
- readonly = cr.fetchall()
- if readonly[0][0] >= 1:
- edit = True
- break
- elif readonly[0][0] == 0:
- edit = False
- else:
- edit = False
-
- if not edit:
- if type(vals[field]) == type([]):
- vals[field] = []
- elif type(vals[field]) == type(0.0):
- vals[field] = 0
- elif type(vals[field]) == type(''):
- vals[field] = '=No Permission='
- else:
- vals[field] = False
+ if fobj:
+ groups = fobj.read
+ if groups:
+ edit = False
+ for group in groups:
+ module = group.split(".")[0]
+ grp = group.split(".")[1]
+ cr.execute("select count(*) from res_groups_users_rel where gid IN (select res_id from ir_model_data where name=%s and module=%s and model=%s) and uid=%s", \
+ (grp, module, 'res.groups', user))
+ readonly = cr.fetchall()
+ if readonly[0][0] >= 1:
+ edit = True
+ break
+ elif readonly[0][0] == 0:
+ edit = False
+ else:
+ edit = False
+
+ if not edit:
+ if type(vals[field]) == type([]):
+ vals[field] = []
+ elif type(vals[field]) == type(0.0):
+ vals[field] = 0
+ elif type(vals[field]) == type(''):
+ vals[field] = '=No Permission='
+ else:
+ vals[field] = False
+
+ if vals[field] is None:
+ vals[field] = False
+
return res
# TODO check READ access
# Attempt to distinguish record rule restriction vs deleted records,
# to provide a more specific error message - check if the missinf
cr.execute('SELECT id FROM ' + self._table + ' WHERE id IN %s', (tuple(missing_ids),))
- if cr.rowcount:
+ forbidden_ids = [x[0] for x in cr.fetchall()]
+ if forbidden_ids:
# the missing ids are (at least partially) hidden by access rules
if uid == SUPERUSER_ID:
return
- _logger.warning('Access Denied by record rules for operation: %s, uid: %s, model: %s', operation, uid, self._name)
+ _logger.warning('Access Denied by record rules for operation: %s on record ids: %r, uid: %s, model: %s', operation, forbidden_ids, uid, self._name)
raise except_orm(_('Access Denied'),
_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
(self._description, operation))
if operation in ('read','unlink'):
# No need to warn about deleting an already deleted record.
# And no error when reading a record that was deleted, to prevent spurious
- # errors for non-transactional search/read sequences coming from clients
+ # errors for non-transactional search/read sequences coming from clients
return
_logger.warning('Failed operation on deleted record(s): %s, uid: %s, model: %s', operation, uid, self._name)
raise except_orm(_('Missing document(s)'),
returned_ids = [x['id'] for x in cr.dictfetchall()]
self._check_record_rules_result_count(cr, uid, sub_ids, returned_ids, operation, context=context)
- def _workflow_trigger(self, cr, uid, ids, trigger, context=None):
- """Call given workflow trigger as a result of a CRUD operation"""
- wf_service = netsvc.LocalService("workflow")
+ def create_workflow(self, cr, uid, ids, context=None):
+ """Create a workflow instance for each given record IDs."""
+ from openerp import workflow
for res_id in ids:
- getattr(wf_service, trigger)(uid, self._name, res_id, cr)
+ workflow.trg_create(uid, self._name, res_id, cr)
+ return True
+
+ def delete_workflow(self, cr, uid, ids, context=None):
+ """Delete the workflow instances bound to the given record IDs."""
+ from openerp import workflow
+ for res_id in ids:
+ workflow.trg_delete(uid, self._name, res_id, cr)
+ return True
+
+ def step_workflow(self, cr, uid, ids, context=None):
+ """Reevaluate the workflow instances of the given record IDs."""
+ from openerp import workflow
+ for res_id in ids:
+ workflow.trg_write(uid, self._name, res_id, cr)
+ return True
- def _workflow_signal(self, cr, uid, ids, signal, context=None):
+ def signal_workflow(self, cr, uid, ids, signal, context=None):
"""Send given workflow signal and return a dict mapping ids to workflow results"""
- wf_service = netsvc.LocalService("workflow")
+ from openerp import workflow
result = {}
for res_id in ids:
- result[res_id] = wf_service.trg_validate(uid, self._name, res_id, signal, cr)
+ result[res_id] = workflow.trg_validate(uid, self._name, res_id, signal, cr)
return result
+ def redirect_workflow(self, cr, uid, old_new_ids, context=None):
+ """ Rebind the workflow instance bound to the given 'old' record IDs to
+ the given 'new' IDs. (``old_new_ids`` is a list of pairs ``(old, new)``.
+ """
+ from openerp import workflow
+ for old_id, new_id in old_new_ids:
+ workflow.trg_redirect(uid, self._name, old_id, new_id, cr)
+ return True
+
def unlink(self, cr, uid, ids, context=None):
"""
Delete records with given ids
property_ids = ir_property.search(cr, uid, [('res_id', 'in', ['%s,%s' % (self._name, i) for i in ids])], context=context)
ir_property.unlink(cr, uid, property_ids, context=context)
- self._workflow_trigger(cr, uid, ids, 'trg_delete', context=context)
+ self.delete_workflow(cr, uid, ids, context=context)
self.check_access_rule(cr, uid, ids, 'unlink', context=context)
pool_model_data = self.pool.get('ir.model.data')
if ir_value_ids:
ir_values_obj.unlink(cr, uid, ir_value_ids, context=context)
- for order, object, store_ids, fields in result_store:
- if object != self._name:
- obj = self.pool.get(object)
+ for order, obj_name, store_ids, fields in result_store:
+ if obj_name != self._name:
+ obj = self.pool[obj_name]
cr.execute('select id from '+obj._table+' where id IN %s', (tuple(store_ids),))
rids = map(lambda x: x[0], cr.fetchall())
if rids:
# TODO: optimize
for f in direct:
if self._columns[f].translate:
- src_trans = self.pool.get(self._name).read(cr, user, ids, [f])[0][f]
+ src_trans = self.pool[self._name].read(cr, user, ids, [f])[0][f]
if not src_trans:
src_trans = vals[f]
# Inserting value to DB
v[val] = vals[val]
unknown_fields.remove(val)
if v:
- self.pool.get(table).write(cr, user, nids, v, context)
+ self.pool[table].write(cr, user, nids, v, context)
if unknown_fields:
_logger.warning(
result.sort()
done = {}
- for order, object, ids_to_update, fields_to_recompute in result:
- key = (object, tuple(fields_to_recompute))
+ for order, model_name, ids_to_update, fields_to_recompute in result:
+ key = (model_name, tuple(fields_to_recompute))
done.setdefault(key, {})
# avoid to do several times the same computation
todo = []
if id not in done[key]:
done[key][id] = True
todo.append(id)
- self.pool.get(object)._store_set_values(cr, user, todo, fields_to_recompute, context)
+ self.pool[model_name]._store_set_values(cr, user, todo, fields_to_recompute, context)
- self._workflow_trigger(cr, user, ids, 'trg_write', context=context)
+ self.step_workflow(cr, user, ids, context=context)
return True
#
del vals[self._inherits[table]]
record_id = tocreate[table].pop('id', None)
-
+
# When linking/creating parent records, force context without 'no_store_function' key that
- # defers stored functions computing, as these won't be computed in batch at the end of create().
+ # defers stored functions computing, as these won't be computed in batch at the end of create().
parent_context = dict(context)
parent_context.pop('no_store_function', None)
-
+
if record_id is None or not record_id:
- record_id = self.pool.get(table).create(cr, user, tocreate[table], context=parent_context)
+ record_id = self.pool[table].create(cr, user, tocreate[table], context=parent_context)
else:
- self.pool.get(table).write(cr, user, [record_id], tocreate[table], context=parent_context)
+ self.pool[table].write(cr, user, [record_id], tocreate[table], context=parent_context)
upd0 += ',' + self._inherits[table]
upd1 += ',%s'
upd0 = upd0 + ',"' + field + '"'
upd1 = upd1 + ',' + self._columns[field]._symbol_set[0]
upd2.append(self._columns[field]._symbol_set[1](vals[field]))
- #for the function fields that receive a value, we set them directly in the database
+ #for the function fields that receive a value, we set them directly in the database
#(they may be required), but we also need to trigger the _fct_inv()
if (hasattr(self._columns[field], '_fnct_inv')) and not isinstance(self._columns[field], fields.related):
#TODO: this way to special case the related fields is really creepy but it shouldn't be changed at
result += self._store_get_values(cr, user, [id_new], vals.keys(), context)
result.sort()
done = []
- for order, object, ids, fields2 in result:
- if not (object, ids, fields2) in done:
- self.pool.get(object)._store_set_values(cr, user, ids, fields2, context)
- done.append((object, ids, fields2))
+ for order, model_name, ids, fields2 in result:
+ if not (model_name, ids, fields2) in done:
+ self.pool[model_name]._store_set_values(cr, user, ids, fields2, context)
+ done.append((model_name, ids, fields2))
if self._log_create and not (context and context.get('no_store_function', False)):
message = self._description + \
"' " + _("created.")
self.log(cr, user, id_new, message, True, context=context)
self.check_access_rule(cr, user, [id_new], 'create', context=context)
- self._workflow_trigger(cr, user, [id_new], 'trg_create', context=context)
+ self.create_workflow(cr, user, [id_new], context=context)
return id_new
def browse(self, cr, uid, select, context=None, list_class=None, fields_process=None):
return browse_null()
def _store_get_values(self, cr, uid, ids, fields, context):
- """Returns an ordered list of fields.functions to call due to
+ """Returns an ordered list of fields.function to call due to
an update operation on ``fields`` of records with ``ids``,
- obtained by calling the 'store' functions of these fields,
+ obtained by calling the 'store' triggers of these fields,
as setup by their 'store' attribute.
:return: [(priority, model_name, [record_ids,], [function_fields,])]
stored_functions = self.pool._store_function.get(self._name, [])
# use indexed names for the details of the stored_functions:
- model_name_, func_field_to_compute_, id_mapping_fnct_, trigger_fields_, priority_ = range(5)
+ model_name_, func_field_to_compute_, target_ids_func_, trigger_fields_, priority_ = range(5)
- # only keep functions that should be triggered for the ``fields``
+ # only keep store triggers that should be triggered for the ``fields``
# being written to.
- to_compute = [f for f in stored_functions \
+ triggers_to_compute = [f for f in stored_functions \
if ((not f[trigger_fields_]) or set(fields).intersection(f[trigger_fields_]))]
- mapping = {}
- fresults = {}
- for function in to_compute:
- fid = id(function[id_mapping_fnct_])
- if not fid in fresults:
+ to_compute_map = {}
+ target_id_results = {}
+ for store_trigger in triggers_to_compute:
+ target_func_id_ = id(store_trigger[target_ids_func_])
+ if not target_func_id_ in target_id_results:
# use admin user for accessing objects having rules defined on store fields
- fresults[fid] = [id2 for id2 in function[id_mapping_fnct_](self, cr, SUPERUSER_ID, ids, context) if id2]
- target_ids = fresults[fid]
+ target_id_results[target_func_id_] = [i for i in store_trigger[target_ids_func_](self, cr, SUPERUSER_ID, ids, context) if i]
+ target_ids = target_id_results[target_func_id_]
# the compound key must consider the priority and model name
- key = (function[priority_], function[model_name_])
+ key = (store_trigger[priority_], store_trigger[model_name_])
for target_id in target_ids:
- mapping.setdefault(key, {}).setdefault(target_id,set()).add(tuple(function))
+ to_compute_map.setdefault(key, {}).setdefault(target_id,set()).add(tuple(store_trigger))
- # Here mapping looks like:
- # { (10, 'model_a') : { target_id1: [ (function_1_tuple, function_2_tuple) ], ... }
- # (20, 'model_a') : { target_id2: [ (function_3_tuple, function_4_tuple) ], ... }
- # (99, 'model_a') : { target_id1: [ (function_5_tuple, function_6_tuple) ], ... }
+ # Here to_compute_map looks like:
+ # { (10, 'model_a') : { target_id1: [ (trigger_1_tuple, trigger_2_tuple) ], ... }
+ # (20, 'model_a') : { target_id2: [ (trigger_3_tuple, trigger_4_tuple) ], ... }
+ # (99, 'model_a') : { target_id1: [ (trigger_5_tuple, trigger_6_tuple) ], ... }
# }
# Now we need to generate the batch function calls list
# call_map =
# { (10, 'model_a') : [(10, 'model_a', [record_ids,], [function_fields,])] }
call_map = {}
- for ((priority,model), id_map) in mapping.iteritems():
- functions_ids_maps = {}
+ for ((priority,model), id_map) in to_compute_map.iteritems():
+ trigger_ids_maps = {}
# function_ids_maps =
# { (function_1_tuple, function_2_tuple) : [target_id1, target_id2, ..] }
- for fid, functions in id_map.iteritems():
- functions_ids_maps.setdefault(tuple(functions), []).append(fid)
- for functions, ids in functions_ids_maps.iteritems():
- call_map.setdefault((priority,model),[]).append((priority, model, ids,
- [f[func_field_to_compute_] for f in functions]))
+ for target_id, triggers in id_map.iteritems():
+ trigger_ids_maps.setdefault(tuple(triggers), []).append(target_id)
+ for triggers, target_ids in trigger_ids_maps.iteritems():
+ call_map.setdefault((priority,model),[]).append((priority, model, target_ids,
+ [t[func_field_to_compute_] for t in triggers]))
ordered_keys = call_map.keys()
ordered_keys.sort()
result = []
# to reach the parent table (if it was not JOINed yet in the query)
parent_alias = child_object._inherits_join_add(child_object, parent_model, query)
# inherited rules are applied on the external table -> need to get the alias and replace
- parent_table = self.pool.get(parent_model)._table
+ parent_table = self.pool[parent_model]._table
added_clause = [clause.replace('"%s"' % parent_table, '"%s"' % parent_alias) for clause in added_clause]
# change references to parent_table to parent_alias, because we now use the alias to refer to the table
new_tables = []
return
# figure out the applicable order_by for the m2o
- dest_model = self.pool.get(order_field_column._obj)
+ dest_model = self.pool[order_field_column._obj]
m2o_order = dest_model._order
if not regex_order.match(m2o_order):
# _order is complex, can't use it here, so we default to _rec_name
else:
continue # ignore non-readable or "non-joinable" fields
elif order_field in self._inherit_fields:
- parent_obj = self.pool.get(self._inherit_fields[order_field][3])
+ parent_obj = self.pool[self._inherit_fields[order_field][3]]
order_column = parent_obj._columns[order_field]
if order_column._classic_read:
inner_clause = self._inherits_join_calc(order_field, query)
limit_str = limit and ' limit %d' % limit or ''
offset_str = offset and ' offset %d' % offset or ''
where_str = where_clause and (" WHERE %s" % where_clause) or ''
+ query_str = 'SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str
if count:
- cr.execute('SELECT count("%s".id) FROM ' % self._table + from_clause + where_str + limit_str + offset_str, where_clause_params)
- res = cr.fetchall()
- return res[0][0]
- cr.execute('SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str, where_clause_params)
+ # /!\ the main query must be executed as a subquery, otherwise
+ # offset and limit apply to the result of count()!
+ cr.execute('SELECT count(*) FROM (%s) AS count' % query_str, where_clause_params)
+ res = cr.fetchone()
+ return res[0]
+
+ cr.execute(query_str, where_clause_params)
res = cr.fetchall()
# TDE note: with auto_join, we could have several lines about the same result
if not args:
args = []
if field in self._inherit_fields:
- return self.pool.get(self._inherit_fields[field][0]).distinct_field_get(cr, uid, field, value, args, offset, limit)
+ return self.pool[self._inherit_fields[field][0]].distinct_field_get(cr, uid, field, value, args, offset, limit)
else:
return self._columns[field].search(cr, self, args, field, value, offset, limit, uid)
if field_to_other in default:
# all the fields of 'other' are given by the record: default[field_to_other],
# except the ones redefined in self
- blacklist.update(set(self.pool.get(other)._all_columns) - set(self._columns))
+ blacklist.update(set(self.pool[other]._all_columns) - set(self._columns))
else:
- blacklist_given_fields(self.pool.get(other))
+ blacklist_given_fields(self.pool[other])
blacklist_given_fields(self)
res = dict(default)
elif field._type == 'many2one':
res[f] = data[f] and data[f][0]
elif field._type == 'one2many':
- other = self.pool.get(field._obj)
+ other = self.pool[field._obj]
# duplicate following the order of the ids because we'll rely on
# it later for copying translations in copy_translation()!
lines = [other.copy_data(cr, uid, line_id, context=context) for line_id in sorted(data[f])]
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
fields = self.fields_get(cr, uid, context=context)
for field_name, field_def in fields.items():
# we must recursively copy the translations for o2o and o2m
if field_def['type'] == 'one2many':
- target_obj = self.pool.get(field_def['relation'])
+ target_obj = self.pool[field_def['relation']]
old_record, new_record = self.read(cr, uid, [old_id, new_id], [field_name], context=context)
# here we rely on the order of the ids to match the translations
# as foreseen in copy_data()
target_obj.copy_translations(cr, uid, old_child, new_child, context=context)
# and for translatable fields we keep them for copy
elif field_def.get('translate'):
- trans_name = ''
+
if field_name in self._columns:
trans_name = self._name + "," + field_name
+ res_id = new_id
+
elif field_name in self._inherit_fields:
trans_name = self._inherit_fields[field_name][0] + "," + field_name
- if trans_name:
- trans_ids = trans_obj.search(cr, uid, [
- ('name', '=', trans_name),
- ('res_id', '=', old_id)
- ])
- translation_records.extend(trans_obj.read(cr, uid, trans_ids, context=context))
+ # get the id of the parent record to set the translation
+ inherit_field_name = self._inherit_fields[field_name][1]
+ res_id = self.read(cr, uid, [new_id], [inherit_field_name], context=context)[0][inherit_field_name][0]
+
+ else:
+ continue
- for record in translation_records:
- del record['id']
- record['res_id'] = new_id
- trans_obj.create(cr, uid, record, context=context)
+ trans_ids = trans_obj.search(cr, uid, [
+ ('name', '=', trans_name),
+ ('res_id', '=', old_id)
+ ])
+ records = trans_obj.read(cr, uid, trans_ids, context=context)
+ for record in records:
+ del record['id']
+ # remove source to avoid triggering _set_src
+ del record['source']
+ record.update({'res_id': res_id})
+ trans_obj.create(cr, uid, record, context=context)
def copy(self, cr, uid, id, default=None, context=None):
get_xml_id = get_external_id
_get_xml_ids = _get_external_ids
+ def print_report(self, cr, uid, ids, name, data, context=None):
+ """
+ Render the report `name` for the given IDs. The report must be defined
+ for this model, not another.
+ """
+ report = self.pool['ir.actions.report.xml']._lookup_report(cr, name)
+ assert self._name == report.table
+ return report.create(cr, uid, ids, data, context)
+
# Transience
def is_transient(self):
""" Return whether the model is transient.
result, record_ids = [], list(command[2])
# read the records and apply the updates
- other_model = self.pool.get(self._all_columns[field_name].column._obj)
+ other_model = self.pool[self._all_columns[field_name].column._obj]
for record in other_model.read(cr, uid, record_ids, fields=fields, context=context):
record.update(updates.get(record['id'], {}))
result.append(record)
""" stuff to do right after the registry is built """
pass
+ def __getattr__(self, name):
+ if name.startswith('signal_'):
+ signal_name = name[len('signal_'):]
+ assert signal_name
+ return (lambda *args, **kwargs:
+ self.signal_workflow(*args, signal=signal_name, **kwargs))
+ get = getattr(super(BaseModel, self), '__getattr__', None)
+ if get is not None: return get(name)
+ raise AttributeError(
+ "'%s' object has no attribute '%s'" % (type(self).__name__, name))
+
# keep this import here, at top it will cause dependency cycle errors
import expression
message = _(u"Missing required value for the field '%s'.") % field_name
field = fields.get(field_name)
if field:
- message = _(u"%s This might be '%s' in the current model, or a field "
- u"of the same name in an o2m.") % (message, field['string'])
+ message = _(u"Missing required value for the field '%s' (%s)") % (field['string'], field_name)
return {
'message': message,
'field': field_name,