[MERGE] trunk improvements
bzr revid: xmo@openerp.com-20110920101957-zl6tiuf49rzc0l2v
This commit is contained in:
commit
f896a2571e
|
@ -9,3 +9,4 @@ RE:^include/
|
|||
RE:^share/
|
||||
RE:^man/
|
||||
RE:^lib/
|
||||
logging.cfg
|
||||
|
|
|
@ -5,8 +5,7 @@ import logging
|
|||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
try:
|
||||
def wsgi_postload():
|
||||
import openerp.wsgi
|
||||
import os
|
||||
import tempfile
|
||||
|
@ -24,9 +23,6 @@ try:
|
|||
#import openerp.wsgi
|
||||
openerp.wsgi.register_wsgi_handler(app)
|
||||
|
||||
except ImportError:
|
||||
_logger.info("standalone mode")
|
||||
|
||||
# TODO
|
||||
# if we detect that we are imported from the openerp server register common.Root() as a wsgi entry point
|
||||
|
||||
|
|
|
@ -45,5 +45,5 @@
|
|||
"static/src/css/base.css",
|
||||
"static/src/css/data_export.css",
|
||||
],
|
||||
'wsgi' : 'app',
|
||||
'post_load' : 'wsgi_postload',
|
||||
}
|
||||
|
|
|
@ -43,30 +43,30 @@ class OpenERPSession(object):
|
|||
|
||||
def build_connection(self):
|
||||
return openerplib.get_connection(hostname=self._server, port=self._port,
|
||||
database=self._db,
|
||||
database=self._db, login=self._login,
|
||||
user_id=self._uid, password=self._password)
|
||||
|
||||
def proxy(self, service):
|
||||
return self.build_connection().get_service(service)
|
||||
|
||||
def bind(self, db, uid, password):
|
||||
def bind(self, db, uid, login, password):
|
||||
self._db = db
|
||||
self._uid = uid
|
||||
self._login = login
|
||||
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
|
||||
self.bind(db, uid, login, password)
|
||||
|
||||
if uid: self.get_context()
|
||||
return uid
|
||||
|
||||
def assert_valid(self):
|
||||
def assert_valid(self, force=False):
|
||||
"""
|
||||
Ensures this session is valid (logged into the openerp server)
|
||||
"""
|
||||
self.build_connection().check_login(False)
|
||||
self.build_connection().check_login(force)
|
||||
|
||||
def execute(self, model, func, *l, **d):
|
||||
self.assert_valid()
|
||||
|
|
|
@ -301,6 +301,7 @@ class Session(openerpweb.Controller):
|
|||
}
|
||||
@openerpweb.jsonrequest
|
||||
def get_session_info(self, req):
|
||||
req.session.assert_valid(force=True)
|
||||
return {
|
||||
"uid": req.session._uid,
|
||||
"context": req.session.get_context() if req.session._uid else False,
|
||||
|
|
|
@ -501,6 +501,8 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web
|
|||
self.on_session_valid();
|
||||
else
|
||||
self.on_session_invalid();
|
||||
}, function() {
|
||||
self.on_session_invalid();
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
@ -741,6 +743,59 @@ openerp.web.SessionAware = openerp.web.CallbackEnabled.extend(/** @lends openerp
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Base class for all visual components. Provides a lot of functionalities helpful
|
||||
* for the management of a part of the DOM.
|
||||
*
|
||||
* Widget handles:
|
||||
* - Rendering with QWeb.
|
||||
* - Life-cycle management and parenting (when a parent is destroyed, all its children are
|
||||
* destroyed too).
|
||||
* - Insertion in DOM.
|
||||
*
|
||||
* Widget also extends SessionAware for ease of use.
|
||||
*
|
||||
* Guide to create implementations of the Widget class:
|
||||
* ==============================================
|
||||
*
|
||||
* Here is a sample child class:
|
||||
*
|
||||
* MyWidget = openerp.base.Widget.extend({
|
||||
* // the name of the QWeb template to use for rendering
|
||||
* template: "MyQWebTemplate",
|
||||
* // identifier prefix, it is useful to put an obvious one for debugging
|
||||
* identifier_prefix: 'my-id-prefix-',
|
||||
*
|
||||
* init: function(parent) {
|
||||
* this._super(parent);
|
||||
* // stuff that you want to init before the rendering
|
||||
* },
|
||||
* start: function() {
|
||||
* this._super();
|
||||
* // stuff you want to make after the rendering, `this.$element` holds a correct value
|
||||
* this.$element.find(".my_button").click(/* an example of event binding * /);
|
||||
*
|
||||
* // if you have some asynchronous operations, it's a good idea to return
|
||||
* // a promise in start()
|
||||
* var promise = this.rpc(...);
|
||||
* return promise;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* Now this class can simply be used with the following syntax:
|
||||
*
|
||||
* var my_widget = new MyWidget(this);
|
||||
* my_widget.appendTo($(".some-div"));
|
||||
*
|
||||
* With these two lines, the MyWidget instance was inited, rendered, it was inserted into the
|
||||
* DOM inside the ".some-div" div and its events were binded.
|
||||
*
|
||||
* And of course, when you don't need that widget anymore, just do:
|
||||
*
|
||||
* my_widget.stop();
|
||||
*
|
||||
* That will kill the widget in a clean way and erase its content from the dom.
|
||||
*/
|
||||
openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widget# */{
|
||||
/**
|
||||
* The name of the QWeb template that will be used for rendering. Must be
|
||||
|
|
|
@ -26,7 +26,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
this.set_default_options(options);
|
||||
this.dataset = dataset;
|
||||
this.model = dataset.model;
|
||||
this.view_id = view_id;
|
||||
this.view_id = view_id || false;
|
||||
this.fields_view = {};
|
||||
this.widgets = {};
|
||||
this.widgets_counter = 0;
|
||||
|
@ -315,9 +315,15 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
var def = $.Deferred();
|
||||
$.when(this.has_been_loaded).then(function() {
|
||||
if (self.can_be_discarded()) {
|
||||
self.dataset.default_get(_.keys(self.fields_view.fields)).then(self.on_record_loaded).then(function() {
|
||||
var keys = _.keys(self.fields_view.fields);
|
||||
if (keys.length) {
|
||||
self.dataset.default_get(keys).then(self.on_record_loaded).then(function() {
|
||||
def.resolve();
|
||||
});
|
||||
} else {
|
||||
self.on_record_loaded({});
|
||||
def.resolve();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return def.promise();
|
||||
|
@ -373,7 +379,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
first_invalid_field.focus();
|
||||
this.on_invalid();
|
||||
return false;
|
||||
} else if (form_dirty) {
|
||||
} else {
|
||||
console.log("About to save", values);
|
||||
if (!this.datarecord.id) {
|
||||
return this.dataset.create(values, function(r) {
|
||||
|
@ -384,11 +390,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
self.on_saved(r, success);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setTimeout(function() {
|
||||
self.on_saved({ result: true }, success);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
},
|
||||
do_save_edit: function() {
|
||||
|
@ -413,7 +414,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
if (!r.result) {
|
||||
// should not happen in the server, but may happen for internal purpose
|
||||
} else {
|
||||
console.debug(_.sprintf("The record #%s has been saved.", this.datarecord.id));
|
||||
if (success) {
|
||||
success(r);
|
||||
}
|
||||
|
@ -986,7 +986,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
|
|||
return !this.invalid;
|
||||
},
|
||||
is_dirty: function() {
|
||||
return this.dirty;
|
||||
return this.dirty && !this.readonly;
|
||||
},
|
||||
get_on_change_value: function() {
|
||||
return this.get_value();
|
||||
|
@ -2551,7 +2551,7 @@ openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
|
||||
template: "FieldStatus",
|
||||
template: "EmptyComponent",
|
||||
start: function() {
|
||||
this._super();
|
||||
this.selected_value = null;
|
||||
|
|
|
@ -133,11 +133,19 @@ db.web.ActionManager = db.web.Widget.extend({
|
|||
(this.client_widget = new ClientWidget(this, action.params)).appendTo(this);
|
||||
},
|
||||
ir_actions_report_xml: function(action) {
|
||||
var self = this;
|
||||
$.blockUI();
|
||||
this.session.get_file({
|
||||
url: '/web/report',
|
||||
data: {action: JSON.stringify(action)},
|
||||
complete: $.unblockUI
|
||||
self.rpc("/web/session/eval_domain_and_context", {
|
||||
contexts: [action.context],
|
||||
domains: []
|
||||
}).then(function(res) {
|
||||
action = _.clone(action);
|
||||
action.context = res.context;
|
||||
self.session.get_file({
|
||||
url: '/web/report',
|
||||
data: {action: JSON.stringify(action)},
|
||||
complete: $.unblockUI
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -342,22 +342,12 @@
|
|||
<li>
|
||||
<a t-att-href="'/' + widget.qs" title="Home" class="home"><img src="/web/static/src/img/header-home.png" width="16" height="16" border="0"/></a>
|
||||
</li>
|
||||
<!--
|
||||
<li>
|
||||
<a href="#requests" title="Requests" class="requests"><img src="/web/static/src/img/header-requests.png" width="16" height="16" border="0"/><small>1</small></a>
|
||||
</li>
|
||||
-->
|
||||
<li class="preferences">
|
||||
<a href="#preferences" title="Preferences" class="preferences"><img src="/web/static/src/img/header-preferences.png" width="16" height="16" border="0"/></a>
|
||||
<a href="javascript:void(0)" title="Preferences" class="preferences"><img src="/web/static/src/img/header-preferences.png" width="16" height="16" border="0"/></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#about" title="About" class="about"><img src="/web/static/src/img/header-about.png" width="16" height="16" border="0"/></a>
|
||||
<a href="javascript:void(0)" title="About" class="about"><img src="/web/static/src/img/header-about.png" width="16" height="16" border="0"/></a>
|
||||
</li>
|
||||
<!--
|
||||
<li>
|
||||
<a href="http://doc.openerp.com/v6.0/book?version=$version" title="Help" target="_blank" class="help"><img src="/web/static/src/img/header-help.png" width="16" height="16" border="0"/></a>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
<div class="block">
|
||||
<a href="#logout" class="logout">LOGOUT</a>
|
||||
|
@ -1428,9 +1418,6 @@
|
|||
</p>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="FieldStatus">
|
||||
<div t-att-id="widget.element_id"></div>
|
||||
</t>
|
||||
<t t-name="FieldStatus.content">
|
||||
<ul class="oe-arrow-list">
|
||||
<t t-set="size" t-value="widget.to_show.length"/>
|
||||
|
|
|
@ -282,6 +282,18 @@ view managers can correctly communicate with them:
|
|||
defining e.g. the ``start`` method) or at the instance level (in the
|
||||
class's ``init``), though you should generally set it on the class.
|
||||
|
||||
Frequent development tasks
|
||||
--------------------------
|
||||
|
||||
There are a number of tasks which OpenERP Web developers do or will need to
|
||||
perform quite regularly. To make these easier, we have written a few guides
|
||||
to help you get started:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
guides/client-action.rst
|
||||
|
||||
Utility behaviors
|
||||
-----------------
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
Creating a new client action
|
||||
============================
|
||||
|
||||
Client actions are the client-side of OpenERP's "Server Actions": instead of
|
||||
allowing for semi-arbitrary code to be executed in the server, they allow
|
||||
for execution of client-customized code.
|
||||
|
||||
On the server side, a client action is an action of type ``ir.actions.client``,
|
||||
which has (at most) two properties: a mandatory ``tag``, which is an arbitrary
|
||||
string by which the client will identify the action, and an optional ``params``
|
||||
which is simply a map of keys and values sent to the client as-is (this way,
|
||||
client actions can be made generic and reused in multiple contexts).
|
||||
|
||||
General Structure
|
||||
-----------------
|
||||
|
||||
In the OpenERP Web code, a client action only requires two pieces of
|
||||
information:
|
||||
|
||||
* Mapping the action's ``tag`` to an OpenERP Web object
|
||||
|
||||
* The OpenERP Web object itself, which must inherit from
|
||||
:js:class:`openerp.web.Widget`
|
||||
|
||||
Our example will be the actual code for the widgets client action (a client
|
||||
action displaying a ``res.widget`` object, used in the homepage dashboard of
|
||||
the web client):
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// Registers the object 'openerp.web_dashboard.Widget' to the client
|
||||
// action tag 'board.home.widgets'
|
||||
openerp.web.client_actions.add(
|
||||
'board.home.widgets', 'openerp.web_dashboard.Widget');
|
||||
// This object inherits from View, but only Widget is required
|
||||
openerp.web_dashboard.Widget = openerp.web.View.extend({
|
||||
template: 'HomeWidget'
|
||||
});
|
||||
|
||||
At this point, the generic ``Widget`` lifecycle takes over, the template is
|
||||
rendered, inserted in the client DOM, bound on the object's ``$element``
|
||||
property and the object is started.
|
||||
|
||||
If the client action takes parameters, these parameters are passed in as a
|
||||
second positional parameter to the constructor:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
init: function (parent, params) {
|
||||
// execute the Widget's init
|
||||
this._super(parent);
|
||||
// board.home.widgets only takes a single param, the identifier of the
|
||||
// res.widget object it should display. Store it for later
|
||||
this.widget_id = params.widget_id;
|
||||
}
|
||||
|
||||
More complex initialization (DOM manipulations, RPC requests, ...) should be
|
||||
performed in the ``start()`` method.
|
||||
|
||||
.. note::
|
||||
As required by ``Widget``'s contract, if ``start`` executes any
|
||||
asynchronous code it should return a ``$.Deferred`` so callers know when
|
||||
it's ready for interaction.
|
||||
|
||||
Although generally speaking client actions are not really interacted with.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
start: function () {
|
||||
return $.when(
|
||||
this._super(),
|
||||
// Simply read the res.widget object this action should display
|
||||
new openerp.web.DataSet(this, 'res.widget').read_ids(
|
||||
[this.widget_id], ['title'], this.on_widget_loaded));
|
||||
}
|
||||
|
||||
The client action can then behave exactly as it wishes to within its root
|
||||
(``this.$element``). In this case, it performs further renderings once its
|
||||
widget's content is retrieved:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
on_widget_loaded: function (widgets) {
|
||||
var widget = widgets[0];
|
||||
var url = _.sprintf(
|
||||
'/web_dashboard/widgets/content?session_id=%s&widget_id=%d',
|
||||
this.session.session_id, widget.id);
|
||||
this.$element.html(QWeb.render('HomeWidget.content', {
|
||||
widget: widget,
|
||||
url: url
|
||||
}));
|
||||
}
|
|
@ -37,6 +37,9 @@ optparser.add_option("--log-level", dest="log_level",
|
|||
default='debug', help="Log level", metavar="LOG_LEVEL")
|
||||
optparser.add_option("--log-config", dest="log_config",
|
||||
default='', help="Log config file", metavar="LOG_CONFIG")
|
||||
optparser.add_option('--multi-threaded', dest='threaded',
|
||||
default=True, action='store_true',
|
||||
help="Use multiple threads to handle requests")
|
||||
|
||||
import web.common.dispatch
|
||||
|
||||
|
@ -55,5 +58,5 @@ if __name__ == "__main__":
|
|||
|
||||
werkzeug.serving.run_simple(
|
||||
'0.0.0.0', options.socket_port, app,
|
||||
use_reloader=options.reloader, threaded=True)
|
||||
use_reloader=options.reloader, threaded=options.threaded)
|
||||
|
||||
|
|
Loading…
Reference in New Issue