diff --git a/addons/base/__openerp__.py b/addons/base/__openerp__.py
index da2b345d217..e17e2d82c6d 100644
--- a/addons/base/__openerp__.py
+++ b/addons/base/__openerp__.py
@@ -3,4 +3,37 @@
"version" : "2.0",
"depends" : [],
'active': True,
+ 'js' : [
+ "static/lib/datejs/date-en-US.js",
+ "static/lib/jquery/jquery-1.5.2.js",
+ "static/lib/jquery.validate/jquery.validate.js",
+ "static/lib/jquery.ba-bbq/jquery.ba-bbq.js",
+ "static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js",
+ "static/lib/jquery.superfish/js/hoverIntent.js",
+ "static/lib/jquery.superfish/js/superfish.js",
+ "static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js",
+ "static/lib/jquery.ui/js/jquery-ui-timepicker-addon.js",
+ "static/lib/jquery.ui.notify/js/jquery.notify.js",
+ "static/lib/json/json2.js",
+ "static/lib/qweb/qweb2.js",
+ "static/lib/underscore/underscore.js",
+ "static/lib/underscore/underscore.string.js",
+ "static/src/js/base.js",
+ "static/src/js/chrome.js",
+ "static/src/js/controller.js",
+ "static/src/js/views.js",
+ "static/src/js/data.js",
+ "static/src/js/dates.js",
+ "static/src/js/form.js",
+ "static/src/js/list.js",
+ "static/src/js/list-editable.js",
+ "static/src/js/search.js",
+ "static/src/js/view_tree.js",
+ ],
+ 'css' : [
+ "static/lib/jquery.superfish/css/superfish.css",
+ "static/lib/jquery.ui/css/smoothness/jquery-ui-1.8.9.custom.css",
+ "static/lib/jquery.ui.notify/css/ui.notify.css",
+ "static/src/css/base.css",
+ ],
}
diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py
index 2507f958cdc..8fe79a19a9a 100644
--- a/addons/base/controllers/main.py
+++ b/addons/base/controllers/main.py
@@ -56,6 +56,99 @@ class Xml2Json:
# OpenERP Web base Controllers
#----------------------------------------------------------
+def manifest_glob(addons, key):
+ files = []
+ for addon in addons:
+ globlist = openerpweb.addons_manifest.get(addon, {}).get(key, [])
+ print globlist
+ for pattern in globlist:
+ for path in glob.glob(os.path.join(openerpweb.path_addons, addon, pattern)):
+ files.append(path[len(openerpweb.path_addons):])
+ return files
+
+def concat_files(file_list):
+ """ Concatenate file content
+ return (concat,timestamp)
+ concat: concatenation of file content
+ timestamp: max(os.path.getmtime of file_list)
+ """
+ root = openerpweb.path_root
+ files_content = []
+ files_timestamp = 0
+ for i in file_list:
+ fname = os.path.join(root, i)
+ ftime = os.path.getmtime(fname)
+ if ftime > files_timestamp:
+ files_timestamp = ftime
+ files_content = open(fname).read()
+ files_concat = "".join(files_content)
+ return files_concat
+
+class WebClient(openerpweb.Controller):
+ _cp_path = "/base/webclient"
+
+ @openerpweb.jsonrequest
+ def csslist(self, req, mods='base'):
+ return manifest_glob(mods.split(','), 'css')
+
+ @openerpweb.jsonrequest
+ def jslist(self, req, mods='base'):
+ return manifest_glob(mods.split(','), 'js')
+
+ @openerpweb.httprequest
+ def css(self, req, mods='base'):
+ cherrypy.response.headers['Content-Type'] = 'text/css'
+ files = manifest_glob(mods.split(','), 'css')
+ concat = concat_files(files)[0]
+ # TODO request set the Date of last modif and Etag
+ return concat
+
+ @openerpweb.httprequest
+ def js(self, req, mods='base'):
+ cherrypy.response.headers['Content-Type'] = 'application/javascript'
+ files = manifest_glob(mods.split(','), 'js')
+ concat = concat_files(files)[0]
+ # TODO request set the Date of last modif and Etag
+ return concat
+
+ @openerpweb.httprequest
+ def home(self, req):
+ template ="""
+
+
+
+ OpenERP
+ %s
+
+
+ %s
+
+
+
+
+ """.replace('\n'+' '*8,'\n')
+
+ # script tags
+ jslist = ['/base/webclient/js']
+ if 1: # debug == 1
+ jslist = manifest_glob(['base'], 'js')
+ js = "\n ".join([''%i for i in jslist])
+
+ # css tags
+ csslist = ['/base/webclient/css']
+ if 1: # debug == 1
+ csslist = manifest_glob(['base'], 'css')
+ css = "\n ".join([' '%i for i in csslist])
+ r = template % (js, css)
+ return r
+
class Database(openerpweb.Controller):
_cp_path = "/base/database"
@@ -170,37 +263,6 @@ class Database(openerpweb.Controller):
class Session(openerpweb.Controller):
_cp_path = "/base/session"
- def manifest_glob(self, addons, key):
- files = []
- for addon in addons:
- globlist = openerpweb.addons_manifest.get(addon, {}).get(key, [])
-
- files.extend([
- resource_path[len(openerpweb.path_addons):]
- for pattern in globlist
- for resource_path in glob.glob(os.path.join(
- openerpweb.path_addons, addon, pattern))
- ])
- return files
-
- def concat_files(self, file_list):
- """ Concatenate file content
- return (concat,timestamp)
- concat: concatenation of file content
- timestamp: max(os.path.getmtime of file_list)
- """
- root = openerpweb.path_root
- files_content = []
- files_timestamp = 0
- for i in file_list:
- fname = os.path.join(root, i)
- ftime = os.path.getmtime(fname)
- if ftime > files_timestamp:
- files_timestamp = ftime
- files_content = open(fname).read()
- files_concat = "".join(files_content)
- return files_concat
-
@openerpweb.jsonrequest
def login(self, req, db, login, password):
req.session.login(db, login, password)
@@ -226,31 +288,12 @@ class Session(openerpweb.Controller):
@openerpweb.jsonrequest
def modules(self, req):
- return {"modules": [name
- for name, manifest in openerpweb.addons_manifest.iteritems()
- if manifest.get('active', True)]}
-
- @openerpweb.jsonrequest
- def csslist(self, req, mods='base'):
- return {'files': self.manifest_glob(mods.split(','), 'css')}
-
- @openerpweb.jsonrequest
- def jslist(self, req, mods='base'):
- return {'files': self.manifest_glob(mods.split(','), 'js')}
-
- def css(self, req, mods='base'):
- files = self.manifest_glob(mods.split(','), 'css')
- concat = self.concat_files(files)[0]
- # TODO request set the Date of last modif and Etag
- return concat
- css.exposed = True
-
- def js(self, req, mods='base'):
- files = self.manifest_glob(mods.split(','), 'js')
- concat = self.concat_files(files)[0]
- # TODO request set the Date of last modif and Etag
- return concat
- js.exposed = True
+ # TODO query server for installed web modules
+ mods = []
+ for name, manifest in openerpweb.addons_manifest.items():
+ if name != 'base' and manifest.get('active', True):
+ mods.append(name)
+ return mods
@openerpweb.jsonrequest
def eval_domain_and_context(self, req, contexts, domains,
@@ -829,6 +872,34 @@ class SearchView(View):
if field.get('context'):
field["context"] = self.parse_domain(field["context"], req.session)
return {'fields': fields}
+
+ @openerpweb.jsonrequest
+ def get_filters(self, req, model):
+ Model = req.session.model("ir.filters")
+ filters = Model.get_filters(model)
+ for filter in filters:
+ filter["context"] = req.session.eval_context(self.parse_context(filter["context"], req.session))
+ filter["domain"] = req.session.eval_domain(self.parse_domain(filter["domain"], req.session))
+ return filters
+
+ @openerpweb.jsonrequest
+ def save_filter(self, req, model, name, context_to_save, domain):
+ Model = req.session.model("ir.filters")
+ ctx = openerpweb.nonliterals.CompoundContext(context_to_save)
+ ctx.session = req.session
+ ctx = ctx.evaluate()
+ domain = openerpweb.nonliterals.CompoundDomain(domain)
+ domain.session = req.session
+ domain = domain.evaluate()
+ uid = req.session._uid
+ context = req.session.eval_context(req.context)
+ to_return = Model.create_or_replace({"context": ctx,
+ "domain": domain,
+ "model_id": model,
+ "name": name,
+ "user_id": uid
+ }, context)
+ return to_return
class Binary(openerpweb.Controller):
_cp_path = "/base/binary"
diff --git a/addons/base/static/lib/LABjs/LAB.js b/addons/base/static/lib/LABjs/LAB.js
deleted file mode 100644
index 1c3c85bd7a3..00000000000
--- a/addons/base/static/lib/LABjs/LAB.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/*! LAB.js (LABjs :: Loading And Blocking JavaScript)
- v1.2.0 (c) Kyle Simpson
- MIT License
-*/
-(function(p){var q="string",w="head",L="body",M="script",u="readyState",j="preloaddone",x="loadtrigger",N="srcuri",E="preload",Z="complete",y="done",z="which",O="preserve",F="onreadystatechange",ba="onload",P="hasOwnProperty",bb="script/cache",Q="[object ",bw=Q+"Function]",bx=Q+"Array]",e=null,h=true,i=false,k=p.document,bc=p.location,bd=p.ActiveXObject,A=p.setTimeout,be=p.clearTimeout,R=function(a){return k.getElementsByTagName(a)},S=Object.prototype.toString,G=function(){},r={},T={},bf=/^[^?#]*\//.exec(bc.href)[0],bg=/^\w+\:\/\/\/?[^\/]+/.exec(bf)[0],by=R(M),bh=p.opera&&S.call(p.opera)==Q+"Opera]",bi=("MozAppearance"in k.documentElement.style),bj=(k.createElement(M).async===true),v={cache:!(bi||bh),order:bi||bh||bj,xhr:h,dupe:h,base:"",which:w};v[O]=i;v[E]=h;r[w]=k.head||R(w);r[L]=R(L);function B(a){return S.call(a)===bw}function U(a,b){var c=/^\w+\:\/\//,d;if(typeof a!=q)a="";if(typeof b!=q)b="";d=((/^\/\//.test(a))?bc.protocol:"")+a;d=(c.test(d)?"":b)+d;return((c.test(d)?"":(d.charAt(0)==="/"?bg:bf))+d)}function bz(a){return(U(a).indexOf(bg)===0)}function bA(a){var b,c=-1;while(b=by[++c]){if(typeof b.src==q&&a===U(b.src)&&b.type!==bb)return h}return i}function H(t,l){t=!(!t);if(l==e)l=v;var bk=i,C=t&&l[E],bl=C&&l.cache,I=C&&l.order,bm=C&&l.xhr,bB=l[O],bC=l.which,bD=l.base,bn=G,J=i,D,s=h,m={},K=[],V=e;C=bl||bm||I;function bo(a,b){if((a[u]&&a[u]!==Z&&a[u]!=="loaded")||b[y]){return i}a[ba]=a[F]=e;return h}function W(a,b,c){c=!(!c);if(!c&&!(bo(a,b)))return;b[y]=h;for(var d in m){if(m[P](d)&&!(m[d][y]))return}bk=h;bn()}function bp(a){if(B(a[x])){a[x]();a[x]=e}}function bE(a,b){if(!bo(a,b))return;b[j]=h;A(function(){r[b[z]].removeChild(a);bp(b)},0)}function bF(a,b){if(a[u]===4){a[F]=G;b[j]=h;A(function(){bp(b)},0)}}function X(b,c,d,g,f,n){var o=b[z];A(function(){if("item"in r[o]){if(!r[o][0]){A(arguments.callee,25);return}r[o]=r[o][0]}var a=k.createElement(M);if(typeof d==q)a.type=d;if(typeof g==q)a.charset=g;if(B(f)){a[ba]=a[F]=function(){f(a,b)};a.src=c;if(bj){a.async=i}}r[o].insertBefore(a,(o===w?r[o].firstChild:e));if(typeof n==q){a.text=n;W(a,b,h)}},0)}function bq(a,b,c,d){T[a[N]]=h;X(a,b,c,d,W)}function br(a,b,c,d){var g=arguments;if(s&&a[j]==e){a[j]=i;X(a,b,bb,d,bE)}else if(!s&&a[j]!=e&&!a[j]){a[x]=function(){br.apply(e,g)}}else if(!s){bq.apply(e,g)}}function bs(a,b,c,d){var g=arguments,f;if(s&&a[j]==e){a[j]=i;f=a.xhr=(bd?new bd("Microsoft.XMLHTTP"):new p.XMLHttpRequest());f[F]=function(){bF(f,a)};f.open("GET",b);f.send("")}else if(!s&&a[j]!=e&&!a[j]){a[x]=function(){bs.apply(e,g)}}else if(!s){T[a[N]]=h;X(a,b,c,d,e,a.xhr.responseText);a.xhr=e}}function bt(a){if(typeof a=="undefined"||!a)return;if(a.allowDup==e)a.allowDup=l.dupe;var b=a.src,c=a.type,d=a.charset,g=a.allowDup,f=U(b,bD),n,o=bz(f);if(typeof d!=q)d=e;g=!(!g);if(!g&&((T[f]!=e)||(s&&m[f])||bA(f))){if(m[f]!=e&&m[f][j]&&!m[f][y]&&o){W(e,m[f],h)}return}if(m[f]==e)m[f]={};n=m[f];if(n[z]==e)n[z]=bC;n[y]=i;n[N]=f;J=h;if(!I&&bm&&o)bs(n,f,c,d);else if(!I&&bl)br(n,f,c,d);else bq(n,f,c,d)}function Y(a){if(t&&!I)K.push(a);if(!t||C)a()}function bu(a){var b=[],c;for(c=-1;++c
-
-
-
- OpenERP
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css
index 6228e0c1e17..483a433eb99 100644
--- a/addons/base/static/src/css/base.css
+++ b/addons/base/static/src/css/base.css
@@ -1093,6 +1093,10 @@ background: linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* W3C */
.openerp .closed-sidebar .toggle-sidebar {
border-left: none;
}
+.openerp li.oe_sidebar_print {
+ padding-left: 20px;
+ background: 1px 3px url(../img/icons/gtk-print.png) no-repeat;
+}
.openerp.kitten-mode-activated .main_table {
background: url(http://placekitten.com/g/1500/800) repeat;
diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js
index 75c34286a80..63226a2f9b9 100644
--- a/addons/base/static/src/js/chrome.js
+++ b/addons/base/static/src/js/chrome.js
@@ -137,63 +137,116 @@ openerp.base.Registry = openerp.base.Class.extend( /** @lends openerp.base.Regis
});
/**
- * Generates an inherited class that replaces all the methods by null methods (methods
- * that does nothing and always return undefined).
- *
- * @param {Class} claz
- * @param {dict} add Additional functions to override.
- * @return {Class}
+ * OpenERP session aware controller
+ * a controller takes an already existing dom element and manage it
*/
-openerp.base.generate_null_object_class = function(claz, add) {
- var newer = {};
- var copy_proto = function(prototype) {
- for (var name in prototype) {
- if(typeof prototype[name] == "function") {
- newer[name] = function() {};
- }
- }
- if (prototype.prototype)
- copy_proto(prototype.prototype);
- };
- copy_proto(claz.prototype);
- newer.init = openerp.base.BasicController.prototype.init;
- var tmpclass = claz.extend(newer);
- return tmpclass.extend(add || {});
-};
-
-openerp.base.Notification = openerp.base.BasicController.extend({
+openerp.base.Controller = openerp.base.Controller.extend( /** @lends openerp.base.Controller# */{
init: function(parent, element_id) {
this._super(parent, element_id);
- this.$element.notify({
- speed: 500,
- expires: 1500
- });
+ if(this.controller_parent && this.controller_parent.session) {
+ this.session = this.controller_parent.session;
+ }
},
- notify: function(title, text) {
- this.$element.notify('create', {
- title: title,
- text: text
- });
+ /**
+ * Performs a JSON-RPC call
+ *
+ * @param {String} url endpoint url
+ * @param {Object} data RPC parameters
+ * @param {Function} success RPC call success callback
+ * @param {Function} error RPC call error callback
+ * @returns {jQuery.Deferred} deferred object for the RPC call
+ */
+ rpc: function(url, data, success, error) {
+ return this.session.rpc(url, data, success, error);
},
- warn: function(title, text) {
- this.$element.notify('create', 'oe_notification_alert', {
- title: title,
- text: text
- });
+ do_action: function(action, on_finished) {
+ return this.parent.do_action(action, on_finished);
}
});
-// Session should be a Class not Controller
-openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.base.Session# */{
+/**
+ * OpenERP session aware widget
+ * A widget is a controller that doesnt take an element_id
+ * it render its own html render() that you should insert into the dom
+ * and bind it at start()
+ */
+openerp.base.BaseWidget = openerp.base.Controller.extend({
+ /**
+ * The name of the QWeb template that will be used for rendering. Must be
+ * redefined in subclasses or the render() method can not be used.
+ *
+ * @type string
+ */
+ template: null,
+ /**
+ * The prefix used to generate an id automatically. Should be redefined in
+ * subclasses. If it is not defined, a default identifier will be used.
+ *
+ * @type string
+ */
+ identifier_prefix: 'generic-identifier',
+ /**
+ * Base class for widgets. Handle rendering (based on a QWeb template),
+ * identifier generation, parenting and destruction of the widget.
+ * Also initialize the identifier.
+ *
+ * @constructs
+ * @params {openerp.base.search.BaseWidget} parent The parent widget.
+ */
+ init: function (parent) {
+ this._super(parent);
+ this.make_id(this.identifier_prefix);
+ },
+ /**
+ * Sets and returns a globally unique identifier for the widget.
+ *
+ * If a prefix is appended, the identifier will be appended to it.
+ *
+ * @params sections prefix sections, empty/falsy sections will be removed
+ */
+ make_id: function () {
+ this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
+ return this.element_id;
+ },
+ /**
+ * Render the widget. This.template must be defined.
+ * The content of the current object is passed as context to the template.
+ *
+ * @param {object} additional Additional context arguments to pass to the template.
+ */
+ render: function (additional) {
+ return QWeb.render(this.template, _.extend({}, this, additional != null ? additional : {}));
+ },
+ /**
+ * "Starts" the widgets. Called at the end of the rendering, this allows
+ * to get a jQuery object referring to the DOM ($element attribute).
+ */
+ start: function () {
+ this._super();
+ var tmp = document.getElementById(this.element_id);
+ this.$element = tmp ? $(tmp) : null;
+ },
+ /**
+ * "Stops" the widgets. Called when the view destroys itself, this
+ * lets the widgets clean up after themselves.
+ */
+ stop: function () {
+ if(this.$element != null) {
+ this.$element.remove();
+ }
+ this._super();
+ }
+});
+
+openerp.base.Session = openerp.base.Controller.extend( /** @lends openerp.base.Session# */{
/**
* @constructs
- * @extends openerp.base.BasicController
* @param element_id to use for exception reporting
* @param server
* @param port
*/
init: function(parent, element_id, server, port) {
- this._super(element_id);
+ 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";
@@ -372,7 +425,7 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
for(var i=0; i', {
'href': file,
'rel': 'stylesheet',
@@ -415,16 +473,23 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
}));
});
},
- do_load_modules_debug: function(result) {
- $LAB.setOptions({AlwaysPreserveOrder: true})
- .script(result.files)
- .wait(this.on_modules_loaded);
- },
- do_load_modules_prod: function() {
- // load merged ones
- // /base/session/css?mod=mod1,mod2,mod3
- // /base/session/js?mod=mod1,mod2,mod3
- // use $.getScript(‘your_3rd_party-script.js’); ? i want to keep lineno !
+ 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' + openerp.base.json_node_to_xml(self.fields_view.arch, true) + '').dialog({ width: '95%', height: 600});
});
- if(this.view_manager.sidebar)
- this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
+ if (this.options.sidebar && this.options.sidebar_id) {
+ this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id);
+ this.sidebar.start();
+ this.sidebar.attachments = new openerp.base.form.SidebarAttachments(this.sidebar, this.sidebar.add_section("Attachments"), this);
+ this.sidebar.add_toolbar(data.fields_view.toolbar);
+ this.sidebar.do_unfold();
+ }
this.has_been_loaded.resolve();
},
do_show: function () {
- var self = this;
var promise;
if (this.dataset.index === null) {
// null index means we should start a new record
@@ -98,13 +107,17 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
} else {
promise = this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded);
}
- self.$element.show();
- if(this.view_manager.sidebar)
- this.view_manager.sidebar.do_refresh(true);
+ this.$element.show();
+ if (this.sidebar) {
+ this.sidebar.$element.show();
+ }
return promise;
},
do_hide: function () {
this.$element.hide();
+ if (this.sidebar) {
+ this.sidebar.$element.hide();
+ }
},
on_record_loaded: function(record) {
if (!record) {
@@ -144,7 +157,9 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
this.on_form_changed();
this.show_invalid = this.ready = true;
this.do_update_pager(record.id == null);
- this.do_update_sidebar();
+ if (this.sidebar) {
+ this.sidebar.attachments.do_update();
+ }
if (this.default_focus_field) {
this.default_focus_field.focus();
}
@@ -389,7 +404,9 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
this.dataset.index = 0;
}
this.do_update_pager();
- this.do_update_sidebar();
+ if (this.sidebar) {
+ this.sidebar.attachments.do_update();
+ }
this.notification.notify("Record created", "The record has been created with id #" + this.datarecord.id);
if (success) {
success(_.extend(r, {created: true}));
@@ -406,52 +423,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
do_cancel: function () {
this.notification.notify("Cancelling form");
},
- do_update_sidebar: function() {
- if (this.flags.sidebar === false) {
- return;
- }
- if (!this.datarecord.id) {
- this.on_attachments_loaded([]);
- } else {
- // TODO fme: modify this so it doesn't try to load attachments when there is not sidebar
- /*(new openerp.base.DataSetSearch(
- this, 'ir.attachment', this.dataset.get_context(),
- [['res_model', '=', this.dataset.model],
- ['res_id', '=', this.datarecord.id],
- ['type', 'in', ['binary', 'url']]])).read_slice(
- ['name', 'url', 'type'], false, false,
- this.on_attachments_loaded);*/
- }
- },
- on_attachments_loaded: function(attachments) {
- this.$sidebar = this.view_manager.sidebar.$element.find('.sidebar-attachments');
- this.attachments = attachments;
- this.$sidebar.html(QWeb.render('FormView.sidebar.attachments', this));
- this.$sidebar.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
- this.$sidebar.find('.oe-binary-file').change(this.on_attachment_changed);
- },
- on_attachment_changed: function(e) {
- window[this.element_id + '_iframe'] = this.do_update_sidebar;
- var $e = $(e.target);
- if ($e.val() != '') {
- this.$sidebar.find('form.oe-binary-form').submit();
- $e.parent().find('input[type=file]').attr('disabled', 'true');
- $e.parent().find('button').attr('disabled', 'true').find('img, span').toggle();
- }
- },
- on_attachment_delete: function(e) {
- var self = this, $e = $(e.currentTarget);
- var name = _.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
- if (confirm("Do you really want to delete the attachment " + name + " ?")) {
- this.rpc('/base/dataset/unlink', {
- model: 'ir.attachment',
- ids: [parseInt($e.attr('data-id'))]
- }, function(r) {
- $e.parent().remove();
- self.notification.notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
- });
- }
- },
reload: function() {
if (this.datarecord.id) {
this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded);
@@ -472,6 +443,54 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
/** @namespace */
openerp.base.form = {};
+openerp.base.form.SidebarAttachments = openerp.base.Controller.extend({
+ init: function(parent, element_id, form_view) {
+ this._super(parent, element_id);
+ this.view = form_view;
+ },
+ do_update: function() {
+ if (!this.view.datarecord.id) {
+ 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);
+ }
+ },
+ on_attachments_loaded: function(attachments) {
+ this.attachments = attachments;
+ this.$element.html(QWeb.render('FormView.sidebar.attachments', this));
+ this.$element.find('.oe-binary-file').change(this.on_attachment_changed);
+ this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
+ },
+ on_attachment_changed: function(e) {
+ window[this.element_id + '_iframe'] = this.do_update;
+ var $e = $(e.target);
+ if ($e.val() != '') {
+ this.$element.find('form.oe-binary-form').submit();
+ $e.parent().find('input[type=file]').attr('disabled', 'true');
+ $e.parent().find('button').attr('disabled', 'true').find('img, span').toggle();
+ }
+ },
+ on_attachment_delete: function(e) {
+ var self = this, $e = $(e.currentTarget);
+ var name = _.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
+ if (confirm("Do you really want to delete the attachment " + name + " ?")) {
+ this.rpc('/base/dataset/unlink', {
+ model: 'ir.attachment',
+ ids: [parseInt($e.attr('data-id'))]
+ }, function(r) {
+ $e.parent().remove();
+ self.notification.notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
+ });
+ }
+ }
+});
+
openerp.base.form.compute_domain = function(expr, fields) {
var stack = [];
for (var i = expr.length - 1; i >= 0; i--) {
@@ -1784,7 +1803,7 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
self.on_ui_change();
});
- this.list_view = new openerp.base.form.Many2ManyListView(this.view.view_manager, this.list_id, this.dataset, false, {
+ this.list_view = new openerp.base.form.Many2ManyListView(new openerp.base.NullViewManager(this), this.list_id, this.dataset, false, {
'addable': 'Add',
'selectable': self.multi_selection
});
@@ -1918,7 +1937,7 @@ openerp.base.form.SelectCreatePopup = openerp.base.BaseWidget.extend({
$sbutton.click(function() {
self.on_select_elements(self.selected_ids);
});
- self.view_list = new openerp.base.form.SelectCreateListView(this,
+ self.view_list = new openerp.base.form.SelectCreateListView(self,
self.element_id + "_view_list", self.dataset, false,
{'deletable': false});
self.view_list.popup = self;
@@ -2219,8 +2238,10 @@ 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.FieldDate',
+// 'datetime' : 'openerp.base.form.FieldDatetime',
+ 'date' : 'openerp.base.form.FieldChar',
+ 'datetime' : 'openerp.base.form.FieldChar',
'selection' : 'openerp.base.form.FieldSelection',
'many2one' : 'openerp.base.form.FieldMany2One',
'many2many' : 'openerp.base.form.FieldMany2Many',
diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js
index 6812d4fac09..a4e516721ca 100644
--- a/addons/base/static/src/js/list.js
+++ b/addons/base/static/src/js/list.js
@@ -24,24 +24,37 @@ openerp.base.list = {
].join('')
}
- var record = row_data[column.id];
- if (record.value === false) {
- return value_if_empty === undefined ? '' : value_if_empty;
+ 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 'many2one':
- // name_get value format
- return record.value[1];
+ 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(record.value),
- Math.round((record.value % 1) * 60));
+ Math.floor(value),
+ Math.round((value % 1) * 60));
case 'progressbar':
return _.sprintf(
'%.2f%% ',
- record.value, record.value);
+ value, value);
+ case 'many2one':
+ // name_get value format
+ return value[1];
default:
- return record.value;
+ return value;
}
}
};
@@ -89,6 +102,7 @@ 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.dataset = dataset;
this.model = dataset.model;
@@ -254,9 +268,11 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
})
.val(self._limit || 'NaN');
});
- if(this.view_manager.sidebar)
- this.view_manager.sidebar.set_toolbar(data.fields_view.toolbar);
-
+ if (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);
+ }
},
/**
* Configures the ListView pager based on the provided dataset's information
@@ -348,16 +364,10 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
}
var aggregation_func = column['group_operator'] || 'sum';
- if (!column[aggregation_func]) {
- return {};
- }
-
- return {
- field: column.id,
- type: column.type,
+ return _.extend({}, column, {
'function': aggregation_func,
label: column[aggregation_func]
- };
+ });
});
},
/**
@@ -386,15 +396,20 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
},
do_show: function () {
this.$element.show();
+ if (this.sidebar) {
+ this.sidebar.$element.show();
+ }
if (this.hidden) {
this.$element.find('.oe-listview-content').append(
this.groups.apoptosis().render());
this.hidden = false;
}
- this.view_manager.sidebar.do_refresh(true);
},
do_hide: function () {
this.$element.hide();
+ if (this.sidebar) {
+ this.sidebar.$element.hide();
+ }
this.hidden = true;
},
/**
@@ -414,7 +429,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
model: this.model,
view_id: this.view_id,
context: this.dataset.get_context(),
- toolbar: !!this.flags.sidebar
+ toolbar: this.options.sidebar
}, callback);
}
},
@@ -476,6 +491,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
var self = this;
return $.when(this.dataset.unlink(ids)).then(function () {
self.groups.drop_records(ids);
+ self.compute_aggregates();
});
},
/**
@@ -504,11 +520,17 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
* @param {Function} callback should be called after the action is executed, if non-null
*/
do_action: function (name, id, callback) {
- var action = _.detect(this.columns, function (field) {
+ 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, callback);
+ this.execute_action(
+ action, this.dataset, this.session.action_manager, id, function () {
+ $.when(callback.apply(this, arguments).then(function () {
+ self.compute_aggregates();
+ }));
+ });
},
/**
* Handles the activation of a record (clicking on it)
@@ -555,7 +577,6 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
compute_aggregates: function (records) {
var columns = _(this.aggregate_columns).filter(function (column) {
return column['function']; });
-
if (_.isEmpty(columns)) { return; }
if (_.isEmpty(records)) {
@@ -563,17 +584,39 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
}
var count = 0, sums = {};
- _(columns).each(function (column) { sums[column.field] = 0; });
+ _(columns).each(function (column) {
+ switch (column['function']) {
+ case 'max':
+ sums[column.id] = -Infinity;
+ break;
+ case 'min':
+ sums[column.id] = Infinity;
+ break;
+ default:
+ sums[column.id] = 0;
+ }
+ });
_(records).each(function (record) {
count += record.count || 1;
_(columns).each(function (column) {
- var field = column.field;
+ var field = column.id,
+ value = record.values[field];
switch (column['function']) {
case 'sum':
- sums[field] += record.values[field];
+ sums[field] += value;
break;
case 'avg':
- sums[field] += record.count * record.values[field];
+ sums[field] += record.count * value;
+ break;
+ case 'min':
+ if (sums[field] > value) {
+ sums[field] = value;
+ }
+ break;
+ case 'max':
+ if (sums[field] < value) {
+ sums[field] = value;
+ }
break;
}
});
@@ -581,14 +624,13 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
var aggregates = {};
_(columns).each(function (column) {
- var field = column.field;
+ var field = column.id;
switch (column['function']) {
- case 'sum':
- aggregates[field] = sums[field];
- break;
case 'avg':
- aggregates[field] = sums[field] / count;
+ aggregates[field] = {value: sums[field] / count};
break;
+ default:
+ aggregates[field] = {value: sums[field]};
}
});
@@ -600,9 +642,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
if (!column['function']) {
return;
}
- var pattern = (column.type == 'integer') ? '%d' : '%.2f';
- $footer_cells.filter(_.sprintf('[data-field=%s]', column.field))
- .text(_.sprintf(pattern, aggregation[column.field]));
+
+ $footer_cells.filter(_.sprintf('[data-field=%s]', column.id))
+ .html(openerp.base.list.render_cell(aggregation, column));
});
}
// TODO: implement reorder (drag and drop rows)
@@ -667,7 +709,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
index = self.row_position($row);
$(self).trigger('action', [field, record_id, function () {
- self.reload_record(index, true);
+ return self.reload_record(index, true);
}]);
})
.delegate('tr', 'click', function (e) {
@@ -805,7 +847,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.
return $.when(read_p).then(function () {
self.$current.children().eq(record_index)
- .replaceWith(self.render_record(record_index)); })
+ .replaceWith(self.render_record(record_index)); });
},
/**
* Renders a list record to HTML
@@ -980,8 +1022,8 @@ 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.text(openerp.base.list.render_cell(
- row_data, group_column, "Undefined"
+ $group_column.html(openerp.base.list.render_cell(
+ row_data, group_column, "Undefined"
));
if (group.openable) {
// Make openable if not terminal group & group_by_no_leaf
@@ -1085,6 +1127,45 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
});
return d.promise();
},
+ setup_resequence_rows: function (list, dataset) {
+ // drag and drop enabled if list is not sorted and there is a
+ // "sequence" column in the view.
+ if ((dataset.sort && dataset.sort())
+ || !_(this.columns).any(function (column) {
+ return column.name === 'sequence'; })) {
+ return;
+ }
+ // ondrop, move relevant record & fix sequences
+ list.$current.sortable({
+ stop: function (event, ui) {
+ var from = ui.item.data('index'),
+ to = ui.item.prev().data('index') || 0;
+ if (from === to) { return; }
+ list.rows.splice(to, 0, list.rows.splice(from, 1)[0]);
+
+ ui.item.parent().children().each(function (i, e) {
+ // reset record-index accelerators on rows and even/odd
+ var even = i%2 === 0;
+ $(e).data('index', i)
+ .toggleClass('even', even)
+ .toggleClass('odd', !even);
+ });
+
+ // resequencing time!
+ var data, index = to,
+ // if drag to 1st row (to = 0), start sequencing from 0
+ // (exclusive lower bound)
+ seq = to ? list.rows[to - 1].data.sequence.value : 0;
+ while (++seq, data = list.rows[index++].data) {
+ data.sequence.value = seq;
+ // write are independent from one another, so we can just
+ // launch them all at the same time and we don't really
+ // give a fig about when they're done
+ dataset.write(data.id.value, {sequence: seq});
+ }
+ }
+ });
+ },
render: function (post_render) {
var self = this;
var $element = $('');
@@ -1103,6 +1184,7 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas
self.children[null] = list;
self.elements =
[list.$current.replaceAll($element)[0]];
+ self.setup_resequence_rows(list, dataset);
if (post_render) { post_render(); }
});
});
diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js
index fcf4a175b3c..8a51abe1335 100644
--- a/addons/base/static/src/js/search.js
+++ b/addons/base/static/src/js/search.js
@@ -110,9 +110,10 @@ openerp.base.SearchView = openerp.base.Controller.extend({
}
},
on_loaded: function(data) {
- var lines = this.make_widgets(
- data.fields_view['arch'].children,
- data.fields_view.fields);
+ var self = this,
+ lines = this.make_widgets(
+ data.fields_view['arch'].children,
+ data.fields_view.fields);
// for extended search view
var ext = new openerp.base.search.ExtendedSearch(this, this.model);
@@ -140,31 +141,86 @@ openerp.base.SearchView = openerp.base.Controller.extend({
return widget.start();
}).value();
- // filters management
- this.$element.find(".oe_search-view-filters-management").change(this.on_filters_management);
-
- var self = this;
$.when.apply(null, widget_starts).then(function () {
self.ready.resolve();
});
+
+ this.reload_managed_filters();
+ },
+ reload_managed_filters: function() {
+ var self = this;
+ return this.rpc('/base/searchview/get_filters', {
+ model: this.dataset.model
+ }).then(function(result) {
+ self.managed_filters = result;
+ var filters = self.$element.find(".oe_search-view-filters-management");
+ filters.html(QWeb.render("SearchView.managed-filters", {filters: result}));
+ filters.change(self.on_filters_management);
+ });
},
/**
* Handle event when the user make a selection in the filters management select box.
*/
on_filters_management: function(e) {
- var select = this.$element.find(".oe_search-view-filters-management"),
- val = select.val();
- select.val("_filters");
+ var self = this;
+ var select = this.$element.find(".oe_search-view-filters-management");
+ var val = select.val();
- if (val.slice(0,1) == "_") // useless action
+ if (val.slice(0,1) == "_") { // useless action
+ select.val("_filters");
return;
+ }
if (val.slice(0, "get:".length) == "get:") {
val = val.slice("get:".length);
- //TODO niv
+ val = parseInt(val);
+ var filter = this.managed_filters[val];
+ this.on_search([filter.domain], [filter.context], []);
} else if (val == "save_filter") {
- //TODO niv
+ select.val("_filters");
+ var data = this.build_search_data();
+ var context = new openerp.base.CompoundContext();
+ _.each(data.contexts, function(x) {
+ context.add(x);
+ });
+ var domain = new openerp.base.CompoundDomain();
+ _.each(data.domains, function(x) {
+ domain.add(x);
+ });
+ var dial_html = QWeb.render("SearchView.managed-filters.add");
+ var $dial = $(dial_html);
+ $dial.dialog({
+ modal: true,
+ title: "Filter Entry",
+ buttons: {
+ Cancel: function() {
+ $(this).dialog("close");
+ },
+ OK: function() {
+ $(this).dialog("close");
+ var name = $(this).find("input").val();
+ self.rpc('/base/searchview/save_filter', {
+ model: self.dataset.model,
+ context_to_save: context,
+ domain: domain,
+ name: name
+ }).then(function(result) {
+ self.reload_managed_filters();
+ });
+ },
+ }
+ });
} else { // manage_filters
- //TODO niv
+ select.val("_filters");
+ this.do_action({
+ res_model: 'ir.filters',
+ views: [[false, 'list'], [false, 'form']],
+ type: 'ir.actions.act_window',
+ context: {"search_default_user_id": this.session.uid,
+ "search_default_model_id": this.dataset.model},
+ target: "current",
+ limit : 80,
+ auto_search : true
+ });
}
},
/**
@@ -179,8 +235,22 @@ openerp.base.SearchView = openerp.base.Controller.extend({
* @param e jQuery event object coming from the "Search" button
*/
do_search: function (e) {
+ // reset filters management
+ var select = this.$element.find(".oe_search-view-filters-management");
+ select.val("_filters");
+
if (e && e.preventDefault) { e.preventDefault(); }
+ var data = this.build_search_data();
+
+ if (data.errors.length) {
+ this.on_invalid(data.errors);
+ return;
+ }
+
+ this.on_search(data.domains, data.contexts, data.groupbys);
+ },
+ build_search_data: function() {
var domains = [],
contexts = [],
errors = [];
@@ -205,19 +275,13 @@ openerp.base.SearchView = openerp.base.Controller.extend({
}
});
- if (errors.length) {
- this.on_invalid(errors);
- return;
- }
-
// TODO: do we need to handle *fields* with group_by in their context?
var groupbys = _(this.enabled_filters)
.chain()
.map(function (filter) { return filter.get_context();})
.compact()
.value();
-
- this.on_search(domains, contexts, groupbys);
+ return {domains: domains, contexts: contexts, errors: errors, groupbys: groupbys};
},
/**
* Triggered after the SearchView has collected all relevant domains and
@@ -593,32 +657,40 @@ openerp.base.search.BooleanField = openerp.base.search.Field.extend({
}
}
});
-openerp.base.search.IntegerField = openerp.base.search.Field.extend({
+openerp.base.search.NumberField = openerp.base.search.Field.extend(/** @lends openerp.base.search.NumberField# */{
get_value: function () {
if (!this.$element.val()) {
return null;
}
- var val = parseInt(this.$element.val());
- var check = Number(this.$element.val());
- if (isNaN(check) || val !== check) {
+ var val = this.parse(this.$element.val()),
+ check = Number(this.$element.val());
+ if (isNaN(val) || val !== check) {
this.$element.addClass('error');
throw new openerp.base.search.Invalid(
- this.attrs.name, this.$element.val(), "not a valid integer");
+ this.attrs.name, this.$element.val(), this.error_message);
}
this.$element.removeClass('error');
return val;
}
});
-openerp.base.search.FloatField = openerp.base.search.Field.extend({
- get_value: function () {
- var val = Number(this.$element.val());
- if (isNaN(val)) {
- this.$element.addClass('error');
- throw new openerp.base.search.Invalid(
- this.attrs.name, this.$element.val(), "not a valid number");
- }
- this.$element.removeClass('error');
- return val;
+/**
+ * @class
+ * @extends openerp.base.search.NumberField
+ */
+openerp.base.search.IntegerField = openerp.base.search.NumberField.extend(/** @lends openerp.base.search.IntegerField# */{
+ error_message: "not a valid integer",
+ parse: function (value) {
+ return parseInt(value, 10);
+ }
+});
+/**
+ * @class
+ * @extends openerp.base.search.NumberField
+ */
+openerp.base.search.FloatField = openerp.base.search.NumberField.extend(/** @lends openerp.base.search.FloatField# */{
+ error_message: "not a valid number",
+ parse: function (value) {
+ return parseFloat(value);
}
});
openerp.base.search.SelectionField = openerp.base.search.Field.extend({
@@ -839,7 +911,9 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
},
check_last_element: function() {
_.each(this.children, function(x) {x.set_last_group(false);});
- this.children[this.children.length - 1].set_last_group(true);
+ if (this.children.length >= 1) {
+ this.children[this.children.length - 1].set_last_group(true);
+ }
}
});
diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js
index 1c8b7487cd2..b98b0bc637f 100644
--- a/addons/base/static/src/js/views.js
+++ b/addons/base/static/src/js/views.js
@@ -11,13 +11,25 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
this.viewmanager = null;
this.current_dialog = null;
// Temporary linking view_manager to session.
- // Will use controller_parent to find it when implementation will be done.
+ // 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({
@@ -49,6 +61,9 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
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) {
@@ -105,7 +120,6 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
{return x instanceof Array? {view_id: x[0], view_type: x[1]} : x;});
this.views = {};
this.flags = this.flags || {};
- this.sidebar = new openerp.base.NullSidebar();
this.registry = openerp.base.views;
},
/**
@@ -119,7 +133,12 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
self.on_mode_switch($(this).data('view-type'));
});
_.each(this.views_src, function(view) {
- self.views[view.view_type] = $.extend({}, view, {controller: null});
+ self.views[view.view_type] = $.extend({}, view, {
+ controller : null,
+ options : _.extend({
+ sidebar_id : self.element_id + '_sidebar_' + view.view_type
+ }, self.flags)
+ });
});
if (this.flags.views_switcher === false) {
this.$element.find('.oe_vm_switch').hide();
@@ -143,7 +162,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
if (!view.controller) {
// Lazy loading of views
var controllerclass = this.registry.get_object(view_type);
- var controller = new controllerclass( this, this.element_id + "_view_" + view_type,
+ var controller = new controllerclass(this, this.element_id + '_view_' + view_type,
this.dataset, view.view_id, view.options);
if (view.embedded_view) {
controller.set_embedded_view(view.embedded_view);
@@ -255,13 +274,15 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
});
openerp.base.NullViewManager = openerp.base.generate_null_object_class(openerp.base.ViewManager, {
- init: function() {
- this._super();
+ init: function(parent) {
+ this._super(parent);
+ if(parent)
+ this.session = parent.session;
this.action = {flags: {}};
- this.sidebar = new openerp.base.NullSidebar();
}
});
+// TODO Will move to action Manager
openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
init: function(parent, element_id, action) {
this.session = parent.session;
@@ -282,19 +303,10 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
// Not elegant but allows to avoid flickering of SearchView#do_hide
this.flags.search_view = this.flags.pager = this.flags.sidebar = this.flags.action_buttons = false;
}
- if (this.flags.sidebar) {
- this.sidebar = new openerp.base.Sidebar(null, this);
- }
},
start: function() {
var inital_view_loaded = this._super();
- // init sidebar
- if (this.flags.sidebar) {
- this.$element.find('.view-manager-main-sidebar').html(this.sidebar.render());
- this.sidebar.start();
- }
-
var search_defaults = {};
_.each(this.action.context, function (value, key) {
var match = /^search_default_(.*)$/.exec(key);
@@ -319,7 +331,6 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
},
stop: function() {
// should be replaced by automatic destruction implemented in BaseWidget
- this.sidebar.stop();
this._super();
},
/**
@@ -346,62 +357,88 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
}
});
-openerp.base.Sidebar = openerp.base.BaseWidget.extend({
- template: "ViewManager.sidebar",
- init: function(parent, view_manager) {
- this._super(parent, view_manager.session);
- this.view_manager = view_manager;
- this.sections = [];
- },
- set_toolbar: function(toolbar) {
- this.sections = [];
- var self = this;
- _.each([["print", "Reports"], ["action", "Actions"], ["relate", "Links"]], function(type) {
- if (toolbar[type[0]].length == 0)
- return;
- var section = {elements:toolbar[type[0]], label:type[1]};
- self.sections.push(section);
- });
- this.do_refresh(true);
- },
- do_refresh: function(new_view) {
- var view = this.view_manager.active_view;
- var the_condition = this.sections.length > 0 && _.detect(this.sections,
- function(x) {return x.elements.length > 0;}) != undefined
- && (!new_view || view != 'list');
-
- this.$element.toggleClass('open-sidebar', the_condition)
- .toggleClass('closed-sidebar', !the_condition);
-
- this.$element.html(QWeb.render("ViewManager.sidebar.internal", { sidebar: this, view: view }));
-
- var self = this;
- this.$element.find(".toggle-sidebar").click(function(e) {
- self.$element.toggleClass('open-sidebar closed-sidebar');
- e.stopPropagation();
- e.preventDefault();
- });
-
- this.$element.find("a.oe_sidebar_action_a").click(function(e) {
- var $this = jQuery(this);
- var index = $this.attr("data-index").split('-');
- var action = self.sections[index[0]].elements[index[1]];
- action.flags = {
- new_window : true
- };
- self.session.action_manager.do_action(action);
- e.stopPropagation();
- e.preventDefault();
- });
+openerp.base.Sidebar = openerp.base.Controller.extend({
+ init: function(parent, element_id) {
+ this._super(parent, element_id);
+ this.items = {};
},
start: function() {
- this._super();
- this.do_refresh(false);
+ var self = this;
+ this._super(this, arguments);
+ this.$element.html(QWeb.render('Sidebar'));
+ this.$element.find(".toggle-sidebar").click(function(e) {
+ self.do_toggle();
+ });
+ },
+ add_toolbar: function(toolbar) {
+ var self = this;
+ _.each([['print', "Reports"], ['action', "Actions"], ['relate', "Links"]], function(type) {
+ var items = toolbar[type[0]];
+ if (items.length) {
+ for (var i = 0; i < items.length; i++) {
+ items[i] = {
+ label: items[i]['name'],
+ action: items[i],
+ classname: 'oe_sidebar_' + type[0]
+ }
+ }
+ self.add_section(type[1], items);
+ }
+ });
+ },
+ add_section: function(name, items) {
+ // For each section, we pass a name/label and optionally an array of items.
+ // If no items are passed, then the section will be created as a custom section
+ // returning back an element_id to be used by a custom controller.
+ // Else, the section is a standard section with items displayed as links.
+ // An item is a dictonary : {
+ // label: label to be displayed for the link,
+ // action: action to be launch when the link is clicked,
+ // callback: a function to be executed when the link is clicked,
+ // classname: optionnal dom class name for the line,
+ // }
+ // Note: The item should have one action or/and a callback
+ var self = this,
+ section_id = _.uniqueId(this.element_id + '_section_');
+ if (items) {
+ for (var i = 0; i < items.length; i++) {
+ items[i].element_id = _.uniqueId(section_id + '_item_');
+ this.items[items[i].element_id] = items[i];
+ }
+ }
+ var $section = $(QWeb.render("Sidebar.section", {
+ section_id: section_id,
+ name: name,
+ items: items
+ }));
+ if (items) {
+ $section.find('a.oe_sidebar_action_a').click(function() {
+ var item = self.items[$(this).attr('id')];
+ if (item.callback) {
+ item.callback();
+ }
+ if (item.action) {
+ item.action.flags = item.action.flags || {};
+ item.action.flags.new_window = true;
+ self.do_action(item.action);
+ }
+ return false;
+ });
+ }
+ $section.appendTo(this.$element.find('div.sidebar-actions'));
+ return section_id;
+ },
+ do_fold: function() {
+ this.$element.addClass('closed-sidebar').removeClass('open-sidebar');
+ },
+ do_unfold: function() {
+ this.$element.addClass('open-sidebar').removeClass('closed-sidebar');
+ },
+ do_toggle: function() {
+ this.$element.toggleClass('open-sidebar closed-sidebar');
}
});
-openerp.base.NullSidebar = openerp.base.generate_null_object_class(openerp.base.Sidebar);
-
openerp.base.Export = openerp.base.Dialog.extend({
dialog_title: "Export",
template: 'ExportDialog',
@@ -422,6 +459,14 @@ openerp.base.Export = openerp.base.Dialog.extend({
});
openerp.base.View = openerp.base.Controller.extend({
+ set_default_options: function(options) {
+ this.options = options || {};
+ _.defaults(this.options, {
+ // All possible views options should be defaulted here
+ sidebar_id: null,
+ sidebar: true
+ });
+ },
/**
* Fetches and executes the action identified by ``action_data``.
*
diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml
index de2c9eb029e..de55e154dbc 100644
--- a/addons/base/static/src/xml/base.xml
+++ b/addons/base/static/src/xml/base.xml
@@ -246,7 +246,7 @@
Database:
-
+
@@ -328,8 +328,8 @@
+
+
+
+
+
+
+
+
+
+
+