[MERGE] Sync with trunk

bzr revid: tde@openerp.com-20130507093818-9hlt7e43r3y6002c
This commit is contained in:
Thibault Delavallée 2013-05-07 11:38:18 +02:00
commit 83a6705cf3
5 changed files with 3527 additions and 2670 deletions

95
addons/web/com.py Normal file
View File

@ -0,0 +1,95 @@
import openerp.service.security as security
import openerp.service.model as model
import openerp
def _sessions():
if getattr(_thread_data, "sessions", None) is None:
_thread_data.sessions = []
return _thread_data.sessions
class Session(object):
def __init__(self, db, uid, password):
self.db = db
self.uid = uid
self.password = password
self.cr = None
def model(self, model_name):
return Model(self, model_name)
def _execute(self, model_name, method, args, kwargs):
self.ensure_transation()
return model.execute_cr(self.cr, self.uid, model_name, method, *args, **kwargs)
def ensure_transation(self):
if self.cr is None:
security.check(self.db, self.uid, self.password)
threading.currentThread().dbname = self.db
self.cr = openerp.registry(self.db).db.cursor()
def close_transaction(self, has_exception=False):
if self.cr is None:
return
if has_exception:
try:
self.cr.rollback()
except:
pass # nothing
else:
try:
self.cr.commit()
except:
pass # nothing
try:
self.cr.close()
except:
pass # nothing
self.cr = None
class Model(object):
def __init__(self, session, name):
self.session = session
self.name = name
def __getattr__(self, method):
def proxy(*args, **kw):
result = self.session._execute(self.name, method, args, kw)
# reorder read
if method == "read":
if isinstance(result, list) and len(result) > 0 and "id" in result[0]:
index = {}
for r in result:
index[r['id']] = r
result = [index[x] for x in args[0] if x in index]
return result
return proxy
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, context=None):
record_ids = self.search(domain or [], offset, limit or False, order or False, context or {})
if not record_ids: return []
records = self.read(record_ids, fields or [], context or {})
return records
import threading
_thread_data = threading.local()
class _ThreadSession:
def __getattr__(self, name):
if len(_sessions()) == 0:
raise Exception("Session not initialized")
return getattr(_sessions()[-1], name)
def __setattr__(self, name, value):
if len(_sessions()) == 0:
raise Exception("Session not initialized")
return setattr(_sessions()[-1], name, value)
def init(self, db, uid, password):
ses = self
class with_obj:
def __enter__(self):
_sessions().append(Session(db, uid, password))
def __exit__(self, type, value, traceback):
_sessions().pop().close_transaction(type is not None)
return with_obj()
transaction = _ThreadSession()

View File

