[MERGE] from trunk

bzr revid: xmo@openerp.com-20110816142515-ohq33zvua5uq8cd3
This commit is contained in:
Xavier Morel 2011-08-16 16:25:15 +02:00
commit ffae7cb4b3
43 changed files with 2274 additions and 1831 deletions

View File

@ -1,12 +1,11 @@
RE:lib/(?!populate.sh).*
.*
*.egg-info
*.orig
bin/
build/
dist/
include/
RE:^bin/
RE:^dist/
RE:^include/
share/
man/
lib/
RE:^share/
RE:^man/
RE:^lib/

View File

@ -22,7 +22,7 @@
"static/lib/underscore/underscore.string.js",
"static/src/js/boot.js",
"static/src/js/core.js",
"static/src/js/dates.js",
"static/src/js/formats.js",
"static/src/js/chrome.js",
"static/src/js/views.js",
"static/src/js/data.js",

View File

@ -18,6 +18,7 @@ import openerpweb
import openerpweb.ast
import openerpweb.nonliterals
from babel.messages.pofile import read_po
# Should move to openerpweb.Xml2Json
class Xml2Json:
@ -156,6 +157,43 @@ class WebClient(openerpweb.Controller):
'css': css
}
return r
@openerpweb.jsonrequest
def translations(self, req, mods, lang):
lang_model = req.session.model('res.lang')
ids = lang_model.search([("code", "=", lang)])
if ids:
lang_obj = lang_model.read(ids[0], ["direction", "date_format", "time_format",
"grouping", "decimal_point", "thousands_sep"])
else:
lang_obj = None
if lang.count("_") > 0:
separator = "_"
else:
separator = "@"
langs = lang.split(separator)
langs = [separator.join(langs[:x]) for x in range(1, len(langs) + 1)]
transs = {}
for addon_name in mods:
transl = {"messages":[]}
transs[addon_name] = transl
for l in langs:
f_name = os.path.join(openerpweb.path_addons, addon_name, "po", l + ".po")
if not os.path.exists(f_name):
continue
try:
with open(f_name) as t_file:
po = read_po(t_file)
except:
continue
for x in po:
if x.id and x.string:
transl["messages"].append({'id': x.id, 'string': x.string})
return {"modules": transs,
"lang_parameters": lang_obj}
class Database(openerpweb.Controller):
_cp_path = "/base/database"
@ -251,10 +289,12 @@ class Session(openerpweb.Controller):
@openerpweb.jsonrequest
def login(self, req, db, login, password):
req.session.login(db, login, password)
ctx = req.session.get_context()
return {
"session_id": req.session_id,
"uid": req.session._uid,
"context": ctx
}
@openerpweb.jsonrequest
@ -390,10 +430,10 @@ def load_actions_from_ir_values(req, key, key2, models, meta, context):
Values = req.session.model('ir.values')
actions = Values.get(key, key2, models, meta, context)
return [(id, name, clean_action(action, req.session))
return [(id, name, clean_action(action, req.session, context=context))
for id, name, action in actions]
def clean_action(action, session):
def clean_action(action, session, context=None):
action.setdefault('flags', {})
if action['type'] != 'ir.actions.act_window':
return action
@ -401,7 +441,7 @@ def clean_action(action, session):
if isinstance(action.get('context'), basestring):
action['context'] = eval(
action['context'],
session.evaluation_context()) or {}
session.evaluation_context(context=context)) or {}
if isinstance(action.get('domain'), basestring):
action['domain'] = eval(
@ -559,6 +599,7 @@ class DataSet(openerpweb.Controller):
:rtype: list
"""
Model = request.session.model(model)
context, domain = eval_context_and_domain(
request.session, request.context, domain)
@ -581,9 +622,14 @@ class DataSet(openerpweb.Controller):
}
@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):
return self.do_get(request, model, ids, fields)
def do_get(self, request, model, ids, fields=False):
""" Fetches and returns the records of the model ``model`` whose ids
are in ``ids``.
@ -668,6 +714,12 @@ class DataSet(openerpweb.Controller):
Model = req.session.model(model)
return Model.default_get(fields, req.session.eval_context(req.context))
@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
@ -760,8 +812,8 @@ class View(openerpweb.Controller):
""" Parses an arbitrary string containing a domain, transforms it
to either a literal domain or a :class:`openerpweb.nonliterals.Domain`
:param domain: the domain to parse, if the domain is not a string it is assumed to
be a literal domain and is returned as-is
:param domain: the domain to parse, if the domain is not a string it
is assumed to be a literal domain and is returned as-is
:param session: Current OpenERP session
:type session: openerpweb.openerpweb.OpenERPSession
"""
@ -777,8 +829,8 @@ class View(openerpweb.Controller):
""" Parses an arbitrary string containing a context, transforms it
to either a literal context or a :class:`openerpweb.nonliterals.Context`
:param context: the context to parse, if the context is not a string it is assumed to
be a literal domain and is returned as-is
:param context: the context to parse, if the context is not a string it
is assumed to be a literal domain and is returned as-is
:param session: Current OpenERP session
:type session: openerpweb.openerpweb.OpenERPSession
"""
@ -1001,6 +1053,19 @@ class Action(openerpweb.Controller):
return clean_action(req.session.model('ir.actions.server').run(
[action_id], req.session.eval_context(req.context)), req.session)
class TreeView(View):
_cp_path = "/base/treeview"
@openerpweb.jsonrequest
def load(self, req, model, view_id, toolbar=False):
return self.fields_view_get(req, model, view_id, 'tree', toolbar=toolbar)
@openerpweb.jsonrequest
def action(self, req, model, id):
return load_actions_from_ir_values(
req,'action', 'tree_but_open',[(model, id)],
False, req.session.eval_context(req.context))
def export_csv(fields, result):
fp = StringIO()
writer = csv.writer(fp, quoting=csv.QUOTE_ALL)
@ -1213,4 +1278,3 @@ class Export(View):
return export_xls(field, result)
else:
return export_csv(field, result)

32
addons/base/po/base.pot Normal file
View File

@ -0,0 +1,32 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
#: addons/base/static/src/js/form.js:1459
msgid "<em>   Search More...</em>"
msgstr ""
#: addons/base/static/src/js/form.js:1472
#, python-format
msgid "<em>   Create \"<strong>%s</strong>\"</em>"
msgstr ""
#: addons/base/static/src/js/form.js:1478
msgid "<em>   Create and Edit...</em>"
msgstr ""

33
addons/base/po/fr_FR.po Normal file
View File

@ -0,0 +1,33 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# niv <nicolas.vanhoren@openerp.com>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-09 17:30+0200\n"
"PO-Revision-Date: 2011-08-09 17:31+0200\n"
"Last-Translator: niv <nicolas.vanhoren@openerp.com>\n"
"Language-Team: French\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.4\n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: addons/base/static/src/js/form.js:1459
msgid "<em>   Search More...</em>"
msgstr "<em>   Chercher plus...</em>"
#: addons/base/static/src/js/form.js:1472
#, python-format
msgid "<em>   Create \"<strong>%s</strong>\"</em>"
msgstr "<em>   Créer \"<strong>%s</strong>\"</em>"
#: addons/base/static/src/js/form.js:1478
msgid "<em>   Create and Edit...</em>"
msgstr "<em>   Créer et éditer...</em>"

View File

@ -1,5 +1,7 @@
// TODO: trim support
// TODO: line number -> https://bugzilla.mozilla.org/show_bug.cgi?id=618650
// TODO: templates orverwritten could be called by t-call="__super__" ?
// TODO: t-set + t-value + children node == scoped variable ?
var QWeb2 = {
expressions_cache: {},
reserved_words: 'true,false,NaN,null,undefined,debugger,in,instanceof,new,function,return,this,typeof,eval,Math,RegExp,Array,Object,Date'.split(','),
@ -21,6 +23,11 @@ var QWeb2 = {
}
throw new Error(prefix + ": " + message);
},
warning : function(message) {
if (typeof(window) !== 'undefined' && window.console) {
window.console.warn(message);
}
},
trim: function(s, mode) {
switch (mode) {
case "left":
@ -638,6 +645,9 @@ QWeb2.Element = (function() {
compile_action_set : function(value) {
var variable = this.format_expression(value);
if (this.actions['value']) {
if (this.children.length) {
this.engine.tools.warning("@set with @value plus node chidren found. Children are ignored.");
}
this.top(variable + " = (" + (this.format_expression(this.actions['value'])) + ");");
this.process_children = false;
} else {

View File

@ -1,6 +1,6 @@
//---------------------------------------------------------
// OpenERP Web Boostrap
//---------------------------------------------------------
/*---------------------------------------------------------
* OpenERP Web Boostrap Code
*---------------------------------------------------------*/
/**
* @name openerp
@ -14,13 +14,8 @@
/** @lends openerp */
var openerp = this.openerp = {
/**
* Debug flag turns on logging
*/
// debug flag
debug: true,
// element_ids registry linked to all controllers on the page
// TODO rename to elements, or keep gtk naming?
screen: {},
// Per session namespace
// openerp.<module> will map to
// openerp.sessions.sessionname.<module> using a closure
@ -40,8 +35,6 @@
// this unique id will be replaced by hostname_databasename by
// openerp.base.Connection on the first connection
_session_id: "session" + session_counter++,
screen: openerp.screen,
sessions: openerp.sessions,
base: {},
web_mobile: {}
};
@ -51,16 +44,17 @@
}
return new_instance;
}
// TODO add initrpc to init core only for RPC
};
})();
//---------------------------------------------------------
// OpenERP base module split
//---------------------------------------------------------
/*---------------------------------------------------------
* OpenERP Web base module split
*---------------------------------------------------------*/
openerp.base = function(instance) {
openerp.base.core(instance);
openerp.base.dates(instance);
openerp.base.formats(instance);
openerp.base.chrome(instance);
openerp.base.data(instance);
if (openerp.base.views) {

View File

@ -1,287 +1,9 @@
/*---------------------------------------------------------
* OpenERP base library
* OpenERP Web chrome
*---------------------------------------------------------*/
openerp.base.chrome = function(openerp) {
openerp.base.Session = openerp.base.Widget.extend( /** @lends openerp.base.Session# */{
/**
* @constructs
* @param element_id to use for exception reporting
* @param server
* @param port
*/
init: function(parent, element_id, server, port) {
this._super(parent, element_id);
this.server = (server == undefined) ? location.hostname : server;
this.port = (port == undefined) ? location.port : port;
this.rpc_mode = (server == location.hostname) ? "ajax" : "jsonp";
this.debug = true;
this.db = "";
this.login = "";
this.password = "";
this.uid = false;
this.session_id = false;
this.module_list = [];
this.module_loaded = {"base": true};
this.context = {};
this.shortcuts = [];
this.active_id = null;
},
start: function() {
this.session_restore();
},
/**
* Executes an RPC call, registering the provided callbacks.
*
* Registers a default error callback if none is provided, and handles
* setting the correct session id and session context in the parameter
* objects
*
* @param {String} url RPC endpoint
* @param {Object} params call parameters
* @param {Function} success_callback function to execute on RPC call success
* @param {Function} error_callback function to execute on RPC call failure
* @returns {jQuery.Deferred} jquery-provided ajax deferred
*/
rpc: function(url, params, success_callback, error_callback) {
var self = this;
// Construct a JSON-RPC2 request, method is currently unused
params.session_id = this.session_id;
// Call using the rpc_mode
var deferred = $.Deferred();
this.rpc_ajax(url, {
jsonrpc: "2.0",
method: "call",
params: params,
id:null
}).then(function () {deferred.resolve.apply(deferred, arguments);},
function(error) {deferred.reject(error, $.Event());});
return deferred.fail(function() {
deferred.fail(function(error, event) {
if (!event.isDefaultPrevented()) {
self.on_rpc_error(error, event);
}
});
}).then(success_callback, error_callback).promise();
},
/**
* Raw JSON-RPC call
*
* @returns {jQuery.Deferred} ajax-based deferred object
*/
rpc_ajax: function(url, payload) {
var self = this;
this.on_rpc_request();
// url can be an $.ajax option object
if (_.isString(url)) {
url = {
url: url
}
}
var ajax = _.extend({
type: "POST",
url: url,
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(payload),
processData: false
}, url);
var deferred = $.Deferred();
$.ajax(ajax).done(function(response, textStatus, jqXHR) {
self.on_rpc_response();
if (!response.error) {
deferred.resolve(response["result"], textStatus, jqXHR);
return;
}
if (response.error.data.type !== "session_invalid") {
deferred.reject(response.error);
return;
}
self.uid = false;
self.on_session_invalid(function() {
self.rpc(url, payload.params,
function() {
deferred.resolve.apply(deferred, arguments);
},
function(error, event) {
event.preventDefault();
deferred.reject.apply(deferred, arguments);
});
});
}).fail(function(jqXHR, textStatus, errorThrown) {
self.on_rpc_response();
var error = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
};
deferred.reject(error);
});
return deferred.promise();
},
on_rpc_request: function() {
},
on_rpc_response: function() {
},
on_rpc_error: function(error) {
},
/**
* The session is validated either by login or by restoration of a previous session
*/
on_session_valid: function() {
if(!openerp._modules_loaded)
this.load_modules();
},
on_session_invalid: function(contination) {
},
session_is_valid: function() {
return this.uid;
},
session_login: function(db, login, password, success_callback) {
var self = this;
this.db = db;
this.login = login;
this.password = password;
var params = { db: this.db, login: this.login, password: this.password };
this.rpc("/base/session/login", params, function(result) {
self.session_id = result.session_id;
self.uid = result.uid;
self.session_save();
self.on_session_valid();
if (success_callback)
success_callback();
});
},
session_logout: function() {
this.uid = false;
},
/**
* Reloads uid and session_id from local storage, if they exist
*/
session_restore: function () {
this.uid = this.get_cookie('uid');
this.session_id = this.get_cookie('session_id');
this.db = this.get_cookie('db');
this.login = this.get_cookie('login');
// we should do an rpc to confirm that this session_id is valid and if it is retrieve the information about db and login
// then call on_session_valid
this.on_session_valid();
},
/**
* Saves the session id and uid locally
*/
session_save: function () {
this.set_cookie('uid', this.uid);
this.set_cookie('session_id', this.session_id);
this.set_cookie('db', this.db);
this.set_cookie('login', this.login);
},
logout: function() {
delete this.uid;
delete this.session_id;
delete this.db;
delete this.login;
this.set_cookie('uid', '');
this.set_cookie('session_id', '');
this.set_cookie('db', '');
this.set_cookie('login', '');
this.on_session_invalid(function() {});
},
/**
* Fetches a cookie stored by an openerp session
*
* @private
* @param name the cookie's name
*/
get_cookie: function (name) {
var nameEQ = this.element_id + '|' + name + '=';
var cookies = document.cookie.split(';');
for(var i=0; i<cookies.length; ++i) {
var cookie = cookies[i].replace(/^\s*/, '');
if(cookie.indexOf(nameEQ) === 0) {
return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
}
}
return null;
},
/**
* Create a new cookie with the provided name and value
*
* @private
* @param name the cookie's name
* @param value the cookie's value
* @param ttl the cookie's time to live, 1 year by default, set to -1 to delete
*/
set_cookie: function (name, value, ttl) {
ttl = ttl || 24*60*60*365;
document.cookie = [
this.element_id + '|' + name + '=' + encodeURIComponent(JSON.stringify(value)),
'max-age=' + ttl,
'expires=' + new Date(new Date().getTime() + ttl*1000).toGMTString()
].join(';');
},
/**
* Load additional web addons of that instance and init them
*/
load_modules: function() {
var self = this;
this.rpc('/base/session/modules', {}, function(result) {
self.module_list = result;
var modules = self.module_list.join(',');
if(self.debug || true) {
self.rpc('/base/webclient/csslist', {"mods": modules}, self.do_load_css);
self.rpc('/base/webclient/jslist', {"mods": modules}, self.do_load_js);
} else {
self.do_load_css(["/base/webclient/css?mods="+modules]);
self.do_load_js(["/base/webclient/js?mods="+modules]);
}
openerp._modules_loaded = true;
});
},
do_load_css: function (files) {
_.each(files, function (file) {
$('head').append($('<link>', {
'href': file,
'rel': 'stylesheet',
'type': 'text/css'
}));
});
},
do_load_js: function(files) {
var self = this;
if(files.length != 0) {
var file = files.shift();
var tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = file;
tag.onload = tag.onreadystatechange = function() {
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
return;
tag.onload_done = true;
self.do_load_js(files);
};
document.head.appendChild(tag);
} else {
this.on_modules_loaded();
}
},
on_modules_loaded: function() {
for(var j=0; j<this.module_list.length; j++) {
var mod = this.module_list[j];
if(this.module_loaded[mod])
continue;
openerp[mod] = {};
// init module mod
if(openerp._openerp[mod] != undefined) {
openerp._openerp[mod](openerp);
this.module_loaded[mod] = true;
}
}
}
});
openerp.base.Notification = openerp.base.Widget.extend({
init: function(parent, element_id) {
this._super(parent, element_id);
@ -307,10 +29,10 @@ openerp.base.Notification = openerp.base.Widget.extend({
openerp.base.Dialog = openerp.base.OldWidget.extend({
dialog_title: "",
identifier_prefix: 'dialog',
init: function (parent, options) {
init: function (parent, dialog_options) {
var self = this;
this._super(parent);
this.options = {
this.dialog_options = {
modal: true,
width: 'auto',
min_width: 0,
@ -320,27 +42,25 @@ openerp.base.Dialog = openerp.base.OldWidget.extend({
max_height: '100%',
autoOpen: false,
buttons: {},
beforeClose: function () {
self.on_close();
}
beforeClose: function () { self.on_close(); }
};
for (var f in this) {
if (f.substr(0, 10) == 'on_button_') {
this.options.buttons[f.substr(10)] = this[f];
this.dialog_options.buttons[f.substr(10)] = this[f];
}
}
if (options) {
this.set_options(options);
if (dialog_options) {
this.set_options(dialog_options);
}
},
set_options: function(options) {
options = options || {};
options.width = this.get_width(options.width || this.options.width);
options.min_width = this.get_width(options.min_width || this.options.min_width);
options.max_width = this.get_width(options.max_width || this.options.max_width);
options.height = this.get_height(options.height || this.options.height);
options.min_height = this.get_height(options.min_height || this.options.min_height);
options.max_height = this.get_height(options.max_height || this.options.max_width);
options.width = this.get_width(options.width || this.dialog_options.width);
options.min_width = this.get_width(options.min_width || this.dialog_options.min_width);
options.max_width = this.get_width(options.max_width || this.dialog_options.max_width);
options.height = this.get_height(options.height || this.dialog_options.height);
options.min_height = this.get_height(options.min_height || this.dialog_options.min_height);
options.max_height = this.get_height(options.max_height || this.dialog_options.max_width);
if (options.width !== 'auto') {
if (options.width > options.max_width) options.width = options.max_width;
@ -353,7 +73,7 @@ openerp.base.Dialog = openerp.base.OldWidget.extend({
if (!options.title && this.dialog_title) {
options.title = this.dialog_title;
}
_.extend(this.options, options);
_.extend(this.dialog_options, options);
},
get_width: function(val) {
return this.get_size(val.toString(), $(window.top).width());
@ -370,20 +90,18 @@ openerp.base.Dialog = openerp.base.OldWidget.extend({
return parseInt(val, 10);
}
},
start: function (auto_open) {
this.$dialog = $('<div id="' + this.element_id + '"></div>').dialog(this.options);
if (auto_open !== false) {
this.open();
}
start: function () {
this.$dialog = $('<div id="' + this.element_id + '"></div>').dialog(this.dialog_options);
this._super();
return this;
},
open: function(options) {
open: function(dialog_options) {
// TODO fme: bind window on resize
if (this.template) {
this.$element.html(this.render());
}
this.set_options(options);
this.$dialog.dialog(this.options).dialog('open');
this.set_options(dialog_options);
this.$dialog.dialog(this.dialog_options).dialog('open');
},
close: function() {
// Closes the dialog but leave it in a state where it could be opened again.
@ -913,7 +631,7 @@ openerp.base.Header = openerp.base.Widget.extend({
},
on_logout: function() {
this.$element.find('#shortcuts ul li').remove();
this.$element.find('.oe-shortcuts ul').empty();
}
});
@ -1005,12 +723,10 @@ openerp.base.Homepage = openerp.base.Widget.extend({
openerp.base.Preferences = openerp.base.Widget.extend({
});
openerp.base.ImportExport = openerp.base.Widget.extend({
});
openerp.base.WebClient = openerp.base.Widget.extend({
init: function(element_id) {
this._super(null, element_id);
openerp.webclient = this;
QWeb.add_template("/base/static/src/xml/base.xml");
var params = {};
@ -1019,10 +735,10 @@ openerp.base.WebClient = openerp.base.Widget.extend({
}
this.$element.html(QWeb.render("Interface", params));
this.session = new openerp.base.Session(this,"oe_errors");
this.session = new openerp.base.Session();
this.loading = new openerp.base.Loading(this,"oe_loading");
this.crashmanager = new openerp.base.CrashManager(this);
this.crashmanager.start(false);
this.crashmanager.start();
// Do you autorize this ? will be replaced by notify() in controller
openerp.base.Widget.prototype.notification = new openerp.base.Notification(this, "oe_notification");
@ -1038,8 +754,6 @@ openerp.base.WebClient = openerp.base.Widget.extend({
this.menu = new openerp.base.Menu(this, "oe_menu", "oe_secondary_menu");
this.menu.on_action.add(this.on_menu_action);
this.header.on_action.add(this.on_menu_action);
},
start: function() {
this.session.start();
@ -1049,8 +763,10 @@ openerp.base.WebClient = openerp.base.Widget.extend({
this.notification.notify("OpenERP Client", "The openerp client has been initialized.");
},
on_logged: function() {
this.action_manager = new openerp.base.ActionManager(this, "oe_app");
this.action_manager.start();
if(this.action_manager)
this.action_manager.stop();
this.action_manager = new openerp.base.ActionManager(this);
this.action_manager.appendTo($("#oe_app"));
// if using saved actions, load the action and give it to action manager
var parameters = jQuery.deparam(jQuery.param.querystring());
@ -1090,7 +806,8 @@ openerp.base.WebClient = openerp.base.Widget.extend({
self.execute_home_action(home_action[0], ds);
})
},
default_home: function () { },
default_home: function () {
},
/**
* Bundles the execution of the home action
*

View File

@ -1,5 +1,5 @@
/*---------------------------------------------------------
* OpenERP controller framework
* OpenERP Web core
*--------------------------------------------------------*/
openerp.base.core = function(openerp) {
@ -305,16 +305,8 @@ openerp.base.Registry = openerp.base.Class.extend( /** @lends openerp.base.Regis
}
});
/**
* Utility class that any class is allowed to extend to easy common manipulations.
*
* It provides rpc calls, callback on all methods preceded by "on_" or "do_" and a
* logging facility.
*/
openerp.base.SessionAware = openerp.base.Class.extend({
init: function(session) {
this.session = session;
openerp.base.CallbackEnabled = openerp.base.Class.extend({
init: function() {
// Transform on_* method into openerp.base.callbacks
for (var name in this) {
if(typeof(this[name]) == "function") {
@ -325,6 +317,19 @@ openerp.base.SessionAware = openerp.base.Class.extend({
}
}
}
}
});
/**
* Utility class that any class is allowed to extend to easy common manipulations.
*
* It provides rpc calls, callback on all methods preceded by "on_" or "do_" and a
* logging facility.
*/
openerp.base.SessionAware = openerp.base.CallbackEnabled.extend({
init: function(session) {
this._super();
this.session = session;
},
/**
* Performs a JSON-RPC call
@ -373,59 +378,6 @@ openerp.base.SessionAware = openerp.base.Class.extend({
}
});
/**
* 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.base.Widget = openerp.base.SessionAware.extend({
/**
* The name of the QWeb template that will be used for rendering. Must be
@ -574,6 +526,18 @@ openerp.base.Widget = openerp.base.SessionAware.extend({
}
return false;
},
do_notify: function() {
if (this.widget_parent) {
return this.widget_parent.do_notify.apply(this,arguments);
}
return false;
},
do_warn: function() {
if (this.widget_parent) {
return this.widget_parent.do_warn.apply(this,arguments);
}
return false;
},
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
var self = this;
@ -599,5 +563,345 @@ openerp.base.OldWidget = openerp.base.Widget.extend({
}
});
openerp.base.TranslationDataBase = openerp.base.Class.extend({
init: function() {
this.db = {};
this.parameters = {"direction": 'ltr',
"date_format": '%m/%d/%Y',
"time_format": '%H:%M:%S',
"grouping": "[]",
"decimal_point": ".",
"thousands_sep": ","};
},
set_bundle: function(translation_bundle) {
var self = this;
this.db = {};
var modules = _.keys(translation_bundle.modules).sort();
if (_.include(modules, "base")) {
modules = ["base"].concat(_.without(modules, "base"));
}
_.each(modules, function(name) {
self.add_module_translation(translation_bundle.modules[name]);
});
if (translation_bundle.lang_parameters) {
this.parameters = translation_bundle.lang_parameters;
}
},
add_module_translation: function(mod) {
var self = this;
_.each(mod.messages, function(message) {
if (self.db[message.id] === undefined) {
self.db[message.id] = message.string;
}
});
},
build_translation_function: function() {
var self = this;
var fcnt = function(str) {
var tmp = self.get(str);
return tmp === undefined ? str : tmp;
}
fcnt.database = this;
return fcnt;
},
get: function(key) {
if (this.db[key])
return this.db[key];
return undefined;
}
});
openerp.base._t = new openerp.base.TranslationDataBase().build_translation_function();
openerp.base.Session = openerp.base.CallbackEnabled.extend( /** @lends openerp.base.Session# */{
/**
* @constructs
* @param element_id to use for exception reporting
* @param server
* @param port
*/
init: function(server, port) {
this._super();
this.server = (server == undefined) ? location.hostname : server;
this.port = (port == undefined) ? location.port : port;
this.rpc_mode = (server == location.hostname) ? "ajax" : "jsonp";
this.debug = true;
this.db = "";
this.login = "";
this.password = "";
this.user_context= {};
this.uid = false;
this.session_id = false;
this.module_list = [];
this.module_loaded = {"base": true};
this.context = {};
this.shortcuts = [];
this.active_id = null;
},
start: function() {
this.session_restore();
},
/**
* Executes an RPC call, registering the provided callbacks.
*
* Registers a default error callback if none is provided, and handles
* setting the correct session id and session context in the parameter
* objects
*
* @param {String} url RPC endpoint
* @param {Object} params call parameters
* @param {Function} success_callback function to execute on RPC call success
* @param {Function} error_callback function to execute on RPC call failure
* @returns {jQuery.Deferred} jquery-provided ajax deferred
*/
rpc: function(url, params, success_callback, error_callback) {
var self = this;
// Construct a JSON-RPC2 request, method is currently unused
params.session_id = this.session_id;
// Call using the rpc_mode
var deferred = $.Deferred();
this.rpc_ajax(url, {
jsonrpc: "2.0",
method: "call",
params: params,
id:null
}).then(function () {deferred.resolve.apply(deferred, arguments);},
function(error) {deferred.reject(error, $.Event());});
return deferred.fail(function() {
deferred.fail(function(error, event) {
if (!event.isDefaultPrevented()) {
self.on_rpc_error(error, event);
}
});
}).then(success_callback, error_callback).promise();
},
/**
* Raw JSON-RPC call
*
* @returns {jQuery.Deferred} ajax-based deferred object
*/
rpc_ajax: function(url, payload) {
var self = this;
this.on_rpc_request();
// url can be an $.ajax option object
if (_.isString(url)) {
url = {
url: url
}
}
var ajax = _.extend({
type: "POST",
url: url,
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(payload),
processData: false
}, url);
var deferred = $.Deferred();
$.ajax(ajax).done(function(response, textStatus, jqXHR) {
self.on_rpc_response();
if (!response.error) {
deferred.resolve(response["result"], textStatus, jqXHR);
return;
}
if (response.error.data.type !== "session_invalid") {
deferred.reject(response.error);
return;
}
self.uid = false;
self.on_session_invalid(function() {
self.rpc(url, payload.params,
function() {
deferred.resolve.apply(deferred, arguments);
},
function(error, event) {
event.preventDefault();
deferred.reject.apply(deferred, arguments);
});
});
}).fail(function(jqXHR, textStatus, errorThrown) {
self.on_rpc_response();
var error = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
};
deferred.reject(error);
});
return deferred.promise();
},
on_rpc_request: function() {
},
on_rpc_response: function() {
},
on_rpc_error: function(error) {
},
/**
* The session is validated either by login or by restoration of a previous session
*/
on_session_valid: function() {
if(!openerp._modules_loaded)
this.load_modules();
},
on_session_invalid: function(contination) {
},
session_is_valid: function() {
return this.uid;
},
session_login: function(db, login, password, success_callback) {
var self = this;
this.db = db;
this.login = login;
this.password = password;
var params = { db: this.db, login: this.login, password: this.password };
this.rpc("/base/session/login", params, function(result) {
self.session_id = result.session_id;
self.uid = result.uid;
self.user_context = result.context;
self.session_save();
self.on_session_valid();
if (success_callback)
success_callback();
});
},
session_logout: function() {
this.uid = false;
},
/**
* Reloads uid and session_id from local storage, if they exist
*/
session_restore: function () {
this.uid = this.get_cookie('uid');
this.session_id = this.get_cookie('session_id');
this.db = this.get_cookie('db');
this.login = this.get_cookie('login');
this.user_context = this.get_cookie("user_context");
// we should do an rpc to confirm that this session_id is valid and if it is retrieve the information about db and login
// then call on_session_valid
this.on_session_valid();
},
/**
* Saves the session id and uid locally
*/
session_save: function () {
this.set_cookie('uid', this.uid);
this.set_cookie('session_id', this.session_id);
this.set_cookie('db', this.db);
this.set_cookie('login', this.login);
this.set_cookie('user_context', this.user_context);
},
logout: function() {
delete this.uid;
delete this.session_id;
delete this.db;
delete this.login;
this.set_cookie('uid', '');
this.set_cookie('session_id', '');
this.set_cookie('db', '');
this.set_cookie('login', '');
this.on_session_invalid(function() {});
},
/**
* Fetches a cookie stored by an openerp session
*
* @private
* @param name the cookie's name
*/
get_cookie: function (name) {
var nameEQ = this.element_id + '|' + name + '=';
var cookies = document.cookie.split(';');
for(var i=0; i<cookies.length; ++i) {
var cookie = cookies[i].replace(/^\s*/, '');
if(cookie.indexOf(nameEQ) === 0) {
return JSON.parse(decodeURIComponent(cookie.substring(nameEQ.length)));
}
}
return null;
},
/**
* Create a new cookie with the provided name and value
*
* @private
* @param name the cookie's name
* @param value the cookie's value
* @param ttl the cookie's time to live, 1 year by default, set to -1 to delete
*/
set_cookie: function (name, value, ttl) {
ttl = ttl || 24*60*60*365;
document.cookie = [
this.element_id + '|' + name + '=' + encodeURIComponent(JSON.stringify(value)),
'max-age=' + ttl,
'expires=' + new Date(new Date().getTime() + ttl*1000).toGMTString()
].join(';');
},
/**
* Load additional web addons of that instance and init them
*/
load_modules: function() {
var self = this;
this.rpc('/base/session/modules', {}, function(result) {
self.module_list = result;
var lang = self.user_context.lang;
self.rpc('/base/webclient/translations',{
mods: ["base"].concat(result),
lang: lang})
.then(function(transs) {
openerp.base._t.database.set_bundle(transs);
var modules = self.module_list.join(',');
if(self.debug || true) {
self.rpc('/base/webclient/csslist', {"mods": modules}, self.do_load_css);
self.rpc('/base/webclient/jslist', {"mods": modules}, self.do_load_js);
} else {
self.do_load_css(["/base/webclient/css?mods="+modules]);
self.do_load_js(["/base/webclient/js?mods="+modules]);
}
openerp._modules_loaded = true;
});
});
},
do_load_css: function (files) {
_.each(files, function (file) {
$('head').append($('<link>', {
'href': file,
'rel': 'stylesheet',
'type': 'text/css'
}));
});
},
do_load_js: function(files) {
var self = this;
if(files.length != 0) {
var file = files.shift();
var tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = file;
tag.onload = tag.onreadystatechange = function() {
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
return;
tag.onload_done = true;
self.do_load_js(files);
};
document.head.appendChild(tag);
} else {
this.on_modules_loaded();
}
},
on_modules_loaded: function() {
for(var j=0; j<this.module_list.length; j++) {
var mod = this.module_list[j];
if(this.module_loaded[mod])
continue;
openerp[mod] = {};
// init module mod
if(openerp._openerp[mod] != undefined) {
openerp._openerp[mod](openerp);
this.module_loaded[mod] = true;
}
}
}
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -268,9 +268,13 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
},
/**
* Read records.
*
* @param {Array} ids identifiers of the records to read
* @param {Array} fields fields to read and return, by default all fields are returned
* @param {Function} callback function called with read result
* @returns {$.Deferred}
*/
read_ids: function (ids, fields, callback) {
var self = this;
return this.rpc('/base/dataset/get', {
model: this.model,
ids: ids,
@ -282,13 +286,20 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
* Read a slice of the records represented by this DataSet, based on its
* domain and context.
*
* @param {Number} [offset=0] The index from which selected records should be returned
* @param {Number} [limit=null] The maximum number of records to return
* @params {Object} options
* @param {Array} [options.fields] fields to read and return, by default all fields are returned
* @param {Number} [options.offset=0] The index from which selected records should be returned
* @param {Number} [options.limit=null] The maximum number of records to return
* @param {Function} callback function called with read_slice result
* @returns {$.Deferred}
*/
read_slice: function (fields, offset, limit, callback) {
},
read_slice: function (options, callback) { return null; },
/**
* Read the indexed record.
* Reads the current dataset record (from its index)
*
* @params {Array} [fields] fields to read and return, by default all fields are returned
* @params {Function} callback function called with read_index result
* @returns {$.Deferred}
*/
read_index: function (fields, callback) {
var def = $.Deferred().then(callback);
@ -304,6 +315,13 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
}
return def.promise();
},
/**
* Reads default values for the current model
*
* @param {Array} [fields] fields to get default values for, by default all defaults are read
* @param {Function} callback function called with default_get result
* @returns {$.Deferred}
*/
default_get: function(fields, callback) {
return this.rpc('/base/dataset/default_get', {
model: this.model,
@ -311,6 +329,14 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
context: this.get_context()
}, callback);
},
/**
* Creates a new record in db
*
* @param {Object} data field values to set on the new record
* @param {Function} callback function called with operation result
* @param {Function} error_callback function called in case of creation error
* @returns {$.Deferred}
*/
create: function(data, callback, error_callback) {
return this.rpc('/base/dataset/create', {
model: this.model,
@ -318,6 +344,15 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
context: this.get_context()
}, callback, error_callback);
},
/**
* Saves the provided data in an existing db record
*
* @param {Number|String} id identifier for the record to alter
* @param {Object} data field values to write into the record
* @param {Function} callback function called with operation result
* @param {Function} error_callback function called in case of write error
* @returns {$.Deferred}
*/
write: function (id, data, callback, error_callback) {
return this.rpc('/base/dataset/save', {
model: this.model,
@ -326,11 +361,27 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
context: this.get_context()
}, callback, error_callback);
},
/**
* Deletes an existing record from the database
*
* @param {Number|String} ids identifier of the record to delete
* @param {Function} callback function called with operation result
* @param {Function} error_callback function called in case of deletion error
*/
unlink: function(ids, callback, error_callback) {
var self = this;
return this.call_and_eval("unlink", [ids, this.get_context()], null, 1,
callback, error_callback);
},
/**
* Calls an arbitrary RPC method
*
* @param {String} method name of the method (on the current model) to call
* @param {Array} [args] arguments to pass to the method
* @param {Function} callback
* @param {Function} error_callback
* @returns {$.Deferred}
*/
call: function (method, args, callback, error_callback) {
return this.rpc('/base/dataset/call', {
model: this.model,
@ -338,6 +389,17 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
args: args || []
}, callback, error_callback);
},
/**
* Calls an arbitrary method, with more crazy
*
* @param {String} method
* @param {Array} [args]
* @param {Number} [domain_id] index of a domain to evaluate in the args array
* @param {Number} [context_id] index of a context to evaluate in the args array
* @param {Function} callback
* @param {Function }error_callback
* @returns {$.Deferred}
*/
call_and_eval: function (method, args, domain_id, context_id, callback, error_callback) {
return this.rpc('/base/dataset/call', {
model: this.model,
@ -347,6 +409,15 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
args: args || []
}, callback, error_callback);
},
/**
* Calls a button method, usually returning some sort of action
*
* @param {String} method
* @param {Array} [args]
* @param {Function} callback
* @param {Function} error_callback
* @returns {$.Deferred}
*/
call_button: function (method, args, callback, error_callback) {
return this.rpc('/base/dataset/call_button', {
model: this.model,
@ -356,17 +427,34 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
args: args || []
}, callback, error_callback);
},
/**
* Fetches the "readable name" for records, based on intrinsic rules
*
* @param {Array} ids
* @param {Function} callback
* @returns {$.Deferred}
*/
name_get: function(ids, callback) {
return this.call_and_eval('name_get', [ids, this.get_context()], null, 1, callback);
},
/*
* args = domain
/**
*
* @param {String} name name to perform a search for/on
* @param {Array} [domain=[]] filters for the objects returned, OpenERP domain
* @param {String} [operator='ilike'] matching operator to use with the provided name value
* @param {Number} [limit=100] maximum number of matches to return
* @param {Function} callback function to call with name_search result
* @returns {$.Deferred}
*/
name_search: function (name, args, operator, limit, callback) {
name_search: function (name, domain, operator, limit, callback) {
return this.call_and_eval('name_search',
[name || '', args || false, operator || 'ilike', this.get_context(), limit || 100],
[name || '', domain || false, operator || 'ilike', this.get_context(), limit || 100],
1, 3, callback);
},
/**
* @param name
* @param callback
*/
name_create: function(name, callback) {
return this.call_and_eval('name_create', [name, this.get_context()], null, 1, callback);
},
@ -377,7 +465,10 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
signal: signal
}, callback);
},
get_context: function() {
get_context: function(request_context) {
if (request_context) {
return new openerp.base.CompoundContext(this.context, request_context);
}
return this.context;
}
});
@ -387,9 +478,11 @@ openerp.base.DataSetStatic = openerp.base.DataSet.extend({
// all local records
this.ids = ids || [];
},
read_slice: function (fields, offset, limit, callback) {
var self = this;
offset = offset || 0;
read_slice: function (options, callback) {
var self = this,
offset = options.offset || 0,
limit = options.limit || false,
fields = options.fields || false;
var end_pos = limit && limit !== -1 ? offset + limit : undefined;
return this.read_ids(this.ids.slice(offset, end_pos), fields, callback);
},
@ -426,25 +519,30 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({
// is it necessary ?
this.ids = [];
},
read_slice: function (fields, offset, limit, callback) {
/**
* Read a slice of the records represented by this DataSet, based on its
* domain and context.
*
* @params {Object} options
* @param {Array} [options.fields] fields to read and return, by default all fields are returned
* @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context
* @param {Array} [options.domain] domain data to add to the request payload, ANDed with the dataset's domain
* @param {Number} [options.offset=0] The index from which selected records should be returned
* @param {Number} [options.limit=null] The maximum number of records to return
* @param {Function} callback function called with read_slice result
* @returns {$.Deferred}
*/
read_slice: function (options, callback) {
var self = this;
offset = offset || 0;
// cached search, not sure it's a good idea
if(this.offset <= offset) {
var start = offset - this.offset;
if(this.ids.length - start >= limit) {
// TODO: check if this could work do only read if possible
// return read_ids(ids.slice(start,start+limit),fields,callback)
}
}
var offset = options.offset || 0;
return this.rpc('/base/dataset/search_read', {
model: this.model,
fields: fields,
domain: this.domain,
context: this.get_context(),
fields: options.fields || false,
domain: this.get_domain(options.domain),
context: this.get_context(options.context),
sort: this.sort(),
offset: offset,
limit: limit
limit: options.limit || false
}, function (result) {
self.ids = result.ids;
self.offset = offset;
@ -453,6 +551,12 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({
}
});
},
get_domain: function (other_domain) {
if (other_domain) {
return new openerp.base.CompoundDomain(this.domain, other_domain);
}
return this.domain;
},
/**
* Reads or changes sort criteria on the dataset.
*
@ -628,48 +732,51 @@ openerp.base.ReadOnlyDataSetSearch = openerp.base.DataSetSearch.extend({
on_unlink: function(ids) {}
});
openerp.base.CompoundContext = function() {
this.__ref = "compound_context";
this.__contexts = [];
this.__eval_context = null;
var self = this;
_.each(arguments, function(x) {
self.add(x);
});
};
openerp.base.CompoundContext.prototype.add = function(context) {
this.__contexts.push(context);
return this;
};
openerp.base.CompoundContext.prototype.set_eval_context = function(eval_context) {
this.__eval_context = eval_context;
return this;
};
openerp.base.CompoundContext.prototype.get_eval_context = function() {
return this.__eval_context;
};
openerp.base.CompoundDomain = function() {
this.__ref = "compound_domain";
this.__domains = [];
this.__eval_context = null;
var self = this;
_.each(arguments, function(x) {
self.add(x);
});
};
openerp.base.CompoundDomain.prototype.add = function(domain) {
this.__domains.push(domain);
return this;
};
openerp.base.CompoundDomain.prototype.set_eval_context = function(eval_context) {
this.__eval_context = eval_context;
return this;
};
openerp.base.CompoundDomain.prototype.get_eval_context = function() {
return this.__eval_context;
};
openerp.base.CompoundContext = openerp.base.Class.extend({
init: function () {
this.__ref = "compound_context";
this.__contexts = [];
this.__eval_context = null;
var self = this;
_.each(arguments, function(x) {
self.add(x);
});
},
add: function (context) {
this.__contexts.push(context);
return this;
},
set_eval_context: function (eval_context) {
this.__eval_context = eval_context;
return this;
},
get_eval_context: function () {
return this.__eval_context;
}
});
openerp.base.CompoundDomain = openerp.base.Class.extend({
init: function () {
this.__ref = "compound_domain";
this.__domains = [];
this.__eval_context = null;
var self = this;
_.each(arguments, function(x) {
self.add(x);
});
},
add: function(domain) {
this.__domains.push(domain);
return this;
},
set_eval_context: function(eval_context) {
this.__eval_context = eval_context;
return this;
},
get_eval_context: function() {
return this.__eval_context;
}
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -1,137 +0,0 @@
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\d/;
var res = regex.exec(str);
if ( res[0] != str ) {
throw "'" + str + "' is not a valid datetime";
}
var obj = Date.parse(str + " GMT");
if (! obj) {
throw "'" + str + "' is not a valid datetime";
}
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;
}
var regex = /\d\d\d\d-\d\d-\d\d/;
var res = regex.exec(str);
if ( res[0] != str ) {
throw "'" + str + "' is not a valid date";
}
var obj = Date.parse(str);
if (! obj) {
throw "'" + str + "' is not a valid date";
}
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;
}
var regex = /\d\d:\d\d:\d\d/;
var res = regex.exec(str);
if ( res[0] != str ) {
throw "'" + str + "' is not a valid time";
}
var obj = Date.parse(str);
if (! obj) {
throw "'" + str + "' is not a valid time";
}
return obj;
};
/*
* Just a simple function to add some '0' if an integer it too small.
*/
var fts = function(str, size) {
str = "" + str;
var to_add = "";
_.each(_.range(size - str.length), function() {
to_add = to_add + "0";
});
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 (!obj) {
return false;
}
return fts(obj.getUTCFullYear(),4) + "-" + fts(obj.getUTCMonth() + 1,2) + "-"
+ fts(obj.getUTCDate(),2) + " " + fts(obj.getUTCHours(),2) + ":"
+ 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 (!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 (!obj) {
return false;
}
return fts(obj.getHours(),2) + ":" + fts(obj.getMinutes(),2) + ":"
+ fts(obj.getSeconds(),2);
};
};

View File

@ -1,4 +1,6 @@
openerp.base.form = function (openerp) {
var _t = openerp.base._t;
openerp.base.views.add('form', 'openerp.base.FormView');
openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormView# */{
@ -19,7 +21,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
*/
init: function(parent, element_id, dataset, view_id, options) {
this._super(parent, element_id);
this.set_default_options();
this.set_default_options(options);
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
@ -36,7 +38,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
this.registry = openerp.base.form.widgets;
this.has_been_loaded = $.Deferred();
this.$form_header = null;
this.options = options || {};
_.defaults(this.options, {"always_show_new_button": true});
},
start: function() {
@ -156,7 +157,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
if (this.sidebar) {
this.sidebar.attachments.do_update();
}
if (this.default_focus_field) {
if (this.default_focus_field && !this.embedded_view) {
this.default_focus_field.focus();
}
},
@ -452,12 +453,14 @@ openerp.base.form.SidebarAttachments = openerp.base.Widget.extend({
this.on_attachments_loaded([]);
} else {
(new openerp.base.DataSetSearch(
this, 'ir.attachment', this.view.dataset.get_context(),
[['res_model', '=', this.view.dataset.model],
['res_id', '=', this.view.datarecord.id],
['type', 'in', ['binary', 'url']]])).read_slice(
['name', 'url', 'type'], false, false,
this.on_attachments_loaded);
this, 'ir.attachment', this.view.dataset.get_context(),
[
['res_model', '=', this.view.dataset.model],
['res_id', '=', this.view.datarecord.id],
['type', 'in', ['binary', 'url']]
])).read_slice(
{fields: ['name', 'url', 'type']},
this.on_attachments_loaded);
}
},
on_attachments_loaded: function(attachments) {
@ -511,36 +514,40 @@ openerp.base.form.compute_domain = function(expr, fields) {
}
}
var field = fields[ex[0]].get_value ? fields[ex[0]].get_value() : fields[ex[0]].value;
var field = fields[ex[0]];
if (!field) {
throw new Error("Domain references unknown field : " + ex[0]);
}
var field_value = field.get_value ? fields[ex[0]].get_value() : fields[ex[0]].value;
var op = ex[1];
var val = ex[2];
switch (op.toLowerCase()) {
case '=':
case '==':
stack.push(field == val);
stack.push(field_value == val);
break;
case '!=':
case '<>':
stack.push(field != val);
stack.push(field_value != val);
break;
case '<':
stack.push(field < val);
stack.push(field_value < val);
break;
case '>':
stack.push(field > val);
stack.push(field_value > val);
break;
case '<=':
stack.push(field <= val);
stack.push(field_value <= val);
break;
case '>=':
stack.push(field >= val);
stack.push(field_value >= val);
break;
case 'in':
stack.push(_(val).contains(field));
stack.push(_(val).contains(field_value));
break;
case 'not in':
stack.push(!_(val).contains(field));
stack.push(!_(val).contains(field_value));
break;
default:
this.log("Unsupported operator in modifiers :", op);
@ -679,6 +686,16 @@ openerp.base.form.WidgetNotebook = openerp.base.form.Widget.extend({
start: function() {
this._super.apply(this, arguments);
this.$element.tabs();
this.view.on_button_new.add_last(this.do_select_first_visible_tab);
},
do_select_first_visible_tab: function() {
for (var i = 0; i < this.pages.length; i++) {
var page = this.pages[i];
if (page.invisible === false) {
this.$element.tabs('select', page.index);
break;
}
}
}
});
@ -696,8 +713,8 @@ openerp.base.form.WidgetNotebookPage = openerp.base.form.WidgetFrame.extend({
this.$element_tab = $('#' + this.element_tab_id);
},
update_dom: function() {
if (this.invisible) {
this.notebook.$element.tabs('select', 0);
if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) {
this.notebook.do_select_first_visible_tab();
}
this.$element_tab.toggle(!this.invisible);
this.$element.toggle(!this.invisible);
@ -758,8 +775,7 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
var self = this;
this.view.execute_action(
this.node.attrs, this.view.dataset, this.session.action_manager,
this.view.datarecord.id, function () {
this.node.attrs, this.view.dataset, this.view.datarecord.id, function () {
self.view.reload();
});
}
@ -794,7 +810,7 @@ openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
var self = this;
this.$element.find("label").dblclick(function() {
var widget = self['for'] || self;
console.log(widget.element_id , widget);
self.log(widget.element_id , widget);
});
}
});
@ -1306,7 +1322,7 @@ openerp.base.form.dialog = function(content, options) {
}, options || {});
options.autoOpen = true;
var dialog = new openerp.base.Dialog(null, options);
dialog.$dialog = $(content).dialog(dialog.options);
dialog.$dialog = $(content).dialog(dialog.dialog_options);
return dialog.$dialog;
}
@ -1440,7 +1456,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
// search more... if more results that max
if (values.length > self.limit) {
values = values.slice(0, self.limit);
values.push({label: "<em>   Search More...</em>", action: function() {
values.push({label: _t("<em>   Search More...</em>"), action: function() {
dataset.name_search(search_val, self.build_domain(), 'ilike'
, false, function(data) {
self._change_int_value(null);
@ -1453,13 +1469,13 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
if (search_val.length > 0 &&
!_.include(raw_result, search_val) &&
(!self.value || search_val !== self.value[1])) {
values.push({label: '<em>   Create "<strong>' +
$('<span />').text(search_val).html() + '</strong>"</em>', action: function() {
values.push({label: _.sprintf(_t('<em>   Create "<strong>%s</strong>"</em>'),
$('<span />').text(search_val).html()), action: function() {
self._quick_create(search_val);
}});
}
// create...
values.push({label: "<em>   Create and Edit...</em>", action: function() {
values.push({label: _t("<em>   Create and Edit...</em>"), action: function() {
self._change_int_value(null);
self._search_create_popup("form", undefined, {"default_name": search_val});
}});
@ -1492,7 +1508,6 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
var dataset = new openerp.base.DataSetStatic(self, self.field.relation, self.build_context());
dataset.name_get([element_ids[0]], function(data) {
self._change_int_ext_value(data[0]);
pop.stop();
});
});
},
@ -1624,20 +1639,22 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
modes = !!modes ? modes.split(",") : ["tree", "form"];
var views = [];
_.each(modes, function(mode) {
var view = {view_id: false, view_type: mode == "tree" ? "list" : mode};
var view = {
view_id: false,
view_type: mode == "tree" ? "list" : mode,
options: { sidebar : false }
};
if (self.field.views && self.field.views[mode]) {
view.embedded_view = self.field.views[mode];
}
if(view.view_type === "list") {
view.options = {
'selectable': self.multi_selection
};
view.options.selectable = self.multi_selection;
}
views.push(view);
});
this.views = views;
this.viewmanager = new openerp.base.ViewManager(this, this.element_id, this.dataset, views);
this.viewmanager = new openerp.base.ViewManager(this, this.dataset, views);
this.viewmanager.registry = openerp.base.views.clone({
list: 'openerp.base.form.One2ManyListView',
form: 'openerp.base.form.One2ManyFormView'
@ -1663,7 +1680,7 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
self.save_form_view();
});
setTimeout(function () {
self.viewmanager.start();
self.viewmanager.appendTo(self.$element);
}, 0);
},
reload_current_view: function() {
@ -1826,16 +1843,16 @@ openerp.base.form.One2ManyListView = openerp.base.ListView.extend({
pop.select_element(self.o2m.field.relation,{
initial_view: "form",
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
auto_create: false,
create_function: function(data) {
return self.o2m.dataset.create(data, function(r) {
self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
self.o2m.dataset.on_change();
});
},
parent_view: self.o2m.view
}, self.o2m.build_domain(), self.o2m.build_context());
pop.on_create.add(function(data) {
self.o2m.dataset.create(data, function(r) {
self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
self.o2m.dataset.on_change();
pop.stop();
self.o2m.reload_current_view();
});
pop.on_select_elements.add_last(function() {
self.o2m.reload_current_view();
});
}
},
@ -1874,7 +1891,7 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
self.on_ui_change();
});
this.list_view = new openerp.base.form.Many2ManyListView(new openerp.base.NullViewManager(this), this.list_id, this.dataset, false, {
this.list_view = new openerp.base.form.Many2ManyListView(this, this.list_id, this.dataset, false, {
'addable': 'Add',
'selectable': self.multi_selection
});
@ -1930,7 +1947,6 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
self.reload_content();
}
});
pop.stop();
});
},
do_activate_record: function(index, id) {
@ -1952,16 +1968,22 @@ openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
* - initial_view: form or search (default search)
* - disable_multiple_selection
* - alternative_form_view
* - auto_create (default true)
* - create_function (defaults to a naive saving behavior)
* - parent_view
*/
select_element: function(model, options, domain, context) {
var self = this;
this.model = model;
this.domain = domain || [];
this.context = context || {};
this.options = _.defaults(options || {}, {"initial_view": "search", "auto_create": true});
this.options = _.defaults(options || {}, {"initial_view": "search", "create_function": function() {
return self.create_row.apply(self, arguments);
}});
this.initial_ids = this.options.initial_ids;
openerp.base.form.dialog(this.render());
this.created_elements = [];
openerp.base.form.dialog(this.render(), {close:function() {
self.check_exit();
}});
this.start();
},
start: function() {
@ -2007,6 +2029,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
}
$sbutton.click(function() {
self.on_select_elements(self.selected_ids);
self.stop();
});
self.view_list = new openerp.base.form.SelectCreateListView(self,
self.element_id + "_view_list", self.dataset, false,
@ -2019,15 +2042,11 @@ openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
});
this.searchview.start();
},
on_create: function(data) {
if (!this.options.auto_create)
return;
create_row: function(data) {
var self = this;
var wdataset = new openerp.base.DataSetSearch(this, this.model, this.context, this.domain);
wdataset.parent_view = this.options.parent_view;
wdataset.create(data, function(r) {
self.on_select_elements([r.result]);
});
return wdataset.create(data);
},
on_select_elements: function(element_ids) {
},
@ -2055,18 +2074,43 @@ openerp.base.form.SelectCreatePopup = openerp.base.OldWidget.extend({
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("SelectCreatePopup.form.buttons"));
$buttons.html(QWeb.render("SelectCreatePopup.form.buttons", {widget:self}));
var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save-new");
$nbutton.click(function() {
self._created = $.Deferred().then(function() {
self._created = undefined;
self.view_form.on_button_new();
});
self.view_form.do_save();
});
var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save");
$nbutton.click(function() {
self._created = $.Deferred().then(function() {
self._created = undefined;
self.check_exit();
});
self.view_form.do_save();
});
var $cbutton = $buttons.find(".oe_selectcreatepopup-form-close");
$cbutton.click(function() {
self.stop();
self.check_exit();
});
});
this.dataset.on_create.add(function(data) {
self.options.create_function(data).then(function(r) {
self.created_elements.push(r.result);
if (self._created) {
self._created.resolve();
}
});
});
this.dataset.on_create.add(this.on_create);
this.view_form.do_show();
},
check_exit: function() {
if (this.created_elements.length > 0) {
this.on_select_elements(this.created_elements);
}
this.stop();
}
});
@ -2076,6 +2120,7 @@ openerp.base.form.SelectCreateListView = openerp.base.ListView.extend({
},
select_record: function(index) {
this.popup.on_select_elements([this.dataset.ids[index]]);
this.popup.stop();
},
do_select: function(ids, records) {
this._super(ids, records);
@ -2199,8 +2244,8 @@ openerp.base.form.FieldBinary = openerp.base.form.Field.extend({
delete(window[this.iframe]);
if (size === false) {
this.notification.warn("File Upload", "There was a problem while uploading your file");
// TODO: use openerp web exception handler
console.log("Error while uploading file : ", name);
// TODO: use openerp web crashmanager
this.log("Error while uploading file : ", name);
} else {
this.on_file_uploaded_and_valid.apply(this, arguments);
this.on_ui_change();
@ -2309,10 +2354,8 @@ openerp.base.form.widgets = new openerp.base.Registry({
'url' : 'openerp.base.form.FieldUrl',
'text' : 'openerp.base.form.FieldText',
'text_wiki' : 'openerp.base.form.FieldText',
// 'date' : 'openerp.base.form.FieldDate',
// 'datetime' : 'openerp.base.form.FieldDatetime',
'date' : 'openerp.base.form.FieldChar',
'datetime' : 'openerp.base.form.FieldChar',
'date' : 'openerp.base.form.FieldDate',
'datetime' : 'openerp.base.form.FieldDatetime',
'selection' : 'openerp.base.form.FieldSelection',
'many2one' : 'openerp.base.form.FieldMany2One',
'many2many' : 'openerp.base.form.FieldMany2Many',

View File

@ -0,0 +1,208 @@
openerp.base.formats = 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\d/;
var res = regex.exec(str);
if ( res[0] != str ) {
throw "'" + str + "' is not a valid datetime";
}
var obj = Date.parse(str + " GMT");
if (! obj) {
throw "'" + str + "' is not a valid datetime";
}
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;
}
var regex = /\d\d\d\d-\d\d-\d\d/;
var res = regex.exec(str);
if ( res[0] != str ) {
throw "'" + str + "' is not a valid date";
}
var obj = Date.parse(str);
if (! obj) {
throw "'" + str + "' is not a valid date";
}
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;
}
var regex = /\d\d:\d\d:\d\d/;
var res = regex.exec(str);
if ( res[0] != str ) {
throw "'" + str + "' is not a valid time";
}
var obj = Date.parse(str);
if (! obj) {
throw "'" + str + "' is not a valid time";
}
return obj;
};
/*
* Left-pad provided arg 1 with zeroes until reaching size provided by second
* argument.
*
* @param {Number|String} str value to pad
* @param {Number} size size to reach on the final padded value
* @returns {String} padded string
*/
var zpad = function(str, size) {
str = "" + str;
return new Array(size - str.length + 1).join('0') + 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 (!obj) {
return false;
}
return zpad(obj.getUTCFullYear(),4) + "-" + zpad(obj.getUTCMonth() + 1,2) + "-"
+ zpad(obj.getUTCDate(),2) + " " + zpad(obj.getUTCHours(),2) + ":"
+ zpad(obj.getUTCMinutes(),2) + ":" + zpad(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 (!obj) {
return false;
}
return zpad(obj.getFullYear(),4) + "-" + zpad(obj.getMonth() + 1,2) + "-"
+ zpad(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 (!obj) {
return false;
}
return zpad(obj.getHours(),2) + ":" + zpad(obj.getMinutes(),2) + ":"
+ zpad(obj.getSeconds(),2);
};
/**
* Formats a single atomic value based on a field descriptor
*
* @param {Object} value read from OpenERP
* @param {Object} descriptor union of orm field and view field
* @param {Object} [descriptor.widget] widget to use to display the value
* @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
* @param {Object} [descriptor.digits] used for the formatting of floats
* @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
*/
openerp.base.format_value = function (value, descriptor, value_if_empty) {
// If NaN value, display as with a `false` (empty cell)
if (typeof value === 'number' && isNaN(value)) {
value = false;
}
switch (value) {
case false:
case Infinity:
case -Infinity:
return value_if_empty === undefined ? '' : value_if_empty;
}
switch (descriptor.widget || descriptor.type) {
case 'integer':
return _.sprintf('%d', value);
case 'float':
var precision = descriptor.digits ? descriptor.digits[1] : 2;
return _.sprintf('%.' + precision + 'f', value);
case 'float_time':
return _.sprintf("%02d:%02d",
Math.floor(value),
Math.round((value % 1) * 60));
case 'progressbar':
return _.sprintf(
'<progress value="%.2f" max="100.0">%.2f%%</progress>',
value, value);
case 'many2one':
// name_get value format
return value[1];
default:
return value;
}
};
/**
* Formats a provided cell based on its field type
*
* @param {Object} row_data record whose values should be displayed in the cell
* @param {Object} column column descriptor
* @param {"button"|"field"} column.tag base control type
* @param {String} column.type widget type for a field control
* @param {String} [column.string] button label
* @param {String} [column.icon] button icon
* @param {String} [value_if_empty=''] what to display if the field's value is ``false``
*/
openerp.base.format_cell = function (row_data, column, value_if_empty) {
var attrs = column.modifiers_for(row_data);
if (attrs.invisible) { return ''; }
if (column.tag === 'button') {
return [
'<button type="button" title="', column.string || '', '">',
'<img src="/base/static/src/img/icons/', column.icon, '.png"',
' alt="', column.string || '', '"/>',
'</button>'
].join('')
}
return openerp.base.format_value(
row_data[column.id].value, column, value_if_empty);
}
};

View File

@ -1,63 +1,5 @@
openerp.base.list = function (openerp) {
openerp.base.views.add('list', 'openerp.base.ListView');
openerp.base.list = {
/**
* Formats the rendring of a given value based on its field type
*
* @param {Object} row_data record whose values should be displayed in the cell
* @param {Object} column column descriptor
* @param {"button"|"field"} column.tag base control type
* @param {String} column.type widget type for a field control
* @param {String} [column.string] button label
* @param {String} [column.icon] button icon
* @param {String} [value_if_empty=''] what to display if the field's value is ``false``
*/
render_cell: function (row_data, column, value_if_empty) {
var attrs = column.modifiers_for(row_data);
if (attrs.invisible) { return ''; }
if (column.tag === 'button') {
return [
'<button type="button" title="', column.string || '', '">',
'<img src="/base/static/src/img/icons/', column.icon, '.png"',
' alt="', column.string || '', '"/>',
'</button>'
].join('')
}
var value = row_data[column.id].value;
// If NaN value, display as with a `false` (empty cell)
if (typeof value === 'number' && isNaN(value)) {
value = false;
}
switch (value) {
case false:
case Infinity:
case -Infinity:
return value_if_empty === undefined ? '' : value_if_empty;
}
switch (column.widget || column.type) {
case 'integer':
return _.sprintf('%d', value);
case 'float':
var precision = column.digits ? column.digits[1] : 2;
return _.sprintf('%.' + precision + 'f', value);
case 'float_time':
return _.sprintf("%02d:%02d",
Math.floor(value),
Math.round((value % 1) * 60));
case 'progressbar':
return _.sprintf(
'<progress value="%.2f" max="100.0">%.2f%%</progress>',
value, value);
case 'many2one':
// name_get value format
return value[1];
default:
return value;
}
}
};
openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListView# */ {
defaults: {
// records can be selected one by one
@ -85,8 +27,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
* the default behaviors and possible options for the list view.
*
* @constructs
* @param view_manager
* @param session An OpenERP session object
* @param parent parent object
* @param element_id the id of the DOM elements this view should link itself to
* @param {openerp.base.DataSet} dataset the dataset the view should work with
* @param {String} view_id the listview's identifier, if any
@ -102,16 +43,13 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
*/
init: function(parent, element_id, dataset, view_id, options) {
this._super(parent, element_id);
this.set_default_options();
this.view_manager = parent || new openerp.base.NullViewManager();
this.set_default_options(_.extend({}, this.defaults, options || {}));
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
this.columns = [];
this.options = _.extend({}, this.defaults, options || {});
this.set_groups(new openerp.base.ListView.Groups(this));
if (this.dataset instanceof openerp.base.DataSetStatic) {
@ -123,7 +61,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
/**
* Retrieves the view's number of records per page (|| section)
*
* options > defaults > view_manager.action.limit > indefinite
* options > defaults > parent.action.limit > indefinite
*
* @returns {Number|null}
*/
@ -131,7 +69,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
if (this._limit === undefined) {
this._limit = (this.options.limit
|| this.defaults.limit
|| (this.view_manager.action || {}).limit
|| (this.widget_parent.action || {}).limit
|| null);
}
return this._limit;
@ -157,7 +95,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
self.do_delete(ids);
},
'action': function (e, action_name, id, callback) {
self.do_action(action_name, id, callback);
self.do_button_action(action_name, id, callback);
},
'row_link': function (e, id, dataset) {
self.do_activate_record(dataset.index, id, dataset);
@ -267,7 +205,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
})
.val(self._limit || 'NaN');
});
if (this.options.sidebar && this.options.sidebar_id) {
if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
this.sidebar.add_toolbar(data.fields_view.toolbar);
@ -389,9 +327,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
view = view || 'form';
this.dataset.index = index;
_.delay(_.bind(function () {
if(this.view_manager) {
this.view_manager.on_mode_switch(view);
}
this.do_switch_view(view);
}, this));
},
do_show: function () {
@ -416,8 +352,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
* Reloads the list view based on the current settings (dataset & al)
*
* @param {Boolean} [grouped] Should the list be displayed grouped
* @param {Object} [context] context to send the server while loading the view
*/
reload_view: function (grouped) {
reload_view: function (grouped, context) {
var self = this;
var callback = function (field_view_get) {
self.on_loaded(field_view_get, grouped);
@ -428,7 +365,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
return this.rpc('/base/listview/load', {
model: this.model,
view_id: this.view_id,
context: this.dataset.get_context(),
context: this.dataset.get_context(context),
toolbar: this.options.sidebar
}, callback);
}
@ -464,11 +401,10 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
* @param {Object} results results of evaluating domain and process for a search
*/
do_actual_search: function (results) {
this.dataset.context = results.context;
this.dataset.domain = results.domain;
this.groups.datagroup = new openerp.base.DataGroup(
this, this.model,
results.domain, results.context,
this.dataset.get_domain(results.domain),
this.dataset.get_context(results.context),
results.group_by);
this.groups.datagroup.sort = this.dataset._sort;
@ -476,7 +412,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
results.group_by = null;
}
this.reload_view(!!results.group_by).then(
this.reload_view(!!results.group_by, results.context).then(
$.proxy(this, 'reload_content'));
},
/**
@ -519,17 +455,16 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
* @param {Object} id id of the record the action should be called on
* @param {Function} callback should be called after the action is executed, if non-null
*/
do_action: function (name, id, callback) {
do_button_action: function (name, id, callback) {
var self = this,
action = _.detect(this.columns, function (field) {
return field.name === name;
});
if (!action) { return; }
this.execute_action(
action, this.dataset, this.session.action_manager, id, function () {
$.when(callback.apply(this, arguments).then(function () {
self.compute_aggregates();
}));
this.execute_action(action, this.dataset, id, function () {
$.when(callback.apply(this, arguments).then(function () {
self.compute_aggregates();
}));
});
},
/**
@ -541,11 +476,11 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
*/
do_activate_record: function (index, id, dataset) {
var self = this;
_.extend(this.dataset, {
domain: dataset.domain,
context: dataset.get_context()
}).read_slice([], 0, false, function () {
self.select_record(index);
this.dataset.read_slice({
context: dataset.get_context(),
domain: dataset.get_domain()
}, function () {
self.select_record(index);
});
},
/**
@ -644,7 +579,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
}
$footer_cells.filter(_.sprintf('[data-field=%s]', column.id))
.html(openerp.base.list.render_cell(aggregation, column));
.html(openerp.base.format_cell(aggregation, column));
});
}
// TODO: implement reorder (drag and drop rows)
@ -732,7 +667,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
this.$current = this.$_element.clone(true);
this.$current.empty().append(
QWeb.render('ListView.rows', _.extend({
render_cell: openerp.base.list.render_cell}, this)));
render_cell: openerp.base.format_cell}, this)));
},
/**
* Gets the ids of all currently selected records, if any
@ -837,7 +772,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
var old_index = this.dataset.index;
this.dataset.index = record_index;
read_p = this.dataset.read_index(
_.filter(_.pluck(this.columns, 'name'), _.identity),
_.pluck(_(this.columns).filter(function (r) {return r.tag === 'field';}), 'name'),
function (record) {
var form_record = self.transform_record(record);
self.rows.splice(record_index, 1, form_record);
@ -863,7 +798,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
row: this.rows[record_index],
row_parity: (record_index % 2 === 0) ? 'even' : 'odd',
row_index: record_index,
render_cell: openerp.base.list.render_cell
render_cell: openerp.base.format_cell
});
},
/**
@ -1023,7 +958,7 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
row_data[group.grouped_on] = group;
var group_column = _(self.columns).detect(function (column) {
return column.id === group.grouped_on; });
$group_column.html(openerp.base.list.render_cell(
$group_column.html(openerp.base.format_cell(
row_data, group_column, "Undefined"
));
if (group.openable) {
@ -1099,10 +1034,11 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
d = new $.Deferred(),
page = this.datagroup.openable ? this.page : view.page;
dataset.read_slice(
_.filter(_.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name'), _.identity),
page * limit, limit,
function (records) {
dataset.read_slice({
fields: _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name'),
offset: page * limit,
limit: limit
}, function (records) {
if (!self.datagroup.openable) {
view.configure_pager(dataset);
} else {

View File

@ -3,7 +3,6 @@ openerp.base.search = function(openerp) {
openerp.base.SearchView = openerp.base.Widget.extend({
init: function(parent, element_id, dataset, view_id, defaults) {
this._super(parent, element_id);
this.view_manager = parent || new openerp.base.NullViewManager();
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
@ -361,9 +360,9 @@ openerp.base.search.fields = new openerp.base.Registry({
'selection': 'openerp.base.search.SelectionField',
'datetime': 'openerp.base.search.DateTimeField',
'date': 'openerp.base.search.DateField',
'one2many': 'openerp.base.search.OneToManyField',
'many2one': 'openerp.base.search.ManyToOneField',
'many2many': 'openerp.base.search.ManyToManyField'
'many2many': 'openerp.base.search.CharField',
'one2many': 'openerp.base.search.CharField'
});
openerp.base.search.Invalid = openerp.base.Class.extend( /** @lends openerp.base.search.Invalid# */{
/**
@ -622,39 +621,6 @@ openerp.base.search.CharField = openerp.base.search.Field.extend( /** @lends ope
return this.$element.val();
}
});
openerp.base.search.BooleanField = openerp.base.search.Field.extend({
template: 'SearchView.field.selection',
init: function () {
this._super.apply(this, arguments);
this.attrs.selection = [
['true', 'Yes'],
['false', 'No']
];
},
/**
* Search defaults likely to be boolean values (for a boolean field).
*
* In the HTML, we only get strings, and our strings here are
* <code>'true'</code> and <code>'false'</code>, so ensure we get only
* those by truth-testing the default value.
*
* @param {Object} defaults default values for this search view
*/
render: function (defaults) {
var name = this.attrs.name;
if (name in defaults) {
defaults[name] = defaults[name] ? "true" : "false";
}
return this._super(defaults);
},
get_value: function () {
switch (this.$element.val()) {
case 'false': return false;
case 'true': return true;
default: return null;
}
}
});
openerp.base.search.NumberField = openerp.base.search.Field.extend(/** @lends openerp.base.search.NumberField# */{
get_value: function () {
if (!this.$element.val()) {
@ -691,93 +657,73 @@ openerp.base.search.FloatField = openerp.base.search.NumberField.extend(/** @len
return parseFloat(value);
}
});
openerp.base.search.SelectionField = openerp.base.search.Field.extend({
/**
* @class
* @extends openerp.base.search.Field
*/
openerp.base.search.SelectionField = openerp.base.search.Field.extend(/** @lends openerp.base.search.SelectionField# */{
template: 'SearchView.field.selection',
get_value: function () {
return this.$element.val();
}
});
openerp.base.search.BooleanField = openerp.base.search.SelectionField.extend(/** @lends openerp.base.search.BooleanField# */{
/**
* @constructs
* @extends openerp.base.search.BooleanField
*/
init: function () {
this._super.apply(this, arguments);
this.attrs.selection = [
['true', 'Yes'],
['false', 'No']
];
},
/**
* Search defaults likely to be boolean values (for a boolean field).
*
* In the HTML, we only want/get strings, and our strings here are ``true``
* and ``false``, so ensure we use precisely those by truth-testing the
* default value (iif there is one in the view's defaults).
*
* @param {Object} defaults default values for this search view
* @returns {String} rendered boolean field
*/
render: function (defaults) {
var name = this.attrs.name;
if (name in defaults) {
defaults[name] = defaults[name] ? "true" : "false";
}
return this._super(defaults);
},
get_value: function () {
switch (this.$element.val()) {
case 'false': return false;
case 'true': return true;
default: return null;
}
}
});
openerp.base.search.DateField = openerp.base.search.Field.extend( /** @lends openerp.base.search.DateField# */{
template: 'SearchView.fields.date',
/**
* enables date picker on the HTML widgets
*/
start: function () {
this._super();
this.$element.find('input').datepicker({
this.$element.addClass('field_date').datepicker({
dateFormat: 'yy-mm-dd'
});
},
stop: function () {
this.$element.find('input').datepicker('destroy');
this.$element.datepicker('destroy');
},
/**
* Returns an object with two optional keys ``from`` and ``to`` providing
* the values for resp. the from and to sections of the date widget.
*
* If a key is absent, then the corresponding field was not filled.
*
* @returns {Object}
*/
get_values: function () {
var values_array = this.$element.find('input').serializeArray();
if (!values_array || !values_array[0]) {
throw new openerp.base.search.Invalid(
this.attrs.name, null, "widget not ready");
}
var from = values_array[0].value,
to = values_array[1].value;
var field_values = {};
if (from) {
field_values.from = from;
}
if (to) {
field_values.to = to;
}
return field_values;
},
get_context: function () {
var values = this.get_values();
if (!this.attrs.context || _.isEmpty(values)) {
return null;
}
return _.extend(
{}, this.attrs.context,
{own_values: {self: values}});
},
get_domain: function () {
var values = this.get_values();
if (_.isEmpty(values)) {
return null;
}
var domain = this.attrs['filter_domain'];
if (!domain) {
domain = [];
if (values.from) {
domain.push([this.attrs.name, '>=', values.from]);
}
if (values.to) {
domain.push([this.attrs.name, '<=', values.to]);
}
return domain;
}
return _.extend(
{}, domain,
{own_values: {self: values}});
get_value: function () {
return this.$element.val();
}
});
openerp.base.search.DateTimeField = openerp.base.search.DateField.extend({
// TODO: time?
});
openerp.base.search.OneToManyField = openerp.base.search.CharField.extend({
// TODO: .relation, .context, .domain
});
openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({
// TODO: @widget
// TODO: .selection, .context, .domain
init: function (view_section, field, view) {
this._super(view_section, field, view);
var self = this;
@ -845,9 +791,6 @@ openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({
return this._super();
}
});
openerp.base.search.ManyToManyField = openerp.base.search.CharField.extend({
// TODO: .related_columns (Array), .context, .domain
});
openerp.base.search.ExtendedSearch = openerp.base.OldWidget.extend({
template: 'SearchView.extended_search',
@ -930,11 +873,10 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
this._super();
var _this = this;
this.add_prop();
this.$element.find('.searchview_extended_add_proposition').click(function (e) {
this.$element.find('.searchview_extended_add_proposition').click(function () {
_this.add_prop();
});
var delete_btn = this.$element.find('.searchview_extended_delete_group');
delete_btn.click(function (e) {
this.$element.find('.searchview_extended_delete_group').click(function () {
_this.stop();
});
},
@ -944,7 +886,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
}).compact().value();
var choice = this.$element.find(".searchview_extended_group_choice").val();
var op = choice == "all" ? "&" : "|";
return [].concat(choice == "none" ? ['!'] : [],
return choice == "none" ? ['!'] : [].concat(
_.map(_.range(_.max([0,props.length - 1])), function() { return op; }),
props);
},
@ -956,10 +898,7 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
parent.check_last_element();
},
set_last_group: function(is_last) {
if(is_last)
this.$element.addClass("last_group");
else
this.$element.removeClass("last_group");
this.$element.toggleClass('last_group', is_last);
}
});
@ -982,8 +921,7 @@ openerp.base.search.ExtendedSearchProposition = openerp.base.OldWidget.extend({
this.$element.find(".searchview_extended_prop_field").change(function() {
_this.changed();
});
var delete_btn = this.$element.find('.searchview_extended_delete_prop');
delete_btn.click(function (e) {
this.$element.find('.searchview_extended_delete_prop').click(function () {
_this.stop();
});
},

View File

@ -5,22 +5,202 @@
openerp.base.view_tree = function(openerp) {
openerp.base.views.add('tree', 'openerp.base.TreeView');
openerp.base.TreeView = openerp.base.Widget.extend({
/**
* Genuine tree view (the one displayed as a tree, not the list)
*/
openerp.base.TreeView = openerp.base.View.extend({
/**
* Indicates that this view is not searchable, and thus that no search
* view should be displayed (if there is one active).
*/
searchable : false,
init: function(parent, element_id, dataset, view_id, options) {
this._super(parent, element_id);
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
this.records = {};
this.options = _.extend({}, this.defaults, options || {});
},
start: function () {
this._super();
this.$element.append('Tree view');
return this.rpc("/base/treeview/load", {
model: this.model,
view_id: this.view_id,
toolbar: this.view_manager ? !!this.view_manager.sidebar : false
}, this.on_loaded);
},
/**
* Returns the list of fields needed to correctly read objects.
*
* Gathers the names of all fields in fields_view_get, and adds the
* field_parent (children_field in the tree view) if it's not already one
* of the fields to fetch
*
* @returns {Array} an array of fields which can be provided to DataSet.read_slice and others
*/
fields_list: function () {
var fields = _.keys(this.fields);
if (!_(fields).contains(this.children_field)) {
fields.push(this.children_field);
}
return fields;
},
on_loaded: function (fields_view) {
var self = this;
var has_toolbar = !!fields_view.arch.attrs.toolbar;
// field name in OpenERP is kinda stupid: this is the name of the field
// holding the ids to the children of the current node, why call it
// field_parent?
this.children_field = fields_view['field_parent'];
this.fields_view = fields_view;
_(this.fields_view.arch.children).each(function (field) {
if (field.attrs.modifiers) {
field.attrs.modifiers = JSON.parse(field.attrs.modifiers);
}
});
this.fields = fields_view.fields;
this.hook_row_click();
this.$element.html(QWeb.render('TreeView', {
'title': this.fields_view.arch.attrs.string,
'fields_view': this.fields_view.arch.children,
'fields': this.fields,
'toolbar': has_toolbar
}));
this.dataset.read_slice({fields: this.fields_list()}, function (records) {
if (!has_toolbar) {
// WARNING: will do a second read on the same ids, but only on
// first load so not very important
self.getdata(null, _(records).pluck('id'));
return;
}
var $select = self.$element.find('select')
.change(function () {
var $option = $(this).find(':selected');
self.getdata($option.val(), $option.data('children'));
});
_(records).each(function (record) {
self.records[record.id] = record;
$('<option>')
.val(record.id)
.text(record.name)
.data('children', record[self.children_field])
.appendTo($select);
});
if (!_.isEmpty(records)) {
$select.change();
}
});
},
/**
* Sets up opening a row
*/
hook_row_click: function () {
var self = this;
this.$element.delegate('.treeview-td span, .treeview-tr span', 'click', function (e) {
e.stopImmediatePropagation();
self.activate($(this).closest('tr').data('id'));
});
this.$element.delegate('.treeview-tr', 'click', function () {
var is_loaded = 0,
$this = $(this),
record_id = $this.data('id'),
record = self.records[record_id],
children_ids = record[self.children_field];
_(children_ids).each(function(childid) {
if (self.$element.find('#treerow_' + childid).length) {
if (self.$element.find('#treerow_' + childid).is(':hidden')) {
is_loaded = -1;
} else {
is_loaded++;
}
}
});
if (is_loaded === 0) {
if (!$this.parent().hasClass('oe-open')) {
self.getdata(record_id, children_ids);
}
} else {
self.showcontent(record_id, is_loaded < 0);
}
});
},
// get child data of selected value
getdata: function (id, children_ids) {
var self = this;
self.dataset.read_ids(children_ids, this.fields_list(), function (records) {
_(records).each(function (record) {
self.records[record.id] = record;
});
var $curr_node = self.$element.find('#treerow_' + id);
var children_rows = QWeb.render('TreeView.rows', {
'records': records,
'children_field': self.children_field,
'fields_view': self.fields_view.arch.children,
'fields': self.fields,
'level': $curr_node.data('level') || 0,
'render': openerp.base.format_value
});
if ($curr_node.length) {
$curr_node.addClass('oe-open');
$curr_node.after(children_rows);
} else {
self.$element.find('tbody').html(children_rows);
}
});
},
// Get details in listview
activate: function(id) {
var self = this;
this.rpc('/base/treeview/action', {
id: id,
model: this.dataset.model,
context: new openerp.base.CompoundContext(
this.dataset.get_context(), {
active_model: this.dataset.model,
active_id: id,
active_ids: [id]})
}, function (actions) {
if (!actions.length) { return; }
var action = actions[0][2];
self.do_action(action);
});
},
// show & hide the contents
showcontent: function (record_id, show) {
this.$element.find('#treerow_' + record_id)
.toggleClass('oe-open', show);
_(this.records[record_id][this.children_field]).each(function (child_id) {
var $child_row = this.$element.find('#treerow_' + child_id);
if ($child_row.hasClass('oe-open')) {
this.showcontent(child_id, false);
}
$child_row.toggle(show);
}, this);
},
do_show: function () {
this.$element.show();
},
do_hide: function () {
this.$element.hide();
this.hidden = true;
}
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -4,72 +4,83 @@
openerp.base.views = function(openerp) {
/**
* Registry for all the client actions key: tag value: widget
*/
openerp.base.client_actions = new openerp.base.Registry();
openerp.base.ActionManager = openerp.base.Widget.extend({
// process all kind of actions
init: function(parent, element_id) {
this._super(parent, element_id);
this.viewmanager = null;
this.current_dialog = null;
// Temporary linking view_manager to session.
// Will use parent to find it when implementation will be done.
this.session.action_manager = this;
identifier_prefix: "actionmanager",
init: function(parent) {
this._super(parent);
this.inner_viewmanager = null;
this.dialog = null;
this.dialog_viewmanager = null;
this.client_widget = null;
},
do_action: function(action, on_closed) {
render: function() {
return "<div id='"+this.element_id+"'></div>";
},
dialog_stop: function () {
if (this.dialog) {
this.dialog_viewmanager.stop();
this.dialog_viewmanager = null;
this.dialog.stop();
this.dialog = null;
}
},
inner_stop: function () {
if (this.inner_viewmanager) {
this.inner_viewmanager.stop();
this.inner_viewmanager = null;
}
},
do_action: function(action, on_close) {
var type = action.type.replace(/\./g,'_');
var popup = action.target === 'new';
action.flags = _.extend({
sidebar : action.target != 'new',
search_view : action.target != 'new',
new_window : false,
views_switcher : action.target != 'new',
action_buttons : action.target != 'new',
pager : action.target != 'new'
views_switcher : !popup,
search_view : !popup,
action_buttons : !popup,
sidebar : !popup,
pager : !popup
}, action.flags || {});
// instantiate the right controllers by understanding the action
if (!(action.type in this)) {
console.log("Action manager can't handle action of type " + action.type, action);
if (!(type in this)) {
this.log("Action manager can't handle action of type " + action.type, action);
return;
}
this[action.type](action, on_closed);
this[type](action, on_close);
},
'ir.actions.act_window': function (action, on_closed) {
if (!action.target && this.current_dialog) {
action.flags.new_window = true;
}
if (action.target == 'new') {
var dialog = this.current_dialog = new openerp.base.ActionDialog(this, {
title: action.name,
width: '50%'
});
if (on_closed) {
dialog.close_callback = on_closed;
ir_actions_act_window: function (action, on_close) {
if (action.target === 'new') {
if (this.dialog == null) {
this.dialog = new openerp.base.Dialog(this, { title: action.name, width: '80%' });
if(on_close)
this.dialog.on_close.add(on_close);
this.dialog.start();
} else {
this.dialog_viewmanager.stop();
}
dialog.start(false);
var viewmanager = dialog.viewmanager = new openerp.base.ViewManagerAction(this, dialog.element_id, action);
viewmanager.start();
dialog.open();
} else if (action.flags.new_window) {
action.flags.new_window = false;
this.dialog_viewmanager = new openerp.base.ViewManagerAction(this, action);
this.dialog_viewmanager.appendTo(this.dialog.$element);
this.dialog.open();
} else {
this.dialog_stop();
this.inner_stop();
this.inner_viewmanager = new openerp.base.ViewManagerAction(this, action);
this.inner_viewmanager.appendTo(this.$element);
}
/* new window code
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);
if (on_closed) {
on_closed();
}
var url = window.location.protocol + "//" + window.location.host + window.location.pathname + "?" + jQuery.param({ s_action : "" + key });
window.open(url,'_blank');
});
} else {
if (this.viewmanager) {
this.viewmanager.stop();
}
this.viewmanager = new openerp.base.ViewManagerAction(this, this.element_id, action);
this.viewmanager.start();
}
*/
},
'ir.actions.act_window_close': function (action, on_closed) {
this.close_dialog();
ir_actions_act_window_close: function (action, on_closed) {
this.dialog_stop();
},
'ir.actions.server': function (action, on_closed) {
ir_actions_server: function (action, on_closed) {
var self = this;
this.rpc('/base/action/run', {
action_id: action.id,
@ -78,47 +89,28 @@ openerp.base.ActionManager = openerp.base.Widget.extend({
self.do_action(action, on_closed)
});
},
'ir.actions.client': function (action) {
var Handler = openerp.base.client_actions.get_object(action.tag);
new Handler(this, this.element_id, action.params).start();
},
close_dialog: function () {
if (this.current_dialog) {
this.current_dialog.stop();
this.current_dialog = null;
}
}
});
openerp.base.ActionDialog = openerp.base.Dialog.extend({
identifier_prefix: 'action_dialog',
on_close: function() {
this._super(this, arguments);
if (this.close_callback) {
this.close_callback();
}
},
stop: function() {
this._super(this, arguments);
if (this.viewmanager) {
this.viewmanager.stop();
}
ir_actions_client: function (action) {
this.client_widget = openerp.base.client_actions.get_object(action.tag);
new this.client_widget(this, this.element_id, action.params).start();
}
});
openerp.base.ViewManager = openerp.base.Widget.extend({
init: function(parent, element_id, dataset, views) {
this._super(parent, element_id);
identifier_prefix: "viewmanager",
init: function(parent, dataset, views) {
this._super(parent);
this.model = dataset.model;
this.dataset = dataset;
this.searchview = null;
this.active_view = null;
this.views_src = _.map(views, function(x)
{return x instanceof Array? {view_id: x[0], view_type: x[1]} : x;});
this.views_src = _.map(views, function(x) {return x instanceof Array? {view_id: x[0], view_type: x[1]} : x;});
this.views = {};
this.flags = this.flags || {};
this.registry = openerp.base.views;
},
render: function() {
return QWeb.render("ViewManager", {"prefix": this.element_id, views: this.views_src})
},
/**
* @returns {jQuery.Deferred} initial view loading promise
*/
@ -126,28 +118,26 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
this._super();
var self = this;
this.dataset.start();
this.$element.html(QWeb.render("ViewManager", {"prefix": this.element_id, views: this.views_src}));
this.$element.find('.oe_vm_switch button').click(function() {
self.on_mode_switch($(this).data('view-type'));
});
var views_ids = {};
_.each(this.views_src, function(view) {
self.views[view.view_type] = $.extend({}, view, {
controller : null,
options : _.extend({
sidebar_id : self.element_id + '_sidebar_' + view.view_type
}, self.flags)
sidebar_id : self.element_id + '_sidebar_' + view.view_type,
action : self.action,
action_views_ids : views_ids
}, self.flags, view.options || {})
});
views_ids[view.view_type] = view.view_id;
});
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].view_type);
},
stop: function() {
},
/**
* Asks the view manager to switch visualization mode.
@ -168,7 +158,8 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
if (view.embedded_view) {
controller.set_embedded_view(view.embedded_view);
}
if (view_type === 'list' && this.flags.search_view === false && this.action && this.action['auto_search']) {
controller.do_switch_view.add_last(this.on_mode_switch);
if (view_type === 'list' && this.flags.search_view === false && this.action && this.action['auto_search']) {
// In case the search view is not instantiated: manually call ListView#search
var domains = !_(self.action.domain).isEmpty()
? [self.action.domain] : [],
@ -217,11 +208,12 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
},
/**
* Event launched when a controller has been inited.
*
*
* @param {String} view_type type of view
* @param {String} view the inited controller
*/
on_controller_inited: function(view_type, view) {},
on_controller_inited: function(view_type, view) {
},
/**
* Sets up the current viewmanager's search view.
*
@ -238,9 +230,8 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
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()),
contexts.concat(self.contexts()), groupbys);
var controller = self.views[self.active_view].controller;
controller.do_search.call(controller, domains, contexts, groupbys);
});
return this.searchview.start();
},
@ -254,50 +245,22 @@ openerp.base.ViewManager = openerp.base.Widget.extend({
on_remove: function() {
},
on_edit: function() {
},
/**
* Domains added on searches by the view manager, to override in subsequent
* view manager in order to add new pieces of domains to searches
*
* @returns an empty list
*/
domains: function () {
return [];
},
/**
* Contexts added on searches by the view manager.
*
* @returns an empty list
*/
contexts: function () {
return [];
}
});
openerp.base.NullViewManager = openerp.base.generate_null_object_class(openerp.base.ViewManager, {
init: function(parent) {
this._super(parent);
if(parent)
this.session = parent.session;
this.action = {flags: {}};
}
});
// TODO Will move to action Manager
openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
init: function(parent, element_id, action) {
init: function(parent, action) {
this.session = parent.session;
this.action = action;
var dataset;
if (!action.res_id) {
dataset = new openerp.base.DataSetSearch(this, action.res_model, action.context || null);
dataset = new openerp.base.DataSetSearch(this, action.res_model, action.context, action.domain);
} else {
dataset = new openerp.base.DataSetStatic(this, action.res_model, {}, [action.res_id]);
if (action.context) {
// TODO fme: should normalize all DataSets constructors to (session, model, context, ...)
dataset.context = action.context;
}
this.action.flags.search_view = false;
dataset = new openerp.base.DataSetStatic(this, action.res_model, action.context, [action.res_id]);
dataset.index = 0;
}
this._super(parent, element_id, dataset, action.views);
this._super(parent, dataset, action.views);
this.action = action;
this.flags = this.action.flags || {};
if (action.res_model == 'board.board' && action.views.length == 1 && action.views) {
@ -307,6 +270,7 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
},
start: function() {
var inital_view_loaded = this._super();
var search_defaults = {};
_.each(this.action.context, function (value, key) {
var match = /^search_default_(.*)$/.exec(key);
@ -329,32 +293,6 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
}
}
},
stop: function() {
// should be replaced by automatic destruction implemented in Widget
this._super();
},
/**
* adds action domain to the search domains
*
* @returns the action's domain
*/
domains: function () {
if (!this.action.domain) {
return [];
}
return [this.action.domain];
},
/**
* adds action context to the search contexts
*
* @returns the action's context
*/
contexts: function () {
if (!this.action.context) {
return [];
}
return [this.action.context];
},
on_mode_switch: function (view_type) {
this._super(view_type);
this.shortcut_check(this.views[view_type]);
@ -394,8 +332,6 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
$shortcut_toggle.addClass("oe-shortcut-remove");
}
});
}
});
@ -491,7 +427,9 @@ openerp.base.View = openerp.base.Widget.extend({
_.defaults(this.options, {
// All possible views options should be defaulted here
sidebar_id: null,
sidebar: true
sidebar: true,
action: null,
action_views_ids: {}
});
},
/**
@ -503,17 +441,12 @@ openerp.base.View = openerp.base.Widget.extend({
* @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 {Object} [record_id] the identifier of the object on which the action is to be applied
* @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action)
*/
execute_action: function (action_data, dataset, action_manager, record_id, on_closed) {
execute_action: function (action_data, dataset, record_id, on_closed) {
var self = this;
if (action_manager.current_dialog) {
on_closed = action_manager.current_dialog.close_callback;
}
var handler = function (r) {
action_manager.close_dialog();
var action = r.result;
if (action && action.constructor == Object) {
action.context = action.context || {};
@ -522,38 +455,36 @@ openerp.base.View = openerp.base.Widget.extend({
active_ids: [record_id || false],
active_model: dataset.model
});
action.flags = {
new_window: true
};
action_manager.do_action(action, on_closed);
self.do_action(action, on_closed);
} else if (on_closed) {
on_closed(action);
}
};
if (!action_data.special) {
var context = new openerp.base.CompoundContext(dataset.get_context(), action_data.context || {});
switch(action_data.type) {
case 'object':
return dataset.call_button(action_data.name, [[record_id], context], handler);
case 'action':
return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10), context: context }, handler);
default:
return dataset.exec_workflow(record_id, action_data.name, handler);
}
} else {
action_manager.close_dialog();
}
var context = new openerp.base.CompoundContext(dataset.get_context(), action_data.context || {});
if (action_data.special) {
handler({result: {"type":"ir.actions.act_window_close"}});
} else if (action_data.type=="object") {
return dataset.call_button(action_data.name, [[record_id], context], handler);
} else if (action_data.type=="action") {
return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10), context: context }, handler);
} else {
return dataset.exec_workflow(record_id, action_data.name, handler);
}
},
/**
* Directly set a view to use instead of calling fields_view_get. This method must
* be called before start(). When an embedded view is set, underlying implementations
* of openerp.base.View must use the provided view instead of any other one.
*
*
* @param embedded_view A view.
*/
set_embedded_view: function(embedded_view) {
this.embedded_view = embedded_view;
this.options.sidebar = false;
},
do_switch_view: function(view) {
},
set_common_sidebar_sections: function(sidebar) {
sidebar.add_section('customize', "Customize", [
@ -598,16 +529,16 @@ openerp.base.View = openerp.base.Widget.extend({
}
},
on_sidebar_edit_workflow: function() {
console.log('Todo');
this.log('Todo');
},
on_sidebar_customize_object: function() {
console.log('Todo');
this.log('Todo');
},
on_sidebar_import: function() {
},
on_sidebar_export: function() {
var export_view = new openerp.base.DataExport(this, this.dataset);
export_view.start(false);
export_view.start();
},
on_sidebar_translate: function() {
},

View File

@ -42,7 +42,6 @@
</td>
<td valign="top">
<div id="oe_app" class="oe-application">
<div style="width: 100%;">&amp;nbsp;</div>
</div>
</td>
</tr>
@ -79,7 +78,7 @@
</table>
<table align="center" class="db_option_table">
<tr>
<td><label for="super_admin_pwd">Super admin password:</label></td>
<td><label for="super_admin_pwd">Master password:</label></td>
<td><input type="password" name="super_admin_pwd"
class="required" autofocus="autofocus"/></td>
</tr>
@ -104,7 +103,7 @@
</td>
</tr>
<tr>
<td><label for="create_admin_pwd">Super admin password:</label></td>
<td><label for="create_admin_pwd">Admin password:</label></td>
<td><input type="password" name="create_admin_pwd" class="required"/></td>
</tr>
<tr>
@ -139,7 +138,7 @@
</td>
</tr>
<tr>
<td><label for="drop_password">Password:</label></td>
<td><label for="drop_password">Master Password:</label></td>
<td><input type="password" name="drop_pwd" class="required"/></td>
</tr>
<tr>
@ -171,7 +170,7 @@
</td>
</tr>
<tr>
<td><label for="backup_pwd">Password:</label></td>
<td><label for="backup_pwd">Master Password:</label></td>
<td><input type="password" name="backup_pwd" class="required"/></td>
</tr>
<tr>
@ -196,7 +195,7 @@
autofocus="autofocus"/></td>
</tr>
<tr>
<td><label for="restore_pwd">Password:</label></td>
<td><label for="restore_pwd">Master Password:</label></td>
<td><input type="password" name="restore_pwd" class="required"/></td>
</tr>
<tr>
@ -214,23 +213,23 @@
<table width="100%">
<tr>
<td class="option_string">
CHANGE DATABASE PASSWORD
CHANGE MASTER PASSWORD
</td>
</tr>
</table>
<table align="center" class="db_option_table">
<tr>
<td><label for="old_pwd">Old password:</label></td>
<td><label for="old_pwd">Master password:</label></td>
<td><input type="password" name="old_pwd" class="required"
minlength="1" autofocus="autofocus"/></td>
</tr>
<tr>
<td><label for="new_pwd">New password:</label></td>
<td><label for="new_pwd">New master password:</label></td>
<td><input type="password" name="new_pwd" class="required"
minlength="1"/></td>
</tr>
<tr>
<td><label for="confirm_pwd">Confirm password:</label></td>
<td><label for="confirm_pwd">Confirm new master password:</label></td>
<td><input type="password" name="confirm_pwd" class="required"
equalTo="input[name=new_pwd]" minlength="1"/></td>
</tr>
@ -458,6 +457,41 @@
</ul>
</div>
</t>
<t t-name="TreeView">
<h2 class="oe_view_title"><t t-esc="title"/></h2>
<select t-if="toolbar" style="width: 30%">
</select>
<table class="oe-treeview-table">
<thead>
<tr>
<th t-foreach="fields_view" t-as="field"
t-if="!field.attrs.modifiers.tree_invisible"
class="treeview-header">
<t t-esc="fields[field.attrs.name].string" />
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</t>
<tr t-name="TreeView.rows"
t-foreach="records" t-as="record"
t-att-id="'treerow_' + record.id"
t-att-data-id="record.id" t-att-data-level="level + 1">
<t t-set="children" t-value="record[children_field]"/>
<t t-set="has_children" t-value="children and children.length"/>
<td t-foreach="fields_view" t-as="field"
t-if="!field.attrs.modifiers.tree_invisible"
t-att-data-id="record.id"
t-att-style="!field_index ? 'background-position: ' + 19*level + 'px; padding-left: ' + 19*level + 'px' : undefined"
t-att-class="!field_index and has_children ? 'treeview-tr' : 'treeview-td'">
<span t-if="!field.attrs.modifiers.invisible">
<t t-esc="render(record[field.attrs.name], fields[field.attrs.name])" />
</span>
</td>
</tr>
<table t-name="ListView" class="oe-listview-content">
<t t-set="columns_count" t-value="visible_columns.length + (options.selectable ? 1 : 0) + (options.deletable ? 1 : 0)"/>
<thead class="ui-widget-header">
@ -522,14 +556,9 @@
</tfoot>
</table>
<t t-name="ListView.rows" t-foreach="rows" t-as="row">
<t t-call="ListView.row">
<t t-set="style" t-value="null"/>
<t-if test="row.color">
<t t-set="style" t-value="'color: ' + row.color"/>
</t-if>
</t>
<t t-call="ListView.row"/>
</t>
<tr t-name="ListView.row" t-att-style="style" t-att-class="row_parity"
<tr t-name="ListView.row" t-att-class="row_parity"
t-att-data-index="row_index">
<t t-foreach="columns" t-as="column">
<td t-if="column.meta">
@ -943,22 +972,6 @@
<t t-if="filters.length" t-raw="filters.render(defaults)"/>
</div>
</t>
<t t-name="SearchView.fields.date">
<label style="display: block" t-att-title="attrs.help"
t-att-for="element_id">
<t t-esc="attrs.string || attrs.name"/>
<span t-if="attrs.help">(?)</span>
</label>
<div style="white-space: nowrap;" t-att-id="element_id">
<input t-att-name="attrs.name" type="text" class="field_date"
t-att-value="defaults[attrs.name] || ''"
t-att-autofocus="attrs.default_focus === '1' ? 'autofocus' : undefined"/>
to
<input t-att-name="attrs.name" type="text" class="field_date"
t-att-value="defaults[attrs.name] || ''"/>
<t t-if="filters.length" t-raw="filters.render(defaults)"/>
</div>
</t>
<t t-name="SearchView.field.selection">
<label style="display: block" t-att-title="attrs.help"
t-att-for="element_id">
@ -1110,11 +1123,17 @@
</t>
<t t-name="SelectCreatePopup.search.buttons">
<button type="button" class="oe_selectcreatepopup-search-select" disabled="disabled">Select</button>
<button type="button" class="oe_selectcreatepopup-search-close">Close</button>
<button type="button" class="oe_selectcreatepopup-search-close">Cancel</button>
</t>
<t t-name="SelectCreatePopup.form.buttons">
<button type="button" class="oe_selectcreatepopup-form-save">Save</button>
<button type="button" class="oe_selectcreatepopup-form-close">Close</button>
<t t-if="widget.options.disable_multiple_selection">
<button type="button" class="oe_selectcreatepopup-form-save">Save</button>
</t>
<t t-if="! widget.options.disable_multiple_selection">
<button type="button" class="oe_selectcreatepopup-form-save-new">Save &amp; New</button>
<button type="button" class="oe_selectcreatepopup-form-save">Save &amp; Close</button>
</t>
<button type="button" class="oe_selectcreatepopup-form-close">Cancel</button>
</t>
<t t-name="FormOpenPopup">
<div t-att-id="element_id">
@ -1123,7 +1142,7 @@
</t>
<t t-name="FormOpenPopup.form.buttons">
<button type="button" class="oe_formopenpopup-form-save">Save</button>
<button type="button" class="oe_formopenpopup-form-close">Close</button>
<button type="button" class="oe_formopenpopup-form-close">Cancel</button>
</t>
<t t-name="ListView.row.frame" t-extend="WidgetFrame">
<t t-jquery="tr">

View File

@ -1,74 +0,0 @@
$(document).ready(function () {
var openerp;
function get_widget(attrs) {
var widget = new openerp.base.search.DateField(
{attrs: attrs}, {name: 'foo'}, {inputs: []});
$('#qunit-fixture').html(widget.render({}));
widget.start();
return widget;
}
module('search-date', {
setup: function () {
openerp = window.openerp.init(true);
window.openerp.base.core(openerp);
window.openerp.base.chrome(openerp);
window.openerp.base.views(openerp);
window.openerp.base.search(openerp);
}
});
test('no values', function () {
var widget = get_widget();
deepEqual(widget.get_values(), {});
strictEqual(widget.get_context(), null);
strictEqual(widget.get_domain(), null);
});
test('filled from', function () {
var widget = get_widget();
widget.$element.find('input:eq(0)').val('1912-06-23');
deepEqual(widget.get_values(), {from: '1912-06-23'});
strictEqual(widget.get_context(), null);
deepEqual(widget.get_domain(), [['foo', '>=', '1912-06-23']]);
});
test('filled to', function () {
var widget = get_widget();
widget.$element.find('input:eq(1)').val('1954-06-07');
deepEqual(widget.get_values(), {to: '1954-06-07'});
strictEqual(widget.get_context(), null);
deepEqual(widget.get_domain(), [['foo', '<=', '1954-06-07']]);
});
test('filled both', function () {
var widget = get_widget();
widget.$element.find('input:eq(0)').val('1912-06-23');
widget.$element.find('input:eq(1)').val('1954-06-07');
deepEqual(widget.get_values(), {from: '1912-06-23', to: '1954-06-07'});
strictEqual(widget.get_context(), null);
deepEqual(widget.get_domain(),
[['foo', '>=', '1912-06-23'], ['foo', '<=', '1954-06-07']]);
});
test('custom context', function () {
var widget = get_widget({context: {__id: -1}});
widget.$element.find('input:eq(0)').val('1912-06-23');
widget.$element.find('input:eq(1)').val('1954-06-07');
deepEqual(
widget.get_context(),
{__id: -1,
own_values: {
self: {from: '1912-06-23', to: '1954-06-07'}}});
});
test('custom filter_domain', function () {
var widget = get_widget({filter_domain: {__id: -42}});
widget.$element.find('input:eq(0)').val('1912-06-23');
widget.$element.find('input:eq(1)').val('1954-06-07');
deepEqual(
widget.get_domain(),
{__id: -42,
own_values: {
self: {from: '1912-06-23', to: '1954-06-07'}}});
});
});

View File

@ -18,7 +18,7 @@
<script src="/base/static/src/js/boot.js"></script>
<script src="/base/static/src/js/core.js"></script>
<script src="/base/static/src/js/dates.js"></script>
<script src="/base/static/src/js/formats.js"></script>
<script src="/base/static/src/js/chrome.js"></script>
<script src="/base/static/src/js/data.js"></script>
<script src="/base/static/src/js/views.js"></script>
@ -39,6 +39,5 @@
</body>
<script type="text/javascript" src="/base/static/test/class.js"></script>
<script type="text/javascript" src="/base/static/test/registry.js"></script>
<script type="text/javascript" src="/base/static/test/search-date.js"></script>
<script type="text/javascript" src="/base/static/test/form.js"></script>
</html>

View File

@ -0,0 +1,19 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"

View File

@ -9,14 +9,23 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
// Dhtmlx scheduler ?
init: function(parent, element_id, dataset, view_id, options) {
this._super(parent, element_id);
this.set_default_options();
this.set_default_options(options);
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
this.domain = this.dataset.domain || [];
this.context = this.dataset.context || {};
this.has_been_loaded = $.Deferred();
this.options = options || {};
this.creating_event_id = null;
this.dataset_events = [];
if (this.options.action_views_ids.form) {
this.form_dialog = new openerp.base_calendar.CalendarFormDialog(this, {}, this.options.action_views_ids.form, dataset);
this.form_dialog.start();
}
this.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' ];
},
start: function() {
this.rpc("/base_calendar/calendarview/load", {"model": this.model, "view_id": this.view_id, 'toolbar': true}, this.on_loaded);
@ -44,14 +53,19 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
this.fields = this.fields_view.fields;
//* Calendar Fields *
this.calendar_fields['date_start'] = {'name': this.date_start, 'kind': this.fields[this.date_start]['type']};
this.calendar_fields.date_start = {'name': this.date_start, 'kind': this.fields[this.date_start].type};
if (this.date_delay) {
this.calendar_fields['date_delay'] = {'name': this.date_delay, 'kind': this.fields[this.date_delay]['type']};
if (this.fields[this.date_delay].type != 'float') {
throw new Error("Calendar view has a 'date_delay' type != float");
}
this.calendar_fields.date_delay = {'name': this.date_delay, 'kind': this.fields[this.date_delay].type};
}
if (this.date_stop) {
this.calendar_fields['date_stop'] = {'name': this.date_stop, 'kind': this.fields[this.date_stop]['type']};
this.calendar_fields.date_stop = {'name': this.date_stop, 'kind': this.fields[this.date_stop].type};
}
if (!this.date_delay && !this.date_stop) {
throw new Error("Calendar view has none of the following attributes : 'date_stop', 'date_delay'");
}
for (var fld = 0; fld < this.fields_view.arch.children.length; fld++) {
@ -90,7 +104,8 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
scheduler.config.start_on_monday = true;
scheduler.config.scroll_hour = 8;
scheduler.config.drag_resize = scheduler.config.drag_create = !!this.date_stop;
scheduler.config.drag_resize = true;
scheduler.config.drag_create = true;
// Initialize Sceduler
this.mode = this.mode || 'month';
@ -100,6 +115,8 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
scheduler.attachEvent('onEventAdded', this.do_create_event);
scheduler.attachEvent('onEventDeleted', this.do_delete_event);
scheduler.attachEvent('onEventChanged', this.do_save_event);
scheduler.attachEvent('onDblClick', this.do_edit_event);
scheduler.attachEvent('onBeforeLightbox', this.do_edit_event);
this.mini_calendar = scheduler.renderCalendar({
container: this.sidebar.navigator.element_id,
@ -116,25 +133,15 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
refresh_minical: function() {
scheduler.updateCalendar(this.mini_calendar);
},
load_scheduler: function() {
var self = this;
this.dataset.read_slice([], 0, false, function(events) {
if (self.session.locale_code) {
// TODO: replace $LAB
$LAB.setOptions({AlwaysPreserveOrder: true}).script([
'/base_calendar/static/lib/dhtmlxScheduler/sources/locale_' + self.session.locale_code + '.js',
'/base_calendar/static/lib/dhtmlxScheduler/sources/locale_recurring_' + self.session.locale_code + '.js'
]).wait(function() {
self.on_events_loaded(events);
});
} else {
self.on_events_loaded(events);
}
});
reload_event: function(id) {
this.dataset.read_ids([id], _.keys(this.fields), this.on_events_loaded);
},
on_events_loaded: function(events) {
get_color: function(index) {
index = index % this.COLOR_PALETTE.length;
return this.COLOR_PALETTE[index];
},
on_events_loaded: function(events, fn_filter, no_filter_reload) {
var self = this;
scheduler.clearAll();
//To parse Events we have to convert date Format
var res_events = [],
@ -147,111 +154,81 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
break;
}
if (this.color_field) {
var filter = evt[this.color_field];
if (filter) {
var filter_item = {
value: (typeof filter === 'object') ? filter[0] : filter,
label: (typeof filter === 'object') ? filter[1] : filter
}
if (typeof(fn_filter) === 'function' && !fn_filter(filter_item.value)) {
continue;
}
var filter_index = _.indexOf(sidebar_ids, filter_item.value);
if (filter_index === -1) {
evt.color = filter_item.color = this.get_color(sidebar_ids.length);
sidebar_items.push(filter_item);
sidebar_ids.push(filter_item.value);
} else {
evt.color = this.get_color(filter_index);
}
evt.textColor = '#ffffff';
}
}
if (this.fields[this.date_start]['type'] == 'date') {
evt[this.date_start] = openerp.base.parse_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss');
}
if (this.date_stop && evt[this.date_stop] && this.fields[this.date_stop]['type'] == 'date') {
evt[this.date_stop] = openerp.base.parse_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss');
}
if (this.color_field) {
var user = evt[this.color_field];
if (user) {
if (_.indexOf(sidebar_ids, user[0]) === -1) {
sidebar_items.push({
id: user[0],
name: user[1],
// TODO: use color table
color: '#dddddd'
});
sidebar_ids.push(user[0]);
}
}
}
res_events.push(this.convert_event(evt));
}
scheduler.parse(res_events, 'json');
this.refresh_scheduler();
this.refresh_minical();
this.sidebar.responsible.on_events_loaded(sidebar_items);
if (!no_filter_reload) {
this.sidebar.responsible.on_events_loaded(sidebar_items);
}
},
convert_event: function(event) {
var starts = event[this.date_start],
ends = event[this.date_delay] || 1,
span = 0,
convert_event: function(evt) {
var date_start = openerp.base.parse_datetime(evt[this.date_start]),
date_stop = this.date_stop ? openerp.base.parse_datetime(evt[this.date_stop]) : null,
date_delay = evt[this.date_delay] || null,
res_text = '',
res_description = [];
var parse_start_date = openerp.base.parse_datetime(starts);
if (event[this.date_stop]) {
var parse_end_date = openerp.base.parse_datetime(event[this.date_stop]);
}
if (this.info_fields) {
var fld = event[this.info_fields[0]];
if (typeof fld == 'object') {
res_text = fld[fld.length -1];
} else {
res_text = fld;
}
var fld = evt[this.info_fields[0]];
res_text = (typeof fld == 'object') ? fld[fld.length -1] : res_text = fld;
var sliced_info_fields = this.info_fields.slice(1);
for (sl_fld in sliced_info_fields) {
var slc_fld = event[sliced_info_fields[sl_fld]];
for (var sl_fld in sliced_info_fields) {
var slc_fld = evt[sliced_info_fields[sl_fld]];
if (typeof slc_fld == 'object') {
res_description.push(slc_fld[slc_fld.length - 1]);
} else {
if(slc_fld) res_description.push(slc_fld);
} else if (slc_fld) {
res_description.push(slc_fld);
}
}
}
if (starts && ends) {
var n = 0,
h = ends;
if (ends == this.day_length) {
span = 1;
} else if (ends > this.day_length) {
n = ends / this.day_length;
h = ends % this.day_length;
n = parseInt(Math.floor(n));
if (h > 0) {
span = n + 1;
} else {
span = n;
}
}
var start = parse_start_date.setTime((parse_start_date.getTime() + (h * 60 * 60) + (n * 24 * 60 * 60)));
ends = parse_start_date;
if (!date_stop && date_delay) {
date_stop = date_start.clone().addHours(date_delay);
}
if (starts && this.date_stop) {
ends = parse_end_date;
if (event[this.date_stop] == undefined) {
var start = parse_start_date.setTime((parse_start_date.getTime() + (h * 60 * 60) + (n * 24 * 60 * 60)));
ends = parse_start_date;
}
var tds = parse_start_date.getTime(),
tde = parse_end_date.getTime();
if (tds >= tde) {
tde = tds + 60 * 60;
parse_end_date.setTime(tde);
ends = parse_end_date;
}
n = (tde - tds) / (60 * 60);
if (n >= this.day_length) {
span = Math.ceil(n / 24);
}
}
return {
'start_date': parse_start_date.toString('yyyy-MM-dd HH:mm:ss'),
'end_date': ends.toString('yyyy-MM-dd HH:mm:ss'),
var r = {
'start_date': date_start.toString('yyyy-MM-dd HH:mm:ss'),
'end_date': date_stop.toString('yyyy-MM-dd HH:mm:ss'),
'text': res_text,
'id': event['id'],
'id': evt.id,
'title': res_description.join()
}
if (evt.color) {
r.color = evt.color;
}
if (evt.textColor) {
r.textColor = evt.textColor;
}
return r;
},
do_create_event: function(event_id, event_obj) {
var self = this,
@ -262,8 +239,10 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
scheduler.changeEventId(event_id, id);
self.refresh_minical();
}, function(r, event) {
// TODO: open form view
self.notification.warn(self.name, "Could not create event");
self.creating_event_id = event_id;
self.form_dialog.form.on_record_loaded(data);
self.form_dialog.open();
event.preventDefault();
});
},
do_save_event: function(event_id, event_obj) {
@ -274,47 +253,62 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
});
},
do_delete_event: function(event_id, event_obj) {
var self = this;
// dhtmlx sends this event even when it does not exist in openerp.
// Eg: use cancel in dhtmlx new event dialog
if (_.indexOf(this.dataset.ids, event_id) > -1) {
this.dataset.unlink(parseInt(event_id, 10, function() {
if (_.indexOf(this.dataset.ids, parseInt(event_id, 10)) > -1) {
this.dataset.unlink(parseInt(event_id, 10), function() {
self.refresh_minical();
}));
});
}
},
do_edit_event: function(event_id) {
event_id = parseInt(event_id, 10);
var index = _.indexOf(this.dataset.ids, event_id);
if (index > -1) {
this.dataset.index = index;
this.form_dialog.form.do_show();
this.form_dialog.open();
return false;
}
return true;
},
get_event_data: function(event_obj) {
var data = {
name: event_obj.text
};
var date_format = this.calendar_fields.date_start.kind == 'time' ? 'HH:mm:ss' : 'yyyy-MM-dd HH:mm:ss';
data[this.date_start] = event_obj.start_date.toString(date_format);
data[this.date_start] = openerp.base.format_datetime(event_obj.start_date);
if (this.date_stop) {
data[this.date_stop] = event_obj.end_date.toString(date_format);
data[this.date_stop] = openerp.base.format_datetime(event_obj.end_date);
}
if (this.date_delay) {
var tds = (event_obj.start_date.getOrdinalNumber() / 1e3 >> 0) - (event_obj.start_date.getOrdinalNumber() < 0);
var tde = (event_obj.end_date.getOrdinalNumber() / 1e3 >> 0) - (event_obj.end_date.getOrdinalNumber() < 0);
var n = (tde - tds) / (60 * 60);
if (n > this.day_length) {
var d = Math.floor(n / 24),
h = n % 24;
n = d * this.day_length + h;
}
data[this.date_delay] = n;
var diff_seconds = Math.round((event_obj.end_date.getTime() - event_obj.start_date.getTime()) / 1000);
data[this.date_delay] = diff_seconds / 3600;
}
return data;
},
do_search: function(domains, contexts, groupbys) {
var self = this;
this.rpc('/base/session/eval_domain_and_context', {
domains: domains,
contexts: contexts,
group_by_seq: groupbys
}, function (results) {
// TODO: handle non-empty results.group_by with read_group
self.dataset.context = self.context = results.context;
self.dataset.domain = self.domain = results.domain;
self.dataset.read_slice(_.keys(self.fields), 0, self.limit, self.on_events_loaded);
scheduler.clearAll();
$.when(this.has_been_loaded).then(function() {
self.rpc('/base/session/eval_domain_and_context', {
domains: domains,
contexts: contexts,
group_by_seq: groupbys
}, function (results) {
// TODO: handle non-empty results.group_by with read_group
self.dataset.context = self.context = results.context;
self.dataset.domain = self.domain = results.domain;
self.dataset.read_slice({
fields: _.keys(self.fields),
offset:0,
limit: self.limit
}, function(events) {
self.dataset_events = events;
self.on_events_loaded(events);
}
);
});
});
},
do_show: function () {
@ -331,53 +325,39 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
if (this.sidebar) {
this.sidebar.$element.hide();
}
}
});
openerp.base_calendar.CalendarFormDialog = openerp.base.Dialog.extend({
init: function(view, options, view_id, dataset) {
this._super(view, options);
this.dataset = dataset;
this.view_id = view_id;
this.view = view;
},
popup_event: function(event_id) {
var self = this;
if (event_id) event_id = parseInt(event_id, 10);
var action = {
res_model: this.dataset.model,
res_id: event_id,
views: [[false, 'form']],
type: 'ir.actions.act_window',
view_type: 'form',
view_mode: 'form',
flags : {
search_view: false,
sidebar : false,
views_switcher : false,
action_buttons : false,
pager: false
}
}
var element_id = _.uniqueId("act_window_dialog");
var dialog = $('<div>', {
'id': element_id
}).dialog({
modal: true,
width: 'auto',
height: 'auto',
buttons: {
Cancel: function() {
$(this).dialog("destroy");
},
Save: function() {
var view_manager = action_manager.viewmanager;
var _dialog = this;
view_manager.views[view_manager.active_view].controller.do_save(function(r) {
$(_dialog).dialog("destroy");
// self.start();
self.load_scheduler();
})
}
}
start: function() {
this._super();
this.form = new openerp.base.FormView(this, this.element_id, this.dataset, this.view_id, {
sidebar: false,
pager: false
});
var action_manager = new openerp.base.ActionManager(this, element_id);
action_manager.start();
action_manager.do_action(action);
//Default_get
if (!event_id) {
this.dataset.index = null;
this.form.start();
this.form.on_created.add_last(this.on_form_dialog_saved);
this.form.on_saved.add_last(this.on_form_dialog_saved);
},
on_form_dialog_saved: function() {
var id = this.dataset.ids[this.dataset.index];
if (this.view.creating_event_id) {
scheduler.changeEventId(this.view.creating_event_id, id);
this.view.creating_event_id = null;
}
this.view.reload_event(id);
this.close();
},
on_close: function() {
if (this.view.creating_event_id) {
scheduler.deleteEvent(this.view.creating_event_id);
this.view.creating_event_id = null;
}
}
});
@ -386,10 +366,25 @@ openerp.base_calendar.SidebarResponsible = openerp.base.Widget.extend({
init: function(parent, element_id, view) {
this._super(parent, element_id);
this.view = view;
this.$element.delegate('input:checkbox', 'change', this.on_filter_click);
},
on_events_loaded: function(users) {
this.$element.html(QWeb.render('CalendarView.sidebar.responsible', { users : users }));
// TODO: bind checkboxes reload sheduler
on_events_loaded: function(filters) {
this.$element.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
},
on_filter_click: function(e) {
var responsibles = [],
$e = $(e.target);
this.$element.find('div.oe_calendar_responsible input:checked').each(function() {
responsibles.push($(this).val());
});
scheduler.clearAll();
if (responsibles.length) {
this.view.on_events_loaded(this.view.dataset_events, function(filter_value) {
return _.indexOf(responsibles, filter_value.toString()) > -1;
}, true);
} else {
this.view.on_events_loaded(this.view.dataset_events, false, true);
}
}
});
@ -401,7 +396,6 @@ openerp.base_calendar.SidebarNavigator = openerp.base.Widget.extend({
on_events_loaded: function(events) {
}
});
};
// DEBUG_RPC:rpc.request:('execute', 'addons-dsh-l10n_us', 1, '*', ('ir.filters', 'get_filters', u'res.partner'))

View File

@ -18,9 +18,9 @@
</div>
</t>
<t t-name="CalendarView.sidebar.responsible">
<div t-foreach="users" t-as="user" class="oe_calendar_responsible" t-attf-style="background: #{user.color}">
<input type="checkbox" name="selection" t-att-value="user.id"/>
<span><t t-esc="user.name"/></span>
<div t-foreach="filters" t-as="filter" class="oe_calendar_responsible" t-attf-style="background: #{filter.color}">
<input type="checkbox" name="selection" t-att-value="filter.value"/>
<span><t t-esc="filter.label"/></span>
</div>
</t>
</template>

View File

@ -0,0 +1,19 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"

View File

@ -228,9 +228,9 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
action_buttons : false,
pager: false
};
new openerp.base.ActionManager(
this, this.view.element_id + '_action_' + action.id)
.do_action(action);
var am = new openerp.base.ActionManager(this);
am.appendTo($("#"+this.view.element_id + '_action_' + action.id));
am.do_action(action);
},
render: function() {
// We should start with three columns available
@ -244,8 +244,8 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
return QWeb.render(this.template, this);
},
do_reload: function() {
this.view.view_manager.stop();
this.view.view_manager.start();
this.view.widget_parent.stop();
this.view.widget_parent.start();
}
});
openerp.base.form.DashBoardLegacy = openerp.base.form.DashBoard.extend({
@ -290,7 +290,7 @@ openerp.base_dashboard.ConfigOverview = openerp.base.View.extend({
this.dataset.domain = [['type', '=', 'manual']];
},
start: function () {
$.when(this.dataset.read_slice(['state', 'action_id', 'category_id']),
$.when(this.dataset.read_slice({fields: ['state', 'action_id', 'category_id']}),
this.dataset.call('progress'))
.then(this.on_records_loaded);
},
@ -333,7 +333,6 @@ openerp.base_dashboard.ConfigOverview = openerp.base.View.extend({
type: 'object',
name: 'action_launch'
}, self.dataset,
self.session.action_manager,
$(this).data('id'), function () {
// after action popup closed, refresh configuration
// thingie
@ -354,8 +353,8 @@ openerp.base_dashboard.ApplicationTiles = openerp.base.View.extend({
start: function () {
var self = this;
this.dataset.read_slice(
['name', 'web_icon_data', 'web_icon_hover_data'],
null, null, function (applications) {
{fields: ['name', 'web_icon_data', 'web_icon_hover_data']},
function (applications) {
// Create a matrix of 3*x applications
var rows = [];
while (applications.length) {
@ -385,7 +384,8 @@ openerp.base_dashboard.Widgets = openerp.base.View.extend({
this.widgets = new openerp.base.DataSetSearch(this, 'res.widget');
},
start: function () {
this.user_widgets.read_slice(['widget_id', 'user_id'], null, null,
this.user_widgets.read_slice(
{fields: ['widget_id', 'user_id']},
this.on_widgets_list_loaded);
},
on_widgets_list_loaded: function (user_widgets) {

View File

@ -0,0 +1,19 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"

View File

@ -86,7 +86,7 @@ openerp.base_default_home = function (openerp) {
$.blockUI({
message: '<img src="/base_default_home/static/src/img/throbber.gif">'
});
Modules.read_slice(['id'], null, null, function (records) {
Modules.read_slice({fields: ['id']}, function (records) {
if (!(records.length === 1)) { return; }
Modules.call('state_update',
[_.pluck(records, 'id'), 'to install', ['uninstalled']],

View File

@ -0,0 +1,19 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"

View File

@ -0,0 +1,19 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"

View File

@ -1,16 +1,14 @@
/*---------------------------------------------------------
* OpenERP base_gantt
*---------------------------------------------------------*/
openerp.base_gantt = function (openerp) {
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.Widget.extend({
openerp.base_gantt.GanttView = openerp.base.View.extend({
init: function(view_manager, session, element_id, dataset, view_id) {
this._super(session, element_id);
this.view_manager = view_manager;
init: function(parent, element_id, dataset, view_id) {
this._super(parent, element_id);
this.view_manager = parent || new openerp.base.NullViewManager();
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
@ -29,7 +27,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
this.calendar_fields = {};
this.info_fields = [];
this.domain = this.dataset._domain ? this.dataset._domain: [];
this.context = {};
this.context = this.dataset.context || {};
},
start: function() {
@ -52,8 +50,8 @@ init: function(view_manager, session, element_id, dataset, view_id) {
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.text = this.fields_view.arch.children[0].children[0].attrs.name;
var arch_children = this.fields_view.arch.children[0];
this.text = arch_children.children[0] ? arch_children.children[0].attrs.name : arch_children.attrs.name;
this.parent = this.fields_view.arch.children[0].attrs.link;
this.format = "yyyy-MM-dd";
@ -81,7 +79,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
get_events: function() {
var self = this;
this.dataset.read_slice(false, false, false, function(result) {
this.dataset.read_slice({}, function(result) {
self.load_event(result);
});
@ -100,7 +98,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
if (result.length != 0){
var show_event = [];
for (i in result){
for (var i in result){
var res = result[i];
if (res[this.date_start] != false){
@ -132,7 +130,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
var child_event = {};
var temp_id = "";
var final_events = [];
for (i in show_event) {
for (var i in show_event) {
var res = show_event[i];
@ -161,11 +159,8 @@ init: function(view_manager, session, element_id, dataset, view_id) {
if (duration == false)
duration = 0
if (self.grp.length == 0){
self.grp.push({'group_by' : this.parent})
}
if (self.grp != undefined){
for (j in self.grp){
if (self.grp.length){
for (var j in self.grp){
var grp_key = res[self.grp[j]['group_by']];
if (typeof(grp_key) == "object"){
grp_key = res[self.grp[j]['group_by']][1];
@ -175,7 +170,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
}
if (grp_key == false){
grp_key = "False";
grp_key = "Undefined";
}
if (j == 0){
@ -207,9 +202,17 @@ init: function(view_manager, session, element_id, dataset, view_id) {
all_events[id] = {'parent': temp_id, 'evt':[id , text, start_date, duration, 100, "", color_box[color]]};
final_events.push(id);
}
else {
if (i == 0) {
var mod_id = "_" + i;
all_events[mod_id] = {'parent': "", 'evt': [mod_id, this.name, start_date, start_date, 100, "", "white"]};
}
all_events[id] = {'parent': mod_id, 'evt':[id , text, start_date, duration, 100, "", color_box[color]]};
final_events.push(id);
}
}
for (i in final_events){
for (var i in final_events){
var evt_id = final_events[i];
var evt_date = all_events[evt_id]['evt'][2];
while (all_events[evt_id]['parent'] != "") {
@ -225,7 +228,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
var evt_duration = "";
var evt_end_date = "";
for (i in final_events){
for (var i in final_events){
evt_id = final_events[i];
evt_date = all_events[evt_id]['evt'][2];
evt_duration = all_events[evt_id]['evt'][3];
@ -242,42 +245,27 @@ init: function(view_manager, session, element_id, dataset, view_id) {
}
}
for (j in self.grp){
for (i in all_events){
res = all_events[i];
if ((typeof(res['evt'][3])) == "object"){
res['evt'][3] = self.hours_between(res['evt'][2],res['evt'][3]);
}
k = res['evt'][0].toString().indexOf('_');
if (k != -1){
if (res['evt'][0].substring(k) == "_"+j){
if (j == 0){
task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
project.addTask(task);
} else {
task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
prt = project.getTaskById(res['parent']);
prt.addChildTask(task);
}
}
}
}
for (var j in self.grp) {
self.render_events(all_events, j);
}
for (i in final_events){
if (!self.grp.length) {
self.render_events(all_events, 0);
}
for (var i in final_events){
evt_id = final_events[i];
res = all_events[evt_id];
task=new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
prt = project.getTaskById(res['parent']);
prt.addChildTask(task);
}
oth_hgt = 264;
min_hgt = 150;
name_min_wdt = 150;
gantt_hgt = jQuery(window).height() - oth_hgt;
search_wdt = jQuery("#oe_app_search").width();
var oth_hgt = 264;
var min_hgt = 150;
var name_min_wdt = 150;
var gantt_hgt = jQuery(window).height() - oth_hgt;
var search_wdt = jQuery("#oe_app_search").width();
if (gantt_hgt > min_hgt){
jQuery('#GanttDiv').height(gantt_hgt).width(search_wdt);
@ -291,13 +279,13 @@ init: function(view_manager, session, element_id, dataset, view_id) {
ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_resize_drag_end(task, "drag");});
ganttChartControl.attachEvent("onTaskDblClick", function(task) {self.open_popup(task);});
taskdiv = jQuery("div.taskPanel").parent();
var taskdiv = jQuery("div.taskPanel").parent();
taskdiv.addClass('ganttTaskPanel');
taskdiv.prev().addClass('ganttDayPanel');
$gantt_panel = jQuery(".ganttTaskPanel , .ganttDayPanel");
var $gantt_panel = jQuery(".ganttTaskPanel , .ganttDayPanel");
ganttrow = jQuery('.taskPanel').closest('tr');
gtd = ganttrow.children(':first-child');
var ganttrow = jQuery('.taskPanel').closest('tr');
var gtd = ganttrow.children(':first-child');
gtd.children().addClass('task-name');
jQuery(".toggle-sidebar").click(function(e) {
@ -319,9 +307,8 @@ init: function(view_manager, session, element_id, dataset, view_id) {
$gantt_panel.width(1);
jQuery(".ganttTaskPanel").parent().width(1);
search_wdt = jQuery("#oe_app_search").width();
day_wdt = jQuery(".ganttDayPanel").children().children().width();
name_wdt = jQuery('.task-name').width();
var search_wdt = jQuery("#oe_app_search").width();
var day_wdt = jQuery(".ganttDayPanel").children().children().width();
jQuery('#GanttDiv').css('width','100%');
if (search_wdt - day_wdt <= name_min_wdt){
@ -345,7 +332,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
var self = this;
dat = this.convert_str_date(dat);
var dat = this.convert_str_date(dat);
var day = Math.floor(duration/self.day_length);
var hrs = duration % self.day_length;
@ -356,47 +343,98 @@ init: function(view_manager, session, element_id, dataset, view_id) {
return dat;
},
hours_between: function(date1, date2) {
hours_between: function(date1, date2, parent_task) {
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.floor(difference_ms / ONE_DAY);
h = (difference_ms % ONE_DAY)/(1000 * 60 * 60);
num = (d * this.day_length) + h;
var d = parent_task? Math.ceil(difference_ms / ONE_DAY) : Math.floor(difference_ms / ONE_DAY);
var h = (difference_ms % ONE_DAY)/(1000 * 60 * 60);
var num = (d * this.day_length) + h;
return parseFloat(num.toFixed(2));
},
render_events : function(all_events, j) {
var self = this;
for (var i in all_events){
var res = all_events[i];
if ((typeof(res['evt'][3])) == "object"){
res['evt'][3] = self.hours_between(res['evt'][2],res['evt'][3], true);
}
k = res['evt'][0].toString().indexOf('_');
if (k != -1) {
if (res['evt'][0].substring(k) == "_"+j){
if (j == 0){
task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
project.addTask(task);
} else {
task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
prt = project.getTaskById(res['parent']);
prt.addChildTask(task);
}
}
}
}
},
open_popup : function(task) {
var event_id = task.getId();
if(event_id.toString().search("_") != -1)
return;
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;
if(event_id) event_id = parseInt(event_id, 10);
var action = {
"res_model": this.dataset.model,
"res_id": event_id,
"views":[[false,"form"]],
"type":"ir.actions.act_window",
"view_type":"form",
"view_mode":"form"
}
this.dataset.index = dataset_event_index;
action.flags = {
search_view: false,
sidebar : false,
views_switcher : false,
pager: false
}
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();
var dialog = jQuery('<div>', {
'id': element_id
}).dialog({
modal: true,
width: 'auto',
height: 'auto',
buttons: {
Cancel: function() {
$(this).dialog("destroy");
},
Save: function() {
var view_manager = action_manager.viewmanager;
var _dialog = this;
view_manager.views[view_manager.active_view].controller.do_save(function(r) {
$(_dialog).dialog("destroy");
self.reload_gantt();
})
}
}
});
var action_manager = new openerp.base.ActionManager(this, element_id);
action_manager.start();
action_manager.do_action(action);
//Default_get
if(!event_id) action_manager.viewmanager.dataset.index = null;
},
on_drag_start : function(task){
st_date = task.getEST();
var st_date = task.getEST();
if(st_date.getHours()){
self.hh = st_date.getHours();
self.mm = st_date.getMinutes();
@ -460,7 +498,7 @@ init: function(view_manager, session, element_id, dataset, view_id) {
reload_gantt: function() {
var self = this;
this.dataset.read_slice(false, false, false, function(response) {
this.dataset.read_slice({}, function(response) {
ganttChartControl.clearAll();
jQuery("#GanttDiv").children().remove();
self.load_event(response);

View File

@ -0,0 +1,19 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"

View File

@ -89,7 +89,7 @@ openerp.base_graph.GraphView = openerp.base.View.extend({
}
this.dataset.domain = domain;
this.dataset.context = this.view_manager.dataset.context;
this.dataset.read_slice(_(this.fields).keys(), 0, false, function(res) {
this.dataset.read_slice({fields: _(this.fields).keys()}, function(res) {
self.schedule_chart(res);
});
}
@ -442,7 +442,7 @@ openerp.base_graph.GraphView = openerp.base.View.extend({
}
views.push(view);
});
this.session.action_manager.do_action({
this.do_action({
"res_model" : this.dataset.model,
"domain" : this.dataset.domain,
"views" : views,
@ -468,9 +468,7 @@ openerp.base_graph.GraphView = openerp.base.View.extend({
}
self.dataset.context = results.context;
self.dataset.domain = results.domain;
self.dataset.read_slice([], 0, false,function(response){
self.load_chart(response);
});
self.dataset.read_slice({}, $.proxy(self, 'load_chart'));
});
}
});

View File

@ -0,0 +1,19 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"

View File

@ -0,0 +1,19 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"

View File

@ -0,0 +1,19 @@
# Translations template for PROJECT.
# Copyright (C) 2011 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2011-08-16 15:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"

View File

@ -12,7 +12,7 @@ openerp.web_mobile.FormView = openerp.base.Widget.extend({
view_id = this.action.views[1][0];
this.dataset = new openerp.base.DataSetSearch(this.session, this.action.res_model, null, null);
this.dataset.read_slice(false, false, false, function (result) {
this.dataset.read_slice({}, function (result) {
for (var i = 0; i < result.length; i++) {
if (result[i].id == id) {
@ -101,4 +101,4 @@ openerp.web_mobile.FormView = openerp.base.Widget.extend({
},
});
}
}

View File

@ -19,10 +19,10 @@
<script type="text/javascript" src="/base/static/src/js/base.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/dates.js"></script>
<script type="text/javascript" src="/base/static/src/js/formats.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/web_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/chrome_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/web_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/chrome_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/list_mobile.js"></script>
<script type="text/javascript" src="/web_mobile/static/src/js/form_mobile.js"></script>

6
babel.cfg Normal file
View File

@ -0,0 +1,6 @@
[extractors]
qweb = npybabel:extract_qweb
[javascript: static/src/js/**.js]
[qweb: static/src/xml/**.xml]

View File

@ -113,6 +113,98 @@ initializing the addon.
Creating new standard roles
---------------------------
Widget
++++++
This is the base class for all visual components. It provides a number of
services for the management of a DOM subtree:
* Rendering with QWeb
* Parenting-child relations
* Life-cycle management (including facilitating children destruction when a
parent object is removed)
* DOM insertion, via jQuery-powered insertion methods. Insertion targets can
be anything the corresponding jQuery method accepts (generally selectors,
DOM nodes and jQuery objects):
:js:func:`~openerp.base.Widget.appendTo`
Renders the widget and inserts it as the last child of the target, uses
`.appendTo()`_
:js:func:`~openerp.base.Widget.prependTo`
Renders the widget and inserts it as the first child of the target, uses
`.prependTo()`_
:js:func:`~openerp.base.Widget.insertAfter`
Renders the widget and inserts it as the preceding sibling of the target,
uses `.insertAfter()`_
:js:func:`~openerp.base.Widget.insertBefore`
Renders the widget and inserts it as the following sibling of the target,
uses `.insertBefore()`_
:js:class:`~openerp.base.Widget` inherits from
:js:class:`~openerp.base.SessionAware`, so subclasses can easily access the
RPC layers.
Subclassing Widget
~~~~~~~~~~~~~~~~~~
:js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the
:js:func:`~openerp.base.Class.extend` method), and provides a number of
abstract properties and concrete methods (which you may or may not want to
override). Creating a subclass looks like this:
.. code-block:: javascript
var MyWidget = openerp.base.Widget.extend({
// QWeb template to use when rendering the object
template: "MyQWebTemplate",
// autogenerated id prefix, specificity helps when debugging
identifier_prefix: 'my-id-prefix-',
init: function(parent) {
this._super(parent);
// insert code to execute before rendering, for object
// initialization
},
start: function() {
this._super();
// post-rendering initialization code, at this point
// ``this.$element`` has been initialized
this.$element.find(".my_button").click(/* an example of event binding * /);
// if ``start`` is asynchronous, return a promise object so callers
// know when the object is done initializing
return this.rpc(/* … */)
}
});
The new class can then be used in the following manner:
.. code-block:: javascript
// Create the instance
var my_widget = new MyWidget(this);
// Render and insert into DOM
my_widget.appendTo(".some-div");
After these two lines have executed (and any promise returned by ``appendTo``
has been resolved if needed), the widget is ready to be used.
If the widget is not needed anymore (because it's transient), simply terminate
it:
.. code-block:: javascript
my_widget.stop();
will unbind all DOM events, remove the widget's content from the DOM and
destroy all widget data.
Views
+++++
@ -236,193 +328,6 @@ replace ``addons`` by the directory in which your own addon lives.
and run ``nosetests addons`` instead of the ``unit2`` command,
the result should be exactly the same.
APIs
----
Javascript
++++++++++
.. js:class:: openerp.base.Widget(view, node)
:param openerp.base.Controller view: The view to which the widget belongs
:param Object node: the ``fields_view_get`` descriptor for the widget
.. js:attribute:: $element
The widget's root element as jQuery object
.. js:class:: openerp.base.DataSet(session, model)
:param openerp.base.Session session: the RPC session object
:param String model: the model managed by this dataset
The DataSet is the abstraction for a sequence of records stored in
database.
It provides interfaces for reading records based on search
criteria, and for selecting and fetching records based on
activated ids.
.. js:function:: fetch([offset][, limit])
:param Number offset: the index from which records should start
being returned (section)
:param Number limit: the maximum number of records to return
:returns: the dataset instance it was called on
Asynchronously fetches the records selected by the DataSet's
domain and context, in the provided sort order if any.
Only fetches the fields selected by the DataSet.
On success, triggers :js:func:`on_fetch`
.. js:function:: on_fetch(records, event)
:param Array records: an array of
:js:class:`openerp.base.DataRecord`
matching the DataSet's selection
:param event: a data holder letting the event handler fetch
meta-informations about the event.
:type event: OnFetchEvent
Fired after :js:func:`fetch` is done fetching the records
selected by the DataSet.
.. js:function:: active_ids
:returns: the dataset instance it was called on
Asynchronously fetches the active records for this DataSet.
On success, triggers :js:func:`on_active_ids`
.. js:function:: on_active_ids(records)
:param Array records: an array of
:js:class:`openerp.base.DataRecord`
matching the currently active ids
Fired after :js:func:`active_ids` fetched the records matching
the DataSet's active ids.
.. js:function:: active_id
:returns: the dataset instance in was called on
Asynchronously fetches the current active record.
On success, triggers :js:func:`on_active_id`
.. js:function:: on_active_id(record)
:param Object record: the record fetched by
:js:func:`active_id`, or ``null``
:type record: openerp.base.DataRecord
Fired after :js:func:`active_id` fetched the record matching
the dataset's active id
.. js:function:: set(options)
:param Object options: the options to set on the dataset
:type options: DataSetOptions
:returns: the dataset instance it was called on
Configures the data set by setting various properties on it
.. js:function:: prev
:returns: the dataset instance it was called on
Activates the id preceding the current one in the active ids
sequence of the dataset.
If the current active id is at the start of the sequence,
wraps back to the last id of the sequence.
.. js:function:: next
:returns: the dataset instance it was called on
Activates the id following the current one in the active ids
sequence.
If the current active id is the last of the sequence, wraps
back to the beginning of the active ids sequence.
.. js:function:: select(ids)
:param Array ids: the identifiers to activate on the dataset
:returns: the dataset instance it was called on
Activates all the ids specified in the dataset, resets the
current active id to be the first id of the new sequence.
The internal order will be the same as the ids list provided.
.. js:function:: get_active_ids
:returns: the list of current active ids for the dataset
.. js:function:: activate(id)
:param Number id: the id to activate
:returns: the dataset instance it was called on
Activates the id provided in the dataset. If no ids are
selected, selects the id in the dataset.
If ids are already selected and the provided id is not in that
selection, raises an error.
.. js:function:: get_active_id
:returns: the dataset's current active id
Ad-hoc objects and structural types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These objects are not associated with any specific class, they're
generally literal objects created on the spot. Names are merely
convenient ways to refer to them and their properties.
.. js:class:: OnFetchEvent
.. js:attribute:: context
The context used for the :js:func:`fetch` call (domain set on
the :js:class:`openerp.base.DataSet` when ``fetch`` was
called)
.. js:attribute:: domain
The domain used for the :js:func:`fetch` call
.. js:attribute:: limit
The limit with which the original :js:func:`fetch` call was
performed
.. js:attribute:: offset
The offset with which the original :js:func:`fetch` call was
performed
.. js:attribute:: sort
The sorting criteria active on the
:js:class:`openerp.base.DataSet` when :js:func:`fetch` was
called
.. js:class:: DataSetOptions
.. js:attribute:: context
.. js:attribute:: domain
.. js:attribute:: sort
Python
++++++
@ -499,3 +404,15 @@ Python
.. _promise object:
http://api.jquery.com/deferred.promise/
.. _.appendTo():
http://api.jquery.com/appendTo/
.. _.prependTo():
http://api.jquery.com/prependTo/
.. _.insertAfter():
http://api.jquery.com/insertAfter/
.. _.insertBefore():
http://api.jquery.com/insertBefore/

View File

@ -374,11 +374,13 @@ Deletion can be overridden by replacing the
calls :js:func:`~openerp.base.DataSet.unlink` in order to remove the records
entirely.
.. note:: the list-wise deletion button (next to the record addition button)
simply proxies to :js:func:`~openerp.base.ListView.do_delete` after
obtaining all selected record ids, but it is possible to override it
alone by replacing
:js:func:`~openerp.base.ListView.do_delete_selected`.
.. note::
the list-wise deletion button (next to the record addition button)
simply proxies to :js:func:`~openerp.base.ListView.do_delete` after
obtaining all selected record ids, but it is possible to override it
alone by replacing
:js:func:`~openerp.base.ListView.do_delete_selected`.
Internal API Doc
----------------

53
gen_translations.sh Executable file
View File

@ -0,0 +1,53 @@
#!/bin/sh
usage() {
cat << EOF
usage: $0 -a
usage: $0 DIR OUTPUT_FILE
OPTIONS:
-a recreate the .pot file for all addons
-h print this message
EOF
exit 0
}
do_all=
while getopts "a" opt
do
case "$opt" in
a)
do_all=true;;
h)
usage;;
\?)
usage;;
esac
done
shift $((OPTIND-1))
if [ -n "$do_all" ]
then
echo "Extracting all the translations"
executable=$0
extract_module() {
$executable addons/$1 addons/$1/po/$1.pot
}
extract_module base
extract_module base_calendar
extract_module base_dashboard
extract_module base_default_home
extract_module base_diagram
extract_module base_gantt
extract_module base_graph
extract_module base_hello
extract_module web_chat
extract_module web_mobile
elif [ -n "$2" ]
then
./npybabel.py extract -F babel.cfg -o $2 -k _t --no-default-keywords $1
else
usage
fi

46
npybabel.py Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'Babel==0.9.6','console_scripts','pybabel'
__requires__ = 'Babel==0.9.6'
import sys
from pkg_resources import load_entry_point
import re
import json
if __name__ == '__main__':
sys.exit(
load_entry_point('Babel==0.9.6', 'console_scripts', 'pybabel')()
)
QWEB_EXPR = re.compile(r"""(?:\< *t\-tr *\>(.*?)\< *\/t\-tr *\>)|(?:\_t *\( *((?:"(?:[^"\\]|\\.)*")|(?:'(?:[^'\\]|\\.)*')) *\))""")
XML_GROUP = 1
JS_GROUP = 2
def extract_qweb(fileobj, keywords, comment_tags, options):
"""Extract messages from XXX files.
:param fileobj: the file-like object the messages should be extracted
from
:param keywords: a list of keywords (i.e. function names) that should
be recognized as translation functions
:param comment_tags: a list of translator tags to search for and
include in the results
:param options: a dictionary of additional options (optional)
:return: an iterator over ``(lineno, funcname, message, comments)``
tuples
:rtype: ``iterator``
"""
content = fileobj.read()
found = QWEB_EXPR.finditer(content)
result = []
index = 0
line_nbr = 0
for f in found:
group = XML_GROUP if f.group(XML_GROUP) else JS_GROUP
mes = f.group(group)
if group == JS_GROUP:
mes = json.loads(mes)
while index < f.start():
if content[index] == "\n":
line_nbr += 1
index += 1
result.append((line_nbr, None, mes, ""))
return result

245
setup.py Executable file → Normal file
View File

@ -1,174 +1,89 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# setup from TinERP
# taken from straw http://www.nongnu.org/straw/index.html
# taken from gnomolicious http://www.nongnu.org/gnomolicious/
# adapted by Nicolas Évrard <nicoe@altern.org>
#
# doc/migrate is not included since about 6.1-dev
# doc/tests is not included
# python25-compat/*py should be in the openerp (and imported appropriately)
import sys
import os
from os.path import join, isfile
import glob
import re
import sys
from setuptools import setup
from setuptools import setup, find_packages
name = 'openerp-web-proto'
version = '6.0.1'
description = "Web Client of OpenERP, the Enterprise Management Software"
long_description = "OpenERP Web is the web client of the OpenERP, a free enterprise management software"
author = "OpenERP S.A."
author_email = "info@openerp.com"
support_email = 'support@openerp.com'
url = "http://www.openerp.com/"
download_url = ''
license = "OEPL"
# Backports os.walk with followlinks from python 2.6.
# Needed to add all addons files to data_files for Windows packaging.
def walk_followlinks(top, topdown=True, onerror=None, followlinks=False):
from os.path import join, isdir, islink
from os import listdir, error
try:
names = listdir(top)
except error, err:
if onerror is not None:
onerror(err)
return
dirs, nondirs = [], []
for name in names:
if isdir(join(top, name)):
dirs.append(name)
else:
nondirs.append(name)
if topdown:
yield top, dirs, nondirs
for name in dirs:
path = join(top, name)
if followlinks or not islink(path):
for x in walk_followlinks(path, topdown, onerror, followlinks):
yield x
if not topdown:
yield top, dirs, nondirs
if sys.version_info < (2, 6):
os.walk = walk_followlinks
py2exe_keywords = {}
py2exe_data_files = []
if os.name == 'nt':
version_dash_incompatible = False
if 'bdist_rpm' in sys.argv:
version_dash_incompatible = True
try:
import py2exe
py2exe_keywords['console'] = [
{ "script": "openerp-server",
"icon_resources": [(1, join("pixmaps","openerp-icon.ico"))],
}]
py2exe_keywords['options'] = {
"py2exe": {
"skip_archive": 1,
"optimize": 2,
"dist_dir": 'dist',
"packages": [
"lxml", "lxml.builder", "lxml._elementpath", "lxml.etree",
"lxml.objectify", "decimal", "xml", "xml", "xml.dom", "xml.xpath",
"encodings", "dateutil", "pychart", "PIL", "pyparsing",
"pydot", "asyncore","asynchat", "reportlab", "vobject",
"HTMLParser", "select", "mako", "poplib",
"imaplib", "smtplib", "email", "yaml", "DAV",
"uuid", "commands", "openerp",
],
"excludes" : ["Tkconstants","Tkinter","tcl"],
}
}
# TODO is it still necessary now that we don't use the library.zip file?
def data_files():
'''For Windows, we consider all the addons as data files.
It seems also that package_data below isn't honored by py2exe.'''
files = []
os.chdir('openerp')
for (dp, dn, names) in os.walk('addons'):
files.append((join('openerp',dp), map(lambda x: join('openerp', dp, x), names)))
os.chdir('..')
files.append(('openerp', [join('openerp', 'import_xml.rng'),]))
from py2exe_utils import opts
version_dash_incompatible = True
except ImportError:
opts = {}
if version_dash_incompatible:
version = version.split('-')[0]
# copy pytz/timzeone
# TODO check if we have to also copy dateutil's timezone data.
import pytz
# Make sure the layout of pytz hasn't changed
assert (pytz.__file__.endswith('__init__.pyc') or
pytz.__file__.endswith('__init__.py')), pytz.__file__
pytz_dir = os.path.dirname(pytz.__file__)
FILE_PATTERNS = \
r'.+\.(py|cfg|po|pot|mo|txt|rst|gif|png|jpg|ico|mako|html|js|css|htc|swf)$'
def find_data_files(source, patterns=FILE_PATTERNS):
file_matcher = re.compile(patterns, re.I)
out = []
for base, _, files in os.walk(source):
cur_files = []
for f in files:
if file_matcher.match(f):
cur_files.append(os.path.join(base, f))
if cur_files:
out.append(
(base, cur_files))
saved_dir = os.getcwd()
os.chdir(pytz_dir)
for dp, dn, names in os.walk('zoneinfo'):
files.append((join('pytz',dp), map(lambda x: join(pytz_dir, dp, x), names)))
os.chdir(saved_dir)
return out
return files
py2exe_data_files = data_files()
execfile(join('openerp', 'release.py'))
setup(name = name,
version = version,
description = description,
long_description = long_desc,
url = url,
author = author,
author_email = author_email,
classifiers = filter(None, classifiers.split("\n")),
license = license,
data_files = [
(join('man', 'man1'), ['man/openerp-server.1']),
(join('man', 'man5'), ['man/openerp_serverrc.5']),
('doc', filter(isfile, glob.glob('doc/*'))),
] + py2exe_data_files,
scripts = ['openerp-server'],
packages = find_packages(),
include_package_data = True,
package_data = {
'': ['*.yml', '*.xml', '*.po', '*.pot', '*.csv'],
},
dependency_links = ['http://download.gna.org/pychart/'],
install_requires = [
# We require the same version as caldav for lxml.
'lxml==2.1.5',
'mako',
'python-dateutil',
'psycopg2',
# TODO the pychart package we include in openerp corresponds to PyChart 1.37.
# It seems there is a single difference, which is a spurious print in generate_docs.py.
# It is probably safe to move to PyChart 1.39 (the latest one).
# (Let setup.py choose the latest one, and we should check we can remove pychart from
# our tree.)
'pychart',
'pydot',
'pytz',
'reportlab',
'caldav',
'pyyaml',
'pywebdav',
'feedparser',
'simplejson >= 2.0',
],
extras_require = {
'SSL' : ['pyopenssl'],
},
**py2exe_keywords
setup(
name=name,
version=version,
description=description,
long_description=long_description,
author=author,
author_email=author_email,
url=url,
download_url=download_url,
license=license,
install_requires=[
"CherryPy >= 3.1.2",
"Babel >= 0.9.6",
"simplejson >= 2.0.9",
"python-dateutil >= 1.4.1",
"pytz",
],
tests_require=[
'unittest2',
'mock',
],
test_suite = 'unittest2.collector',
zip_safe=False,
packages=[
'addons',
'addons.base',
'addons.base.controllers',
'addons.base_calendar',
'addons.base_hello',
'openerpweb',
],
classifiers=[
'Development Status :: 6 - Production/Stable',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Environment :: Web Environment',
'Topic :: Office/Business :: Financial',
],
scripts=['scripts/openerp-web'],
data_files=(find_data_files('addons')
+ opts.pop('data_files', [])
),
**opts
)