[MERGE] latest trunk-proto61.

bzr revid: vda@tinyerp.com-20110517093550-zvxyvwm62nvqhy5z
This commit is contained in:
vda (OpenERP)
2011-05-17 15:05:50 +05:30
28 changed files with 1207 additions and 653 deletions
+17
View File
@@ -357,6 +357,10 @@ class DataSet(openerpweb.Controller):
reads = Model.read(ids, fields or False, request.context)
reads.sort(key=lambda obj: ids.index(obj['id']))
return reads
@openerpweb.jsonrequest
def read(self, request, model, ids, fields=False):
return self.do_search_read(request, model, ids, fields)
@openerpweb.jsonrequest
def get(self, request, model, ids, fields=False):
@@ -424,6 +428,19 @@ class DataSet(openerpweb.Controller):
m = req.session.model(model)
r = m.default_get(fields, context)
return {'result': r}
@openerpweb.jsonrequest
def name_search(self, req, model, search_str, domain=[], context={}):
m = req.session.model(model)
r = m.name_search(search_str+'%', domain, '=ilike', context)
return {'result': r}
class DataGroup(openerpweb.Controller):
_cp_path = "/base/group"
@openerpweb.jsonrequest
def read(self, request, model, group_by_fields, domain=None, context=None):
Model = request.session.model(model)
return Model.read_group(domain or False, False, group_by_fields, 0, False, context or False)
class View(openerpweb.Controller):
def fields_view_get(self, request, model, view_id, view_type,
+2 -1
View File
@@ -24,10 +24,11 @@
<script type="text/javascript" src="/base/static/src/js/dates.js"></script>
<script type="text/javascript" src="/base/static/src/js/chrome.js"></script>
<script type="text/javascript" src="/base/static/src/js/data.js"></script>
<script type="text/javascript" src="/base/static/src/js/views.js"></script>
<script type="text/javascript" src="/base/static/src/js/form.js"></script>
<script type="text/javascript" src="/base/static/src/js/list.js"></script>
<script type="text/javascript" src="/base/static/src/js/search.js"></script>
<script type="text/javascript" src="/base/static/src/js/views.js"></script>
<script type="text/javascript" src="/base/static/src/js/m2o.js"></script>
<link rel="stylesheet" type="text/css" media="screen" href="/base/static/lib/jquery.ui/css/smoothness/jquery-ui-1.8.9.custom.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/base/static/lib/jquery.ui.notify/css/ui.notify.css" />
+40 -20
View File
@@ -221,8 +221,6 @@ body.openerp {
height: 63px;
width: 200px;
margin-right: 10px;
line-height: 63px;
text-align: center;
border: 1px solid white;
border-right-color: black;
border-bottom-color: black;
@@ -232,7 +230,12 @@ body.openerp {
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FFFFFF', endColorstr='#CECECE',GradientType=0 );
}
.openerp .company_logo {
vertical-align: middle
margin-top: 7px;
margin-left: 10px;
display: block;
background: url(/base/static/src/img/logo.png);
width:180px;
height:46px;
}
.openerp .header_title {
float: left;
@@ -773,44 +776,61 @@ body.openerp {
background-position: 21px 0;
}
.openerp .kitten-mode-activated {
.openerp.kitten-mode-activated .main_table {
background: url(http://placekitten.com/g/1500/800) repeat;
}
.openerp .kitten-mode-activated .header {
.openerp.kitten-mode-activated .header {
background: url(http://placekitten.com/g/211/65) repeat;
}
.openerp .kitten-mode-activated .secondary_menu {
.openerp.kitten-mode-activated .secondary_menu {
background: url(http://placekitten.com/g/212/100) repeat;
}
.openerp .kitten-mode-activated .menu {
.openerp.kitten-mode-activated .menu {
background: #828282;
background: -moz-linear-gradient(top, #828282 0%, #4D4D4D 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#828282), color-stop(100%,#4D4D4D));
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#828282', endColorstr='#4D4D4D',GradientType=0 );
}
.openerp .kitten-mode-activated .menu a {
.openerp.kitten-mode-activated .menu a {
background: none;
}
.openerp .kitten-mode-activated .menu span {
.openerp.kitten-mode-activated .menu span {
background: none;
}
.openerp .kitten-mode-activated .sidebar-displaying-div li a,
.openerp .kitten-mode-activated .view-manager-main-content h2.oe_view_title,
.openerp .kitten-mode-activated .view-manager-main-content a.searchview_group_string,
.openerp .kitten-mode-activated .view-manager-main-content label {
.openerp.kitten-mode-activated .sidebar-displaying-div li a,
.openerp.kitten-mode-activated .oe-application .view-manager-main-content h2.oe_view_title,
.openerp.kitten-mode-activated .oe-application .view-manager-main-content a.searchview_group_string,
.openerp.kitten-mode-activated .oe-application .view-manager-main-content label {
color: white;
}
.openerp .kitten-mode-activated .menu,
.openerp .kitten-mode-activated .header_corner,
.openerp .kitten-mode-activated .secondary_menu div h3,
.openerp .kitten-mode-activated .sidebar-displaying-div h2,
.openerp .kitten-mode-activated .view-manager-main-content #oe_app_view_list,
.openerp .kitten-mode-activated .view-manager-main-content .oe_form_notebook,
.openerp .kitten-mode-activated .view-manager-main-content .oe_form_hpaned {
.openerp.kitten-mode-activated .menu,
.openerp.kitten-mode-activated .header_corner,
.openerp.kitten-mode-activated .header_title,
.openerp.kitten-mode-activated .secondary_menu div,
.openerp.kitten-mode-activated .oe-application,
.openerp.kitten-mode-activated .oe_footer,
.openerp.kitten-mode-activated .loading,
.openerp.kitten-mode-activated .ui-dialog {
opacity:0.8;
filter:alpha(opacity=80);
}
.openerp.kitten-mode-activated .header .company_logo {
background: url(http://placekitten.com/g/180/46);
}
.openerp.kitten-mode-activated .loading {
background: #828282;
border-color: #828282;
}
/* Many2one Autosearch */
.openerp .ui_combo {
height: 20px;
margin-right: 1px;
top: 4px;
width: 22px;
}
/* ------------- End autocomplete ------- */
Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

+1
View File
@@ -126,6 +126,7 @@ openerp.base = function(instance) {
openerp.base.views(instance);
openerp.base.search(instance);
openerp.base.list(instance);
openerp.base.m2o(instance);
openerp.base.form(instance);
};
+1 -5
View File
@@ -790,10 +790,6 @@ openerp.base.Header = openerp.base.Controller.extend({
this.do_update();
},
do_update: function() {
if(jQuery.param != undefined &&
jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
this.kitten = 1;
}
this.$element.html(QWeb.render("Header", this));
}
});
@@ -897,7 +893,7 @@ openerp.base.WebClient = openerp.base.Controller.extend({
var params = {};
if(jQuery.param != undefined &&
jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
params = {kitten:1};
this.$element.addClass("kitten-mode-activated");
}
this.$element.html(QWeb.render("Interface", params));
+138 -5
View File
@@ -3,15 +3,139 @@ openerp.base.data = function(openerp) {
openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.base.DataGroup# */{
/**
* Management interface between views and the collection of selected OpenERP
* records (represents the view's state?)
* Management interface between views and grouped collections of OpenERP
* records.
*
* The root DataGroup is instantiated with the relevant information
* (a session, a model, a domain, a context and a group_by sequence), the
* domain and context may be empty. It is then interacted with via
* :js:func:`~openerp.base.DataGroup.list`, which is used to read the
* content of the current grouping level, and
* :js:func:`~openerp.base.DataGroup.get`, which is used to fetch an item
* of the current grouping level.
*
* @constructs
* @extends openerp.base.Controller
*
* @param {openerp.base.Session} session Current OpenERP session
* @param {String} model name of the model managed by this DataGroup
* @param {Array} domain search domain for this DataGroup
* @param {Object} context context of the DataGroup's searches
* @param {Array} group_by sequence of fields by which to group
*/
init: function(session) {
init: function(session, model, domain, context, group_by) {
this._super(session, null);
this.model = model;
this.context = context;
this.domain = domain;
this.group_by = group_by;
this.groups = null;
},
fetch: function () {
// internal method
var d = new $.Deferred();
var self = this;
if (this.groups) {
d.resolveWith(this, [this.groups]);
} else {
this.rpc('/base/group/read', {
model: this.model,
context: this.context,
domain: this.domain,
group_by_fields: this.group_by
}, function () { }).then(function (response) {
self.groups = response.result;
// read_group results are annoying: they use the name of the
// field grouped on to hold the value and the count, so no
// generic access to those values is possible.
// Alias them to `value` and `length`.
d.resolveWith(self, [_(response.result).map(function (group) {
var field_name = self.group_by[0];
return _.extend({}, group, {
// provide field used for grouping
grouped_on: field_name,
length: group[field_name + '_count'],
value: group[field_name]
});
})]);
}, function () {
d.rejectWith.apply(d, self, [arguments]);
});
}
return d.promise();
},
/**
* Retrieves the content of an item in the DataGroup, which results in
* either a DataSet or new DataGroup instance.
*
* Calling :js:func:`~openerp.base.DataGroup.get` without having called
* :js:func:`~openerp.base.DataGroup.list` beforehand will likely result
* in an error.
*
* The resulting :js:class:`~openerp.base.DataGroup` or
* :js:class:`~openerp.base.DataSet` will be provided through the relevant
* callback function. In both functions, the current DataGroup will be
* provided as context (``this``)
*
* @param {Number} index the index of the group to open in the datagroup's collection
* @param {Function} ifDataSet executed if the item results in a DataSet, provided with the new dataset as parameter
* @param {Function} ifDataGroup executed if the item results in a DataSet, provided with the new datagroup as parameter
*/
get: function (index, ifDataSet, ifDataGroup) {
var group = this.groups[index];
if (!group) {
throw new Error("No group at index " + index);
}
var child_context = _.extend({}, this.context, group.__context);
if (group.__context.group_by.length) {
var datagroup = new openerp.base.DataGroup(
this.session, this.model, group.__domain, child_context,
group.__context.group_by);
ifDataGroup.call(this, datagroup);
} else {
var dataset = new openerp.base.DataSetSearch(this.session, this.model);
dataset.domain = group.__domain;
dataset.context = child_context;
ifDataSet.call(this, dataset);
}
},
/**
* Gathers the content of the current data group (the current grouping
* level), and provides it via a promise object to which callbacks should
* be added::
*
* datagroup.list().then(function (list) {
* // manipulate list here
* });
*
* The argument to the callback is the list of elements fetched, the
* context (``this``) is the datagroup itself.
*
* The items of a list are the standard objects returned by ``read_group``
* with three properties added:
*
* ``length``
* the number of records contained in the group (and all of its
* sub-groups). This does *not* provide the size of the "next level"
* of the group, unless the group is terminal (no more groups within
* it).
* ``grouped_on``
* the name of the field this level was grouped on, this is mostly
* used for display purposes, in order to know the name of the current
* level of grouping. The ``grouped_on`` should be the same for all
* objects of the list.
* ``value``
* the value which led to this group (this is the value all contained
* records have for the current ``grouped_on`` field name).
*
* @returns {$.Deferred}
*/
list: function () {
return this.fetch();
}
});
@@ -89,12 +213,12 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
context: this.context
}, callback);
},
create: function(data, callback) {
create: function(data, callback, error_callback) {
return this.rpc('/base/dataset/create', {
model: this.model,
data: data,
context: this.context
}, callback);
}, callback, error_callback);
},
write: function (id, data, callback) {
return this.rpc('/base/dataset/save', {
@@ -119,6 +243,15 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
args: args
}, callback);
},
name_search: function (search_str, callback) {
search_str = search_str || '';
return this.rpc('/base/dataset/name_search', {
model: this.model,
search_str: search_str,
domain: this.domain || [],
context: this.context
}, callback);
},
exec_workflow: function (id, signal, callback) {
return this.rpc('/base/dataset/exec_workflow', {
model: this.model,
+54 -6
View File
@@ -1,11 +1,21 @@
openerp.base.dates = function(openerp) {
/**
* Converts a string to a Date javascript object using OpenERP's
* datetime string format (exemple: '2011-12-01 15:12:35').
*
* The timezone is assumed to be UTC (standard for OpenERP 6.1)
* and will be converted to the browser's timezone.
*
* @param {String} str A string representing a datetime.
* @returns {Date}
*/
openerp.base.parse_datetime = function(str) {
if(!str) {
return str;
}
var regex = /\d\d\d\d-\d\d-\d\) \d\d:\d\d:\d\d/;
var regex = /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/;
var res = regex.exec(str);
if ( res[0] != str ) {
throw "'" + str + "' is not a valid datetime";
@@ -17,6 +27,13 @@ openerp.base.parse_datetime = function(str) {
return obj;
};
/**
* Converts a string to a Date javascript object using OpenERP's
* date string format (exemple: '2011-12-01').
*
* @param {String} str A string representing a date.
* @returns {Date}
*/
openerp.base.parse_date = function(str) {
if(!str) {
return str;
@@ -33,6 +50,13 @@ openerp.base.parse_date = function(str) {
return obj;
};
/**
* Converts a string to a Date javascript object using OpenERP's
* time string format (exemple: '15:12:35').
*
* @param {String} str A string representing a time.
* @returns {Date}
*/
openerp.base.parse_time = function(str) {
if(!str) {
return str;
@@ -49,7 +73,7 @@ openerp.base.parse_time = function(str) {
return obj;
};
/**
/*
* Just a simple function to add some '0' if an integer it too small.
*/
var fts = function(str, size) {
@@ -61,8 +85,18 @@ var fts = function(str, size) {
return to_add + str;
};
/**
* Converts a Date javascript object to a string using OpenERP's
* datetime string format (exemple: '2011-12-01 15:12:35').
*
* The timezone of the Date object is assumed to be the one of the
* browser and it will be converted to UTC (standard for OpenERP 6.1).
*
* @param {Date} obj
* @returns {String} A string representing a datetime.
*/
openerp.base.format_datetime = function(obj) {
if(! str) {
if (!obj) {
return false;
}
return fts(obj.getUTCFullYear(),4) + "-" + fts(obj.getUTCMonth() + 1,2) + "-"
@@ -70,20 +104,34 @@ openerp.base.format_datetime = function(obj) {
+ fts(obj.getUTCMinutes(),2) + ":" + fts(obj.getUTCSeconds(),2);
};
/**
* Converts a Date javascript object to a string using OpenERP's
* date string format (exemple: '2011-12-01').
*
* @param {Date} obj
* @returns {String} A string representing a date.
*/
openerp.base.format_date = function(obj) {
if(! str) {
if (!obj) {
return false;
}
return fts(obj.getFullYear(),4) + "-" + fts(obj.getMonth() + 1,2) + "-"
+ fts(obj.getDate(),2);
};
/**
* Converts a Date javascript object to a string using OpenERP's
* time string format (exemple: '15:12:35').
*
* @param {Date} obj
* @returns {String} A string representing a time.
*/
openerp.base.format_time = function(obj) {
if(! str) {
if (!obj) {
return false;
}
return fts(obj.getHours(),2) + ":" + fts(obj.getMinutes(),2) + ":"
+ fts(obj.getSeconds(),2);
};
};
};
+252 -35
View File
@@ -2,7 +2,7 @@
openerp.base.form = function (openerp) {
openerp.base.views.add('form', 'openerp.base.FormView');
openerp.base.FormView = openerp.base.Controller.extend( /** @lends openerp.base.FormView# */{
openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormView# */{
/**
* Indicates that this view is not searchable, and thus that no search
* view should be displayed (if there is one active).
@@ -143,14 +143,21 @@ openerp.base.FormView = openerp.base.Controller.extend( /** @lends openerp.base
var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/);
if (call) {
var method = call[1], args = [];
var argument_replacement = {
'False' : false,
'True' : true,
'None' : null
}
_.each(call[2].split(','), function(a) {
var field = _.trim(a);
if (self.fields[field]) {
if (field in argument_replacement) {
args.push(argument_replacement[field]);
} else if (self.fields[field]) {
var value = self.fields[field].value;
args.push(value == null ? false : value);
} else {
args.push(false);
this.log("warning : on_change can't find field " + field, onchange);
self.log("warning : on_change can't find field " + field, onchange);
}
});
var ajax = {
@@ -365,7 +372,8 @@ openerp.base.form.compute_domain = function(expr, fields) {
}
}
return _.indexOf(stack, false) == -1;
},
}
openerp.base.form.Widget = openerp.base.Controller.extend({
init: function(view, node) {
this.view = view;
@@ -532,7 +540,7 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
on_confirmed: function() {
var self = this;
this.execute_action(
this.view.execute_action(
this.node.attrs, this.view.dataset, this.session.action_manager,
this.view.datarecord.id, function (result) {
self.log("Button returned", result);
@@ -540,9 +548,6 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
});
}
});
// let WidgetButton execute actions
_.extend(openerp.base.form.WidgetButton.prototype,
openerp.base.ActionExecutor);
openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
init: function(view, node) {
@@ -649,7 +654,24 @@ openerp.base.form.FieldChar = openerp.base.form.Field.extend({
openerp.base.form.FieldEmail = openerp.base.form.FieldChar.extend({
init: function(view, node) {
this._super(view, node);
this.template = "FieldEmail";
this.validation_regex = /@/;
},
start: function() {
this._super.apply(this, arguments);
this.$element.find('button').click(this.on_button_clicked);
},
on_button_clicked: function() {
if (!this.value || this.invalid) {
this.notification.warn("E-mail error", "Can't send email to invalid e-mail address");
} else {
location.href = 'mailto:' + this.value;
}
},
set_value: function(value) {
this._super.apply(this, arguments);
var show_value = (value != null && value !== false) ? value : '';
this.$element.find('a').attr('href', 'mailto:' + show_value);
}
});
@@ -681,32 +703,69 @@ openerp.base.form.FieldFloat = openerp.base.form.FieldChar.extend({
}
});
openerp.base.form.FieldDate = openerp.base.form.FieldChar.extend({
openerp.base.form.FieldDatetime = openerp.base.form.Field.extend({
init: function(view, node) {
this._super(view, node);
this.template = "FieldDate";
this.validation_regex = /^\d+-\d+-\d+$/;
this.jqueryui_object = 'datetimepicker';
},
start: function() {
this._super.apply(this, arguments);
this.$element.find('input').change(this.on_ui_change).datepicker({
dateFormat: 'yy-mm-dd'
});
}
});
openerp.base.form.FieldDatetime = openerp.base.form.FieldChar.extend({
init: function(view, node) {
this._super(view, node);
this.template = "FieldDatetime";
this.validation_regex = /^\d+-\d+-\d+( \d+:\d+(:\d+)?)?$/;
},
start: function() {
this._super.apply(this, arguments);
this.$element.find('input').change(this.on_ui_change).datetimepicker({
this.$element.find('input').change(this.on_ui_change)[this.jqueryui_object]({
dateFormat: 'yy-mm-dd',
timeFormat: 'hh:mm:ss'
});
},
set_value: function(value) {
this._super.apply(this, arguments);
if (value == null || value == false) {
this.$element.find('input').val('');
} else {
this.value = this.parse(value);
this.$element.find('input')[this.jqueryui_object]('setDate', this.value);
}
},
set_value_from_ui: function() {
this.value = this.$element.find('input')[this.jqueryui_object]('getDate') || false;
if (this.value) {
this.value = this.format(this.value);
}
},
validate: function() {
this.invalid = this.required && this.value === false;
},
parse: openerp.base.parse_datetime,
format: openerp.base.format_datetime
});
openerp.base.form.FieldDate = openerp.base.form.FieldDatetime.extend({
init: function(view, node) {
this._super(view, node);
this.jqueryui_object = 'datepicker';
},
parse: openerp.base.parse_date,
format: openerp.base.format_date
});
openerp.base.form.FieldFloatTime = openerp.base.form.FieldChar.extend({
init: function(view, node) {
this._super(view, node);
this.validation_regex = /^\d+:\d+$/;
},
set_value: function(value) {
value = value || 0;
this._super.apply(this, [value]);
var show_value = _.sprintf("%02d:%02d", Math.floor(value), Math.round((value % 1) * 60));
this.$element.find('input').val(show_value);
},
validate: function() {
if (typeof(this.value) == "string") {
this._super.apply(this, arguments);
if (!this.invalid) {
var time = this.value.split(':');
this.value = parseInt(time[0], 10) + parseInt(time[1], 10) / 60;
}
}
}
});
@@ -827,10 +886,33 @@ openerp.base.form.FieldSelection = openerp.base.form.Field.extend({
}
});
openerp.base.form.FieldMany2OneDatasSet = openerp.base.DataSetStatic.extend({
start: function() {
},
write: function (id, data, callback) {
this._super(id, data, callback);
},
});
openerp.base.form.FieldMany2OneViewManager = openerp.base.ViewManager.extend({
init: function(session, element_id, dataset, views) {
this._super(session, element_id, dataset, views);
}
});
openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
init: function(view, node) {
this._super(view, node);
this.template = "FieldMany2One";
this.is_field_m2o = true;
},
start: function() {
this.$element = $('#' + this.element_id);
this.dataset = new openerp.base.form.FieldMany2OneDatasSet(this.session, this.field.relation);
var views = [ [false,"list"], [false,"form"] ];
this.viewmanager = new openerp.base.form.FieldMany2OneViewManager(this.view.session, this.element_id, this.dataset, views);
new openerp.base.m2o(this.viewmanager, this.$element, this.field.relation, this.dataset, this.session)
this.$element.find('input').change(this.on_ui_change);
},
set_value: function(value) {
this._super.apply(this, arguments);
@@ -840,6 +922,16 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
this.value = value[0];
}
this.$element.find('input').val(show_value);
this.$element.find('input').attr('m2o_id', this.value);
},
get_value: function() {
var val = this.$element.find('input').attr('m2o_id') || this.value
return val;
},
on_ui_change: function() {
this.touched = this.view.touched = true;
}
});
@@ -900,6 +992,8 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
this._super(view, node);
this.template = "FieldMany2Many";
this.list_id = _.uniqueId("many2many");
this.is_started = false;
this.is_setted = false;
},
start: function() {
this._super.apply(this, arguments);
@@ -909,23 +1003,35 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
var self = this;
this.list_view.m2m_field = this;
this.list_view.start();
var hack = {loaded: false};
this.list_view.on_loaded.add_last(function() {
if (! hack.loaded) {
self.is_started = true;
self.check_load();
hack.loaded = true;
}
});
},
set_value: function(value) {
if (value != false) {
this.dataset.ids = value;
this.dataset.count = value.length;
this.list_view.do_reload();
this.is_setted = true;
this.check_load();
}
},
get_value: function() {
return [[6,false,this.dataset.ids]];
},
check_load: function() {
if(this.is_started && this.is_setted) {
this.list_view.do_reload();
}
}
});
openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
do_delete: function (e) {
e.stopImmediatePropagation();
var ids = [this.rows[$(e.currentTarget).closest('tr').prevAll().length].data.id.value];
do_delete: function (ids) {
this.dataset.ids = _.without.apply(null, [this.dataset.ids].concat(ids));
this.dataset.count = this.dataset.ids.length;
// there may be a faster way
@@ -947,12 +1053,20 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
},
do_add_record: function (e) {
e.stopImmediatePropagation();
// TODO: need to open a popup with search view
var pop = new openerp.base.form.Many2XSelectPopup(null, this.m2m_field.view.session);
pop.select_element(this.model);
var self = this;
pop.on_select_element.add(function(element_id) {
if(! _.detect(self.dataset.ids, function(x) {return x == element_id;})) {
self.dataset.ids.push(element_id);
self.dataset.count = self.dataset.ids.length;
self.do_reload();
}
pop.stop();
});
},
on_select_row: function(event) {
var $target = $(event.currentTarget);
var row = this.rows[$target.prevAll().length];
var id = row.data.id.value;
select_record: function(index) {
var id = this.rows[index].data.id.value;
if(! id) {
return;
}
@@ -969,6 +1083,97 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
}
});
openerp.base.form.Many2XSelectPopup = openerp.base.BaseWidget.extend({
identifier_prefix: "many2xselectpopup",
template: "Many2XSelectPopup",
select_element: function(model) {
this.model = model;
var html = this.render();
jQuery(html).dialog({title: '',
modal: true,
minWidth: 800});
this.start();
},
start: function() {
this._super();
this.dataset = new openerp.base.DataSetSearch(this.session, this.model);
this.setup_search_view();
},
setup_search_view: function() {
var self = this;
if (this.searchview) {
this.searchview.stop();
}
this.searchview = new openerp.base.SearchView(this, this.session, this.element_id + "_search",
this.dataset, false, {});
this.searchview.on_search.add(function(domains, contexts, groupbys) {
self.view_list.do_search.call(
self, domains, contexts, groupbys);
});
this.searchview.on_loaded.add_last(function () {
var $buttons = self.searchview.$element.find(".oe_search-view-buttons");
$buttons.append(QWeb.render("Many2XSelectPopup.search.buttons"));
var $nbutton = $buttons.find(".oe_many2xselectpopup-search-new");
$nbutton.click(function() {
self.new_object();
});
var $cbutton = $buttons.find(".oe_many2xselectpopup-search-close");
$cbutton.click(function() {
self.stop();
});
self.view_list = new openerp.base.form.Many2XPopupListView( null, self.session,
self.element_id + "_view_list", self.dataset, false);
self.view_list.popup = self;
self.view_list.do_show();
self.view_list.start();
var tmphack = {"loaded": false};
self.view_list.on_loaded.add_last(function() {
if ( !tmphack.loaded ) {
self.view_list.do_reload();
tmphack.loaded = true;
};
});
});
this.searchview.start();
},
on_select_element: function(element_id) {
},
new_object: function() {
var self = this;
this.searchview.hide();
this.view_list.$element.hide();
this.dataset.index = null;
this.view_form = new openerp.base.FormView({}, this.session,
this.element_id + "_view_form", this.dataset, false);
this.view_form.start();
this.view_form.on_loaded.add_last(function() {
var $buttons = self.view_form.$element.find(".oe_form_buttons");
$buttons.html(QWeb.render("Many2XSelectPopup.form.buttons"));
var $nbutton = $buttons.find(".oe_many2xselectpopup-form-save");
$nbutton.click(function() {
self.view_form.do_save();
});
var $cbutton = $buttons.find(".oe_many2xselectpopup-form-close");
$cbutton.click(function() {
self.stop();
});
});
this.view_form.on_created.add_last(function(r, success) {
if (r.result) {
var id = arguments[0].result;
self.on_select_element(id);
}
});
this.view_form.do_show();
}
});
openerp.base.form.Many2XPopupListView = openerp.base.ListView.extend({
switch_to_record: function(index) {
this.popup.on_select_element(this.dataset.ids[index]);
}
});
openerp.base.form.FieldReference = openerp.base.form.Field.extend({
init: function(view, node) {
this._super(view, node);
@@ -976,6 +1181,17 @@ openerp.base.form.FieldReference = openerp.base.form.Field.extend({
}
});
openerp.base.form.FieldImage = openerp.base.form.Field.extend({
init: function(view, node) {
this._super(view, node);
this.template = "FieldImage";
},
set_value: function(value) {
this._super.apply(this, arguments);
this.$element.find('img').show().attr('src', 'data:image/png;base64,' + this.value);
}
});
/**
* Registry of form widgets, called by :js:`openerp.base.FormView`
*/
@@ -1002,7 +1218,8 @@ openerp.base.form.widgets = new openerp.base.Registry({
'float' : 'openerp.base.form.FieldFloat',
'integer': 'openerp.base.form.FieldFloat',
'progressbar': 'openerp.base.form.FieldProgressBar',
'float_time': 'openerp.base.form.FieldFloat'
'float_time': 'openerp.base.form.FieldFloatTime',
'image': 'openerp.base.form.FieldImage'
});
};
+65 -13
View File
@@ -1,7 +1,6 @@
openerp.base.list = function (openerp) {
openerp.base.views.add('list', 'openerp.base.ListView');
openerp.base.ListView = openerp.base.Controller.extend(
/** @lends openerp.base.ListView# */ {
openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListView# */ {
defaults: {
// records can be selected one by one
'selectable': true,
@@ -61,7 +60,11 @@ openerp.base.ListView = openerp.base.Controller.extend(
columns: this.columns,
rows: this.rows
});
$(this.list).bind({
this.groups = new openerp.base.ListView.Groups({
options: this.options,
columns: this.columns
});
$([this.list, this.groups]).bind({
'selected': function (e, selection) {
self.$element.find('#oe-list-delete')
.toggle(!!selection.length);
@@ -80,7 +83,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
id, self.do_reload);
},
'row_link': function (e, index) {
self.switch_to_record(index);
self.select_record(index);
}
});
@@ -233,7 +236,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
* @param {Number|null} index the record index (in the current dataset) to switch to
* @param {String} [view="form"] the view type to switch to
*/
switch_to_record:function (index, view) {
select_record:function (index, view) {
view = view || 'form';
this.dataset.index = index;
_.delay(_.bind(function () {
@@ -290,6 +293,14 @@ openerp.base.ListView = openerp.base.Controller.extend(
// TODO: handle non-empty results.group_by with read_group
self.dataset.context = results.context;
self.dataset.domain = results.domain;
if (results.group_by.length) {
self.groups.datagroup = new openerp.base.DataGroup(
self.session, self.dataset.model,
results.domain, results.context,
results.group_by);
self.$element.html(self.groups.render());
return;
}
return self.do_reload();
});
},
@@ -338,7 +349,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
*/
do_add_record: function () {
this.notification.notify('Add', "New record");
this.switch_to_record(null);
this.select_record(null);
},
/**
* Handles deletion of all selected lines
@@ -349,10 +360,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
}
// TODO: implement reorder (drag and drop rows)
});
_.extend(openerp.base.ListView.prototype, openerp.base.ActionExecutor);
openerp.base.ListView.List = Class.extend(
/** @lends openerp.base.ListView.List# */{
openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List# */{
/**
* List display for the ListView, handles basic DOM events and transforms
* them in the relevant higher-level events, to which the list view (or
@@ -464,10 +472,54 @@ openerp.base.ListView.List = Class.extend(
// drag and drop
// editable?
});
openerp.base.TreeView = openerp.base.Controller.extend({
openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Groups# */{
/**
* Grouped display for the ListView. Handles basic DOM events and interacts
* with the :js:class:`~openerp.base.DataGroup` bound to it.
*
* Provides events similar to those of
* :js:class:`~openerp.base.ListView.List`
*/
init: function (opts) {
this.options = opts.options;
this.columns = opts.columns;
this.datagroup = {};
},
make_level: function (datagroup) {
var self = this, $root = $('<dl>');
datagroup.list().then(function (list) {
_(list).each(function (group, index) {
var $title = $('<dt>')
.text(group.grouped_on + ': ' + group.value + ' (' + group.length + ')')
.appendTo($root);
$title.click(function () {
datagroup.get(index, function (new_dataset) {
var $content = $('<ul>').appendTo(
$('<dd>').insertAfter($title));
new_dataset.read_slice([], null, null, function (records) {
_(records).each(function (record) {
$('<li>')
.appendTo($content)
.text(_(record).map(function (value, key) {
return key + ': ' + value;
}).join(', '));
});
});
}, function (new_datagroup) {
console.log(new_datagroup);
$('<dd>')
.insertAfter($title)
.append(self.make_level(new_datagroup));
});
});
});
});
return $root;
},
render: function () {
return this.make_level(this.datagroup);
}
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
+121
View File
@@ -0,0 +1,121 @@
openerp.base.m2o = function(openerp){
openerp.base.m2o = openerp.base.Controller.extend({
init: function(view_manager, element_id, model, dataset, session){
this._super(element_id, model, dataset, session);
this.view_manager = view_manager
this.session = session;
this.element = element_id.find('input');
this.dataset = dataset;
var cache = {};
var lastXhr;
this.relation = model;
this.result_ids = []
var self = this
var $input = this.element.autocomplete({
source: function(request, response){
var search_val = request.term;
if (search_val in cache) {
response(cache[search_val]);
return;
}
//pass request to server
lastXhr = self.dataset.name_search(search_val, function(obj, status, xhr){
var result = obj.result
var values = [];
if (!result.length) {
values.push({'value': 'Create...', id: 'create'})
}
$.each(result, function(i, val){
values.push({
value: val[1],
id: val[0],
orig_val: val[1]
});
self.result_ids.push(result[i][0])
});
if (values.length > 10) {
values = values.slice(0, 10);
values.push({'value': 'More...', id: 'more'})
}
//process response
cache[search_val] = values;
response(values);
});
return;
},
select: function(event, ui){
ui.item.value = ui.item.orig_val? ui.item.orig_val : self.element.data( "autocomplete" ).term
if (ui.item.id == 'more') {
self.dataset.ids = self.result_ids;
self.dataset.count = self.dataset.ids.length;
self.dataset.domain = []
self.element.val('')
var pop = new openerp.base.form.Many2XSelectPopup(null, self.session);
pop.select_element(self.relation, self.dataset);
return;
}
if (ui.item.id == 'create') {
var val = self.element.val()
self.dataset.create({'name': ui.item.value},
function(r){}, function(r){
var element_id = _.uniqueId("act_window_dialog");
var dialog = jQuery('<div>',
{'id': element_id
}).dialog({
modal: true,
minWidth: 800
});
self.element.val('')
var event_form = new openerp.base.FormView(self.view_manager, self.session, element_id, self.dataset, false);
event_form.start();
});
$input.val(self.element.data( "autocomplete" ).term);
return true;
}
self.element.attr('m2o_id', ui.item.id);
},
minLength: 0,
focus: function(event, ui) {
if (ui.item.id == ('create')) {
return true;
}
ui.item.value = self.element.data("autocomplete").term.length ? self.element.val() + '[' + ui.item.orig_val.substring(self.element.data("autocomplete").term.length) + ']' : this.lastSearch
},
});
$("<div type='button' class='ui_combo'>&nbsp;</div>")
.attr("tabIndex", -1)
.attr("title", "Show All Items")
.insertAfter($input)
.button({
icons: {
primary: "ui-icon-triangle-1-s"
},
text: false
})
.removeClass("ui-corner-all")
.addClass("ui-corner-right ui-button-icon")
.click(function() {
// close if already visible
if ($input.autocomplete("widget").is(":visible")) {
$input.autocomplete( "close" );
return;
}
$(this).blur();
$input.autocomplete("search", "" );
$input.focus();
});
}
});
}
+4 -1
View File
@@ -121,7 +121,10 @@ openerp.base.SearchView = openerp.base.Controller.extend({
'lines': lines,
'defaults': this.defaults
});
this.$element.html(render);
// We don't understand why the following commented line does not work in Chrome but
// the non-commented line does. As far as we investigated, only God knows.
//this.$element.html(render);
jQuery(render).appendTo(this.$element);
var f = this.$element.find('form');
this.$element.find('form')
+26
View File
@@ -0,0 +1,26 @@
/*---------------------------------------------------------
* OpenERP base library
*---------------------------------------------------------*/
openerp.base.view_tree = function(openerp) {
openerp.base.views.add('tree', 'openerp.base.TreeView');
openerp.base.TreeView = openerp.base.Controller.extend({
/**
* Genuine tree view (the one displayed as a tree, not the list)
*/
start: function () {
this._super();
this.$element.append('Tree view');
},
do_show: function () {
this.$element.show();
},
do_hide: function () {
this.$element.hide();
}
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
+72 -147
View File
@@ -17,17 +17,23 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
/**
* Process an action
* Supported actions: act_window
*
* If the action contains a 'no_sidebar' key, the resulting view will never contain a sidebar.
*/
do_action: function(action) {
var self = this;
action.flags = _.extend({
sidebar : true,
search_view : true,
new_window : false,
views_switcher : true,
toolbar : true,
pager : true
}, action.flags || {});
// instantiate the right controllers by understanding the action
switch (action.type) {
case 'ir.actions.act_window':
if (action.target == 'new') {
var element_id = _.uniqueId("act_window_dialog");
var dialog = $('<div id="'+element_id+'"></div>');
var dialog = $('<div id="' + element_id + '"></div>');
dialog.dialog({
title: action.name,
modal: true,
@@ -37,18 +43,21 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
// When dialog is closed with ESC key or close manually, branch to act_window_close logic
self.do_action({ type: 'ir.actions.act_window_close' });
});
var viewmanager = new openerp.base.ViewManagerAction(this.session, element_id, action, false);
var viewmanager = new openerp.base.ViewManagerAction(this.session, element_id, action);
viewmanager.start();
this.dialog_stack.push(viewmanager);
} else if (action.flags.new_window) {
action.flags.new_window = false;
this.rpc("/base/session/save_session_action", { the_action : action}, function(key) {
var url = window.location.protocol + "//" + window.location.host +
window.location.pathname + "?" + jQuery.param({ s_action : "" + key });
window.open(url);
});
} else {
if (this.viewmanager) {
this.viewmanager.stop();
}
var sidebar = true;
if(action.no_sidebar) {
sidebar = false;
}
this.viewmanager = new openerp.base.ViewManagerAction(this.session, this.element_id, action, sidebar);
this.viewmanager = new openerp.base.ViewManagerAction(this.session, this.element_id, action);
this.viewmanager.start();
}
break;
@@ -63,63 +72,6 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
}
});
/**
* Mixin for action-executing objects, provides handling of OpenERP actions to
* all clients.
*
* Mix into existing classes via ``_.extend`` of the class's prototype.
*
* @class
*/
openerp.base.ActionExecutor =
/**
* @lends openerp.base.ActionExecutor#
*/ {
/**
* Fetches and executes the action identified by ``action_data``.
*
* @param {Object} action_data the action descriptor data
* @param {String} action_data.name the action name, used to uniquely identify the action to find and execute it
* @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``)
* @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'``
* @param {Object} [action_data.context=null] additional action context, to add to the current context
* @param {openerp.base.DataSet} dataset a dataset object used to communicate with the server
* @param {openerp.base.ActionManager} action_manager object able to actually execute the action, if any is fetched
* @param {Number} [record_id] the identifier of the object on which the action is to be applied
* @param {Function} on_no_action callback to execute if the action does not generate any result (no new action)
*/
execute_action: function (action_data, dataset, action_manager, record_id, on_no_action) {
var handler = function (r) {
if (r.result && r.result.constructor == Object) {
action_manager.do_action(r.result);
} else {
on_no_action(r.result);
}
};
if (action_data.special) {
handler({
result : { type: 'ir.actions.act_window_close' }
});
} else {
var context = _.extend({}, dataset.context, action_data.context || {});
switch(action_data.type) {
case 'object':
return dataset.call(action_data.name, [record_id], [context], handler);
case 'action':
return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10) }, handler);
default:
return dataset.exec_workflow(record_id, action_data.name, handler);
}
}
}
};
/**
* Registry for all the main views
*/
openerp.base.views = new openerp.base.Registry();
openerp.base.ViewManager = openerp.base.Controller.extend({
init: function(session, element_id, dataset, views) {
this._super(session, element_id);
@@ -129,6 +81,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
this.active_view = null;
this.views_src = views;
this.views = {};
this.flags = this.flags || {};
},
/**
* @returns {jQuery.Deferred} initial view loading promise
@@ -143,6 +96,9 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
_.each(this.views_src, function(view) {
self.views[view[1]] = { view_id: view[0], controller: null };
});
if (this.flags.views_switcher === false) {
this.$element.find('.oe_vm_switch').hide();
}
// switch to the first one in sequence
return this.on_mode_switch(this.views_src[0][1]);
},
@@ -166,7 +122,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
this.views[view_type].controller = controller;
}
if(this.searchview) {
if (this.searchview) {
if (view.controller.searchable === false) {
this.searchview.hide();
} else {
@@ -202,6 +158,9 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
this.searchview.stop();
}
this.searchview = new openerp.base.SearchView(this, this.session, this.element_id + "_search", this.dataset, view_id, search_defaults);
if (this.flags.search_view === false) {
this.searchview.hide();
}
this.searchview.on_search.add(function(domains, contexts, groupbys) {
self.views[self.active_view].controller.do_search.call(
self, domains.concat(self.domains()),
@@ -240,7 +199,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
});
openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
init: function(session, element_id, action, sidebar) {
init: function(session, element_id, action) {
var dataset;
if(!action.res_id) {
dataset = new openerp.base.DataSetSearch(session, action.res_model);
@@ -251,9 +210,10 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
}
this._super(session, element_id, dataset, action.views);
this.action = action;
this.sidebar = sidebar;
if (sidebar)
this.flags = this.action.flags || {};
if (this.flags.sidebar) {
this.sidebar = new openerp.base.Sidebar(null, this);
}
},
start: function() {
var inital_view_loaded = this._super();
@@ -346,7 +306,10 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
var i = $this.attr("data-i");
var j = $this.attr("data-j");
var action = self.sections[i].elements[j];
(new openerp.base.ExternalActionManager(self.view_manager.session, null)) .handle_action(action);
action.flags = {
new_window : true
}
self.session.action_manager.do_action(action);
e.stopPropagation();
e.preventDefault();
});
@@ -357,89 +320,51 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
}
});
openerp.base.ExternalActionManager = openerp.base.Controller.extend({
handle_action: function(action) {
if(action.type=="ir.actions.act_window") {
if(action.target=="new") {
var element_id = _.uniqueId("act_window_dialog");
var dialog = $('<div id="'+element_id+'"></div>');
dialog.dialog({
title: action.name
});
var viewmanager = new openerp.base.ViewManagerAction(this.session ,element_id, action, false);
viewmanager.start();
} else if (action.target == "current") {
this.rpc("/base/session/save_session_action", {the_action:action}, function(key) {
var url = window.location.protocol + "//" + window.location.host +
window.location.pathname + "?" + jQuery.param({s_action:""+key});
window.open(url);
});
openerp.base.View = openerp.base.Controller.extend({
/**
* Fetches and executes the action identified by ``action_data``.
*
* @param {Object} action_data the action descriptor data
* @param {String} action_data.name the action name, used to uniquely identify the action to find and execute it
* @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``)
* @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'``
* @param {Object} [action_data.context=null] additional action context, to add to the current context
* @param {openerp.base.DataSet} dataset a dataset object used to communicate with the server
* @param {openerp.base.ActionManager} action_manager object able to actually execute the action, if any is fetched
* @param {Number} [record_id] the identifier of the object on which the action is to be applied
* @param {Function} on_no_action callback to execute if the action does not generate any result (no new action)
*/
execute_action: function (action_data, dataset, action_manager, record_id, on_no_action) {
var handler = function (r) {
if (r.result && r.result.constructor == Object) {
action_manager.do_action(r.result);
} else {
on_no_action(r.result);
}
};
if (action_data.special) {
handler({
result : { type: 'ir.actions.act_window_close' }
});
} else {
var context = _.extend({}, dataset.context, action_data.context || {});
switch(action_data.type) {
case 'object':
return dataset.call(action_data.name, [record_id], [context], handler);
case 'action':
return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10) }, handler);
default:
return dataset.exec_workflow(record_id, action_data.name, handler);
}
}
// TODO: show an error like "not implemented" here
// since we don't currently have any way to handle errors do you have any better idea
// than using todos?
}
});
openerp.base.views.add('calendar', 'openerp.base.CalendarView');
openerp.base.CalendarView = openerp.base.Controller.extend({
start: function () {
this._super();
this.$element.append('Calendar view');
},
do_show: function () {
this.$element.show();
},
do_hide: function () {
this.$element.hide();
}
});
openerp.base.views.add('gantt', 'openerp.base.GanttView');
openerp.base.GanttView = openerp.base.Controller.extend({
start: function () {
this._super();
this.$element.append('Gantt view');
},
do_show: function () {
this.$element.show();
},
do_hide: function () {
this.$element.hide();
}
});
openerp.base.views.add('tree', 'openerp.base.TreeView');
openerp.base.TreeView = openerp.base.Controller.extend({
/**
* Genuine tree view (the one displayed as a tree, not the list)
* Registry for all the main views
*/
start: function () {
this._super();
this.$element.append('Tree view');
},
do_show: function () {
this.$element.show();
},
do_hide: function () {
this.$element.hide();
}
});
openerp.base.views.add('graph', 'openerp.base.GraphView');
openerp.base.GraphView = openerp.base.Controller.extend({
start: function () {
this._super();
this.$element.append('Graph view');
},
do_show: function () {
this.$element.show();
},
do_hide: function () {
this.$element.hide();
}
});
openerp.base.views = new openerp.base.Registry();
openerp.base.ProcessView = openerp.base.Controller.extend({
});
+47 -15
View File
@@ -18,8 +18,7 @@
</div>
</div>
<div id="oe_login" class="login"></div>
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%"
t-att-class="'main_table' + (typeof kitten == 'undefined' ? '' : ' kitten-mode-activated')">
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" class="main_table">
<tr>
<td colspan="2">
<div id="oe_header" class="header"></div>
@@ -55,8 +54,7 @@
</t>
<t t-name="Header">
<a href="/" class="company_logo_link">
<img t-att-src="typeof kitten == 'undefined' ? '/base/static/src/img/logo.png' :
'http://placekitten.com/g/179/46'" border="0" class="company_logo"/>
<div class="company_logo" />
</a>
<h1 class="header_title" t-if="session.session_is_valid()">
<span class="company">$company</span> - (<span class="database">$database</span>)<br/>
@@ -259,7 +257,7 @@
<t t-foreach="row" t-as="td">
<td t-att-colspan="td.colspan gt 1 ? td.colspan : undefined"
t-att-width="td.width ? td.width : undefined"
t-att-nowrap="td.is_field_label ? 'true' : undefined"
t-att-nowrap="td.is_field_label or td.is_field_m2o? 'true' : undefined"
t-att-valign="td.table ? 'top' : undefined"
t-att-id="td.element_id"
t-att-class="'oe_form_' + (td.is_field_label ? 'label' : (td.field ? 'field_' + td.type : td.type))"
@@ -306,6 +304,20 @@
t-att-class="'field_' + widget.type" style="width: 100%"
/>
</t>
<t t-name="FieldEmail">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td width="100%">
<t t-call="FieldChar"/>
</td>
<td width="16">
<button class="button" title="Send an e-mail with your default e-mail client">
<img src="/base/static/src/img/icons/terp-mail-message-new.png"/>
</button>
</td>
</tr>
</table>
</t>
<t t-name="FieldText">
<textarea rows="6" style="width: 100%;"
t-att-name="widget.name"
@@ -320,13 +332,6 @@
t-att-class="'field_' + widget.type"
/>
</t>
<t t-name="FieldDatetime">
<input type="text" style="width: 100%"
t-att-name="widget.name"
t-att-id="widget.element_id + '_field'"
t-att-class="'field_' + widget.type"
/>
</t>
<t t-name="FieldSelection">
<select
t-att-name="widget.name"
@@ -345,7 +350,9 @@
t-att-name="widget.name"
t-att-id="widget.element_id + '_field'"
t-att-class="'field_' + widget.type"
t-att-type="widget.type"
style="width: 100%;"/>
</t>
<t t-name="FieldOne2Many">
<div t-att-id="widget.element_id">
@@ -369,6 +376,15 @@
<span></span>
</div>
</t>
<t t-name="FieldImage">
<img align="left" border="1" style="display: none"
t-att-id="widget.element_id + '_field'"
t-att-name="widget.name"
t-att-class="'field_' + widget.type"
t-att-width="widget.node.attrs.img_width || widget.node.attrs.width"
t-att-height="widget.node.attrs.img_height || widget.node.attrs.height"
/>
</t>
<t t-name="WidgetButton">
<button type="button"
t-att-id="widget.element_id + '_button'"
@@ -382,7 +398,7 @@
<h2 class="oe_view_title"><t t-esc="view.attrs['string']"/></h2>
<form>
<t t-call="SearchView.render_lines"/>
<div style="text-align:right;">
<div class="oe_search-view-buttons" style="text-align:right;">
<input type="submit" value="Search"/>
<input type="reset" value="Clear"/>
</div>
@@ -418,7 +434,7 @@
<span t-if="attrs.help">(?)</span>
</label>
<div style="white-space: nowrap;">
<input type="text" t-att-name="attrs.name"
<input type="text" size="15" t-att-name="attrs.name"
t-att-autofocus="attrs.default_focus === '1' ? 'autofocus' : undefined"
t-att-id="element_id"
t-att-value="defaults[attrs.name] || ''"/>
@@ -490,7 +506,8 @@
<t t-set="expand" t-value="false"/>
<t t-set="label" t-value="'Custom Filters'"/>
<t t-set="content">
<div class="searchview_extended_groups_list"/>
<div class="searchview_extended_groups_list">
</div>
<button class="searchview_extended_add_group"
type="button"><span>Add group of conditions</span></button>
</t>
@@ -570,4 +587,19 @@
</p>
</div>
</t>
<t t-name="Many2XSelectPopup">
<div t-att-id="element_id">
<div t-att-id="element_id + '_search'"></div>
<div t-att-id="element_id + '_view_list'"></div>
<div t-att-id="element_id + '_view_form'"></div>
</div>
</t>
<t t-name="Many2XSelectPopup.search.buttons">
<button type="button" class="oe_many2xselectpopup-search-new">New</button>
<button type="button" class="oe_many2xselectpopup-search-close">Close</button>
</t>
<t t-name="Many2XSelectPopup.form.buttons">
<button type="button" class="oe_many2xselectpopup-form-save">Save</button>
<button type="button" class="oe_many2xselectpopup-form-close">Close</button>
</t>
</templates>
+1
View File
@@ -22,6 +22,7 @@
<script src="/base/static/src/js/data.js"></script>
<script src="/base/static/src/js/views.js"></script>
<script src="/base/static/src/js/search.js"></script>
<script src="/base/static/src/js/m2o.js"></script>
<script src="/base/static/src/js/form.js"></script>
<script src="/base/static/src/js/list.js"></script>
<script type="text/javascript">
@@ -107,9 +107,11 @@ openerp.base.form.Action = openerp.base.form.Widget.extend({
on_load_action: function(result) {
var action = result.action;
action.search_view = action.search_view_id = false;
action.no_sidebar = true;
action.flags = {
search_view : false,
sidebar : false,
views_switcher : false
}
var node_attrs = this.node.attrs;
var content_id = jQuery('#'+node_attrs.name).find('.portlet-content').attr('id')
var action_manager = new openerp.base.ActionManager(this.session, content_id);
@@ -143,4 +145,4 @@ openerp.base.form.widgets.add('hpaned', 'openerp.base.form.Board');
openerp.base.form.widgets.add('child', 'openerp.base.form.Dashbar');
openerp.base.form.widgets.add('vpaned', 'openerp.base.form.Vpaned');
openerp.base.form.widgets.add('action', 'openerp.base.form.Action');
}
}
+2 -2
View File
@@ -3,8 +3,8 @@
"version": "2.0",
"depends": ['base'],
"js": [
'static/lib/dhtmlxGantt/codebase/dhtmlxcommon.js',
'static/lib/dhtmlxGantt/codebase/dhtmlxgantt.js',
'static/lib/dhtmlxGantt/sources/dhtmlxcommon.js',
'static/lib/dhtmlxGantt/sources/dhtmlxgantt.js',
'static/src/js/gantt.js'
],
"css": ['static/lib/dhtmlxGantt/codebase/dhtmlxgantt.css'],
+5 -178
View File
@@ -1,184 +1,11 @@
import glob, os
from xml.etree import ElementTree
import math
import simplejson
import openerpweb
import time
import datetime
from base.controllers.main import Xml2Json
from base.controllers.main import View
COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
'#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
'#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
'#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900', ]
_colorline = ['#%02x%02x%02x' % (25 + ((r + 10) % 11) * 23, 5 + ((g + 1) % 11) * 20, 25 + ((b + 4) % 11) * 23) for r in range(11) for g in range(11) for b in range(11) ]
def choice_colors(n):
if n > len(COLOR_PALETTE):
return _colorline[0:-1:len(_colorline) / (n + 1)]
elif n:
return COLOR_PALETTE[:n]
return []
class GanttView(openerpweb.Controller):
class GanttView(View):
_cp_path = "/base_gantt/ganttview"
date_start = None
date_delay = None
date_stop = None
color_field = None
day_length = 8
fields = {}
events = []
calendar_fields = {}
ids = []
model = ''
domain = []
context = {}
event_res = []
colors = {}
color_values = []
@openerpweb.jsonrequest
def load(self, req, model, view_id):
m = req.session.model(model)
r = m.fields_view_get(view_id, 'gantt')
r["arch"] = Xml2Json.convert_to_structure(r["arch"])
return {'fields_view':r}
@openerpweb.jsonrequest
def get_events(self, req, **kw):
fields_view = self.fields_view_get(req, model, view_id, 'gantt')
return {'fields_view':fields_view}
self.model = kw['model']
self.fields = kw['fields']
self.day_length = kw['day_length']
self.calendar_fields = kw['calendar_fields']
self.color_field = kw.get('color_field') or self.color_field or None
self.fields[self.color_field] = ""
self.colors = kw.get('colors') or {}
self.text = self.calendar_fields['text']['name']
self.date_start = self.calendar_fields['date_start']['name']
self.fields[self.date_start] = ""
if self.calendar_fields.get('date_stop'):
self.date_stop = self.calendar_fields['date_stop']['name']
self.fields[self.date_stop] = ""
if self.calendar_fields.get('date_delay'):
self.date_delay = self.calendar_fields['date_delay']['name']
self.fields[self.date_delay] = ""
if self.calendar_fields.get('parent'):
self.parent = self.calendar_fields['parent']['name']
self.fields[self.parent] = ""
model = req.session.model(self.model)
event_ids = model.search([])
return self.create_event(event_ids, model)
def create_event(self, event_ids, model):
self.events = model.read(event_ids, self.fields.keys())
result = []
for evt in self.events:
event_res = {}
key = evt[self.color_field]
name = key
value = key
if isinstance(key, list): # M2O, XMLRPC returns List instead of Tuple
name = key[0]
value = key[-1]
evt[self.color_field] = key = key[-1]
if isinstance(key, tuple): # M2O
value, name = key
self.colors[key] = (name, value, None)
st_date = evt.get(self.date_start)
if st_date:
self.set_format(st_date)
if self.date_delay:
duration = evt.get(self.date_delay)
else:
en_date = evt.get(self.date_stop)
duration = (time.mktime(time.strptime(en_date, self.format))-\
time.mktime(time.strptime(st_date, self.format)))/ (60 * 60)
if duration > self.day_length :
d = math.floor(duration / 24)
h = duration % 24
duration = d * self.day_length + h
event_res = {}
event_res['start_date'] = st_date
event_res['duration'] = duration
event_res['text'] = evt.get(self.text)
event_res['id'] = evt['id']
event_res['parent'] = evt.get(self.parent)
result.append(event_res)
colors = choice_colors(len(self.colors))
for i, (key, value) in enumerate(self.colors.items()):
self.colors[key] = [value[0], value[1], colors[i]]
return {'result': result,'sidebar': self.colors}
def set_format(self, st_date):
if len(st_date) == 10 :
self.format = "%Y-%m-%d"
else :
self.format = "%Y-%m-%d %H:%M:%S"
return
def check_format(self, date):
if self.format == "%Y-%m-%d %H:%M:%S":
date = date + " 00:00:00"
return date
@openerpweb.jsonrequest
def on_event_resize(self, req, **kw):
if self.date_delay:
key = self.date_delay
value = kw['duration']
else:
key = self.date_stop
value = self.check_format(kw['end_date'])
try:
model = req.session.model(self.model)
res = model.write(kw['id'], {key : value})
except Exception, e:
print "eeeeeeeeeeeeeeeeeeeeeeeeeee",e
return True
@openerpweb.jsonrequest
def on_event_drag(self, req, **kw):
start_date = self.check_format(kw['start_date'])
if self.date_delay:
key = self.date_delay
value = kw['duration']
else:
key = self.date_stop
value = self.check_format(kw['end_date'])
try:
model = req.session.model(self.model)
res = model.write(kw['id'], {self.date_start : start_date, key : value})
except Exception, e:
print "eeeeeeeeeeeeeeeeeeeeeeeeeee",e
return True
@openerpweb.jsonrequest
def reload_gantt(self, req, **kw):
model = req.session.model(kw['model'])
if (kw['domain']):
domain = (kw['color_field'],'in', kw['domain'])
event_ids = model.search([domain])
else:
event_ids = model.search([])
return self.create_event(event_ids, model)
@@ -173,8 +173,9 @@ GanttProjectInfo.prototype.getTaskByIdInTree = function(parentTask, id)
* @type: public
* @topic: 0
*/
function GanttTaskInfo(id, name, est, duration, percentCompleted, predecessorTaskId)
function GanttTaskInfo(id, name, est, duration, percentCompleted, predecessorTaskId, color)
{
this.color = color || "white";
this.Id = id;
this.Name = name;
this.EST = est;
@@ -348,7 +349,7 @@ GanttProject.prototype.create = function()
* @type: public
* @topic: 0
*/
function GanttChart()
function GanttChart(day_length)
{
this.Error = new GanttError();
this.dhtmlXMLSenderObject = new dhtmlXMLSenderObject(this);
@@ -356,7 +357,7 @@ function GanttChart()
//settings
this.heightTaskItem = 12;
this.dayInPixels = 24;
this.hoursInDay = 8;
this.hoursInDay = day_length;
this._showTreePanel = true;
this._showTooltip = true;
this.isShowDescTask = false;
@@ -4869,11 +4870,13 @@ GanttTask.prototype.createTaskItem = function()
cellTblTask.height = this.Chart.heightTaskItem + "px";
cellTblTask.width = this.TaskInfo.PercentCompleted + "%";
cellTblTask.style.lineHeight = "1px";
cellTblTask.style.backgroundColor = self.TaskInfo.color;
var imgPr = document.createElement("img");
imgPr.style.width = (this.TaskInfo.PercentCompleted * this.TaskInfo.Duration * this.Chart.hourInPixelsWork) / 100 + "px";
imgPr.style.height = this.Chart.heightTaskItem + "px";
cellTblTask.appendChild(imgPr);
imgPr.src = this.Chart.imgs + "progress_filled.png";
//imgPr.src = this.Chart.imgs + "progress_filled.png";
}
if (this.TaskInfo.PercentCompleted != 100)
@@ -4943,9 +4946,9 @@ GanttTask.prototype.createTaskItem = function()
}
var taskClick = function() {
self.Chart.callEvent("onTaskClick", [self]);
self.Chart.callEvent("onTaskDblClick", [self]);
};
this.addEvent(divMove, 'click', taskClick, false);
this.addEvent(divMove, 'dblclick', taskClick, false);
if (this.Chart.isEditable)
{
+285 -166
View File
@@ -7,7 +7,8 @@ QWeb.add_template('/base_gantt/static/src/xml/base_gantt.xml');
openerp.base.views.add('gantt', 'openerp.base_gantt.GanttView');
openerp.base_gantt.GanttView = openerp.base.Controller.extend({
init: function(view_manager, session, element_id, dataset, view_id) {
init: function(view_manager, session, element_id, dataset, view_id) {
this._super(session, element_id);
this.view_manager = view_manager;
this.dataset = dataset;
@@ -16,207 +17,326 @@ openerp.base_gantt.GanttView = openerp.base.Controller.extend({
this.fields_views = {};
this.widgets = {};
this.widgets_counter = 0;
this.fields = {};
this.datarecord = {};
this.fields = this.dataset.fields ? this.dataset.fields: {};
this.ids = this.dataset.ids;
this.name = "";
this.date_start = "";
this.date_delay = "";
this.date_stop = "";
this.color_field = "";
this.day_lenth = 8;
this.colors = [];
this.color_values = [];
this.calendar_fields = {};
this.info_fields = [];
this.domain = this.dataset._domain ? this.dataset._domain: [];
this.context = {};
},
do_show: function () {
// TODO: re-trigger search
this.$element.show();
},
do_hide: function () {
this.$element.hide();
},
start: function() {
this.rpc("/base_gantt/ganttview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
this.rpc("/base_gantt/ganttview/load",
{"model": this.model, "view_id": this.view_id}, this.on_loaded);
},
on_loaded: function(data) {
this.fields_view = data.fields_view;
var self = this;
this.fields_view = data.fields_view;
this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
this.view_id = this.fields_view.view_id;
this.date_start = this.fields_view.arch.attrs.date_start;
this.date_delay = this.fields_view.arch.attrs.date_delay;
this.date_stop = this.fields_view.arch.attrs.date_stop;
this.color_field = this.fields_view.arch.attrs.color;
this.color_field = this.fields_view.arch.attrs.color;
this.day_length = this.fields_view.arch.attrs.day_length || 8;
this.colors = this.fields_view.arch.attrs.colors;
this.fields = this.fields_view.fields;
this.text = this.fields_view.arch.children[0].children[0].attrs.name;
this.parent = this.fields_view.arch.children[0].attrs.link;
this.calendar_fields['parent'] = {'name': this.parent};
this.calendar_fields['date_start'] = {'name': this.date_start};
this.calendar_fields['text'] = {'name': this.text};
if(this.date_delay)
this.calendar_fields['date_delay'] = {'name': this.date_delay};
if(this.date_stop)
this.calendar_fields['date_stop'] = {'name': this.date_stop};
this.format = "yyyy-MM-dd";
self.create_gantt();
self.get_events();
this.calendar_fields['day_length'] = this.day_length;
this.rpc('/base_gantt/ganttview/get_events',
{'model': this.model,
'fields': this.fields,
'color_field': this.color_field,
'day_length': this.day_length,
'calendar_fields': this.calendar_fields,
'colors': this.colors,
'info_fields': this.info_fields
},
function(res) {
self.create_gantt();
self.load_event(res);
})
this.$element.html(QWeb.render("GanttView", {"view": this, "fields_view": this.fields_view}));
},
convert_date_format: function(date) {
date=date+"";
if(typeof (date)!="string"||date.length===0){
return null;
}
var iso=date.split("-");
if(iso.length===0){
return null;
}
var day = iso[2];
var iso_hours = day.split(' ');
if (iso_hours.length > 1) {
day = iso_hours[0];
var iso_date_hours = iso_hours[1].split(':')
var new_date = new Date(iso[0], iso[1] - 1, day);
new_date.setHours(iso_date_hours[0]);
new_date.setMinutes(iso_date_hours[1]);
new_date.setSeconds(iso_date_hours[2]);
}
else {
var new_date = new Date(iso[0], iso[1] - 1, day);
}
new_date.setFullYear(iso[0]);
new_date.setMonth(iso[1]-1);
new_date.setDate(day);
return new_date;
},
create_gantt: function() {
ganttChartControl = new GanttChart();
ganttChartControl = new GanttChart(this.day_length);
ganttChartControl.setImagePath("/base_gantt/static/lib/dhtmlxGantt/codebase/imgs/");
ganttChartControl.setEditable(true);
ganttChartControl.showTreePanel(true);
ganttChartControl.showContextMenu(true);
ganttChartControl.showDescTask(true,'d,s-f');
ganttChartControl.showDescProject(true,'n,d');
},
load_event: function(res) {
var self = this
var result = res.result;
var sidebar = res.sidebar;
var project_id = new Array();
var project = new Array();
var j = -1;
var self = this;
for (i in result) {
var parent_id = result[i]['parent'][0];
var parent_name = result[i]['parent'][1];
if (jQuery.inArray(parent_id, project_id) == -1){
if (parent_id == undefined){
parent_name = "";
}
j = j + 1;
project[j] = new GanttProjectInfo(parent_id, parent_name, new Date(2011, 1, 1));
project_id[j] = parent_id;
}
var id = result[i]['id'];
var text = result[i]['text'];
var start_date = this.convert_date_format(result[i]['start_date']);
var duration = result[i]['duration'];
var task = new GanttTaskInfo(id, text, start_date, duration, 100, "");
k = project_id.indexOf(parent_id);
project[k].addTask(task);
}
for (i in project_id){
ganttChartControl.addProject(project[i]);
}
ganttChartControl.create("GanttDiv");
ganttChartControl.attachEvent("onTaskEndResize", function(task) {self.on_task_end_resize(task);})
ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_task_end_drag(task);})
//Create Sidebar
if (jQuery('#cal-sidebar-option').length == 0){
jQuery('#gantt-sidebar').append(
jQuery('<table>',{'width':'100%','cellspacing': 0, 'cellpadding': 0, 'id':'cal-sidebar-option'})
)
for(s in sidebar) {
jQuery('#cal-sidebar-option').append(
jQuery('<tr>').append(
jQuery('<td>').append(
jQuery('<div>')
.append(
jQuery('<input>',
{
'type': 'checkbox',
'id':sidebar[s][0],
'value':sidebar[s][0]
}).bind('click',function(){
self.reload_gantt(self.color_field,self.model)
}),
sidebar[s][1]
)
.css('background-color',sidebar[s][sidebar[s].length-1])
)
)
)
},
get_events: function() {
var self = this;
this.dataset.read_ids(this.dataset.ids, {}, function(result) {
self.load_event(result);
});
},
load_event: function(events) {
var self = this;
var result = events;
var project = {};
var project_smalldate = {};
var proj_id = [];
var proj_id_text = [];
COLOR_PALETTE = ['#ccccff', '#cc99ff', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
'#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
'#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
'#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900']
//Smallest date of child is parent start date
for (i in result){
var res = result[i];
if (res[this.date_start] != false){
var parent_id = res[this.parent][0] || res[this.parent];
var parent_name = res[this.parent][1];
var start_date = this.convert_str_date(res[this.date_start]);
if (project_smalldate[parent_id] == undefined){
project_smalldate[parent_id] = start_date;
proj_id.push(parent_id);
if (parent_name != undefined){
proj_id_text.push(res[this.parent]);
}
}
else{
if (start_date < project_smalldate[parent_id]){
project_smalldate[parent_id] = start_date;
}
}
}
}
//get parent text
if (parent_name == undefined){
var ajax = {
url: '/base/dataset/call',
async: false
};
this.rpc(ajax, {
model: this.dataset.model,
method: "name_get",
ids: proj_id,
args: ""
}, function(response) {
proj_id_text = response.result;
});
}
//create parents
for (i in proj_id){
var id = proj_id_text[i][0];
var text = proj_id_text[i][1];
project[id] = new GanttProjectInfo(id, text, project_smalldate[id]);
}
//create childs
var k = 0;
var color_box = {};
for (i in result) {
var res = result[i];
if (res[this.date_start] != false){
var parent_id = res[this.parent][0] || res[this.parent];
var id = res['id'];
var text = res[this.text];
var start_date = this.convert_str_date(res[this.date_start]);
var color = res[this.color_field][0] || res[this.color_field];
if (color_box[color] == undefined){
color_box[color] = COLOR_PALETTE[k];
k = k + 1;
}
if (this.date_stop != undefined){
if (res[this.date_stop] != false){
var stop_date = this.convert_str_date(res[this.date_stop]);
var duration= self.hours_between(start_date, stop_date);
}
else{
var duration = 0;
}
}
else{
var duration = res[this.date_delay];
}
if (duration == false)
duration = 0
task = new GanttTaskInfo(id, text, start_date, duration, 100, "", color_box[color]);
project[parent_id].addTask(task);
}
}
//Add parent
for (i in proj_id){
ganttChartControl.addProject(project[proj_id[i]]);
}
ganttChartControl.create("GanttDiv");
ganttChartControl.attachEvent("onTaskStartDrag", function(task) {self.on_drag_start(task);});
ganttChartControl.attachEvent("onTaskEndResize", function(task) {self.on_resize_drag_end(task, "resize");});
ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_resize_drag_end(task, "drag");});
ganttChartControl.attachEvent("onTaskDblClick", function(task) {self.open_popup(task);});
},
reload_gantt: function(color_field, model) {
var domain = [];
hours_between: function(date1, date2) {
var ONE_DAY = 1000 * 60 * 60 * 24;
var date1_ms = date1.getTime();
var date2_ms = date2.getTime();
var difference_ms = Math.abs(date1_ms - date2_ms);
d = Math.round(difference_ms / ONE_DAY);
h = Math.round((difference_ms % ONE_DAY)/(1000 * 60 * 60));
return (d * this.day_length) + h;
},
open_popup : function(task) {
var event_id = task.getId();
if (event_id) {
event_id = parseInt(event_id, 10);
var dataset_event_index = jQuery.inArray(event_id, this.ids);
} else {
var dataset_event_index = null;
}
this.dataset.index = dataset_event_index;
var element_id = _.uniqueId("act_window_dialog");
var dialog = jQuery('<div>',
{'id': element_id
}).dialog({
title: 'Gantt Chart',
modal: true,
minWidth: 800,
position: 'top'
});
var event_form = new openerp.base.FormView(this.view_manager, this.session, element_id, this.dataset, false);
event_form.start();
},
on_drag_start : function(task){
st_date = task.getEST();
if(st_date.getHours()){
self.hh = st_date.getHours();
self.mm = st_date.getMinutes();
}
},
on_resize_drag_end : function(task, evt){
var event_id = task.getId();
var data = {};
if (evt == "drag"){
full_date = task.getEST().set({hour: self.hh, minute : self.mm, second:0});
data[this.date_start] = this.convert_date_str(full_date);
}
if (this.date_stop != undefined){
tm = (task.getDuration() % this.day_length);
stp = task.getFinishDate().add(tm).hour();
data[this.date_stop] = this.convert_date_str(stp);
}else{
data[this.date_delay] = task.getDuration();
}
this.dataset.write(event_id, data, function(result) {});
},
do_show: function () {
this.$element.show();
},
do_hide: function () {
this.$element.hide();
},
convert_str_date: function (str){
if (str.length == 19){
this.format = "yyyy-MM-dd HH:mm:ss";
return openerp.base.parse_datetime(str);
} else if (str.length == 10){
this.format = "yyyy-MM-dd";
return openerp.base.parse_date(str);
} else if (str.length == 8){
this.format = "HH:mm:ss";
return openerp.base.parse_time(str);
}
throw "Unrecognized date/time format";
},
convert_date_str: function(full_date) {
if (this.format == "yyyy-MM-dd HH:mm:ss"){
return openerp.base.format_datetime(full_date);
} else if (this.format == "yyyy-MM-dd"){
return openerp.base.format_date(full_date);
} else if (this.format == "HH:mm:ss"){
return openerp.base.format_time(full_date);
}
throw "Unrecognized date/time format";
},
reload_gantt: function(domain) {
var self = this;
jQuery('input[type=checkbox]:checked','#cal-sidebar-option').each(function() {
domain.push(parseInt(jQuery(this).attr('id')))
var ajax = {
url: '/base/dataset/search_read',
async: false
};
this.rpc(ajax, {
model: this.dataset.model,
domain: self.dataset.domain,
context :self.dataset.context
}, function(response) {
ganttChartControl.clearAll();
jQuery("#GanttDiv").children().remove();
self.load_event(response);
});
},
do_search: function (domains, contexts, groupbys) {
var self = this;
return this.rpc('/base/session/eval_domain_and_context', {
domains: domains,
contexts: contexts,
group_by_seq: groupbys
}, function (results) {
self.dataset.context = results.context;
self.dataset.domain = results.domain;
return self.reload_gantt(self.dataset.domain);
});
this.rpc('/base_gantt/ganttview/reload_gantt',{
'domain':domain,
'color_field':color_field,
'model': model
},function(res) {
ganttChartControl.clearAll();
jQuery("#GanttDiv").children().remove();
self.load_event(res);
});
},
reverse_convert_date_format: function(date) {
return date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate();
},
on_task_end_resize : function(task) {
this.rpc('/base_gantt/ganttview/on_event_resize',
{'id' : task.getId(),
'end_date' : this.reverse_convert_date_format(task.getFinishDate()),
'duration' : task.getDuration()
},
function(result) {
})
},
on_task_end_drag : function(task) {
this.rpc('/base_gantt/ganttview/on_event_drag',
{'id' : task.getId(),
'start_date' : this.reverse_convert_date_format(task.getEST()),
'end_date' : this.reverse_convert_date_format(task.getFinishDate()),
'duration' : task.getDuration()
},
function(result) {
})
}
});
@@ -224,5 +344,4 @@ openerp.base_gantt.GanttView = openerp.base.Controller.extend({
// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them
};
// vim:et fdc=0 fdl=0:
@@ -3,12 +3,9 @@
<h3 class="title"><t t-esc="view.fields_view.arch.attrs.string"/></h3>
<table class="gantt-view" width="100%" height="100%" cellspacing="0" cellpadding="0">
<tr>
<td style="width:85%">
<td>
<div style="width:100%;height:300px;position:relative" id="GanttDiv"/>
</td>
<td valign = "top">
<div id="gantt-sidebar"/>
</td>
</tr>
</table>
</t>
+1
View File
@@ -10,5 +10,6 @@
'static/src/js/web_chat.js'
],
"css": [],
# 'active': True,
'active': False,
}
-2
View File
@@ -307,8 +307,6 @@ Javascript
:returns: the dataset's current active id
.. js:class:: openerp.base.DataRecord(session, model, fields, values)
Ad-hoc objects and structural types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-24
View File
@@ -1,27 +1,3 @@
[global]
server.environment = "development"
# Some server parameters that you may want to tweak
server.socket_host = "0.0.0.0"
server.socket_port = 8080
# Sets the number of threads the server uses
server.thread_pool = 10
tools.sessions.on = True
tools.sessions.persistent = False
# logging
#log.access_file = "/var/log/openerp-web/access.log"
#log.error_file = "/var/log/openerp-web/error.log"
log.access_level = "INFO"
log.error_level = "INFO"
# OpenERP Server
openerp.server.host = 'localhost'
openerp.server.port = '8070'
openerp.server.protocol = 'socket'
openerp.server.timeout = 450
# Regex for dbname %{subdomain}s for the subdomain
#openerp.web.database.list = "%{subdomain}s_.*"
#openerp.web.database.manager = False
+29 -3
View File
@@ -20,9 +20,6 @@
#
##############################################################################
""" We assume that, just like the OpenERP server, the python part of the client
doesn't need to handle timezone-aware datetimes, this could be changed in the future. """
import datetime
DEFAULT_SERVER_DATE_FORMAT = "%Y-%m-%d"
@@ -32,31 +29,60 @@ DEFAULT_SERVER_DATETIME_FORMAT = "%s %s" % (
DEFAULT_SERVER_TIME_FORMAT)
def parse_datetime(str):
"""
Converts a string to a datetime object using OpenERP's
datetime string format (exemple: '2011-12-01 15:12:35').
No timezone information is added, the datetime is a naive instance, but
according to OpenERP 6.1 specification the timezone is always UTC.
"""
if not str:
return str
return datetime.datetime.strptime(str, DEFAULT_SERVER_DATETIME_FORMAT)
def parse_date(str):
"""
Converts a string to a date object using OpenERP's
date string format (exemple: '2011-12-01').
"""
if not str:
return str
return datetime.datetime.strptime(str, DEFAULT_SERVER_DATE_FORMAT).date()
def parse_time(str):
"""
Converts a string to a time object using OpenERP's
time string format (exemple: '15:12:35').
"""
if not str:
return str
return datetime.datetime.strptime(str, DEFAULT_SERVER_TIME_FORMAT).time()
def format_datetime(obj):
"""
Converts a datetime object to a string using OpenERP's
datetime string format (exemple: '2011-12-01 15:12:35').
The datetime instance should not have an attached timezone and be in UTC.
"""
if not obj:
return False
return obj.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
def format_date(obj):
"""
Converts a date object to a string using OpenERP's
date string format (exemple: '2011-12-01').
"""
if not obj:
return False
return obj.strftime(DEFAULT_SERVER_DATE_FORMAT)
def format_time(obj):
"""
Converts a time object to a string using OpenERP's
time string format (exemple: '15:12:35').
"""
if not obj:
return False
return obj.strftime(DEFAULT_SERVER_TIME_FORMAT)
+28 -16
View File
@@ -500,30 +500,42 @@ def main(argv):
# change the timezone of the program to the OpenERP server's assumed timezone
os.environ["TZ"] = "UTC"
# Parse config
op = optparse.OptionParser()
op.add_option("-p", "--port", dest="socket_port", help="listening port", metavar="NUMBER", default=8002)
op.add_option("-s", "--session-path", dest="storage_path",
help="directory used for session storage", metavar="DIR",
default=os.path.join(tempfile.gettempdir(), "cpsessions"))
(o, args) = op.parse_args(argv[1:])
# Prepare cherrypy config from options
if not os.path.exists(o.storage_path):
os.mkdir(o.storage_path, 0700)
config = {
'server.socket_port': int(o.socket_port),
DEFAULT_CONFIG = {
'server.socket_port': 8002,
'server.socket_host': '0.0.0.0',
#'server.thread_pool' = 10,
'tools.sessions.on': True,
'tools.sessions.storage_type': 'file',
'tools.sessions.storage_path': o.storage_path,
'tools.sessions.storage_path': os.path.join(tempfile.gettempdir(), "cpsessions"),
'tools.sessions.timeout': 60
}
# Parse config
op = optparse.OptionParser()
op.add_option("-p", "--port", dest="server.socket_port", help="listening port",
type="int", metavar="NUMBER")
op.add_option("-s", "--session-path", dest="tools.sessions.storage_path",
help="directory used for session storage", metavar="DIR")
(o, args) = op.parse_args(argv[1:])
o = vars(o)
for k in o.keys():
if o[k] == None:
del(o[k])
# Setup and run cherrypy
cherrypy.tree.mount(Root())
cherrypy.config.update(config)
cherrypy.config.update(config=DEFAULT_CONFIG)
if os.path.exists(os.path.join(os.path.dirname(
os.path.dirname(__file__)),'openerp-web.cfg')):
cherrypy.config.update(os.path.join(os.path.dirname(
os.path.dirname(__file__)),'openerp-web.cfg'))
if os.path.exists(os.path.expanduser('~/.openerp_webrc')):
cherrypy.config.update(os.path.expanduser('~/.openerp_webrc'))
cherrypy.config.update(o)
if not os.path.exists(cherrypy.config['tools.sessions.storage_path']):
os.mkdir(cherrypy.config['tools.sessions.storage_path'], 0700)
cherrypy.server.subscribe()
cherrypy.engine.start()
cherrypy.engine.block()