@ -19,6 +19,7 @@ import time
import traceback
import urlparse
import uuid
import errno
import babel.core
import simplejson
@ -117,6 +118,11 @@ class WebRequest(object):
# we use _ as seprator where RFC2616 uses '-'
self.lang = lang.replace('-', '_')
def wrap_transaction(self, fct):
from openerp.addons.web.com import transaction
with transaction.init(self.session._db, self.session._uid, self.session._password):
return fct()
def reject_nonliteral(dct):
if '__ref' in dct:
raise ValueError(
@ -199,8 +205,8 @@ class JsonRequest(WebRequest):
if _logger.isEnabledFor(logging.DEBUG):
_logger.debug("--> %s.%s\n%s", method.im_class.__name__, method.__name__, pprint.pformat(self.jsonrequest))
response['id'] = self.jsonrequest.get('id')
response["result"] = method(self, **self.params)
except session.AuthenticationError:
response["result"] = self.wrap_transaction(lambda: method(self, **self.params))
except session.AuthenticationError, e:
se = serialize_exception(e)
error = {
'code': 100,
@ -293,7 +299,7 @@ class HttpRequest(WebRequest):
akw[key] = type(value)
_logger.debug("%s --> %s.%s %r", self.httprequest.method, method.im_class.__name__, method.__name__, akw)
try:
r = method(self, **self.params)
r = self.wrap_transaction(lambda: method(self, **self.params))
except Exception, e:
_logger.exception("An exception occured during an http request")
se = serialize_exception(e)
@ -354,17 +360,31 @@ def httprequest(f):
addons_module = {}
addons_manifest = {}
controllers_class = []
controllers_class_path = {}
controllers_object = {}
controllers_object_path = {}
controllers_path = {}
class ControllerType(type):
def __init__(cls, name, bases, attrs):
super(ControllerType, cls).__init__(name, bases, attrs)
controllers_class.append(("%s.%s" % (cls.__module__, cls.__name__), cls))
name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls)
controllers_class.append(name_class)
path = attrs.get('_cp_path')
if path not in controllers_class_path:
controllers_class_path[path] = name_class
class Controller(object):
__metaclass__ = ControllerType
def __new__(cls, *args, **kwargs):
subclasses = [c for c in cls.__subclasses__() if c._cp_path == cls._cp_path]
if subclasses:
name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses))
cls = type(name, tuple(reversed(subclasses)), {})
return object.__new__(cls)
#----------------------------------------------------------
# Session context manager
#----------------------------------------------------------
@ -476,8 +496,15 @@ def session_path():
except Exception:
username = "unknown"
path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
if not os.path.exists(path):
try:
os.mkdir(path, 0700)
except OSError as exc:
if exc.errno == errno.EEXIST:
# directory exists: ensure it has the correct permissions
# this will fail if the directory is not owned by the current user
os.chmod(path, 0700)
else:
raise
return path
class Root(object):
@ -541,7 +568,7 @@ class Root(object):
controllers and configure them. """
for addons_path in openerp.modules.module.ad_paths:
for module in sorted(os.listdir(addons_path)):
for module in sorted(os.listdir(str(addons_path))):
if module not in addons_module:
manifest_path = os.path.join(addons_path, module, '__openerp__.py')
path_static = os.path.join(addons_path, module, 'static')
@ -557,10 +584,11 @@ class Root(object):
addons_manifest[module] = manifest
self.statics['/%s/static' % module] = path_static
for k, v in controllers_class:
if k not in controllers_object:
o = v()
controllers_object[k] = o
for k, v in controllers_class_path.items():
if k not in controllers_object_path and hasattr(v[1], '_cp_path'):
o = v[1]()
controllers_object[v[0]] = o
controllers_object_path[k] = o
if hasattr(o, '_cp_path'):
controllers_path[o._cp_path] = o
@ -591,6 +619,9 @@ class Root(object):
elif exposed == 'http':
_logger.debug("Dispatch http to %s %s %s", ps, c, method_name)
return lambda request: HttpRequest(request).dispatch(method)
elif method_name != "index":
method_name = "index"
continue
ps, _slash, method_name = ps.rpartition('/')
if not ps and method_name:
ps = '/'

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2013-04-29 04:42+0000\n"
"X-Launchpad-Export-Date: 2013-04-30 05:42+0000\n"
"X-Generator: Launchpad (build 16580)\n"
#. module: web_kanban

View File

@ -133,12 +133,16 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
}
switch (node.tag) {
case 'field':
if (this.fields_view.fields[node.attrs.name].type === 'many2many') {
var ftype = this.fields_view.fields[node.attrs.name].type;
ftype = node.attrs.widget ? node.attrs.widget : ftype;
if (ftype === 'many2many') {
if (_.indexOf(this.many2manys, node.attrs.name) < 0) {
this.many2manys.push(node.attrs.name);
}
node.tag = 'div';
node.attrs['class'] = (node.attrs['class'] || '') + ' oe_form_field oe_tags';
} else if (instance.web_kanban.fields_registry.contains(ftype)) {
// do nothing, the kanban record will handle it
} else {
node.tag = QWeb.prefix;
node.attrs[QWeb.prefix + '-esc'] = 'record.' + node.attrs['name'] + '.value';
@ -766,6 +770,7 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({
};
}
this.state = this.view.state.records[this.id];
this.fields = {};
},
set_record: function(record) {
var self = this;
@ -779,7 +784,16 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({
this.record = this.transform_record(record);
},
start: function() {
var self = this;
this._super();
this.init_content();
},
init_content: function() {
var self = this;
self.sub_widgets = [];
this.$("[data-field_id]").each(function() {
self.add_widget($(this));
});
this.$el.data('widget', this);
this.bind_events();
},
@ -815,6 +829,28 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({
'content': this.view.qweb.render('kanban-box', this.qweb_context)
});
this.replaceElement($el);
this.replace_fields();
},
replace_fields: function() {
var self = this;
this.$("field").each(function() {
var $field = $(this);
var $nfield = $("<span></span");
var id = _.uniqueId("kanbanfield");
self.fields[id] = $field;
$nfield.attr("data-field_id", id);
$field.replaceWith($nfield);
});
},
add_widget: function($node) {
var $orig = this.fields[$node.data("field_id")];
var field = this.record[$orig.attr("name")];
var type = field.type;
type = $orig.attr("widget") ? $orig.attr("widget") : type;
var obj = instance.web_kanban.fields_registry.get_object(type);
var widget = new obj(this, field, $orig);
this.sub_widgets.push(widget);
widget.replace($node);
},
bind_events: function() {
var self = this;
@ -956,11 +992,14 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({
do_reload: function() {
var self = this;
this.view.dataset.read_ids([this.id], this.view.fields_keys.concat(['__last_update'])).done(function(records) {
_.each(self.sub_widgets, function(el) {
el.destroy();
});
self.sub_widgets = [];
if (records.length) {
self.set_record(records[0]);
self.renderElement();
self.$el.data('widget', self);
self.bind_events();
self.init_content();
self.group.compute_cards_auto_height();
self.view.postprocess_m2m_tags();
} else {
@ -1114,6 +1153,44 @@ instance.web_kanban.QuickCreate = instance.web.Widget.extend({
});
}
});
/**
* Interface to be implemented by kanban fields.
*
*/
instance.web_kanban.FieldInterface = {
/**
Constructor.
- parent: The widget's parent.
- field: A dictionary giving details about the field, including the current field's value in the
raw_value field.
- $node: The field <field> tag as it appears in the view, encapsulated in a jQuery object.
*/
init: function(parent, field, $node) {},
};
/**
* Abstract class for classes implementing FieldInterface.
*
* Properties:
* - value: useful property to hold the value of the field. By default, the constructor
* sets value property.
*
*/
instance.web_kanban.AbstractField = instance.web.Widget.extend(instance.web_kanban.FieldInterface, {
/**
Constructor that saves the field and $node parameters and sets the "value" property.
*/
init: function(parent, field, $node) {
this._super(parent);
this.field = field;
this.$node = $node;
this.options = instance.web.py_eval(this.$node.attr("options") || '{}');
this.set("value", field.raw_value);
},
});
instance.web_kanban.fields_registry = new instance.web.Registry({});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: