[MERGE] Merge with trunk upto revision no 733.
bzr revid: ysa@tinyerp.com-20110805101414-12wfpgz69la5qetr
@@ -5,7 +5,7 @@
|
||||
'active': True,
|
||||
'js' : [
|
||||
"static/lib/datejs/date-en-US.js",
|
||||
"static/lib/jquery/jquery-1.5.2.js",
|
||||
"static/lib/jquery/jquery-1.6.2.js",
|
||||
"static/lib/jquery.form/jquery.form.js",
|
||||
"static/lib/jquery.validate/jquery.validate.js",
|
||||
"static/lib/jquery.ba-bbq/jquery.ba-bbq.js",
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import base64, glob, os, re
|
||||
import base64
|
||||
import csv
|
||||
import glob
|
||||
import operator
|
||||
import os
|
||||
import re
|
||||
import simplejson
|
||||
import textwrap
|
||||
import xmlrpclib
|
||||
from xml.etree import ElementTree
|
||||
from cStringIO import StringIO
|
||||
|
||||
import operator
|
||||
import simplejson
|
||||
import cherrypy
|
||||
|
||||
import openerpweb
|
||||
import openerpweb.ast
|
||||
import openerpweb.nonliterals
|
||||
|
||||
import cherrypy
|
||||
import xmlrpclib
|
||||
import csv
|
||||
|
||||
# Should move to openerpweb.Xml2Json
|
||||
class Xml2Json:
|
||||
@@ -86,6 +90,27 @@ def concat_files(file_list):
|
||||
files_concat = "".join(files_content)
|
||||
return files_concat
|
||||
|
||||
home_template = textwrap.dedent("""<!DOCTYPE html>
|
||||
<html style="height: 100%%">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>OpenERP</title>
|
||||
%(javascript)s
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
QWeb = new QWeb2.Engine();
|
||||
openerp.init().base.webclient("oe");
|
||||
});
|
||||
</script>
|
||||
<link rel="shortcut icon" href="/base/static/src/img/favicon.ico" type="image/x-icon"/>
|
||||
%(css)s
|
||||
<!--[if lte IE 7]>
|
||||
<link rel="stylesheet" href="/base/static/src/css/base-ie7.css" type="text/css"/>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body id="oe" class="openerp"></body>
|
||||
</html>
|
||||
""")
|
||||
class WebClient(openerpweb.Controller):
|
||||
_cp_path = "/base/webclient"
|
||||
|
||||
@@ -114,41 +139,22 @@ class WebClient(openerpweb.Controller):
|
||||
return concat
|
||||
|
||||
@openerpweb.httprequest
|
||||
def home(self, req):
|
||||
template ="""<!DOCTYPE html>
|
||||
<html style="height: 100%%">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>OpenERP</title>
|
||||
%s
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
QWeb = new QWeb2.Engine();
|
||||
openerp.init().base.webclient("oe");
|
||||
});
|
||||
</script>
|
||||
<link rel="shortcut icon" href="/base/static/src/img/favicon.ico" type="image/x-icon"/>
|
||||
%s
|
||||
<!--[if lte IE 7]>
|
||||
<link rel="stylesheet" href="/base/static/src/css/base-ie7.css" type="text/css"/>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body id="oe" class="openerp"></body>
|
||||
</html>
|
||||
""".replace('\n'+' '*8,'\n')
|
||||
|
||||
def home(self, req, s_action=None):
|
||||
# script tags
|
||||
jslist = ['/base/webclient/js']
|
||||
if 1: # debug == 1
|
||||
jslist = manifest_glob(['base'], 'js')
|
||||
js = "\n ".join(['<script type="text/javascript" src="%s"></script>'%i for i in jslist])
|
||||
js = "\n ".join(['<script type="text/javascript" src="%s"></script>'%i for i in jslist])
|
||||
|
||||
# css tags
|
||||
csslist = ['/base/webclient/css']
|
||||
if 1: # debug == 1
|
||||
csslist = manifest_glob(['base'], 'css')
|
||||
css = "\n ".join(['<link rel="stylesheet" href="%s">'%i for i in csslist])
|
||||
r = template % (js, css)
|
||||
css = "\n ".join(['<link rel="stylesheet" href="%s">'%i for i in csslist])
|
||||
r = home_template % {
|
||||
'javascript': js,
|
||||
'css': css
|
||||
}
|
||||
return r
|
||||
|
||||
class Database(openerpweb.Controller):
|
||||
@@ -172,9 +178,13 @@ class Database(openerpweb.Controller):
|
||||
def create(self, req, fields):
|
||||
|
||||
params = dict(map(operator.itemgetter('name', 'value'), fields))
|
||||
create_attrs = operator.itemgetter(
|
||||
'super_admin_pwd', 'db_name', 'demo_data', 'db_lang', 'create_admin_pwd')(
|
||||
params)
|
||||
create_attrs = (
|
||||
params['super_admin_pwd'],
|
||||
params['db_name'],
|
||||
bool(params.get('demo_data')),
|
||||
params['db_lang'],
|
||||
params['create_admin_pwd']
|
||||
)
|
||||
|
||||
try:
|
||||
return req.session.proxy("db").create(*create_attrs)
|
||||
@@ -364,6 +374,11 @@ class Session(openerpweb.Controller):
|
||||
return None
|
||||
return saved_actions["actions"].get(key)
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def check(self, req):
|
||||
req.session.assert_valid()
|
||||
return None
|
||||
|
||||
def eval_context_and_domain(session, context, domain=None):
|
||||
e_context = session.eval_context(context)
|
||||
# should we give the evaluated context as an evaluation context to the domain?
|
||||
@@ -379,22 +394,21 @@ def load_actions_from_ir_values(req, key, key2, models, meta, context):
|
||||
for id, name, action in actions]
|
||||
|
||||
def clean_action(action, session):
|
||||
action.setdefault('flags', {})
|
||||
if action['type'] != 'ir.actions.act_window':
|
||||
return action
|
||||
# values come from the server, we can just eval them
|
||||
if isinstance(action.get('context', None), basestring):
|
||||
if isinstance(action.get('context'), basestring):
|
||||
action['context'] = eval(
|
||||
action['context'],
|
||||
session.evaluation_context()) or {}
|
||||
|
||||
if isinstance(action.get('domain', None), basestring):
|
||||
if isinstance(action.get('domain'), basestring):
|
||||
action['domain'] = eval(
|
||||
action['domain'],
|
||||
session.evaluation_context(
|
||||
action.get('context', {}))) or []
|
||||
if 'flags' not in action:
|
||||
# Set empty flags dictionary for web client.
|
||||
action['flags'] = dict()
|
||||
|
||||
return fix_view_modes(action)
|
||||
|
||||
def generate_views(action):
|
||||
|
||||
@@ -19,7 +19,7 @@ var QWeb2 = {
|
||||
if (context.template) {
|
||||
prefix += " - template['" + context.template + "']";
|
||||
}
|
||||
throw prefix + ": " + message;
|
||||
throw new Error(prefix + ": " + message);
|
||||
},
|
||||
trim: function(s, mode) {
|
||||
switch (mode) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Underscore.js 1.1.5
|
||||
// Underscore.js 1.1.7
|
||||
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the MIT license.
|
||||
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||
@@ -55,25 +55,26 @@
|
||||
module.exports = _;
|
||||
_._ = _;
|
||||
} else {
|
||||
root._ = _;
|
||||
// Exported as a string, for Closure Compiler "advanced" mode.
|
||||
root['_'] = _;
|
||||
}
|
||||
|
||||
// Current version.
|
||||
_.VERSION = '1.1.5';
|
||||
_.VERSION = '1.1.7';
|
||||
|
||||
// Collection Functions
|
||||
// --------------------
|
||||
|
||||
// The cornerstone, an `each` implementation, aka `forEach`.
|
||||
// Handles objects implementing `forEach`, arrays, and raw objects.
|
||||
// Handles objects with the built-in `forEach`, arrays, and raw objects.
|
||||
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
||||
var each = _.each = _.forEach = function(obj, iterator, context) {
|
||||
if (obj == null) return;
|
||||
if (nativeForEach && obj.forEach === nativeForEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (_.isNumber(obj.length)) {
|
||||
} else if (obj.length === +obj.length) {
|
||||
for (var i = 0, l = obj.length; i < l; i++) {
|
||||
if (iterator.call(context, obj[i], i, obj) === breaker) return;
|
||||
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
|
||||
}
|
||||
} else {
|
||||
for (var key in obj) {
|
||||
@@ -106,7 +107,7 @@
|
||||
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
||||
}
|
||||
each(obj, function(value, index, list) {
|
||||
if (!initial && index === 0) {
|
||||
if (!initial) {
|
||||
memo = value;
|
||||
initial = true;
|
||||
} else {
|
||||
@@ -168,7 +169,6 @@
|
||||
// Delegates to **ECMAScript 5**'s native `every` if available.
|
||||
// Aliased as `all`.
|
||||
_.every = _.all = function(obj, iterator, context) {
|
||||
iterator = iterator || _.identity;
|
||||
var result = true;
|
||||
if (obj == null) return result;
|
||||
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
||||
@@ -187,9 +187,9 @@
|
||||
if (obj == null) return result;
|
||||
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (result = iterator.call(context, value, index, list)) return breaker;
|
||||
if (result |= iterator.call(context, value, index, list)) return breaker;
|
||||
});
|
||||
return result;
|
||||
return !!result;
|
||||
};
|
||||
|
||||
// Determine if a given value is included in the array or object using `===`.
|
||||
@@ -208,7 +208,7 @@
|
||||
_.invoke = function(obj, method) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return _.map(obj, function(value) {
|
||||
return (method ? value[method] : value).apply(value, args);
|
||||
return (method.call ? method || value : value[method]).apply(value, args);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -252,10 +252,20 @@
|
||||
}), 'value');
|
||||
};
|
||||
|
||||
// Groups the object's values by a criterion produced by an iterator
|
||||
_.groupBy = function(obj, iterator) {
|
||||
var result = {};
|
||||
each(obj, function(value, index) {
|
||||
var key = iterator(value, index);
|
||||
(result[key] || (result[key] = [])).push(value);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Use a comparator function to figure out at what index an object should
|
||||
// be inserted so as to maintain order. Uses binary search.
|
||||
_.sortedIndex = function(array, obj, iterator) {
|
||||
iterator = iterator || _.identity;
|
||||
iterator || (iterator = _.identity);
|
||||
var low = 0, high = array.length;
|
||||
while (low < high) {
|
||||
var mid = (low + high) >> 1;
|
||||
@@ -268,7 +278,7 @@
|
||||
_.toArray = function(iterable) {
|
||||
if (!iterable) return [];
|
||||
if (iterable.toArray) return iterable.toArray();
|
||||
if (_.isArray(iterable)) return iterable;
|
||||
if (_.isArray(iterable)) return slice.call(iterable);
|
||||
if (_.isArguments(iterable)) return slice.call(iterable);
|
||||
return _.values(iterable);
|
||||
};
|
||||
@@ -317,8 +327,7 @@
|
||||
|
||||
// Return a version of the array that does not contain the specified value(s).
|
||||
_.without = function(array) {
|
||||
var values = slice.call(arguments, 1);
|
||||
return _.filter(array, function(value){ return !_.include(values, value); });
|
||||
return _.difference(array, slice.call(arguments, 1));
|
||||
};
|
||||
|
||||
// Produce a duplicate-free version of the array. If the array has already
|
||||
@@ -331,9 +340,15 @@
|
||||
}, []);
|
||||
};
|
||||
|
||||
// Produce an array that contains the union: each distinct element from all of
|
||||
// the passed-in arrays.
|
||||
_.union = function() {
|
||||
return _.uniq(_.flatten(arguments));
|
||||
};
|
||||
|
||||
// Produce an array that contains every item shared between all the
|
||||
// passed-in arrays.
|
||||
_.intersect = function(array) {
|
||||
// passed-in arrays. (Aliased as "intersect" for back-compat.)
|
||||
_.intersection = _.intersect = function(array) {
|
||||
var rest = slice.call(arguments, 1);
|
||||
return _.filter(_.uniq(array), function(item) {
|
||||
return _.every(rest, function(other) {
|
||||
@@ -342,6 +357,12 @@
|
||||
});
|
||||
};
|
||||
|
||||
// Take the difference between one array and another.
|
||||
// Only the elements present in just the first array will remain.
|
||||
_.difference = function(array, other) {
|
||||
return _.filter(array, function(value){ return !_.include(other, value); });
|
||||
};
|
||||
|
||||
// Zip together multiple lists into a single array -- elements that share
|
||||
// an index go together.
|
||||
_.zip = function() {
|
||||
@@ -408,8 +429,9 @@
|
||||
// Create a function bound to a given object (assigning `this`, and arguments,
|
||||
// optionally). Binding with arguments is also known as `curry`.
|
||||
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
|
||||
// We check for `func.bind` first, to fail fast when `func` is undefined.
|
||||
_.bind = function(func, obj) {
|
||||
if (nativeBind && func.bind === nativeBind) return func.bind.apply(func, slice.call(arguments, 1));
|
||||
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
||||
var args = slice.call(arguments, 2);
|
||||
return function() {
|
||||
return func.apply(obj, args.concat(slice.call(arguments)));
|
||||
@@ -428,7 +450,7 @@
|
||||
// Memoize an expensive function by storing its results.
|
||||
_.memoize = function(func, hasher) {
|
||||
var memo = {};
|
||||
hasher = hasher || _.identity;
|
||||
hasher || (hasher = _.identity);
|
||||
return function() {
|
||||
var key = hasher.apply(this, arguments);
|
||||
return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
||||
@@ -502,13 +524,21 @@
|
||||
var funcs = slice.call(arguments);
|
||||
return function() {
|
||||
var args = slice.call(arguments);
|
||||
for (var i=funcs.length-1; i >= 0; i--) {
|
||||
for (var i = funcs.length - 1; i >= 0; i--) {
|
||||
args = [funcs[i].apply(this, args)];
|
||||
}
|
||||
return args[0];
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will only be executed after being called N times.
|
||||
_.after = function(times, func) {
|
||||
return function() {
|
||||
if (--times < 1) { return func.apply(this, arguments); }
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// Object Functions
|
||||
// ----------------
|
||||
|
||||
@@ -529,13 +559,19 @@
|
||||
// Return a sorted list of the function names available on the object.
|
||||
// Aliased as `methods`
|
||||
_.functions = _.methods = function(obj) {
|
||||
return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
|
||||
var names = [];
|
||||
for (var key in obj) {
|
||||
if (_.isFunction(obj[key])) names.push(key);
|
||||
}
|
||||
return names.sort();
|
||||
};
|
||||
|
||||
// Extend a given object with all the properties in passed-in object(s).
|
||||
_.extend = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) obj[prop] = source[prop];
|
||||
for (var prop in source) {
|
||||
if (source[prop] !== void 0) obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
@@ -543,7 +579,9 @@
|
||||
// Fill in a given object with default properties.
|
||||
_.defaults = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) if (obj[prop] == null) obj[prop] = source[prop];
|
||||
for (var prop in source) {
|
||||
if (obj[prop] == null) obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
@@ -577,6 +615,7 @@
|
||||
if (b._chain) b = b._wrapped;
|
||||
// One of them implements an isEqual()?
|
||||
if (a.isEqual) return a.isEqual(b);
|
||||
if (b.isEqual) return b.isEqual(a);
|
||||
// Check dates' integer values.
|
||||
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
|
||||
// Both are NaN?
|
||||
@@ -618,6 +657,11 @@
|
||||
return toString.call(obj) === '[object Array]';
|
||||
};
|
||||
|
||||
// Is a given variable an object?
|
||||
_.isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
};
|
||||
|
||||
// Is a given variable an arguments object?
|
||||
_.isArguments = function(obj) {
|
||||
return !!(obj && hasOwnProperty.call(obj, 'callee'));
|
||||
|
||||
@@ -194,25 +194,25 @@ ul.db_options li {
|
||||
}
|
||||
|
||||
table.db_option_table input.required {
|
||||
background-color: #D2D2FF;
|
||||
background-color: #D2D2FF !important;
|
||||
}
|
||||
|
||||
.db_option_table input[type="text"], .db_option_table input[type="password"], .db_option_table select {
|
||||
width: 300px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.option_string {
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
label.error {
|
||||
float: none;
|
||||
color: red;
|
||||
float: none;
|
||||
color: red;
|
||||
padding-left: .5em;
|
||||
vertical-align: top;
|
||||
}
|
||||
@@ -506,6 +506,7 @@ label.error {
|
||||
}
|
||||
|
||||
/* Main Application */
|
||||
.openerp .oe-main-content,
|
||||
.openerp .oe-application {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
|
||||
@@ -161,6 +161,8 @@ openerp.base.Session = openerp.base.Widget.extend( /** @lends openerp.base.Sessi
|
||||
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();
|
||||
@@ -171,12 +173,18 @@ openerp.base.Session = openerp.base.Widget.extend( /** @lends openerp.base.Sessi
|
||||
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() {
|
||||
this.uid = this.get_cookie('uid');
|
||||
this.session_id = this.get_cookie('session_id');
|
||||
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() {});
|
||||
},
|
||||
/**
|
||||
@@ -858,7 +866,7 @@ openerp.base.Menu = openerp.base.Widget.extend({
|
||||
init: function(parent, element_id, secondary_menu_id) {
|
||||
this._super(parent, element_id);
|
||||
this.secondary_menu_id = secondary_menu_id;
|
||||
this.$secondary_menu = $("#" + secondary_menu_id);
|
||||
this.$secondary_menu = $("#" + secondary_menu_id).hide();
|
||||
this.menu = false;
|
||||
},
|
||||
start: function() {
|
||||
@@ -917,7 +925,7 @@ openerp.base.Menu = openerp.base.Widget.extend({
|
||||
this.on_menu_action_loaded);
|
||||
}
|
||||
|
||||
$('.active', this.$element.add(this.$secondary_menu)).removeClass('active');
|
||||
$('.active', this.$element.add(this.$secondary_menu.show())).removeClass('active');
|
||||
$parent.addClass('active');
|
||||
$menu.addClass('active');
|
||||
$menu.parent('h4').addClass('active');
|
||||
@@ -988,14 +996,64 @@ openerp.base.WebClient = openerp.base.Widget.extend({
|
||||
|
||||
// if using saved actions, load the action and give it to action manager
|
||||
var parameters = jQuery.deparam(jQuery.param.querystring());
|
||||
if(parameters["s_action"] != undefined) {
|
||||
var key = parseInt(parameters["s_action"]);
|
||||
if (parameters["s_action"] != undefined) {
|
||||
var key = parseInt(parameters["s_action"], 10);
|
||||
var self = this;
|
||||
this.rpc("/base/session/get_session_action", {key:key}, function(action) {
|
||||
self.action_manager.do_action(action);
|
||||
});
|
||||
} else if (openerp._modules_loaded) { // TODO: find better option than this
|
||||
this.load_url_state()
|
||||
} else {
|
||||
this.session.on_modules_loaded.add({
|
||||
callback: $.proxy(this, 'load_url_state'),
|
||||
unique: true,
|
||||
position: 'last'
|
||||
})
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Loads state from URL if any, or checks if there is a home action and
|
||||
* loads that, assuming we're at the index
|
||||
*/
|
||||
load_url_state: function () {
|
||||
var self = this;
|
||||
// TODO: add actual loading if there is url state to unpack, test on window.location.hash
|
||||
|
||||
// not logged in
|
||||
if (!this.session.uid) { return; }
|
||||
var ds = new openerp.base.DataSetSearch(this, 'res.users');
|
||||
ds.read_ids([this.session.uid], ['action_id'], function (users) {
|
||||
var home_action = users[0].action_id;
|
||||
if (!home_action) {
|
||||
self.default_home();
|
||||
return;
|
||||
}
|
||||
self.execute_home_action(home_action[0], ds);
|
||||
})
|
||||
},
|
||||
default_home: function () { },
|
||||
/**
|
||||
* Bundles the execution of the home action
|
||||
*
|
||||
* @param {Number} action action id
|
||||
* @param {openerp.base.DataSet} dataset action executor
|
||||
*/
|
||||
execute_home_action: function (action, dataset) {
|
||||
var self = this;
|
||||
this.rpc('/base/action/load', {
|
||||
action_id: action,
|
||||
context: dataset.get_context()
|
||||
}, function (meh) {
|
||||
var action = meh.result;
|
||||
action.context = _.extend(action.context || {}, {
|
||||
active_id: false,
|
||||
active_ids: [false],
|
||||
active_model: dataset.model
|
||||
});
|
||||
self.action_manager.do_action(action);
|
||||
});
|
||||
},
|
||||
on_menu_action: function(action) {
|
||||
this.action_manager.do_action(action);
|
||||
},
|
||||
|
||||
@@ -318,13 +318,13 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data
|
||||
context: this.get_context()
|
||||
}, callback, error_callback);
|
||||
},
|
||||
write: function (id, data, callback) {
|
||||
write: function (id, data, callback, error_callback) {
|
||||
return this.rpc('/base/dataset/save', {
|
||||
model: this.model,
|
||||
id: id,
|
||||
data: data,
|
||||
context: this.get_context()
|
||||
}, callback);
|
||||
}, callback, error_callback);
|
||||
},
|
||||
unlink: function(ids, callback, error_callback) {
|
||||
var self = this;
|
||||
@@ -409,6 +409,14 @@ openerp.base.DataSetStatic = openerp.base.DataSet.extend({
|
||||
}
|
||||
});
|
||||
openerp.base.DataSetSearch = openerp.base.DataSet.extend({
|
||||
/**
|
||||
* @constructs
|
||||
*
|
||||
* @param {Object} parent
|
||||
* @param {String} model
|
||||
* @param {Object} context
|
||||
* @param {Array} domain
|
||||
*/
|
||||
init: function(parent, model, context, domain) {
|
||||
this._super(parent, model, context);
|
||||
this.domain = domain || [];
|
||||
|
||||
@@ -546,7 +546,7 @@ openerp.base.form.compute_domain = function(expr, fields) {
|
||||
this.log("Unsupported operator in modifiers :", op);
|
||||
}
|
||||
}
|
||||
return _.all(stack);
|
||||
return _.all(stack, _.identity);
|
||||
};
|
||||
|
||||
openerp.base.form.Widget = openerp.base.Widget.extend({
|
||||
@@ -788,6 +788,14 @@ openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
|
||||
}
|
||||
// Actual label widgets should not have a false and have type label
|
||||
return QWeb.render(this.template, {widget: this});
|
||||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
var self = this;
|
||||
this.$element.find("label").dblclick(function() {
|
||||
var widget = self['for'] || self;
|
||||
console.log(widget.element_id , widget);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -213,6 +213,9 @@ openerp.base.list.editable = function (openerp) {
|
||||
save_row: function (edit_next) {
|
||||
var self = this;
|
||||
this.edition_form.do_save(function (result) {
|
||||
if (result.created && !self.edition_index) {
|
||||
self.edition_index = self.dataset.index;
|
||||
}
|
||||
self.cancel_pending_edition().then(function () {
|
||||
$(self).trigger('saved', [self.dataset]);
|
||||
if (!edit_next) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
openerp.base.views = function(openerp) {
|
||||
|
||||
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) {
|
||||
@@ -14,24 +15,7 @@ openerp.base.ActionManager = openerp.base.Widget.extend({
|
||||
// Will use parent to find it when implementation will be done.
|
||||
this.session.action_manager = this;
|
||||
},
|
||||
/**
|
||||
* Process an action
|
||||
* Supported actions: act_window
|
||||
*/
|
||||
action_window: function() {
|
||||
},
|
||||
action_window_close: function() {
|
||||
},
|
||||
action_server: function() {
|
||||
},
|
||||
action_url: function() {
|
||||
},
|
||||
action_report: function() {
|
||||
},
|
||||
action_client: function() {
|
||||
},
|
||||
do_action: function(action, on_closed) {
|
||||
var self = this;
|
||||
action.flags = _.extend({
|
||||
sidebar : action.target != 'new',
|
||||
search_view : action.target != 'new',
|
||||
@@ -41,54 +25,64 @@ openerp.base.ActionManager = openerp.base.Widget.extend({
|
||||
pager : action.target != 'new'
|
||||
}, action.flags || {});
|
||||
// instantiate the right controllers by understanding the action
|
||||
switch (action.type) {
|
||||
case 'ir.actions.act_window':
|
||||
if (!action.target && this.current_dialog) {
|
||||
action.flags.new_window = true;
|
||||
if (!(action.type in this)) {
|
||||
console.log("Action manager can't handle action of type " + action.type, action);
|
||||
return;
|
||||
}
|
||||
this[action.type](action, on_closed);
|
||||
},
|
||||
|
||||
'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;
|
||||
}
|
||||
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.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();
|
||||
}
|
||||
if (action.target == 'new') {
|
||||
var dialog = this.current_dialog = new openerp.base.ActionDialog(this, { title: action.name, width: '90%' });
|
||||
if (on_closed) {
|
||||
dialog.close_callback = on_closed;
|
||||
}
|
||||
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.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();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this.viewmanager) {
|
||||
this.viewmanager.stop();
|
||||
}
|
||||
this.viewmanager = new openerp.base.ViewManagerAction(this, this.element_id, action);
|
||||
this.viewmanager.start();
|
||||
}
|
||||
break;
|
||||
case 'ir.actions.act_window_close':
|
||||
this.close_dialog();
|
||||
break;
|
||||
case 'ir.actions.server':
|
||||
this.rpc('/base/action/run', {
|
||||
action_id: action.id,
|
||||
context: {active_id: 66, active_ids: [66], active_model: 'ir.ui.menu'}
|
||||
}).then(function (action) {
|
||||
self.do_action(action, on_closed)
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log("Action manager can't handle action of type " + action.type, action);
|
||||
});
|
||||
} else {
|
||||
if (this.viewmanager) {
|
||||
this.viewmanager.stop();
|
||||
}
|
||||
this.viewmanager = new openerp.base.ViewManagerAction(this, this.element_id, action);
|
||||
this.viewmanager.start();
|
||||
}
|
||||
},
|
||||
close_dialog: function() {
|
||||
'ir.actions.act_window_close': function (action, on_closed) {
|
||||
this.close_dialog();
|
||||
},
|
||||
'ir.actions.server': function (action, on_closed) {
|
||||
var self = this;
|
||||
this.rpc('/base/action/run', {
|
||||
action_id: action.id,
|
||||
context: {active_id: 66, active_ids: [66], active_model: 'ir.ui.menu'}
|
||||
}).then(function (action) {
|
||||
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;
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="demo_data">Load Demonstration data:</label></td>
|
||||
<td><input type="checkbox" name="demo_data" checked='checked'/></td>
|
||||
<td><input type="checkbox" name="demo_data"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="db_lang">Default language:</label></td>
|
||||
@@ -652,8 +652,7 @@
|
||||
<t t-name="WidgetLabel">
|
||||
<label t-att-for="widget.element_id + '_field'"
|
||||
t-att-class="'oe_form_label' + (widget.help ? '_help' : '')"
|
||||
t-att-title="widget.help"
|
||||
t-att-ondblclick="'console.log(\'' + widget.element_id + '\', openerp.screen.' + widget.element_id + ')'">
|
||||
t-att-title="widget.help">
|
||||
<t t-esc="widget.string"/>
|
||||
<span t-if="widget.help">?</span>
|
||||
<t t-if="widget.string and widget.node.tag != 'label'">:</t>
|
||||
|
||||
@@ -21,6 +21,9 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
|
||||
start: function() {
|
||||
this.rpc("/base_calendar/calendarview/load", {"model": this.model, "view_id": this.view_id, 'toolbar': true}, this.on_loaded);
|
||||
},
|
||||
stop: function() {
|
||||
scheduler.clearAll();
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
this.calendar_fields = {};
|
||||
this.ids = this.dataset.ids;
|
||||
@@ -64,41 +67,41 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
|
||||
this.sidebar.add_toolbar(data.fields_view.toolbar);
|
||||
this.set_common_sidebar_sections(this.sidebar);
|
||||
this.sidebar.do_unfold();
|
||||
this.sidebar.do_fold.add_last(this.resize_scheduler);
|
||||
this.sidebar.do_unfold.add_last(this.resize_scheduler);
|
||||
this.sidebar.do_toggle.add_last(this.resize_scheduler);
|
||||
this.sidebar.do_fold.add_last(this.refresh_scheduler);
|
||||
this.sidebar.do_unfold.add_last(this.refresh_scheduler);
|
||||
this.sidebar.do_toggle.add_last(this.refresh_scheduler);
|
||||
}
|
||||
|
||||
this.init_scheduler();
|
||||
this.load_scheduler();
|
||||
this.has_been_loaded.resolve();
|
||||
if (this.dataset.ids.length) {
|
||||
this.dataset.read_ids(this.dataset.ids, _.keys(this.fields), this.on_events_loaded);
|
||||
}
|
||||
},
|
||||
init_scheduler: function() {
|
||||
var self = this;
|
||||
scheduler.clearAll();
|
||||
scheduler.config.api_date = "%Y-%m-%d %H:%M:%S";
|
||||
//scheduler.config.details_on_dblclick = true;
|
||||
//scheduler.config.details_on_create = true;
|
||||
scheduler.keys.edit_cancel = 27;
|
||||
|
||||
if (this.fields[this.date_start]['type'] == 'time') {
|
||||
scheduler.config.xml_date = "%H:%M:%S";
|
||||
} else {
|
||||
scheduler.config.xml_date = "%Y-%m-%d %H:%M:%S";
|
||||
scheduler.config.xml_date = "%Y-%m-%d %H:%i";
|
||||
}
|
||||
|
||||
this.mode = this.mode || 'month';
|
||||
|
||||
scheduler.config.api_date = "%Y-%m-%d %H:%i";
|
||||
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;
|
||||
|
||||
// Initialize Sceduler
|
||||
this.mode = this.mode || 'month';
|
||||
scheduler.init('openerp_scheduler', null, this.mode);
|
||||
|
||||
scheduler.detachAllEvents();
|
||||
scheduler.attachEvent('onEventAdded', this.do_create_event);
|
||||
scheduler.attachEvent('onEventDeleted', this.do_delete_event);
|
||||
scheduler.attachEvent('onEventChanged', this.do_save_event);
|
||||
|
||||
scheduler.renderCalendar({
|
||||
this.mini_calendar = scheduler.renderCalendar({
|
||||
container: this.sidebar.navigator.element_id,
|
||||
navigation: true,
|
||||
date: scheduler._date,
|
||||
@@ -107,9 +110,12 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
|
||||
}
|
||||
});
|
||||
},
|
||||
resize_scheduler: function() {
|
||||
refresh_scheduler: function() {
|
||||
scheduler.setCurrentView(scheduler._date);
|
||||
},
|
||||
refresh_minical: function() {
|
||||
scheduler.updateCalendar(this.mini_calendar);
|
||||
},
|
||||
load_scheduler: function() {
|
||||
var self = this;
|
||||
this.dataset.read_slice([], 0, false, function(events) {
|
||||
@@ -119,14 +125,14 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
|
||||
'/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.schedule_events(events);
|
||||
self.on_events_loaded(events);
|
||||
});
|
||||
} else {
|
||||
self.schedule_events(events);
|
||||
self.on_events_loaded(events);
|
||||
}
|
||||
});
|
||||
},
|
||||
schedule_events: function(events) {
|
||||
on_events_loaded: function(events) {
|
||||
var self = this;
|
||||
scheduler.clearAll();
|
||||
|
||||
@@ -163,9 +169,9 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
|
||||
}
|
||||
res_events.push(this.convert_event(evt));
|
||||
}
|
||||
|
||||
scheduler.parse(res_events, 'json');
|
||||
this.resize_scheduler();
|
||||
this.refresh_scheduler();
|
||||
this.refresh_minical();
|
||||
this.sidebar.responsible.on_events_loaded(sidebar_items);
|
||||
},
|
||||
convert_event: function(event) {
|
||||
@@ -254,21 +260,27 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
|
||||
var id = parseInt(r.result, 10);
|
||||
self.dataset.ids.push(id);
|
||||
scheduler.changeEventId(event_id, id);
|
||||
self.refresh_minical();
|
||||
}, function(r, event) {
|
||||
// TODO: open form view
|
||||
self.notification.warn(self.name, "Could not create event");
|
||||
event.preventDefault();
|
||||
});
|
||||
},
|
||||
do_save_event: function(event_id, event_obj) {
|
||||
var self = this,
|
||||
data = this.get_event_data(event_obj);
|
||||
this.dataset.write(event_id, data);
|
||||
this.dataset.write(parseInt(event_id, 10), data, function() {
|
||||
self.refresh_minical();
|
||||
});
|
||||
},
|
||||
do_delete_event: function(event_id, event_obj) {
|
||||
var self = this;
|
||||
this.dataset.unlink(event_id, function(r) {
|
||||
});
|
||||
// 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() {
|
||||
self.refresh_minical();
|
||||
}));
|
||||
}
|
||||
},
|
||||
get_event_data: function(event_obj) {
|
||||
var data = {
|
||||
@@ -302,9 +314,7 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({
|
||||
// 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, function(events) {
|
||||
self.schedule_events(events);
|
||||
});
|
||||
self.dataset.read_slice(_.keys(self.fields), 0, self.limit, self.on_events_loaded);
|
||||
});
|
||||
},
|
||||
do_show: function () {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<t t-name="CalendarView">
|
||||
<h3 class="title"><t t-esc="fields_view.arch.attrs.string"/></h3>
|
||||
<div id="openerp_scheduler" class="dhx_cal_container" style="height: 1000px;">
|
||||
<div id="openerp_scheduler" class="dhx_cal_container" style="height: 600px;">
|
||||
<div class="dhx_cal_navline">
|
||||
<div class="dhx_cal_prev_button"/>
|
||||
<div class="dhx_cal_next_button"/>
|
||||
<div class="dhx_cal_today_button"/>
|
||||
<div class="dhx_cal_date"/>
|
||||
<div class="dhx_cal_tab" name="day_tab" style="right:204px;"/>
|
||||
<div class="dhx_cal_tab" name="week_tab" style="right:140px;"/>
|
||||
<div class="dhx_cal_tab" name="month_tab" style="right:76px;"/>
|
||||
<div class="dhx_cal_prev_button">&nbsp;</div>
|
||||
<div class="dhx_cal_next_button">&nbsp;</div>
|
||||
<div class="dhx_cal_today_button" t-opentag="true"></div>
|
||||
<div class="dhx_cal_date" t-opentag="true"></div>
|
||||
<div class="dhx_cal_tab" name="day_tab" style="right:204px;" t-opentag="true"></div>
|
||||
<div class="dhx_cal_tab" name="week_tab" style="right:140px;" t-opentag="true"></div>
|
||||
<div class="dhx_cal_tab" name="month_tab" style="right:76px;" t-opentag="true"></div>
|
||||
</div>
|
||||
<div class="dhx_cal_header">
|
||||
</div>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
#!/usr/bin/env python
|
||||
import controllers
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import openerpweb
|
||||
|
||||
class Widgets(openerpweb.openerpweb.Controller):
|
||||
_cp_path = '/base_dashboard/widgets'
|
||||
|
||||
@openerpweb.httprequest
|
||||
def content(self, request, widget_id):
|
||||
return request.session.model('res.widget').read(
|
||||
[widget_id], ['content'], request.session.eval_context(request.context)
|
||||
)[0]['content']
|
||||
@@ -19,6 +19,7 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
.openerp a.oe-dashboard-action-rename {
|
||||
float: left;
|
||||
padding-right: 4px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
@@ -46,10 +47,6 @@
|
||||
float: right;
|
||||
}
|
||||
|
||||
.openerp .oe-dashboard-action .oe-dashboard-action-content {
|
||||
/*padding: 0.4em;*/
|
||||
}
|
||||
|
||||
.openerp .oe-dashboard .ui-sortable-placeholder {
|
||||
border: 1px dotted black;
|
||||
visibility: visible !important;
|
||||
@@ -131,6 +128,110 @@
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.openerp .oe-dashboard-config-overview .oe-config-progress {
|
||||
border: 1px inset #d9d9d9;
|
||||
background-color: #f2f2f2;
|
||||
padding: 0.5em;
|
||||
-webkit-border-radius: 0.5em;
|
||||
-moz-border-radius: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
.openerp .oe-dashboard-config-overview .oe-config-progress-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
.openerp .oe-dashboard-config-overview dt {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
border-top: 1px solid #eee;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
.openerp .oe-dashboard-config-overview dt:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
.openerp .oe-dashboard-config-overview dd {
|
||||
margin-left: 1em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
.openerp .oe-dashboard-config-overview ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.openerp .oe-dashboard-config-overview li {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
text-indent: 20px;
|
||||
}
|
||||
.openerp .oe-dashboard-config-overview li:hover {
|
||||
cursor: pointer;
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.openerp .oe-dashboard-config-overview li.oe-done {
|
||||
cursor: default;
|
||||
color: #999999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.openerp .oe-dashboard-layout_2-1 .index_0 .oe-dashboard-config-overview ul,
|
||||
.openerp .oe-dashboard-layout_1-2 .index_1 .oe-dashboard-config-overview ul {
|
||||
-moz-column-count: 2;
|
||||
-webkit-column-count: 2;
|
||||
column-count: 2;
|
||||
}
|
||||
.openerp .oe-dashboard-config-overview li span.ui-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.openerp .oe-dashboard-home-tile {
|
||||
text-align: center;
|
||||
margin: 10px;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: 3px 3px 5px 3px #DADDDD;
|
||||
-moz-box-shadow: 3px 3px 5px 3px #DADDDD;
|
||||
box-shadow: 3px 3px 5px 3px #DADDDD;
|
||||
}
|
||||
.openerp .oe-dashboard-home-tile span {
|
||||
display: block;
|
||||
padding: 0 0 15px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
color: #555;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.openerp .oe-dashboard-home-tile-icon {
|
||||
height: 100px;
|
||||
}
|
||||
.openerp .oe-dashboard-home-tile-icon img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.openerp .oe-dashboard-home-tile-icon img.hover {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe-dashboard-home-tile:hover {
|
||||
background-color: #fafafa;
|
||||
-webkit-box-shadow: 3px 3px 5px 3px #979797;
|
||||
-moz-box-shadow: 3px 3px 5px 3px #979797;
|
||||
box-shadow: 3px 3px 5px 3px #979797;
|
||||
}
|
||||
.openerp .oe-dashboard-home-tile:hover img {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe-dashboard-home-tile:hover img.hover {
|
||||
display: block;
|
||||
}
|
||||
.openerp .oe-dashboard-home-tile:hover span {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.openerp .oe-dashboard-action .view-manager-main-content {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
|
||||
type : 'ir.actions.act_window',
|
||||
limit : 80,
|
||||
auto_search : true,
|
||||
domain : [['type', '=', 'ir.actions.act_window']],
|
||||
flags : {
|
||||
sidebar : false,
|
||||
views_switcher : false,
|
||||
@@ -74,22 +73,24 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
|
||||
// TODO: create a Dialog controller which optionally takes an action
|
||||
// Should set width & height automatically and take buttons & views callback
|
||||
var dialog_id = _.uniqueId('act_window_dialog');
|
||||
var $dialog = $('<div id=' + dialog_id + '>').dialog({
|
||||
modal : true,
|
||||
title : 'Actions',
|
||||
width : 800,
|
||||
height : 600,
|
||||
buttons : {
|
||||
Cancel : function() {
|
||||
$(this).dialog('destroy');
|
||||
},
|
||||
Add : function() {
|
||||
self.do_add_widget(action_manager);
|
||||
$(this).dialog('destroy');
|
||||
}
|
||||
}
|
||||
});
|
||||
new openerp.base.ViewManagerAction(this, dialog_id, action).start();
|
||||
var action_manager = new openerp.base.ActionManager(this, dialog_id);
|
||||
$('<div id=' + dialog_id + '>').dialog({
|
||||
modal : true,
|
||||
title : 'Actions',
|
||||
width : 800,
|
||||
height : 600,
|
||||
buttons : {
|
||||
Cancel : function() {
|
||||
$(this).dialog('destroy');
|
||||
},
|
||||
Add : function() {
|
||||
self.do_add_widget(action_manager);
|
||||
$(this).dialog('destroy');
|
||||
}
|
||||
}
|
||||
});
|
||||
action_manager.start();
|
||||
action_manager.do_action(action);
|
||||
// TODO: should bind ListView#select_record in order to catch record clicking
|
||||
},
|
||||
do_add_widget : function(action_manager) {
|
||||
@@ -148,7 +149,7 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
|
||||
modal: true,
|
||||
title: 'Edit Layout',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
height: 'auto'
|
||||
}).html(QWeb.render('DashBoard.layouts', qdict));
|
||||
$dialog.find('li').click(function() {
|
||||
var layout = $(this).attr('data-layout');
|
||||
@@ -227,8 +228,9 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
|
||||
action_buttons : false,
|
||||
pager: false
|
||||
};
|
||||
new openerp.base.ViewManagerAction(this,
|
||||
this.view.element_id + '_action_' + action.id, action).start();
|
||||
new openerp.base.ActionManager(
|
||||
this, this.view.element_id + '_action_' + action.id)
|
||||
.do_action(action);
|
||||
},
|
||||
render: function() {
|
||||
// We should start with three columns available
|
||||
@@ -273,4 +275,141 @@ openerp.base.form.DashBoardLegacy = openerp.base.form.DashBoard.extend({
|
||||
openerp.base.form.widgets.add('hpaned', 'openerp.base.form.DashBoardLegacy');
|
||||
openerp.base.form.widgets.add('vpaned', 'openerp.base.form.DashBoardLegacy');
|
||||
openerp.base.form.widgets.add('board', 'openerp.base.form.DashBoard');
|
||||
|
||||
openerp.base.client_actions.add(
|
||||
'board.config.overview', 'openerp.base_dashboard.ConfigOverview'
|
||||
);
|
||||
if (!openerp.base_dashboard) {
|
||||
openerp.base_dashboard = {};
|
||||
}
|
||||
openerp.base_dashboard.ConfigOverview = openerp.base.View.extend({
|
||||
init: function (parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
this.dataset = new openerp.base.DataSetSearch(
|
||||
this, 'ir.actions.todo');
|
||||
this.dataset.domain = [['type', '=', 'manual']];
|
||||
},
|
||||
start: function () {
|
||||
$.when(this.dataset.read_slice(['state', 'action_id', 'category_id']),
|
||||
this.dataset.call('progress'))
|
||||
.then(this.on_records_loaded);
|
||||
},
|
||||
on_records_loaded: function (read_response, progress_response) {
|
||||
var records = read_response[0].records,
|
||||
progress = progress_response[0];
|
||||
|
||||
var grouped_todos = _(records).chain()
|
||||
.map(function (record) {
|
||||
return {
|
||||
id: record.id,
|
||||
name: record.action_id[1],
|
||||
done: record.state !== 'open',
|
||||
to_do: record.state === 'open',
|
||||
category: record['category_id'][1] || "Uncategorized"
|
||||
}
|
||||
})
|
||||
.groupBy(function (record) {return record.category})
|
||||
.value();
|
||||
this.$element.html(QWeb.render('ConfigOverview', {
|
||||
completion: 100 * progress.done / progress.total,
|
||||
groups: grouped_todos
|
||||
}));
|
||||
var $progress = this.$element.find('div.oe-config-progress-bar');
|
||||
$progress.progressbar({value: $progress.data('completion')});
|
||||
|
||||
var self = this;
|
||||
this.$element.find('div.oe-dashboard-config-overview ul')
|
||||
.delegate('input', 'click', function (e) {
|
||||
// switch todo status
|
||||
e.stopImmediatePropagation();
|
||||
var new_state = this.checked ? 'done' : 'open',
|
||||
todo_id = parseInt($(this).val(), 10);
|
||||
self.dataset.write(todo_id, {state: new_state}, function () {
|
||||
self.start();
|
||||
});
|
||||
})
|
||||
.delegate('li:not(.oe-done)', 'click', function () {
|
||||
self.execute_action({
|
||||
type: 'object',
|
||||
name: 'action_launch'
|
||||
}, self.dataset,
|
||||
self.session.action_manager,
|
||||
$(this).data('id'), function () {
|
||||
// after action popup closed, refresh configuration
|
||||
// thingie
|
||||
self.start();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.client_actions.add(
|
||||
'board.home.applications', 'openerp.base_dashboard.ApplicationTiles');
|
||||
openerp.base_dashboard.ApplicationTiles = openerp.base.View.extend({
|
||||
init: function (parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
this.dataset = new openerp.base.DataSetSearch(
|
||||
this, 'ir.ui.menu', null, [['parent_id', '=', false]]);
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
this.dataset.read_slice(
|
||||
['name', 'web_icon_data', 'web_icon_hover_data'],
|
||||
null, null, function (applications) {
|
||||
// Create a matrix of 3*x applications
|
||||
var rows = [];
|
||||
while (applications.length) {
|
||||
rows.push(applications.splice(0, 3));
|
||||
}
|
||||
self.$element
|
||||
.append(QWeb.render(
|
||||
'ApplicationTiles', {rows: rows}))
|
||||
.find('.oe-dashboard-home-tile')
|
||||
.click(function () {
|
||||
var $this = $(this);
|
||||
$this.closest('.openerp')
|
||||
.find('.menu a[data-menu=' + $this.data('menuid') + ']')
|
||||
.click();});
|
||||
});
|
||||
}
|
||||
});
|
||||
openerp.base.client_actions.add(
|
||||
'board.home.widgets', 'openerp.base_dashboard.Widgets');
|
||||
openerp.base_dashboard.Widgets = openerp.base.View.extend({
|
||||
init: function (parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
this.user_widgets = new openerp.base.DataSetSearch(
|
||||
this, 'res.widget.user', null,
|
||||
['|', ['user_id', '=', false],
|
||||
['user_id', '=', parseInt(this.session.uid, 10)]]);
|
||||
this.widgets = new openerp.base.DataSetSearch(this, 'res.widget');
|
||||
},
|
||||
start: function () {
|
||||
this.user_widgets.read_slice(['widget_id', 'user_id'], null, null,
|
||||
this.on_widgets_list_loaded);
|
||||
},
|
||||
on_widgets_list_loaded: function (user_widgets) {
|
||||
var self = this;
|
||||
var widget_user = {};
|
||||
_(user_widgets).each(function (widget) {
|
||||
widget['widget_id'] = widget['widget_id'][0];
|
||||
widget_user[widget['widget_id']] = {
|
||||
removable: widget['user_id'] !== false,
|
||||
user_widget_id: widget['id']
|
||||
};
|
||||
});
|
||||
this.widgets.read_ids(_(user_widgets).pluck('widget_id'), ['title'], function (widgets) {
|
||||
_(widgets).each(function (widget) {
|
||||
_.extend(widget, widget_user[widget['id']]);
|
||||
});
|
||||
var url = _.sprintf(
|
||||
'/base_dashboard/widgets/content?session_id=%s&widget_id=',
|
||||
self.session.session_id);
|
||||
self.$element.html(QWeb.render('HomeWidgets', {
|
||||
widgets: widgets,
|
||||
url: url
|
||||
}));
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -64,4 +64,54 @@
|
||||
</board>
|
||||
</form>
|
||||
</t>
|
||||
<div t-name="ConfigOverview" class="oe-dashboard-config-overview">
|
||||
<div class="oe-config-progress">
|
||||
<h6 class="oe-config-progress-title">
|
||||
progress: <t t-esc="Math.round(completion)"/>%
|
||||
</h6>
|
||||
<div class="oe-config-progress-bar" t-att-data-completion="completion"/>
|
||||
</div>
|
||||
<dl>
|
||||
<t t-foreach="groups" t-as="category">
|
||||
<dt><t t-esc="category"/></dt>
|
||||
<dd><ul>
|
||||
<li t-foreach="category_value" t-as="todo"
|
||||
t-att-class="todo.done ? 'oe-done' : undefined"
|
||||
t-att-data-id="todo.id"
|
||||
t-att-title="!todo.done ? 'Execute task \'' + todo.name + '\'' : undefined">
|
||||
<input type="checkbox" t-att-value="todo.id"
|
||||
t-att-title="!todo.done ? 'Mark this task as done' : undefined"
|
||||
t-att-checked="todo.done ? 'checked' : undefined"/>
|
||||
<t t-esc="todo.name"/>
|
||||
</li>
|
||||
</ul></dd>
|
||||
</t>
|
||||
|
||||
</dl>
|
||||
</div>
|
||||
<div t-name="ApplicationTiles" class="oe-dashboard-home-tiles">
|
||||
<table width="100%">
|
||||
<tr t-foreach="rows" t-as="row">
|
||||
<td t-foreach="row" t-as="application" width="33.3%">
|
||||
<div class="oe-dashboard-home-tile"
|
||||
t-att-data-menuid="application.id">
|
||||
<div class="oe-dashboard-home-tile-icon">
|
||||
<img t-if="application.web_icon_data"
|
||||
t-att-src="'data:image/png;base64,' + application.web_icon_data"
|
||||
/><img t-if="application.web_icon_hover_data" class="hover"
|
||||
t-att-src="'data:image/png;base64,' + application.web_icon_hover_data"/>
|
||||
</div>
|
||||
<span><t t-esc="application.name"/></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div t-name="HomeWidgets" class="oe-dashboard-home-widgets">
|
||||
<div class="oe-dashboard-home-widgets-widget"
|
||||
t-foreach="widgets" t-as="widget">
|
||||
<h3><t t-esc="widget.title"/></h3>
|
||||
<iframe width="100%" frameborder="0" t-att-src="url + widget.id"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name" : "OpenERP Web installer home",
|
||||
"version" : "2.0",
|
||||
"depends" : ['base'],
|
||||
'active': True,
|
||||
'js': ['static/src/js/home.js'],
|
||||
'css': ['static/src/css/home.css']
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
.oe-static-home {
|
||||
text-align: center;
|
||||
}
|
||||
.oe-static-home-banner {
|
||||
display: inline-block;
|
||||
margin: 0.5em 0;
|
||||
padding: 0.5em 5em;
|
||||
border: 1px inset #808080;
|
||||
background-color: #e6e3e3;
|
||||
-moz-border-radius: 2em;
|
||||
-webkit-border-radius: 2em;
|
||||
border-radius: 2em;
|
||||
text-align: left;
|
||||
}
|
||||
.oe-static-home-banner h1 {
|
||||
margin: 0 0 0.3em
|
||||
}
|
||||
.oe-static-home-banner p {
|
||||
|
||||
}
|
||||
.oe-static-home-banner address {
|
||||
font-style: normal;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.oe-static-home-tiles {
|
||||
text-align: left;
|
||||
}
|
||||
.oe-static-home-tiles td {
|
||||
vertical-align: top;
|
||||
}
|
||||
.oe-static-home-tile {
|
||||
margin: 0.5em;
|
||||
padding: 0 1em;
|
||||
}
|
||||
.oe-static-home-tile-logo {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.oe-static-home-tile-text h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
.oe-static-home-tile-text p {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
@@ -0,0 +1,101 @@
|
||||
openerp.base_default_home = function (openerp) {
|
||||
QWeb.add_template('/base_default_home/static/src/xml/base_default_home.xml');
|
||||
|
||||
var old_home = openerp.base.WebClient.prototype.default_home;
|
||||
|
||||
openerp.base_default_home = {
|
||||
applications: [
|
||||
[
|
||||
{
|
||||
module: 'crm', name: 'CRM',
|
||||
help: "Acquire leads, follow opportunities, manage prospects and phone calls, \u2026"
|
||||
}, {
|
||||
module: 'sale', name: 'Sales',
|
||||
help: "Do quotations, follow sales orders, invoice and control deliveries"
|
||||
}, {
|
||||
module: 'account_voucher', name: 'Invoicing',
|
||||
help: "Send invoice, track payments and reminders"
|
||||
}, {
|
||||
module: 'project', name: 'Projects',
|
||||
help: "Manage projects, track tasks, invoice task works, follow issues, \u2026"
|
||||
}
|
||||
], [
|
||||
{
|
||||
module: 'purchase', name: 'Purchase',
|
||||
help: "Do purchase orders, control invoices and reception, follow your suppliers, \u2026"
|
||||
}, {
|
||||
module: 'stock', name: 'Warehouse',
|
||||
help: "Track your stocks, schedule product moves, manage incoming and outgoing shipments, \u2026"
|
||||
}, {
|
||||
module: 'hr', name: 'Human Resources',
|
||||
help: "Manage employees and their contracts, follow laves, recruit people, \u2026"
|
||||
}, {
|
||||
module: 'point_of_sale', name: 'Point of Sales',
|
||||
help: "Manage shop sales, use touch-screen POS"
|
||||
}
|
||||
], [
|
||||
{
|
||||
module: 'profile_tools', name: 'Extra Tools',
|
||||
help: "Track ideas, manage lunch, create surveys, share data"
|
||||
}, {
|
||||
module: 'mrp', name: 'Manufacturing',
|
||||
help: "Manage your manufacturing, control your supply chain, personalize master data, \u2026"
|
||||
}, {
|
||||
module: 'marketing', name: 'Marketing',
|
||||
help: "Manage campaigns, follow activities, automate emails, \u2026"
|
||||
}, {
|
||||
module: 'knowledge', name: 'Knowledge',
|
||||
help: "Track your documents, browse your files, \u2026"
|
||||
}
|
||||
]
|
||||
]
|
||||
};
|
||||
|
||||
_.extend(openerp.base.WebClient.prototype, {
|
||||
default_home: function () {
|
||||
var self = this;
|
||||
var Installer = new openerp.base.DataSet(
|
||||
this, 'base.setup.installer');
|
||||
Installer.call('already_installed', [], function (installed_modules) {
|
||||
if (!_(installed_modules).isEmpty()) {
|
||||
return old_home.call(self);
|
||||
}
|
||||
self.$element.find('.oe-application').html(
|
||||
QWeb.render('StaticHome', {
|
||||
url: window.location.protocol + '//' + window.location.host + window.location.pathname,
|
||||
session: self.session,
|
||||
rows: openerp.base_default_home.applications
|
||||
})).delegate('.oe-static-home-tile-text button', 'click', function () {
|
||||
self.install_module($(this).val());
|
||||
})
|
||||
|
||||
}, function (err, event) {
|
||||
event.preventDefault();
|
||||
return old_home.call(self);
|
||||
});
|
||||
},
|
||||
install_module: function (module_name) {
|
||||
var Modules = new openerp.base.DataSetSearch(
|
||||
this, 'ir.module.module', null,
|
||||
[['name', '=', module_name], ['state', '=', 'uninstalled']]),
|
||||
Upgrade = new openerp.base.DataSet(this, 'base.module.upgrade');
|
||||
|
||||
$.blockUI({
|
||||
message: '<img src="/base_default_home/static/src/img/throbber.gif">'
|
||||
});
|
||||
Modules.read_slice(['id'], null, null, function (records) {
|
||||
if (!(records.length === 1)) { return; }
|
||||
Modules.call('state_update',
|
||||
[_.pluck(records, 'id'), 'to install', ['uninstalled']],
|
||||
function () {
|
||||
Upgrade.call('upgrade_module', [[]], function () {
|
||||
$.unblockUI();
|
||||
// TODO: less brutal reloading
|
||||
window.location.reload(true);
|
||||
});
|
||||
}
|
||||
)
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div t-name="StaticHome" class="oe-static-home">
|
||||
<div class="oe-static-home-banner">
|
||||
<h1>Welcome to OpenERP</h1>
|
||||
<p>
|
||||
Don't forget to bookmark your application address to come back
|
||||
later:
|
||||
</p>
|
||||
<address>
|
||||
URL: <a t-att-href="url"><t t-esc="url"/></a><br/>
|
||||
login: <t t-esc="session.login"/>
|
||||
</address>
|
||||
</div>
|
||||
|
||||
<div class="oe-static-home-tiles">
|
||||
<table width="100%">
|
||||
<tr t-foreach="rows" t-as="row">
|
||||
<td t-foreach="row" t-as="application" width="25%">
|
||||
<table class="oe-static-home-tile">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="oe-static-home-tile-logo">
|
||||
<img t-att-src="'/base_default_home/static/src/img/' + application.module + '.png'"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="oe-static-home-tile-text">
|
||||
<h2><t t-esc="application.name"/></h2>
|
||||
<p><t t-esc="application.help"/></p>
|
||||
<button type="button"
|
||||
t-att-value="application.module">
|
||||
Install</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="stylesheet" href="/web_mobile/static/lib/jquery_mobile/css/jquery.mobile-1.0a4.1.css" />
|
||||
|
||||
<script type="text/javascript" src="/base/static/lib/LABjs/LAB.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/jquery/jquery-1.5.2.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/jquery/jquery-1.6.2.js"></script>
|
||||
<script type="text/javascript" src="/base/static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web_mobile/static/lib/jquery_mobile/js/jquery.mobile-1.0a4.1.js"></script>
|
||||
|
||||
@@ -107,15 +107,20 @@ class OpenERPSession(object):
|
||||
if uid: self.get_context()
|
||||
return uid
|
||||
|
||||
def execute(self, model, func, *l, **d):
|
||||
def assert_valid(self):
|
||||
"""
|
||||
Ensures this session is valid (logged into the openerp server)
|
||||
"""
|
||||
if not (self._db and self._uid and self._password):
|
||||
raise OpenERPUnboundException()
|
||||
|
||||
def execute(self, model, func, *l, **d):
|
||||
self.assert_valid()
|
||||
r = self.proxy('object').execute(self._db, self._uid, self._password, model, func, *l, **d)
|
||||
return r
|
||||
|
||||
def exec_workflow(self, model, id, signal):
|
||||
if not (self._db and self._uid and self._password):
|
||||
raise OpenERPUnboundException()
|
||||
self.assert_valid()
|
||||
r = self.proxy('object').exec_workflow(self._db, self._uid, self._password, model, signal, id)
|
||||
return r
|
||||
|
||||
@@ -366,7 +371,7 @@ class HttpRequest(object):
|
||||
self.context = kw.get('context', {})
|
||||
host = cherrypy.config['openerp.server.host']
|
||||
port = cherrypy.config['openerp.server.port']
|
||||
self.session = self.httpsession.setdefault(kw.get('session_id'), OpenERPSession(host, port))
|
||||
self.session = self.httpsession.setdefault(kw.pop('session_id', None), OpenERPSession(host, port))
|
||||
self.result = ""
|
||||
if request.method == 'GET':
|
||||
print "GET --> %s.%s %s %r" % (controller.__class__.__name__, f.__name__, request, kw)
|
||||
|
||||