[MERGE] Merge with trunk

bzr revid: fme@openerp.com-20110831134044-y7f0fmhzndpw6qt9
This commit is contained in:
Fabien Meghazi
2011-08-31 15:40:44 +02:00
28 changed files with 381 additions and 272 deletions
+5
View File
@@ -1 +1,6 @@
import common
import controllers
# TODO
# if we detect that we are imported from the openerp server register common.Root() as a wsgi entry point
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/python
# TODO if from openerpserver use backendlocal
# from backendlocal import *
from backendrpc import *
from dispatch import *
+23
View File
@@ -0,0 +1,23 @@
#----------------------------------------------------------
# OpenERPSession local openerp backend access
#----------------------------------------------------------
class OpenERPUnboundException(Exception):
pass
class OpenERPConnector(object):
pass
class OpenERPAuth(object):
pass
class OpenERPModel(object):
def __init__(self, session, model):
self._session = session
self._model = model
def __getattr__(self, name):
return lambda *l:self._session.execute(self._model, name, *l)
class OpenERPSession(object):
def __init__(self, model_factory=OpenERPModel):
pass
+212
View File
@@ -0,0 +1,212 @@
#!/usr/bin/python
import datetime
import urllib
import dateutil.relativedelta
import functools
import logging
import optparse
import os
import sys
import tempfile
import time
import traceback
import uuid
import xmlrpclib
import cherrypy
import cherrypy.lib.static
import simplejson
import nonliterals
#----------------------------------------------------------
# OpenERPSession RPC openerp backend access
#----------------------------------------------------------
class OpenERPUnboundException(Exception):
pass
class OpenERPConnector(object):
pass
class OpenERPAuth(object):
pass
class OpenERPModel(object):
def __init__(self, session, model):
self._session = session
self._model = model
def __getattr__(self, name):
return lambda *l:self._session.execute(self._model, name, *l)
class OpenERPSession(object):
"""
An OpenERP RPC session, a given user can own multiple such sessions
in a web session.
.. attribute:: context
The session context, a ``dict``. Can be reloaded by calling
:meth:`openerpweb.openerpweb.OpenERPSession.get_context`
.. attribute:: domains_store
A ``dict`` matching domain keys to evaluable (but non-literal) domains.
Used to store references to non-literal domains which need to be
round-tripped to the client browser.
"""
def __init__(self, server='127.0.0.1', port=8069, model_factory=OpenERPModel):
self._server = server
self._port = port
self._db = False
self._uid = False
self._login = False
self._password = False
self.model_factory = model_factory
self._locale = 'en_US'
self.context = {}
self.contexts_store = {}
self.domains_store = {}
self._lang = {}
self.remote_timezone = 'utc'
self.client_timezone = False
def proxy(self, service):
s = xmlrpclib.ServerProxy('http://%s:%s/xmlrpc/%s' % (self._server, self._port, service))
return s
def bind(self, db, uid, password):
self._db = db
self._uid = uid
self._password = password
def login(self, db, login, password):
uid = self.proxy('common').login(db, login, password)
self.bind(db, uid, password)
self._login = login
if uid: self.get_context()
return uid
def assert_valid(self):
"""
Ensures this session is valid (logged into the openerp server)
"""
if not (self._db and self._uid and self._password):
raise OpenERPUnboundException()
def execute(self, model, func, *l, **d):
self.assert_valid()
r = self.proxy('object').execute(self._db, self._uid, self._password, model, func, *l, **d)
return r
def exec_workflow(self, model, id, signal):
self.assert_valid()
r = self.proxy('object').exec_workflow(self._db, self._uid, self._password, model, signal, id)
return r
def model(self, model):
""" Get an RPC proxy for the object ``model``, bound to this session.
:param model: an OpenERP model name
:type model: str
:rtype: :class:`openerpweb.openerpweb.OpenERPModel`
"""
return self.model_factory(self, model)
def get_context(self):
""" Re-initializes the current user's session context (based on
his preferences) by calling res.users.get_context() with the old
context
:returns: the new context
"""
assert self._uid, "The user needs to be logged-in to initialize his context"
self.context = self.model('res.users').context_get(self.context)
self.context = self.context or {}
self.client_timezone = self.context.get("tz", False)
# invalid code, anyway we decided the server will be in UTC
#if self.client_timezone:
# self.remote_timezone = self.execute('common', 'timezone_get')
self._locale = self.context.get('lang','en_US')
lang_ids = self.execute('res.lang','search', [('code', '=', self._locale)])
if lang_ids:
self._lang = self.execute('res.lang', 'read',lang_ids[0], [])
return self.context
@property
def base_eval_context(self):
""" Default evaluation context for the session.
Used to evaluate contexts and domains.
"""
base = dict(
uid=self._uid,
current_date=datetime.date.today().strftime('%Y-%m-%d'),
time=time,
datetime=datetime,
relativedelta=dateutil.relativedelta.relativedelta
)
base.update(self.context)
return base
def evaluation_context(self, context=None):
""" Returns the session's evaluation context, augmented with the
provided context if any.
:param dict context: to add merge in the session's base eval context
:returns: the augmented context
:rtype: dict
"""
d = dict(self.base_eval_context)
if context:
d.update(context)
d['context'] = d
return d
def eval_context(self, context_to_eval, context=None):
""" Evaluates the provided context_to_eval in the context (haha) of
the context. Also merges the evaluated context with the session's context.
:param context_to_eval: a context to evaluate. Must be a dict or a
non-literal context. If it's a dict, will be
returned as-is
:type context_to_eval: openerpweb.nonliterals.Context
:returns: the evaluated context
:rtype: dict
:raises: ``TypeError`` if ``context_to_eval`` is neither a dict nor
a Context
"""
ctx = dict(
self.base_eval_context,
**(context or {}))
# adding the context of the session to send to the openerp server
ccontext = nonliterals.CompoundContext(self.context, context_to_eval or {})
ccontext.session = self
return ccontext.evaluate(ctx)
def eval_domain(self, domain, context=None):
""" Evaluates the provided domain using the provided context
(merged with the session's evaluation context)
:param domain: an OpenERP domain as a list or as a
:class:`openerpweb.nonliterals.Domain` instance
In the second case, it will be evaluated and returned.
:type domain: openerpweb.nonliterals.Domain
:param dict context: the context to use in the evaluation, if any.
:returns: the evaluated domain
:rtype: list
:raises: ``TypeError`` if ``domain`` is neither a list nor a Domain
"""
if isinstance(domain, list):
return domain
cdomain = nonliterals.CompoundDomain(domain)
cdomain.session = self
return cdomain.evaluate(context or {})
@@ -3,6 +3,7 @@ import datetime
import urllib
import dateutil.relativedelta
import functools
import logging
import optparse
import os
import sys
@@ -17,14 +18,18 @@ import cherrypy.lib.static
import simplejson
import nonliterals
import logging
# TODO if from openerpserver use backendlocal
# from backendlocal import *
from backendrpc import *
#-----------------------------------------------------------
# Globals
#-----------------------------------------------------------
path_root = os.path.dirname(os.path.dirname(os.path.normpath(__file__)))
path_addons = os.path.join(path_root, 'addons')
import __main__
path_root = __main__.path_root
path_addons = __main__.path_addons
cherrypy_root = None
#-----------------------------------------------------------
@@ -38,199 +43,6 @@ controllers_class = {}
controllers_object = {}
controllers_path = {}
#----------------------------------------------------------
# OpenERP Client Library
#----------------------------------------------------------
class OpenERPUnboundException(Exception):
pass
class OpenERPConnector(object):
pass
class OpenERPAuth(object):
pass
class OpenERPModel(object):
def __init__(self, session, model):
self._session = session
self._model = model
def __getattr__(self, name):
return lambda *l:self._session.execute(self._model, name, *l)
class OpenERPSession(object):
"""
An OpenERP RPC session, a given user can own multiple such sessions
in a web session.
.. attribute:: context
The session context, a ``dict``. Can be reloaded by calling
:meth:`openerpweb.openerpweb.OpenERPSession.get_context`
.. attribute:: domains_store
A ``dict`` matching domain keys to evaluable (but non-literal) domains.
Used to store references to non-literal domains which need to be
round-tripped to the client browser.
"""
def __init__(self, server='127.0.0.1', port=8069, model_factory=OpenERPModel):
self._server = server
self._port = port
self._db = False
self._uid = False
self._login = False
self._password = False
self.model_factory = model_factory
self._locale = 'en_US'
self.context = {}
self.contexts_store = {}
self.domains_store = {}
self._lang = {}
self.remote_timezone = 'utc'
self.client_timezone = False
def proxy(self, service):
s = xmlrpclib.ServerProxy('http://%s:%s/xmlrpc/%s' % (self._server, self._port, service))
return s
def bind(self, db, uid, password):
self._db = db
self._uid = uid
self._password = password
def login(self, db, login, password):
uid = self.proxy('common').login(db, login, password)
self.bind(db, uid, password)
self._login = login
if uid: self.get_context()
return uid
def assert_valid(self):
"""
Ensures this session is valid (logged into the openerp server)
"""
if not (self._db and self._uid and self._password):
raise OpenERPUnboundException()
def execute(self, model, func, *l, **d):
self.assert_valid()
r = self.proxy('object').execute(self._db, self._uid, self._password, model, func, *l, **d)
return r
def exec_workflow(self, model, id, signal):
self.assert_valid()
r = self.proxy('object').exec_workflow(self._db, self._uid, self._password, model, signal, id)
return r
def model(self, model):
""" Get an RPC proxy for the object ``model``, bound to this session.
:param model: an OpenERP model name
:type model: str
:rtype: :class:`openerpweb.openerpweb.OpenERPModel`
"""
return self.model_factory(self, model)
def get_context(self):
""" Re-initializes the current user's session context (based on
his preferences) by calling res.users.get_context() with the old
context
:returns: the new context
"""
assert self._uid, "The user needs to be logged-in to initialize his context"
self.context = self.model('res.users').context_get(self.context)
self.context = self.context or {}
self.client_timezone = self.context.get("tz", False)
# invalid code, anyway we decided the server will be in UTC
#if self.client_timezone:
# self.remote_timezone = self.execute('common', 'timezone_get')
self._locale = self.context.get('lang','en_US')
lang_ids = self.execute('res.lang','search', [('code', '=', self._locale)])
if lang_ids:
self._lang = self.execute('res.lang', 'read',lang_ids[0], [])
return self.context
@property
def base_eval_context(self):
""" Default evaluation context for the session.
Used to evaluate contexts and domains.
"""
base = dict(
uid=self._uid,
current_date=datetime.date.today().strftime('%Y-%m-%d'),
time=time,
datetime=datetime,
relativedelta=dateutil.relativedelta.relativedelta
)
base.update(self.context)
return base
def evaluation_context(self, context=None):
""" Returns the session's evaluation context, augmented with the
provided context if any.
:param dict context: to add merge in the session's base eval context
:returns: the augmented context
:rtype: dict
"""
d = dict(self.base_eval_context)
if context:
d.update(context)
d['context'] = d
return d
def eval_context(self, context_to_eval, context=None):
""" Evaluates the provided context_to_eval in the context (haha) of
the context. Also merges the evaluated context with the session's context.
:param context_to_eval: a context to evaluate. Must be a dict or a
non-literal context. If it's a dict, will be
returned as-is
:type context_to_eval: openerpweb.nonliterals.Context
:returns: the evaluated context
:rtype: dict
:raises: ``TypeError`` if ``context_to_eval`` is neither a dict nor
a Context
"""
ctx = dict(
self.base_eval_context,
**(context or {}))
# adding the context of the session to send to the openerp server
ccontext = nonliterals.CompoundContext(self.context, context_to_eval or {})
ccontext.session = self
return ccontext.evaluate(ctx)
def eval_domain(self, domain, context=None):
""" Evaluates the provided domain using the provided context
(merged with the session's evaluation context)
:param domain: an OpenERP domain as a list or as a
:class:`openerpweb.nonliterals.Domain` instance
In the second case, it will be evaluated and returned.
:type domain: openerpweb.nonliterals.Domain
:param dict context: the context to use in the evaluation, if any.
:returns: the evaluated domain
:rtype: list
:raises: ``TypeError`` if ``domain`` is neither a list nor a Domain
"""
if isinstance(domain, list):
return domain
cdomain = nonliterals.CompoundDomain(domain)
cdomain.session = self
return cdomain.evaluate(context or {})
#----------------------------------------------------------
# OpenERP Web RequestHandler
#----------------------------------------------------------
@@ -425,12 +237,16 @@ class Root(object):
controllers_path[o._cp_path] = o
def default(self, *l, **kw):
#print "default",l,kw
print "default",l,kw
# handle static files
if len(l) > 2 and l[1] == 'static':
# sanitize path
p = os.path.normpath(os.path.join(*l))
return cherrypy.lib.static.serve_file(os.path.join(path_addons, p))
p2 = os.path.join(path_addons, p)
print "p",p
print "p2",p2
return cherrypy.lib.static.serve_file(p2)
elif len(l) > 1:
for i in range(len(l), 1, -1):
ps = "/" + "/".join(l[0:i])
+5 -3
View File
@@ -15,9 +15,11 @@ from cStringIO import StringIO
import cherrypy
import openerpweb
import openerpweb.ast
import openerpweb.nonliterals
import base.common as openerpweb
import base.common.ast
import base.common.nonliterals
openerpweb.ast = base.common.ast
openerpweb.nonliterals = base.common.nonliterals
from babel.messages.pofile import read_po
+10 -7
View File
@@ -597,13 +597,13 @@ label.error {
background-color: #ccc;
}
.openerp .searchview_group.folded .searchview_group_string {
background: url("../img/ui/group-folded.png") no-repeat scroll 0 50%;
background: url("/base/static/src/img/ui/group-folded.png") no-repeat scroll 0 50%;
}
.openerp .searchview_group.folded .searchview_group_content {
display: none;
}
.openerp .searchview_group.expanded .searchview_group_string {
background: url("../img/ui/group-expanded.png") no-repeat scroll 0 50%;
background: url("/base/static/src/img/ui/group-expanded.png") no-repeat scroll 0 50%;
}
.openerp .searchview_group.expanded .searchview_group_content {
display: block;
@@ -635,13 +635,13 @@ label.error {
}
.openerp .oe_search-view-custom-filter-btn span {
background: url(../img/icons/gtk-add.png) repeat-y;
background: url(/base/static/src/img/icons/gtk-add.png) repeat-y;
padding-left: 18px;
}
.openerp .searchview_extended_add_proposition span {
font-size: 0.9em;
background: url(../img/icons/gtk-add.png) repeat-y;
background: url(/base/static/src/img/icons/gtk-add.png) repeat-y;
padding-left: 18px;
}
@@ -657,7 +657,7 @@ label.error {
.openerp .searchview_extended_delete_group span,
.openerp .searchview_extended_delete_prop span {
font-size: 0.9em;
background: url(../img/icons/gtk-close.png) repeat-y;
background: url(/base/static/src/img/icons/gtk-close.png) repeat-y;
padding-left: 18px;
}
/* List */
@@ -931,6 +931,9 @@ label.error {
margin: 3px 4px 3px 1px;
height: 15px;
}
.openerp .oe_form_frame_cell.oe_form_separator_vertical {
border-left: 1px solid #666;
}
.openerp td.required input, .openerp td.required select {
background-color: #D2D2FF !important;
}
@@ -1091,7 +1094,7 @@ label.error {
cursor: pointer;
border: 1px solid #D2CFCF;
display: block;
background: url(../img/toggle-a-bg.png);
background: url(/base/static/src/img/toggle-a-bg.png);
width: 21px;
height: 21px;
z-index: 10;
@@ -1106,7 +1109,7 @@ label.error {
}
.openerp li.oe_sidebar_print {
padding-left: 20px;
background: 1px 3px url(../img/icons/gtk-print.png) no-repeat;
background: 1px 3px url(/base/static/src/img/icons/gtk-print.png) no-repeat;
}
.openerp.kitten-mode-activated .main_table {
+39 -18
View File
@@ -83,9 +83,11 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
this.$form_header.find('button.oe_form_button_cancel').click(this.do_cancel);
this.$form_header.find('button.oe_form_button_new').click(this.on_button_new);
this.$form_header.find('button.oe_get_xml_view').click(function() {
$('<xmp>' + openerp.base.json_node_to_xml(self.fields_view.arch, true) + '</xmp>').dialog({ width: '95%', height: 600});
});
if (this.session.debug) {
this.$form_header.find('button.oe_get_xml_view').click(function() {
$('<xmp>' + openerp.base.json_node_to_xml(self.fields_view.arch, true) + '</xmp>').dialog({ width: '95%', height: 600});
});
}
if (this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
@@ -577,18 +579,23 @@ openerp.base.form.Widget = openerp.base.Widget.extend({
this.view.widgets[this.element_id] = this;
this.children = node.children;
this.colspan = parseInt(node.attrs.colspan || 1);
this.colspan = parseInt(node.attrs.colspan || 1, 10);
this.decrease_max_width = 0;
this.string = this.string || node.attrs.string;
this.help = this.help || node.attrs.help;
this.invisible = this.modifiers['invisible'] === true;
this.classname = 'oe_form_' + this.type;
this.width = this.node.attrs.width;
},
start: function() {
this.$element = $('#' + this.element_id);
},
stop: function() {
this.$element = $('#' + this.element_id);
this.$element.remove();
if (this.$element) {
this.$element.remove();
}
},
process_modifiers: function() {
var compute_domain = openerp.base.form.compute_domain;
@@ -609,7 +616,7 @@ openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({
template: 'WidgetFrame',
init: function(view, node) {
this._super(view, node);
this.columns = node.attrs.col || 4;
this.columns = parseInt(node.attrs.col || 4, 10);
this.x = 0;
this.y = 0;
this.table = [];
@@ -635,15 +642,21 @@ openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({
return row;
},
set_row_cells_with: function(row) {
var bypass = 0,
max_width = 100;
for (var i = 0; i < row.length; i++) {
bypass += row[i].width === undefined ? 0 : 1;
max_width -= row[i].decrease_max_width;
}
var size_unit = Math.round(max_width / (this.columns - bypass)),
colspan_sum = 0;
for (var i = 0; i < row.length; i++) {
var w = row[i];
if (w.is_field_label) {
w.width = "1%";
if (row[i + 1]) {
row[i + 1].width = Math.round((100 / this.columns) * (w.colspan + 1) - 1) + '%';
}
} else if (w.width === undefined) {
w.width = Math.round((100 / this.columns) * w.colspan) + '%';
colspan_sum += w.colspan;
if (w.width === undefined) {
var width = (i === row.length - 1 && colspan_sum === this.columns) ? max_width : Math.round(size_unit * w.colspan);
max_width -= width;
w.width = width + '%';
}
}
},
@@ -733,6 +746,11 @@ openerp.base.form.WidgetSeparator = openerp.base.form.Widget.extend({
init: function(view, node) {
this._super(view, node);
this.template = "WidgetSeparator";
this.orientation = node.attrs.orientation || 'horizontal';
if (this.orientation === 'vertical') {
this.width = '1';
}
this.classname += '_' + this.orientation;
}
});
@@ -796,14 +814,15 @@ openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
this._super(view, node);
// TODO fme: support for attrs.align
if (this.node.tag == 'label' && this.node.attrs.colspan) {
this.is_field_label = false;
if (this.node.tag == 'label' && (this.node.attrs.colspan || (this.string && this.string.length > 32))) {
this.template = "WidgetParagraph";
this.colspan = this.node.attrs.colspan;
this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
} else {
this.is_field_label = true;
this.template = "WidgetLabel";
this.colspan = 1;
this.width = '1%';
this.decrease_max_width = 1;
this.nowrap = true;
}
},
render: function () {
@@ -845,6 +864,8 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
this.required = this.modifiers['required'] === true;
this.invalid = false;
this.dirty = false;
this.classname = 'oe_form_field_' + this.type;
},
start: function() {
this._super.apply(this, arguments);
+8 -6
View File
@@ -28,8 +28,10 @@ openerp.base.format_value = function (value, descriptor, value_if_empty) {
case 'float':
var precision = descriptor.digits ? descriptor.digits[1] : 2;
var int_part = Math.floor(value);
var dec_part = Math.floor((value % 1) * Math.pow(10, precision));
return _.sprintf('%d' + openerp.base._t.database.parameters.decimal_point + '%d', int_part, dec_part);
var dec_part = Math.abs(Math.floor((value % 1) * Math.pow(10, precision)));
return _.sprintf('%d%s%d',
int_part,
openerp.base._t.database.parameters.decimal_point, dec_part);
case 'float_time':
return _.sprintf("%02d:%02d",
Math.floor(value),
@@ -45,7 +47,7 @@ openerp.base.format_value = function (value, descriptor, value_if_empty) {
if (typeof(value) == "string")
value = openerp.base.str_to_datetime(value);
try {
return value.toString(_.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate,
return value.toString(_.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate,
Date.CultureInfo.formatPatterns.longTime));
} catch (e) {
return value.format("%m/%d/%Y %H:%M:%S");
@@ -108,7 +110,7 @@ openerp.base.parse_value = function (value, descriptor, value_if_empty) {
case 'progressbar':
return openerp.base.parse_value(value, {type: "float"});
case 'datetime':
var tmp = Date.parseExact(value, _.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate,
var tmp = Date.parseExact(value, _.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate,
Date.CultureInfo.formatPatterns.longTime));
if (tmp !== null)
return tmp;
@@ -117,7 +119,7 @@ openerp.base.parse_value = function (value, descriptor, value_if_empty) {
return tmp;
throw value + " is not a valid datetime";
case 'date':
var tmp = Date.parseExact(Date.CultureInfo.formatPatterns.shortDate);
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.shortDate);
if (tmp !== null)
return tmp;
tmp = Date.parse(value);
@@ -125,7 +127,7 @@ openerp.base.parse_value = function (value, descriptor, value_if_empty) {
return tmp;
throw value + " is not a valid date";
case 'time':
var tmp = Date.parseExact(Date.CultureInfo.formatPatterns.longTime);
var tmp = Date.parseExact(value, Date.CultureInfo.formatPatterns.longTime);
if (tmp !== null)
return tmp;
tmp = Date.parse(value);
+7 -6
View File
@@ -622,7 +622,7 @@
</t>
<t t-name="FormView">
<div class="oe_form_header" t-att-id="view.element_id + '_header'">
<h2 class="oe_view_title"><t t-esc="view.fields_view.arch.attrs.string"/> <button class="oe_get_xml_view">xml</button></h2>
<h2 class="oe_view_title"><t t-esc="view.fields_view.arch.attrs.string"/> <button t-if="view.session.debug" class="oe_get_xml_view">View#<t t-esc="view.fields_view.view_id"/></button></h2>
<div class="oe_form_buttons" t-if="view.options.action_buttons !== false">
<!--<button type="button" class="oe_form_button_save">
<span class="oe_form_on_update">Save</span>
@@ -690,11 +690,11 @@
<tr t-foreach="widget.table" t-as="row">
<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 or td.is_field_m2o? 'true' : undefined"
t-att-width="td.width"
t-att-nowrap="td.nowrap 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_frame_cell oe_form_' + (td.is_field_label ? 'label' : (td.field ? 'field_' + td.type : td.type))"
t-attf-class="oe_form_frame_cell #{td.classname}"
>
<t t-raw="td.render()"/>
</td>
@@ -720,7 +720,7 @@
</div>
</t>
<t t-name="WidgetSeparator">
<div t-att-class="'separator ' + (widget.node.attrs.orientation || 'horizontal')">
<div t-if="widget.orientation !== 'vertical'" t-att-class="'separator ' + widget.orientation">
<t t-esc="widget.string"/>
</div>
</t>
@@ -861,7 +861,8 @@
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td align="center">
<img src="/base/static/src/img/placeholder.png" class="oe-binary-image" border="1"
<img src="/base/static/src/img/placeholder.png" class="oe-binary-image"
t-att-border="widget.readonly ? 0 : 1"
t-att-id="widget.element_id + '_field'"
t-att-name="widget.name"
t-att-class="'field_' + widget.type"
+12 -9
View File
@@ -11,17 +11,17 @@ $(document).ready(function () {
test("format_datetime", function () {
var date = openerp.base.str_to_datetime("2009-05-04 12:34:23");
var str = openerp.base.format_value(date, {type:"datetime"});
equal(str, date.format("%m/%d/%Y %H:%M:%S"));
equal(str, date.toString("M/d/yyyy h:mm:ss tt"));
});
test("format_date", function () {
var date = openerp.base.str_to_datetime("2009-05-04 12:34:23");
var str = openerp.base.format_value(date, {type:"date"});
equal(str, date.format("%m/%d/%Y"));
equal(str, date.toString("M/d/yyyy"));
});
test("format_time", function () {
var date = openerp.base.str_to_datetime("2009-05-04 12:34:23");
var str = openerp.base.format_value(date, {type:"time"});
equal(str, date.format("%H:%M:%S"));
equal(str, date.toString("h:mm:ss tt"));
});
test("format_float", function () {
var fl = 12.1234;
@@ -30,22 +30,25 @@ $(document).ready(function () {
});
test("parse_datetime", function () {
var val = openerp.base.str_to_datetime("2009-05-04 12:34:23");
var res = openerp.base.parse_value(val.format("%m/%d/%Y %H:%M:%S"), {type:"datetime"});
equal(val.format("%m/%d/%Y %H:%M:%S"), res.format("%m/%d/%Y %H:%M:%S"));
var res = openerp.base.parse_value(val.toString("M/d/yyyy h:mm:ss tt"), {type:"datetime"});
equal(val.toString("M/d/yyyy h:mm:ss tt"), res.toString("M/d/yyyy h:mm:ss tt"));
});
test("parse_date", function () {
var val = openerp.base.str_to_date("2009-05-04");
var res = openerp.base.parse_value(val.format("%m/%d/%Y"), {type:"date"});
equal(val.format("%m/%d/%Y %H:%M:%S"), res.format("%m/%d/%Y %H:%M:%S"));
var res = openerp.base.parse_value(val.toString("M/d/yyyy"), {type:"date"});
equal(val.toString("M/d/yyyy"), res.toString("M/d/yyyy"));
});
test("parse_time", function () {
var val = openerp.base.str_to_time("12:34:23");
var res = openerp.base.parse_value(val.format("%H:%M:%S"), {type:"time"});
equal(val.format("%m/%d/%Y %H:%M:%S"), res.format("%m/%d/%Y %H:%M:%S"));
var res = openerp.base.parse_value(val.toString("h:mm:ss tt"), {type:"time"});
equal(val.toString("h:mm:ss tt"), res.toString("h:mm:ss tt"));
});
test("parse_float", function () {
var str = "134,112.1234";
var val = openerp.base.parse_value(str, {type:"float"});
equal(val, 134112.1234);
var str = "-134,112.1234";
var val = openerp.base.parse_value(str, {type:"float"});
equal(val, -134112.1234);
});
});
+5 -1
View File
@@ -14,7 +14,11 @@
<!-- jquery -->
<script src="/base/static/lib/jquery/jquery-1.6.2.js"></script>
<script src="/base/static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js"></script>
<script src="/base/static/lib/datejs/date-en-US.js"></script>
<script src="/base/static/lib/datejs/globalization/en-US.js"></script>
<script src="/base/static/lib/datejs/core.js"></script>
<script src="/base/static/lib/datejs/parser.js"></script>
<script src="/base/static/lib/datejs/sugarpak.js"></script>
<script src="/base/static/lib/datejs/extras.js"></script>
<script src="/base/static/lib/qweb/qweb.js"></script>
+2 -1
View File
@@ -1,5 +1,6 @@
import base.common as openerpweb
from base.controllers.main import View
import openerpweb
class CalendarView(View):
_cp_path = "/base_calendar/calendarview"
+2 -2
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
import openerpweb
import base.common as openerpweb
WIDGET_CONTENT_PATTERN = """<!DOCTYPE html>
<html>
@@ -18,7 +18,7 @@ WIDGET_CONTENT_PATTERN = """<!DOCTYPE html>
</body>
</html>
"""
class Widgets(openerpweb.openerpweb.Controller):
class Widgets(openerpweb.Controller):
_cp_path = '/base_dashboard/widgets'
@openerpweb.httprequest
+1 -1
View File
@@ -1,5 +1,5 @@
import base.common as openerpweb
from base.controllers.main import View
import openerpweb
class DiagramView(View):
_cp_path = "/base_diagram/diagram"
+1 -1
View File
@@ -1,4 +1,4 @@
import openerpweb
import base.common as openerpweb
from base.controllers.main import View
class GanttView(View):
+2 -2
View File
@@ -1,5 +1,5 @@
from base.controllers.main import View
import openerpweb
import base.common as openerpweb
class GraphView(View):
_cp_path = "/base_graph/graphview"
@@ -8,4 +8,4 @@ class GraphView(View):
def load(self, req, model, view_id):
fields_view = self.fields_view_get(req, model, view_id, 'graph')
all_fields = req.session.model(model).fields_get()
return {'fields_view': fields_view, 'all_fields':all_fields}
return {'fields_view': fields_view, 'all_fields':all_fields}
+1 -1
View File
@@ -4,7 +4,7 @@ import cherrypy
import simplejson
import random
import openerpweb
import base.common as openerpweb
#----------------------------------------------------------
# OpenERP Web ajaxim Controllers
+11 -11
View File
@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
import glob, os
import pprint
import simplejson
import openerpweb
import openerpweb.ast
import openerpweb.nonliterals
import cherrypy
#import glob, os
#import pprint
#
#import simplejson
#
#import openerpweb
#import openerpweb.ast
#import openerpweb.nonliterals
#
#import cherrypy
#
+16 -3
View File
@@ -1,7 +1,20 @@
#!/usr/bin/env python
import sys
import os,sys
import openerpweb
path_root = os.path.dirname(os.path.abspath(__file__))
path_addons = os.path.join(path_root, 'addons')
if path_addons not in sys.path:
sys.path.insert(0, path_addons)
import base
def main():
# TODO move the code of base.common.main here !
# change the code to configure a simple werzug server -> base.common.Root()
base.common.main(sys.argv)
if __name__ == "__main__":
openerpweb.main(sys.argv)
main()
-2
View File
@@ -1,2 +0,0 @@
#!/usr/bin/python
from openerpweb import *