diff --git a/addons/base/__openerp__.py b/addons/base/__openerp__.py index 2c2d990364d..9cf8a6c893e 100644 --- a/addons/base/__openerp__.py +++ b/addons/base/__openerp__.py @@ -6,8 +6,11 @@ 'js' : [ "static/lib/datejs/date-en-US.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", "static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js", + "static/lib/jquery.blockUI/jquery.blockUI.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", @@ -17,12 +20,13 @@ "static/lib/qweb/qweb2.js", "static/lib/underscore/underscore.js", "static/lib/underscore/underscore.string.js", - "static/src/js/base.js", + "static/src/js/boot.js", + "static/src/js/core.js", + "static/src/js/dates.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/data_export.js", "static/src/js/form.js", "static/src/js/list.js", "static/src/js/list-editable.js", @@ -34,5 +38,6 @@ "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", + "static/src/css/data_export.css", ], } diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index 15d0b9951c7..6f63e5c7e24 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- + import base64, glob, os, re from xml.etree import ElementTree from cStringIO import StringIO +import operator import simplejson import openerpweb @@ -10,6 +12,8 @@ import openerpweb.ast import openerpweb.nonliterals import cherrypy +import xmlrpclib +import csv # Should move to openerpweb.Xml2Json class Xml2Json: @@ -151,16 +155,86 @@ class Database(openerpweb.Controller): _cp_path = "/base/database" @openerpweb.jsonrequest - def get_databases_list(self, req): + def get_list(self, req): proxy = req.session.proxy("db") dbs = proxy.list() h = req.httprequest.headers['Host'].split(':')[0] d = h.split('.')[0] - r = cherrypy.config['openerp.dbfilter'].replace('%h',h).replace('%d',d) - print "h,d",h,d,r - dbs = [i for i in dbs if re.match(r,i)] + r = cherrypy.config['openerp.dbfilter'].replace('%h', h).replace('%d', d) + dbs = [i for i in dbs if re.match(r, i)] return {"db_list": dbs} + @openerpweb.jsonrequest + def progress(self, req, password, id): + return req.session.proxy('db').get_progress(password, id) + + @openerpweb.jsonrequest + 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) + + try: + return req.session.proxy("db").create(*create_attrs) + except xmlrpclib.Fault, e: + if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied': + return {'error': e.faultCode, 'title': 'Create Database'} + return {'error': 'Could not create database !', 'title': 'Create Database'} + + @openerpweb.jsonrequest + def drop(self, req, fields): + password, db = operator.itemgetter( + 'drop_pwd', 'drop_db')( + dict(map(operator.itemgetter('name', 'value'), fields))) + + try: + return req.session.proxy("db").drop(password, db) + except xmlrpclib.Fault, e: + if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied': + return {'error': e.faultCode, 'title': 'Drop Database'} + return {'error': 'Could not drop database !', 'title': 'Drop Database'} + + @openerpweb.httprequest + def backup(self, req, backup_db, backup_pwd, token): + try: + db_dump = base64.decodestring( + req.session.proxy("db").dump(backup_pwd, backup_db)) + cherrypy.response.headers['Content-Type'] = "application/octet-stream; charset=binary" + cherrypy.response.headers['Content-Disposition'] = 'attachment; filename="' + backup_db + '.dump"' + cherrypy.response.cookie['fileToken'] = token + cherrypy.response.cookie['fileToken']['path'] = '/' + return db_dump + except xmlrpclib.Fault, e: + if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied': + return 'Backup Database|' + e.faultCode + return 'Backup Database|Could not generate database backup' + + @openerpweb.httprequest + def restore(self, req, db_file, restore_pwd, new_db): + try: + data = base64.encodestring(db_file.file.read()) + req.session.proxy("db").restore(restore_pwd, new_db, data) + return '' + except xmlrpclib.Fault, e: + if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied': + raise cherrypy.HTTPError(403) + + raise cherrypy.HTTPError() + + @openerpweb.jsonrequest + def change_password(self, req, fields): + old_password, new_password = operator.itemgetter( + 'old_pwd', 'new_pwd')( + dict(map(operator.itemgetter('name', 'value'), fields))) + try: + return req.session.proxy("db").change_admin_password(old_password, new_password) + except xmlrpclib.Fault, e: + if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied': + return {'error': e.faultCode, 'title': 'Change Password'} + return {'error': 'Error, password not changed !', 'title': 'Change Password'} + class Session(openerpweb.Controller): _cp_path = "/base/session" @@ -178,6 +252,16 @@ class Session(openerpweb.Controller): return req.session.model('ir.ui.view_sc').get_sc(req.session._uid, "ir.ui.menu", req.session.eval_context(req.context)) + @openerpweb.jsonrequest + def get_lang_list(self, req): + try: + return { + 'lang_list': (req.session.proxy("db").list_lang() or []), + 'error': "" + } + except Exception, e: + return {"error": e, "title": "Languages"} + @openerpweb.jsonrequest def modules(self, req): # TODO query server for installed web modules @@ -222,7 +306,7 @@ class Session(openerpweb.Controller): context, domain = eval_context_and_domain(req.session, openerpweb.nonliterals.CompoundContext(*(contexts or [])), openerpweb.nonliterals.CompoundDomain(*(domains or []))) - + group_by_sequence = [] for candidate in (group_by_seq or []): ctx = req.session.eval_context(candidate, context) @@ -233,7 +317,7 @@ class Session(openerpweb.Controller): group_by_sequence.append(group_by) else: group_by_sequence.extend(group_by) - + return { 'context': context, 'domain': domain, @@ -246,7 +330,7 @@ class Session(openerpweb.Controller): This method store an action object in the session object and returns an integer identifying that action. The method get_session_action() can be used to get back the action. - + :param the_action: The action to save in the session. :type the_action: anything :return: A key identifying the saved action. @@ -269,7 +353,7 @@ class Session(openerpweb.Controller): """ Gets back a previously saved action. This method can return None if the action was saved since too much time (this case should be handled in a smart way). - + :param key: The key given by save_session_action() :type key: integer :return: The saved action or None. @@ -408,7 +492,7 @@ class Menu(openerpweb.Controller): menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id'], context) menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, '']} menu_items.append(menu_root) - + # make a tree using parent_id menu_items_map = dict((menu_item["id"], menu_item) for menu_item in menu_items) for menu_item in menu_items: @@ -515,7 +599,7 @@ class DataSet(openerpweb.Controller): record_map = dict((record['id'], record) for record in records) return [record_map[id] for id in ids if record_map.get(id)] - + @openerpweb.jsonrequest def load(self, req, model, id, fields): m = req.session.model(model) @@ -678,7 +762,7 @@ class View(openerpweb.Controller): except ValueError: # not a literal return openerpweb.nonliterals.Domain(session, domain) - + def parse_context(self, context, session): """ Parses an arbitrary string containing a context, transforms it to either a literal context or a :class:`openerpweb.nonliterals.Context` @@ -906,3 +990,217 @@ class Action(openerpweb.Controller): def run(self, req, action_id): return clean_action(req.session.model('ir.actions.server').run( [action_id], req.session.eval_context(req.context)), req.session) + +def export_csv(fields, result): + fp = StringIO() + writer = csv.writer(fp, quoting=csv.QUOTE_ALL) + + writer.writerow(fields) + + for data in result: + row = [] + for d in data: + if isinstance(d, basestring): + d = d.replace('\n',' ').replace('\t',' ') + try: + d = d.encode('utf-8') + except: + pass + if d is False: d = None + row.append(d) + writer.writerow(row) + + fp.seek(0) + data = fp.read() + fp.close() + return data + +def export_xls(fieldnames, table): + try: + import xlwt + except ImportError: + common.error(_('Import Error.'), _('Please install xlwt library to export to MS Excel.')) + + workbook = xlwt.Workbook() + worksheet = workbook.add_sheet('Sheet 1') + + for i, fieldname in enumerate(fieldnames): + worksheet.write(0, i, str(fieldname)) + worksheet.col(i).width = 8000 # around 220 pixels + + style = xlwt.easyxf('align: wrap yes') + + for row_index, row in enumerate(table): + for cell_index, cell_value in enumerate(row): + cell_value = str(cell_value) + cell_value = re.sub("\r", " ", cell_value) + worksheet.write(row_index + 1, cell_index, cell_value, style) + + + fp = StringIO() + workbook.save(fp) + fp.seek(0) + data = fp.read() + fp.close() + #return data.decode('ISO-8859-1') + return unicode(data, 'utf-8', 'replace') + +class Export(View): + _cp_path = "/base/export" + + def fields_get(self, req, model): + Model = req.session.model(model) + fields = Model.fields_get(False, req.session.eval_context(req.context)) + return fields + + @openerpweb.jsonrequest + def get_fields(self, req, model, prefix='', name= '', field_parent=None, params={}): + import_compat = params.get("import_compat", False) + + fields = self.fields_get(req, model) + field_parent_type = params.get("parent_field_type",False) + + if import_compat and field_parent_type and field_parent_type == "many2one": + fields = {} + + fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) + records = [] + fields_order = fields.keys() + fields_order.sort(lambda x,y: -cmp(fields[x].get('string', ''), fields[y].get('string', ''))) + + for index, field in enumerate(fields_order): + value = fields[field] + record = {} + if import_compat and value.get('readonly', False): + ok = False + for sl in value.get('states', {}).values(): + for s in sl: + ok = ok or (s==['readonly',False]) + if not ok: continue + + id = prefix + (prefix and '/'or '') + field + nm = name + (name and '/' or '') + value['string'] + record.update(id=id, string= nm, action='javascript: void(0)', + target=None, icon=None, children=[], field_type=value.get('type',False), required=value.get('required', False)) + records.append(record) + + if len(nm.split('/')) < 3 and value.get('relation', False): + if import_compat: + ref = value.pop('relation') + cfields = self.fields_get(req, ref) + if (value['type'] == 'many2many'): + record['children'] = [] + record['params'] = {'model': ref, 'prefix': id, 'name': nm} + + elif value['type'] == 'many2one': + record['children'] = [id + '/id', id + '/.id'] + record['params'] = {'model': ref, 'prefix': id, 'name': nm} + + else: + cfields_order = cfields.keys() + cfields_order.sort(lambda x,y: -cmp(cfields[x].get('string', ''), cfields[y].get('string', ''))) + children = [] + for j, fld in enumerate(cfields_order): + cid = id + '/' + fld + cid = cid.replace(' ', '_') + children.append(cid) + record['children'] = children or [] + record['params'] = {'model': ref, 'prefix': id, 'name': nm} + else: + ref = value.pop('relation') + cfields = self.fields_get(req, ref) + cfields_order = cfields.keys() + cfields_order.sort(lambda x,y: -cmp(cfields[x].get('string', ''), cfields[y].get('string', ''))) + children = [] + for j, fld in enumerate(cfields_order): + cid = id + '/' + fld + cid = cid.replace(' ', '_') + children.append(cid) + record['children'] = children or [] + record['params'] = {'model': ref, 'prefix': id, 'name': nm} + + records.reverse() + return records + + @openerpweb.jsonrequest + def save_export_lists(self, req, name, model, field_list): + result = {'resource':model, 'name':name, 'export_fields': []} + for field in field_list: + result['export_fields'].append((0, 0, {'name': field})) + return req.session.model("ir.exports").create(result, req.session.eval_context(req.context)) + + @openerpweb.jsonrequest + def exist_export_lists(self, req, model): + export_model = req.session.model("ir.exports") + return export_model.read(export_model.search([('resource', '=', model)]), ['name']) + + @openerpweb.jsonrequest + def delete_export(self, req, export_id): + req.session.model("ir.exports").unlink(export_id, req.session.eval_context(req.context)) + return True + + @openerpweb.jsonrequest + def namelist(self,req, model, export_id): + + result = self.get_data(req, model, req.session.eval_context(req.context)) + ir_export_obj = req.session.model("ir.exports") + ir_export_line_obj = req.session.model("ir.exports.line") + + field = ir_export_obj.read(export_id) + fields = ir_export_line_obj.read(field['export_fields']) + + name_list = {} + [name_list.update({field['name']: result.get(field['name'])}) for field in fields] + return name_list + + def get_data(self, req, model, context=None): + ids = [] + context = context or {} + fields_data = {} + proxy = req.session.model(model) + fields = self.fields_get(req, model) + if not ids: + f1 = proxy.fields_view_get(False, 'tree', context)['fields'] + f2 = proxy.fields_view_get(False, 'form', context)['fields'] + + fields = dict(f1) + fields.update(f2) + fields.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) + + def rec(fields): + _fields = {'id': 'ID' , '.id': 'Database ID' } + def model_populate(fields, prefix_node='', prefix=None, prefix_value='', level=2): + fields_order = fields.keys() + fields_order.sort(lambda x,y: -cmp(fields[x].get('string', ''), fields[y].get('string', ''))) + + for field in fields_order: + fields_data[prefix_node+field] = fields[field] + if prefix_node: + fields_data[prefix_node + field]['string'] = '%s%s' % (prefix_value, fields_data[prefix_node + field]['string']) + st_name = fields[field]['string'] or field + _fields[prefix_node+field] = st_name + if fields[field].get('relation', False) and level>0: + fields2 = self.fields_get(req, fields[field]['relation']) + fields2.update({'id': {'string': 'ID'}, '.id': {'string': 'Database ID'}}) + model_populate(fields2, prefix_node+field+'/', None, st_name+'/', level-1) + model_populate(fields) + return _fields + return rec(fields) + + @openerpweb.jsonrequest + def export_data(self, req, model, fields, ids, domain, import_compat=False, export_format="csv", context=None): + context = req.session.eval_context(req.context) + modle_obj = req.session.model(model) + ids = ids or modle_obj.search(domain, context=context) + + field = fields.keys() + result = modle_obj.export_data(ids, field , context).get('datas',[]) + + if not import_compat: + field = [val.strip() for val in fields.values()] + + if export_format == 'xls': + return export_xls(field, result) + else: + return export_csv(field, result) + diff --git a/addons/base/static/lib/jquery.blockUI/jquery.blockUI.js b/addons/base/static/lib/jquery.blockUI/jquery.blockUI.js new file mode 100644 index 00000000000..04db883c76e --- /dev/null +++ b/addons/base/static/lib/jquery.blockUI/jquery.blockUI.js @@ -0,0 +1,499 @@ +/*! + * jQuery blockUI plugin + * Version 2.39 (23-MAY-2011) + * @requires jQuery v1.2.3 or later + * + * Examples at: http://malsup.com/jquery/block/ + * Copyright (c) 2007-2010 M. Alsup + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Thanks to Amir-Hossein Sobhi for some excellent contributions! + */ + +;(function($) { + +if (/1\.(0|1|2)\.(0|1|2)/.test($.fn.jquery) || /^1.1/.test($.fn.jquery)) { + alert('blockUI requires jQuery v1.2.3 or later! You are using v' + $.fn.jquery); + return; +} + +$.fn._fadeIn = $.fn.fadeIn; + +var noOp = function() {}; + +// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle +// retarded userAgent strings on Vista) +var mode = document.documentMode || 0; +var setExpr = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8); +var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent) && !mode; + +// global $ methods for blocking/unblocking the entire page +$.blockUI = function(opts) { install(window, opts); }; +$.unblockUI = function(opts) { remove(window, opts); }; + +// convenience method for quick growl-like notifications (http://www.google.com/search?q=growl) +$.growlUI = function(title, message, timeout, onClose) { + var $m = $('
'); + if (title) $m.append('

'+title+'

'); + if (message) $m.append('

'+message+'

'); + if (timeout == undefined) timeout = 3000; + $.blockUI({ + message: $m, fadeIn: 700, fadeOut: 1000, centerY: false, + timeout: timeout, showOverlay: false, + onUnblock: onClose, + css: $.blockUI.defaults.growlCSS + }); +}; + +// plugin method for blocking element content +$.fn.block = function(opts) { + return this.unblock({ fadeOut: 0 }).each(function() { + if ($.css(this,'position') == 'static') + this.style.position = 'relative'; + if ($.browser.msie) + this.style.zoom = 1; // force 'hasLayout' + install(this, opts); + }); +}; + +// plugin method for unblocking element content +$.fn.unblock = function(opts) { + return this.each(function() { + remove(this, opts); + }); +}; + +$.blockUI.version = 2.39; // 2nd generation blocking at no extra cost! + +// override these in your code to change the default behavior and style +$.blockUI.defaults = { + // message displayed when blocking (use null for no message) + message: '

Please wait...

', + + title: null, // title string; only used when theme == true + draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded) + + theme: false, // set to true to use with jQuery UI themes + + // styles for the message when blocking; if you wish to disable + // these and use an external stylesheet then do this in your code: + // $.blockUI.defaults.css = {}; + css: { + padding: 0, + margin: 0, + width: '30%', + top: '40%', + left: '35%', + textAlign: 'center', + color: '#000', + border: '3px solid #aaa', + backgroundColor:'#fff', + cursor: 'wait' + }, + + // minimal style set used when themes are used + themedCSS: { + width: '30%', + top: '40%', + left: '35%' + }, + + // styles for the overlay + overlayCSS: { + backgroundColor: '#000', + opacity: 0.6, + cursor: 'wait' + }, + + // styles applied when using $.growlUI + growlCSS: { + width: '350px', + top: '10px', + left: '', + right: '10px', + border: 'none', + padding: '5px', + opacity: 0.6, + cursor: 'default', + color: '#fff', + backgroundColor: '#000', + '-webkit-border-radius': '10px', + '-moz-border-radius': '10px', + 'border-radius': '10px' + }, + + // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w + // (hat tip to Jorge H. N. de Vasconcelos) + iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank', + + // force usage of iframe in non-IE browsers (handy for blocking applets) + forceIframe: false, + + // z-index for the blocking overlay + baseZ: 1000, + + // set these to true to have the message automatically centered + centerX: true, // <-- only effects element blocking (page block controlled via css above) + centerY: true, + + // allow body element to be stetched in ie6; this makes blocking look better + // on "short" pages. disable if you wish to prevent changes to the body height + allowBodyStretch: true, + + // enable if you want key and mouse events to be disabled for content that is blocked + bindEvents: true, + + // be default blockUI will supress tab navigation from leaving blocking content + // (if bindEvents is true) + constrainTabKey: true, + + // fadeIn time in millis; set to 0 to disable fadeIn on block + fadeIn: 200, + + // fadeOut time in millis; set to 0 to disable fadeOut on unblock + fadeOut: 400, + + // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock + timeout: 0, + + // disable if you don't want to show the overlay + showOverlay: true, + + // if true, focus will be placed in the first available input field when + // page blocking + focusInput: true, + + // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity) + applyPlatformOpacityRules: true, + + // callback method invoked when fadeIn has completed and blocking message is visible + onBlock: null, + + // callback method invoked when unblocking has completed; the callback is + // passed the element that has been unblocked (which is the window object for page + // blocks) and the options that were passed to the unblock call: + // onUnblock(element, options) + onUnblock: null, + + // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493 + quirksmodeOffsetHack: 4, + + // class name of the message block + blockMsgClass: 'blockMsg' +}; + +// private data and functions follow... + +var pageBlock = null; +var pageBlockEls = []; + +function install(el, opts) { + var full = (el == window); + var msg = opts && opts.message !== undefined ? opts.message : undefined; + opts = $.extend({}, $.blockUI.defaults, opts || {}); + opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {}); + var css = $.extend({}, $.blockUI.defaults.css, opts.css || {}); + var themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {}); + msg = msg === undefined ? opts.message : msg; + + // remove the current block (if there is one) + if (full && pageBlock) + remove(window, {fadeOut:0}); + + // if an existing element is being used as the blocking content then we capture + // its current place in the DOM (and current display style) so we can restore + // it when we unblock + if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) { + var node = msg.jquery ? msg[0] : msg; + var data = {}; + $(el).data('blockUI.history', data); + data.el = node; + data.parent = node.parentNode; + data.display = node.style.display; + data.position = node.style.position; + if (data.parent) + data.parent.removeChild(node); + } + + $(el).data('blockUI.onUnblock', opts.onUnblock); + var z = opts.baseZ; + + // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform; + // layer1 is the iframe layer which is used to supress bleed through of underlying content + // layer2 is the overlay layer which has opacity and a wait cursor (by default) + // layer3 is the message content that is displayed while blocking + + var lyr1 = ($.browser.msie || opts.forceIframe) + ? $('') + : $(''); + + var lyr2 = opts.theme + ? $('') + : $(''); + + var lyr3, s; + if (opts.theme && full) { + s = ''; + } + else if (opts.theme) { + s = ''; + } + else if (full) { + s = ''; + } + else { + s = ''; + } + lyr3 = $(s); + + // if we have a message, style it + if (msg) { + if (opts.theme) { + lyr3.css(themedCSS); + lyr3.addClass('ui-widget-content'); + } + else + lyr3.css(css); + } + + // style the overlay + if (!opts.theme && (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform)))) + lyr2.css(opts.overlayCSS); + lyr2.css('position', full ? 'fixed' : 'absolute'); + + // make iframe layer transparent in IE + if ($.browser.msie || opts.forceIframe) + lyr1.css('opacity',0.0); + + //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el); + var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el); + $.each(layers, function() { + this.appendTo($par); + }); + + if (opts.theme && opts.draggable && $.fn.draggable) { + lyr3.draggable({ + handle: '.ui-dialog-titlebar', + cancel: 'li' + }); + } + + // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling) + var expr = setExpr && (!$.boxModel || $('object,embed', full ? null : el).length > 0); + if (ie6 || expr) { + // give body 100% height + if (full && opts.allowBodyStretch && $.boxModel) + $('html,body').css('height','100%'); + + // fix ie6 issue when blocked element has a border width + if ((ie6 || !$.boxModel) && !full) { + var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth'); + var fixT = t ? '(0 - '+t+')' : 0; + var fixL = l ? '(0 - '+l+')' : 0; + } + + // simulate fixed position + $.each([lyr1,lyr2,lyr3], function(i,o) { + var s = o[0].style; + s.position = 'absolute'; + if (i < 2) { + full ? s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"') + : s.setExpression('height','this.parentNode.offsetHeight + "px"'); + full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"') + : s.setExpression('width','this.parentNode.offsetWidth + "px"'); + if (fixL) s.setExpression('left', fixL); + if (fixT) s.setExpression('top', fixT); + } + else if (opts.centerY) { + if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'); + s.marginTop = 0; + } + else if (!opts.centerY && full) { + var top = (opts.css && opts.css.top) ? parseInt(opts.css.top) : 0; + var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"'; + s.setExpression('top',expression); + } + }); + } + + // show the message + if (msg) { + if (opts.theme) + lyr3.find('.ui-widget-content').append(msg); + else + lyr3.append(msg); + if (msg.jquery || msg.nodeType) + $(msg).show(); + } + + if (($.browser.msie || opts.forceIframe) && opts.showOverlay) + lyr1.show(); // opacity is zero + if (opts.fadeIn) { + var cb = opts.onBlock ? opts.onBlock : noOp; + var cb1 = (opts.showOverlay && !msg) ? cb : noOp; + var cb2 = msg ? cb : noOp; + if (opts.showOverlay) + lyr2._fadeIn(opts.fadeIn, cb1); + if (msg) + lyr3._fadeIn(opts.fadeIn, cb2); + } + else { + if (opts.showOverlay) + lyr2.show(); + if (msg) + lyr3.show(); + if (opts.onBlock) + opts.onBlock(); + } + + // bind key and mouse events + bind(1, el, opts); + + if (full) { + pageBlock = lyr3[0]; + pageBlockEls = $(':input:enabled:visible',pageBlock); + if (opts.focusInput) + setTimeout(focus, 20); + } + else + center(lyr3[0], opts.centerX, opts.centerY); + + if (opts.timeout) { + // auto-unblock + var to = setTimeout(function() { + full ? $.unblockUI(opts) : $(el).unblock(opts); + }, opts.timeout); + $(el).data('blockUI.timeout', to); + } +}; + +// remove the block +function remove(el, opts) { + var full = (el == window); + var $el = $(el); + var data = $el.data('blockUI.history'); + var to = $el.data('blockUI.timeout'); + if (to) { + clearTimeout(to); + $el.removeData('blockUI.timeout'); + } + opts = $.extend({}, $.blockUI.defaults, opts || {}); + bind(0, el, opts); // unbind events + + if (opts.onUnblock === null) { + opts.onUnblock = $el.data('blockUI.onUnblock'); + $el.removeData('blockUI.onUnblock'); + } + + var els; + if (full) // crazy selector to handle odd field errors in ie6/7 + els = $('body').children().filter('.blockUI').add('body > .blockUI'); + else + els = $('.blockUI', el); + + if (full) + pageBlock = pageBlockEls = null; + + if (opts.fadeOut) { + els.fadeOut(opts.fadeOut); + setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut); + } + else + reset(els, data, opts, el); +}; + +// move blocking element back into the DOM where it started +function reset(els,data,opts,el) { + els.each(function(i,o) { + // remove via DOM calls so we don't lose event handlers + if (this.parentNode) + this.parentNode.removeChild(this); + }); + + if (data && data.el) { + data.el.style.display = data.display; + data.el.style.position = data.position; + if (data.parent) + data.parent.appendChild(data.el); + $(el).removeData('blockUI.history'); + } + + if (typeof opts.onUnblock == 'function') + opts.onUnblock(el,opts); +}; + +// bind/unbind the handler +function bind(b, el, opts) { + var full = el == window, $el = $(el); + + // don't bother unbinding if there is nothing to unbind + if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked'))) + return; + if (!full) + $el.data('blockUI.isBlocked', b); + + // don't bind events when overlay is not in use or if bindEvents is false + if (!opts.bindEvents || (b && !opts.showOverlay)) + return; + + // bind anchors and inputs for mouse and key events + var events = 'mousedown mouseup keydown keypress'; + b ? $(document).bind(events, opts, handler) : $(document).unbind(events, handler); + +// former impl... +// var $e = $('a,:input'); +// b ? $e.bind(events, opts, handler) : $e.unbind(events, handler); +}; + +// event handler to suppress keyboard/mouse events when blocking +function handler(e) { + // allow tab navigation (conditionally) + if (e.keyCode && e.keyCode == 9) { + if (pageBlock && e.data.constrainTabKey) { + var els = pageBlockEls; + var fwd = !e.shiftKey && e.target === els[els.length-1]; + var back = e.shiftKey && e.target === els[0]; + if (fwd || back) { + setTimeout(function(){focus(back)},10); + return false; + } + } + } + var opts = e.data; + // allow events within the message content + if ($(e.target).parents('div.' + opts.blockMsgClass).length > 0) + return true; + + // allow events for content that is not being blocked + return $(e.target).parents().children().filter('div.blockUI').length == 0; +}; + +function focus(back) { + if (!pageBlockEls) + return; + var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0]; + if (e) + e.focus(); +}; + +function center(el, x, y) { + var p = el.parentNode, s = el.style; + var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth'); + var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth'); + if (x) s.left = l > 0 ? (l+'px') : '0'; + if (y) s.top = t > 0 ? (t+'px') : '0'; +}; + +function sz(el, p) { + return parseInt($.css(el,p))||0; +}; + +})(jQuery); diff --git a/addons/base/static/lib/jquery.form/jquery.form.js b/addons/base/static/lib/jquery.form/jquery.form.js new file mode 100644 index 00000000000..66ac5142238 --- /dev/null +++ b/addons/base/static/lib/jquery.form/jquery.form.js @@ -0,0 +1,911 @@ +/*! + * jQuery Form Plugin + * version: 2.83 (11-JUL-2011) + * @requires jQuery v1.3.2 or later + * + * Examples and documentation at: http://malsup.com/jquery/form/ + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ +;(function($) { + +/* + Usage Note: + ----------- + Do not use both ajaxSubmit and ajaxForm on the same form. These + functions are intended to be exclusive. Use ajaxSubmit if you want + to bind your own submit handler to the form. For example, + + $(document).ready(function() { + $('#myForm').bind('submit', function(e) { + e.preventDefault(); // <-- important + $(this).ajaxSubmit({ + target: '#output' + }); + }); + }); + + Use ajaxForm when you want the plugin to manage all the event binding + for you. For example, + + $(document).ready(function() { + $('#myForm').ajaxForm({ + target: '#output' + }); + }); + + When using ajaxForm, the ajaxSubmit function will be invoked for you + at the appropriate time. +*/ + +/** + * ajaxSubmit() provides a mechanism for immediately submitting + * an HTML form using AJAX. + */ +$.fn.ajaxSubmit = function(options) { + // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) + if (!this.length) { + log('ajaxSubmit: skipping submit process - no element selected'); + return this; + } + + var method, action, url, $form = this; + + if (typeof options == 'function') { + options = { success: options }; + } + + method = this.attr('method'); + action = this.attr('action'); + url = (typeof action === 'string') ? $.trim(action) : ''; + url = url || window.location.href || ''; + if (url) { + // clean url (don't include hash vaue) + url = (url.match(/^([^#]+)/)||[])[1]; + } + + options = $.extend(true, { + url: url, + success: $.ajaxSettings.success, + type: method || 'GET', + iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' + }, options); + + // hook for manipulating the form data before it is extracted; + // convenient for use with rich editors like tinyMCE or FCKEditor + var veto = {}; + this.trigger('form-pre-serialize', [this, options, veto]); + if (veto.veto) { + log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); + return this; + } + + // provide opportunity to alter form data before it is serialized + if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { + log('ajaxSubmit: submit aborted via beforeSerialize callback'); + return this; + } + + var n,v,a = this.formToArray(options.semantic); + if (options.data) { + options.extraData = options.data; + for (n in options.data) { + if(options.data[n] instanceof Array) { + for (var k in options.data[n]) { + a.push( { name: n, value: options.data[n][k] } ); + } + } + else { + v = options.data[n]; + v = $.isFunction(v) ? v() : v; // if value is fn, invoke it + a.push( { name: n, value: v } ); + } + } + } + + // give pre-submit callback an opportunity to abort the submit + if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { + log('ajaxSubmit: submit aborted via beforeSubmit callback'); + return this; + } + + // fire vetoable 'validate' event + this.trigger('form-submit-validate', [a, this, options, veto]); + if (veto.veto) { + log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); + return this; + } + + var q = $.param(a); + + if (options.type.toUpperCase() == 'GET') { + options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; + options.data = null; // data is null for 'get' + } + else { + options.data = q; // data is the query string for 'post' + } + + var callbacks = []; + if (options.resetForm) { + callbacks.push(function() { $form.resetForm(); }); + } + if (options.clearForm) { + callbacks.push(function() { $form.clearForm(); }); + } + + // perform a load on the target only if dataType is not provided + if (!options.dataType && options.target) { + var oldSuccess = options.success || function(){}; + callbacks.push(function(data) { + var fn = options.replaceTarget ? 'replaceWith' : 'html'; + $(options.target)[fn](data).each(oldSuccess, arguments); + }); + } + else if (options.success) { + callbacks.push(options.success); + } + + options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg + var context = options.context || options; // jQuery 1.4+ supports scope context + for (var i=0, max=callbacks.length; i < max; i++) { + callbacks[i].apply(context, [data, status, xhr || $form, $form]); + } + }; + + // are there files to upload? + var fileInputs = $('input:file', this).length > 0; + var mp = 'multipart/form-data'; + var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); + + // options.iframe allows user to force iframe mode + // 06-NOV-09: now defaulting to iframe mode if file input is detected + if (options.iframe !== false && (fileInputs || options.iframe || multipart)) { + // hack to fix Safari hang (thanks to Tim Molendijk for this) + // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d + if (options.closeKeepAlive) { + $.get(options.closeKeepAlive, function() { fileUpload(a); }); + } + else { + fileUpload(a); + } + } + else { + // IE7 massage (see issue 57) + if ($.browser.msie && method == 'get') { + var ieMeth = $form[0].getAttribute('method'); + if (typeof ieMeth === 'string') + options.type = ieMeth; + } + $.ajax(options); + } + + // fire 'notify' event + this.trigger('form-submit-notify', [this, options]); + return this; + + + // private function for handling file uploads (hat tip to YAHOO!) + function fileUpload(a) { + var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; + var useProp = !!$.fn.prop; + + if (a) { + // ensure that every serialized input is still enabled + for (i=0; i < a.length; i++) { + el = $(form[a[i].name]); + el[ useProp ? 'prop' : 'attr' ]('disabled', false); + } + } + + if ($(':input[name=submit],:input[id=submit]', form).length) { + // if there is an input with a name or id of 'submit' then we won't be + // able to invoke the submit fn on the form (at least not x-browser) + alert('Error: Form elements must not have name or id of "submit".'); + return; + } + + s = $.extend(true, {}, $.ajaxSettings, options); + s.context = s.context || s; + id = 'jqFormIO' + (new Date().getTime()); + if (s.iframeTarget) { + $io = $(s.iframeTarget); + n = $io.attr('name'); + if (n == null) + $io.attr('name', id); + else + id = n; + } + else { + $io = $(' @@ -384,7 +596,7 @@
  • + + '&field=datas&fieldname=name&t=' + (new Date().getTime())"/> @@ -399,7 +611,7 @@ - +
    + + + + + + + + + + + + + + + + + + + + diff --git a/addons/base/static/test/list.js b/addons/base/static/test/list.js index 3d441f190db..ebce7e8535a 100644 --- a/addons/base/static/test/list.js +++ b/addons/base/static/test/list.js @@ -18,15 +18,6 @@ $(document).ready(function () { } }}; - var fuck_that_shit = { - action: { - flags: {} - }, - sidebar: { - set_toolbar: function () {} - } - }; - var openerp; module("ListView", { setup: function () { @@ -42,8 +33,7 @@ $(document).ready(function () { test('render selection checkboxes', 2, function () { var listview = new openerp.base.ListView( - fuck_that_shit, null, - 'qunit-fixture', {model: null, ids: [null, null, null], index: 0}); + null, 'qunit-fixture', {model: null, ids: [null, null, null], index: 0}); listview.on_loaded(fvg); @@ -60,8 +50,7 @@ $(document).ready(function () { }); test('render no checkbox if selectable=false', 1, function () { var listview = new openerp.base.ListView( - fuck_that_shit, null, - 'qunit-fixture', {model: null, ids: [null, null, null], index: 0}, false, + null, 'qunit-fixture', {model: null, ids: [null, null, null], index: 0}, false, {selectable: false}); listview.on_loaded(fvg); @@ -75,8 +64,7 @@ $(document).ready(function () { }); test('select a bunch of records', 2, function () { var listview = new openerp.base.ListView( - fuck_that_shit, null, 'qunit-fixture', - {model: null, ids: [1, 2, 3], index: 0}); + null, 'qunit-fixture', {model: null, ids: [1, 2, 3], index: 0}); listview.on_loaded(fvg); listview.do_fill_table({records: [ @@ -94,8 +82,7 @@ $(document).ready(function () { }); test('render deletion button if list is deletable', 1, function () { var listview = new openerp.base.ListView( - fuck_that_shit, null, 'qunit-fixture', - {model: null, ids: [null, null, null], index: 0}); + null, 'qunit-fixture', {model: null, ids: [null, null, null], index: 0}); listview.on_loaded(fvg); @@ -112,7 +99,7 @@ $(document).ready(function () { 2, function () { var deleted; var listview = new openerp.base.ListView( - fuck_that_shit, null, 'qunit-fixture', + null, 'qunit-fixture', {model: null, unlink: function (ids) { deleted = ids; }, ids: [1, 2, 3], index: 0}); @@ -132,7 +119,7 @@ $(document).ready(function () { test('multiple records deletion', 1, function () { var deleted; var listview = new openerp.base.ListView( - fuck_that_shit, null, 'qunit-fixture', + null, 'qunit-fixture', {model: null, unlink: function (ids) { deleted = ids; }, ids: [1, 2, 3], index: 0}); diff --git a/addons/base_calendar/__openerp__.py b/addons/base_calendar/__openerp__.py index a01cf0df8cf..02e75b5ebef 100644 --- a/addons/base_calendar/__openerp__.py +++ b/addons/base_calendar/__openerp__.py @@ -1,55 +1,14 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - { - "name" : "Basic Calendar Functionality", - "version" : "1.0", - "depends" : ["base"], - 'description': """ -This is a full-featured calendar system. -======================================== - -It supports: - - Calendar of events - - Alerts (create requests) - - Recurring events - - Invitations to people""", - "author" : "OpenERP SA", - 'category': 'Tools', - 'website': 'http://www.openerp.com', - "init_xml" : [ - 'base_calendar_data.xml' + "name": "Base calendar", + "version": "2.0", + "depends": ['base'], + "js": [ + 'static/lib/dhtmlxScheduler/codebase/dhtmlxscheduler.js', + 'static/lib/dhtmlxScheduler/codebase/ext/dhtmlxscheduler_minical.js', + 'static/src/js/calendar.js' ], - "demo_xml" : [], - "update_xml" : [ - 'security/calendar_security.xml', - 'security/ir.model.access.csv', - 'wizard/base_calendar_invite_attendee_view.xml', - 'base_calendar_view.xml' - ], - "test" : ['test/base_calendar_test.yml'], - "installable" : True, - "active" : False, - "certificate" : "00694071962960352821", - 'images': ['images/base_calendar1.jpeg','images/base_calendar2.jpeg','images/base_calendar3.jpeg','images/base_calendar4.jpeg',], + "css": ['static/lib/dhtmlxScheduler/codebase/dhtmlxscheduler.css', + 'static/lib/dhtmlxScheduler/codebase/ext/dhtmlxscheduler_ext.css' + ], + 'active': True } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/base_calendar/controllers/main.py b/addons/base_calendar/controllers/main.py index a54d75c5b9b..28c871bf2b4 100644 --- a/addons/base_calendar/controllers/main.py +++ b/addons/base_calendar/controllers/main.py @@ -1,356 +1,10 @@ from base.controllers.main import View -import openerpweb, time, math, re, datetime as DT, pytz - -COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400', - '#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f', - '#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00', - '#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900', ] - -_colorline = ['#%02x%02x%02x' % (25 + ((r + 10) % 11) * 23, 5 + ((g + 1) % 11) * 20, 25 + ((b + 4) % 11) * 23) for r in range(11) for g in range(11) for b in range(11) ] - -DT_SERVER_FORMATS = { - 'datetime' : '%Y-%m-%d %H:%M:%S', - 'date' : '%Y-%m-%d', - 'time' : '%H:%M:%S' -} - -DT_FORMAT_INFO = {'datetime' : ('%Y-%m-%d %H:%M:%S', DT.datetime, 0, 6), - 'date': ('%Y-%m-%d', DT.date, 0, 3), - 'time': ('%H:%M:%S', DT.time, 3, 6)} - -def choice_colors(n): - if n > len(COLOR_PALETTE): - return _colorline[0:-1:len(_colorline) / (n + 1)] - elif n: - return COLOR_PALETTE[:n] - return [] +import openerpweb class CalendarView(View): _cp_path = "/base_calendar/calendarview" - mode = 'month' - date_start = None - date_delay = None - date_stop = None - color_field = None - day_length = 8 - use_search = False - selected_day = None - date_format = '%Y-%m-%d' - info_fields = [] - fields = {} - events = [] - - colors = {} - color_values = [] - - remote_timezone = 'utc' - client_timezone = False - - calendar_fields = {} - concurrency_info = None - - ids = [] - model = '' - domain = [] - context = {} - @openerpweb.jsonrequest - def load(self, req, model, view_id): - fields_view = self.fields_view_get(req, model, view_id, 'calendar') - return {'fields_view':fields_view} - - def convert(self, event): - fields = [self.date_start] - if self.date_stop: - fields.append(self.date_stop) - - for fld in fields: - fld_type = self.fields[fld]['type'] - fmt = DT_SERVER_FORMATS[fld_type] - if event[fld] and fmt: - event[fld] = time.strptime(event[fld], fmt) - - # default start/stop time is 9:00 AM / 5:00 PM - if fld_type == 'date' and event[fld]: - ds = list(event[fld]) - if fld == self.date_start: - ds[3] = 9 - elif fld == self.date_stop: - ds[3] = 17 - event[fld] = tuple(ds) - - - @openerpweb.jsonrequest - def schedule_events(self, req, **kw): - self.model = kw['model'] - self.mode = kw.get('mode') or self.mode or 'month' - self.fields = kw['fields'] - self.color_field = kw.get('color_field') or self.color_field or None - self.colors = kw.get('colors') or {} - self.calendar_fields = kw['calendar_fields'] - self.info_fields = kw['info_fields'] - self.date_start = self.calendar_fields['date_start']['name'] - self.domain = kw.get('domain') or [] - - self.remote_timezone = req.session.remote_timezone - self.client_timezone = req.session.client_timezone - - if self.calendar_fields.get('date_stop'): - self.date_stop = self.calendar_fields['date_stop']['name'] - - if self.calendar_fields.get('date_delay'): - self.date_delay = self.calendar_fields['date_delay']['name'] - - model = req.session.model(self.model) - event_ids = model.search(self.domain) - - self.events = model.read(event_ids, self.fields.keys()) - result = [] - self.date_format = req.session._lang and req.session._lang['date_format'] - - if self.color_field: - for evt in self.events: - key = evt[self.color_field] - name = key - value = key - if isinstance(key, list): # M2O, XMLRPC returns List instead of Tuple - name = key[0] - value = key[-1] - evt[self.color_field] = key = key[-1] - if isinstance(key, tuple): # M2O - value, name = key - self.colors[key] = (name, value, None) - - colors = choice_colors(len(self.colors)) - for i, (key, value) in enumerate(self.colors.items()): - self.colors[key] = [value[0], value[1], colors[i]] - - for evt in self.events: - self.convert(evt) - a = self.get_event_widget(evt) - result.append(a) - - return {'result':result,'sidebar':self.colors} - - def parsedatetime(self, string): - - kind = 'datetime' - - if '-' in string and ':' in string: - kind = 'datetime' - elif '-' in string: - kind = 'date' - elif ':' in string: - kind = 'time' - - fmt, obj, i, j = DT_FORMAT_INFO[kind] - return obj(*time.strptime(string, fmt)[i:j]) - - def parse_datetime(self, value, kind="datetime", as_timetuple=False): - server_format = DT_SERVER_FORMATS[kind] - local_format = self.date_format - if not value: - return False - - if isinstance(value, (time.struct_time, tuple)): - value = time.strftime(local_format, value) - - try: - value = time.strptime(value, local_format) - except ValueError: - try: - # might be in server format already (e.g. filter domain) - value = time.strptime(value, server_format) - except ValueError: - try: - dt = list(time.localtime()) - dt[2] = int(value) - value = tuple(dt) - except: - return False - - if kind == "datetime": - try: - value = self.tz_convert(value, 'parse') - except Exception,e: - print "*******************Error in timezone parsing *********",e - - if as_timetuple: - return value - - return time.strftime(server_format, value) - - - @openerpweb.jsonrequest - def edit_events(self, req,**kw): - data = {} - ds = self.parsedatetime(kw['start_date']) - de = self.parsedatetime(kw['end_date']) - data[kw['calendar_fields']['date_start']['name']] = self.parse_datetime(ds.timetuple()) - - if 'date_stop' in kw['calendar_fields']: - data[kw['calendar_fields']['date_stop']['name']] = self.parse_datetime(de.timetuple()) - elif 'date_delay' in kw['calendar_fields']: - day_length = kw['calendar_fields']['day_length'] - - tds = time.mktime(ds.timetuple()) - tde = time.mktime(de.timetuple()) - - n = (tde - tds) / (60 * 60) - - if n > day_length: - d = math.floor(n / 24) - h = n % 24 - - n = d * day_length + h - - data[kw['calendar_fields']['date_delay']['name']] = n - error = None - try: - req.session.model(kw['model']).write([int(kw['id'])], data) - except Exception, e: - error = e - return error - - def tz_convert(self, struct_time, action): - # if no client timezone is configured, consider the client is in the same - # timezone as the server - lzone = pytz.timezone(self.client_timezone or self.remote_timezone) - szone = pytz.timezone(self.remote_timezone) - dt = DT.datetime.fromtimestamp(time.mktime(struct_time)) - - if action == 'parse': - fromzone = lzone - tozone = szone - elif action == 'format': - fromzone = szone - tozone = lzone - else: - raise Exception("_tz_convert action should be 'parse' or 'format'. Not '%s'" % (action, )) - - localized_original_datetime = fromzone.localize(dt, is_dst=True) - destination_datetime = localized_original_datetime.astimezone(tozone) - return destination_datetime.timetuple() - - def format_datetime(self, value, kind="datetime", as_timetuple=False): - """Convert date value to the local datetime considering timezone info. - - @param value: the date value - @param kind: type of the date value (date, time or datetime) - @param as_timetuple: return timetuple - - @type value: basestring or time.time_tuple) - - @return: string or timetuple - """ - - server_format = DT_SERVER_FORMATS[kind] - local_format = self.date_format - - if not value: - return '' - - if isinstance(value, (time.struct_time, tuple)): - value = time.strftime(server_format, value) - - if isinstance(value, DT.datetime): - value = value - try: - value = DT.datetime.strptime(value[:10], server_format) - return value.strftime(local_format) - except: - return '' - - value = value.strip() - - # remove trailing miliseconds - value = re.sub("(.*?)(\s+\d{2}:\d{2}:\d{2})(\.\d+)?$", "\g<1>\g<2>", value) - - # add time part in value if missing - if kind == 'datetime' and not re.search('\s+\d{2}:\d{2}:\d{2}?$', value): - value += ' 00:00:00' - - # remove time part from value - elif kind == 'date': - value = re.sub('\s+\d{2}:\d{2}:\d{2}(\.\d+)?$', '', value) - - value = time.strptime(value, server_format) - - if kind == "datetime": - try: - value = self.tz_convert(value, 'format') - except Exception, e: - print "\n\n\n************ Error in timezone formatting", e - - if as_timetuple: - return value - - return time.strftime(local_format, value) - - def get_event_widget(self, event): - title = '' # the title - description = [] # the description - - if self.info_fields: - - f = self.info_fields[0] - s = event[f] - - if isinstance(s, (tuple, list)): s = s[-1] - - title = s - for f in self.info_fields[1:]: - s = event[f] - if isinstance(s, (tuple, list)): - s = s[-1] - if s: - description.append(str(s)) - - starts = event.get(self.date_start) - ends = event.get(self.date_delay) or 1.0 - span = 0 - - if starts and ends: - - n = 0 - h = ends - - if ends == self.day_length: - span = 1 - - elif ends > self.day_length: - n = ends / self.day_length - h = ends % self.day_length - - n = int(math.floor(n)) - - if h > 0: - span = n + 1 - else: - span = n - ends = time.localtime(time.mktime(starts) + (h * 60 * 60) + (n * 24 * 60 * 60)) - - if starts and self.date_stop: - - ends = event.get(self.date_stop) - if not ends: - ends = time.localtime(time.mktime(starts) + 60 * 60) - - tds = time.mktime(starts) - tde = time.mktime(ends) - - if tds >= tde: - tde = tds + 60 * 60 - ends = time.localtime(tde) - - n = (tde - tds) / (60 * 60) - - if n >= self.day_length: - span = math.ceil(n / 24) - - starts = self.format_datetime(starts, "datetime", True) - ends = self.format_datetime(ends, "datetime", True) - title = title.strip() - description = ', '.join(description).strip() - return {'id': event['id'], 'start_date': str(DT.datetime(*starts[:6])), 'end_date': str(DT.datetime(*ends[:6])), 'text': title, 'title': description, 'color': self.colors[event[self.color_field]][-1]} + def load(self, req, model, view_id, toolbar=False): + fields_view = self.fields_view_get(req, model, view_id, 'calendar', toolbar=toolbar) + return {'fields_view': fields_view} diff --git a/addons/base_calendar/static/src/js/calendar.js b/addons/base_calendar/static/src/js/calendar.js index 82f59e7c0cf..ed16ea998e3 100644 --- a/addons/base_calendar/static/src/js/calendar.js +++ b/addons/base_calendar/static/src/js/calendar.js @@ -7,218 +7,390 @@ QWeb.add_template('/base_calendar/static/src/xml/base_calendar.xml'); openerp.base.views.add('calendar', 'openerp.base_calendar.CalendarView'); openerp.base_calendar.CalendarView = openerp.base.View.extend({ // Dhtmlx scheduler ? - init: function(parent, element_id, dataset, view_id) { + init: function(parent, element_id, dataset, view_id, options) { this._super(parent, element_id); - this.view_manager = parent || new openerp.base.NullViewManager(); + this.set_default_options(); this.dataset = dataset; - this.dataset_index = 0; this.model = dataset.model; this.view_id = view_id; - this.fields_view = {}; - this.widgets = {}; - this.widgets_counter = 0; - this.fields = this.dataset.fields ? this.dataset.fields: {}; - this.datarecord = {}; - this.name = ""; - this.date_start = ""; - this.date_delay = ""; - this.date_stop = ""; - this.color_field = ""; - this.day_lenth = 8; - this.colors = []; - this.color_values = []; - this.calendar_fields = {}; - this.info_fields = []; - this.domain = this.dataset._domain ? this.dataset._domain: []; - this.context = {}; - }, - start: function() { - this.rpc("/base_calendar/calendarview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded); - }, - on_loaded: function(result) { - var self = this; - var params = {}; - this.fields_view = result.fields_view; - this.name = this.fields_view.name || this.fields_view.arch.attrs.string; - this.view_id = this.fields_view.view_id; - - this.date_start = this.fields_view.arch.attrs.date_start; - this.date_delay = this.fields_view.arch.attrs.date_delay; - this.date_stop = this.fields_view.arch.attrs.date_stop; - - this.colors = this.fields_view.arch.attrs.colors; - this.day_length = this.fields_view.arch.attrs.day_length || 8; - this.color_field = this.fields_view.arch.attrs.color; - this.fields = this.fields_view.fields; - - //* Calendar Fields * - this.calendar_fields['date_start'] = {'name': this.date_start, 'kind': this.fields[this.date_start]['type']}; - - if(this.date_delay) - this.calendar_fields['date_delay'] = {'name': this.date_delay, 'kind': this.fields[this.date_delay]['type']}; - - if(this.date_stop) - this.calendar_fields['date_stop'] = {'name': this.date_stop, 'kind': this.fields[this.date_stop]['type']}; - - this.calendar_fields['day_length'] = this.day_length; - //* ------- * - - for(var fld=0;fld',{'width':'100%','cellspacing': 0, 'cellpadding': 0, 'id':'cal-sidebar-option'}) - ) - for(s in sidebar) { - jQuery('#cal-sidebar-option').append( - jQuery('').append( - jQuery('

    -
    +
    @@ -800,7 +1012,7 @@ -
    @@ -849,29 +1061,6 @@ - - - - - - - @@ -946,4 +1135,145 @@ .unwrap(); + + + Export + + + +
    + + + + + + + + + + +
    + This wizard will export all data that matches the current search criteria to a CSV file. + You can export all data or only the fields that can be reimported after modification. +
    + + + + + + + + +
    + + + +
    +
    + + + + + + + + + + + +
    Available fieldsFields to export + Save fields list +
    +
    +
    +
    +
    +
    + + + + + + + + + + +
    + +
    + +
    + +
    +
    + +
    +
    +
    + + + + + +
    Name
    +
    + + +
    + + + + + + + + + + +
    &nbsp; + + + + + + + + + +
    +
    + +
    ').append( - jQuery('
    ') - .append( - jQuery('', - { - 'type': 'checkbox', - 'id':sidebar[s][0], - 'value':sidebar[s][0] - }).bind('click',function(){ - self.reload_scheduler() - }), - sidebar[s][1] - ) - .css('background-color',sidebar[s][sidebar[s].length-1]) - ) - ) - ) - } - }, - - convert_date_format: function(start_date, end_date) { - var params = {}; - params['start_date'] = start_date.getFullYear() +'-' + start_date.getMonth()+'-' + start_date.getDate()+' '+start_date.getHours()+':'+start_date.getMinutes()+':'+start_date.getSeconds(); - if(end_date) { - params['end_date'] = end_date.getFullYear() +'-' + end_date.getMonth()+'-' + end_date.getDate()+' '+end_date.getHours()+':'+end_date.getMinutes()+':'+end_date.getSeconds(); - } - return params; - }, - - edit_event: function(evt_id, evt_object) { - var dates = this.convert_date_format(evt_object.start_date, evt_object.end_date); - this.rpc( - '/base_calendar/calendarview/edit_events', - { - 'start_date': dates.start_date, - 'end_date': dates.end_date, - 'id': evt_id, - 'model': this.model, - 'info_fields': this.info_fields, - 'fields': this.fields, - 'calendar_fields': this.calendar_fields - } - ); - }, - - mini_calendar: function() { - - if(scheduler.isCalendarVisible()) { - scheduler.destroyCalendar(); - } else { - scheduler.renderCalendar({ - position:"dhx_minical_icon", - date:scheduler._date, - navigation:true, - handler:function(date,calendar){ - scheduler.setCurrentView(date); - scheduler.destroyCalendar() - } - }); - } - }, - - reload_scheduler: function() { -// self.color_field - console.log('Reload Scheduler>>>') - }, - do_search: function (domains, contexts, groupbys) { - this.notification.notify("Searching caldendar"); + this.domain = this.dataset.domain || []; + this.context = this.dataset.context || {}; + this.has_been_loaded = $.Deferred(); + this.options = options || {}; }, - do_show: function () { - this.$element.show(); + start: function() { + this.rpc("/base_calendar/calendarview/load", {"model": this.model, "view_id": this.view_id, 'toolbar': true}, this.on_loaded); + }, + on_loaded: function(data) { + this.calendar_fields = {}; + this.ids = this.dataset.ids; + this.color_values = []; + this.info_fields = []; + + this.fields_view = data.fields_view; + this.name = this.fields_view.name || this.fields_view.arch.attrs.string; + this.view_id = this.fields_view.view_id; + + this.date_start = this.fields_view.arch.attrs.date_start; + this.date_delay = this.fields_view.arch.attrs.date_delay; + this.date_stop = this.fields_view.arch.attrs.date_stop; + + this.colors = this.fields_view.arch.attrs.colors; + this.day_length = this.fields_view.arch.attrs.day_length || 8; + this.color_field = this.fields_view.arch.attrs.color; + this.fields = this.fields_view.fields; + + //* Calendar Fields * + this.calendar_fields['date_start'] = {'name': this.date_start, 'kind': this.fields[this.date_start]['type']}; + + if (this.date_delay) { + this.calendar_fields['date_delay'] = {'name': this.date_delay, 'kind': this.fields[this.date_delay]['type']}; + } + + if (this.date_stop) { + this.calendar_fields['date_stop'] = {'name': this.date_stop, 'kind': this.fields[this.date_stop]['type']}; + } + + for (var fld = 0; fld < this.fields_view.arch.children.length; fld++) { + this.info_fields.push(this.fields_view.arch.children[fld].attrs.name); + } + this.$element.html(QWeb.render("CalendarView", {"fields_view": this.fields_view})); + + if (this.options.sidebar && this.options.sidebar_id) { + this.sidebar = new openerp.base.Sidebar(this, this.options.sidebar_id); + this.sidebar.start(); + this.sidebar.navigator = new openerp.base_calendar.SidebarNavigator(this.sidebar, this.sidebar.add_section('navigator', "Navigator"), this); + this.sidebar.responsible = new openerp.base_calendar.SidebarResponsible(this.sidebar, this.sidebar.add_section('responsible', "Responsible"), this); + 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.init_scheduler(); + this.load_scheduler(); + this.has_been_loaded.resolve(); + }, + 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"; + } + + this.mode = this.mode || 'month'; + + scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views + + // Initialize Sceduler + scheduler.init('openerp_scheduler', null, this.mode); + + scheduler.attachEvent('onEventAdded', this.do_create_event); + scheduler.attachEvent('onEventDeleted', this.do_delete_event); + scheduler.attachEvent('onEventChanged', this.do_save_event); + + scheduler.renderCalendar({ + container: this.sidebar.navigator.element_id, + navigation: true, + date: scheduler._date, + handler: function(date, calendar) { + scheduler.setCurrentView(date, 'day'); + } + }); + }, + resize_scheduler: function() { + scheduler.setCurrentView(scheduler._date); + }, + load_scheduler: function() { + var self = this; + this.dataset.read_slice([], 0, false, function(events) { + if (self.session.locale_code) { + // TODO: replace $LAB + $LAB.setOptions({AlwaysPreserveOrder: true}).script([ + '/base_calendar/static/lib/dhtmlxScheduler/sources/locale_' + self.session.locale_code + '.js', + '/base_calendar/static/lib/dhtmlxScheduler/sources/locale_recurring_' + self.session.locale_code + '.js' + ]).wait(function() { + self.schedule_events(events); + }); + } else { + self.schedule_events(events); + } + }); + }, + schedule_events: function(events) { + var self = this; + scheduler.clearAll(); + + //To parse Events we have to convert date Format + var res_events = [], + sidebar_items = [], + sidebar_ids = []; + for (var e = 0; e < events.length; e++) { + var evt = events[e]; + if (!evt[this.date_start]) { + this.notification.warn("Start date is not defined for event :", evt['id']); + break; + } + + if (this.fields[this.date_start]['type'] == 'date') { + evt[this.date_start] = openerp.base.parse_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss'); + } + if (this.date_stop && evt[this.date_stop] && this.fields[this.date_stop]['type'] == 'date') { + evt[this.date_stop] = openerp.base.parse_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss'); + } + if (this.color_field) { + var user = evt[this.color_field]; + if (user) { + if (_.indexOf(sidebar_ids, user[0]) === -1) { + sidebar_items.push({ + id: user[0], + name: user[1], + // TODO: use color table + color: '#dddddd' + }); + sidebar_ids.push(user[0]); + } + } + } + res_events.push(this.convert_event(evt)); + } + + scheduler.parse(res_events, 'json'); + this.resize_scheduler(); + this.sidebar.responsible.on_events_loaded(sidebar_items); + }, + convert_event: function(event) { + var starts = event[this.date_start], + ends = event[this.date_delay] || 1, + span = 0, + res_text = '', + res_description = []; + + var parse_start_date = openerp.base.parse_datetime(starts); + if (event[this.date_stop]) { + var parse_end_date = openerp.base.parse_datetime(event[this.date_stop]); + } + if (this.info_fields) { + var fld = event[this.info_fields[0]]; + + if (typeof fld == 'object') { + res_text = fld[fld.length -1]; + } else { + res_text = fld; + } + + var sliced_info_fields = this.info_fields.slice(1); + for (sl_fld in sliced_info_fields) { + var slc_fld = event[sliced_info_fields[sl_fld]]; + + if (typeof slc_fld == 'object') { + res_description.push(slc_fld[slc_fld.length - 1]); + } else { + if(slc_fld) res_description.push(slc_fld); + } + } + } + + if (starts && ends) { + var n = 0, + h = ends; + if (ends == this.day_length) { + span = 1; + } else if (ends > this.day_length) { + n = ends / this.day_length; + h = ends % this.day_length; + n = parseInt(Math.floor(n)); + + if (h > 0) { + span = n + 1; + } else { + span = n; + } + } + var start = parse_start_date.setTime((parse_start_date.getTime() + (h * 60 * 60) + (n * 24 * 60 * 60))); + ends = parse_start_date; + } + + if (starts && this.date_stop) { + ends = parse_end_date; + if (event[this.date_stop] == undefined) { + var start = parse_start_date.setTime((parse_start_date.getTime() + (h * 60 * 60) + (n * 24 * 60 * 60))); + ends = parse_start_date; + } + var tds = parse_start_date.getTime(), + tde = parse_end_date.getTime(); + + if (tds >= tde) { + tde = tds + 60 * 60; + parse_end_date.setTime(tde); + ends = parse_end_date; + } + n = (tde - tds) / (60 * 60); + if (n >= this.day_length) { + span = Math.ceil(n / 24); + } + } + return { + 'start_date': parse_start_date.toString('yyyy-MM-dd HH:mm:ss'), + 'end_date': ends.toString('yyyy-MM-dd HH:mm:ss'), + 'text': res_text, + 'id': event['id'], + 'title': res_description.join() + } + }, + do_create_event: function(event_id, event_obj) { + var self = this, + data = this.get_event_data(event_obj); + this.dataset.create(data, function(r) { + var id = parseInt(r.result, 10); + self.dataset.ids.push(id); + scheduler.changeEventId(event_id, id); + }, 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); + }, + do_delete_event: function(event_id, event_obj) { + var self = this; + this.dataset.unlink(event_id, function(r) { + }); + }, + get_event_data: function(event_obj) { + var data = { + name: event_obj.text + }; + var date_format = this.calendar_fields.date_start.kind == 'time' ? 'HH:mm:ss' : 'yyyy-MM-dd HH:mm:ss'; + data[this.date_start] = event_obj.start_date.toString(date_format); + if (this.date_stop) { + data[this.date_stop] = event_obj.end_date.toString(date_format); + } + if (this.date_delay) { + var tds = (event_obj.start_date.getOrdinalNumber() / 1e3 >> 0) - (event_obj.start_date.getOrdinalNumber() < 0); + var tde = (event_obj.end_date.getOrdinalNumber() / 1e3 >> 0) - (event_obj.end_date.getOrdinalNumber() < 0); + var n = (tde - tds) / (60 * 60); + if (n > this.day_length) { + var d = Math.floor(n / 24), + h = n % 24; + n = d * this.day_length + h; + } + data[this.date_delay] = n; + } + return data; + }, + do_search: function(domains, contexts, groupbys) { + var self = this; + this.rpc('/base/session/eval_domain_and_context', { + domains: domains, + contexts: contexts, + group_by_seq: groupbys + }, function (results) { + // TODO: handle non-empty results.group_by with read_group + self.dataset.context = self.context = results.context; + self.dataset.domain = self.domain = results.domain; + self.dataset.read_slice(_.keys(self.fields), 0, self.limit, function(events) { + self.schedule_events(events); + }); + }); + }, + do_show: function () { + var self = this; + $.when(this.has_been_loaded).then(function() { + self.$element.show(); + if (self.sidebar) { + self.sidebar.$element.show(); + } + }); }, - do_hide: function () { this.$element.hide(); + if (this.sidebar) { + this.sidebar.$element.hide(); + } + }, + popup_event: function(event_id) { + var self = this; + if (event_id) event_id = parseInt(event_id, 10); + var action = { + res_model: this.dataset.model, + res_id: event_id, + views: [[false, 'form']], + type: 'ir.actions.act_window', + view_type: 'form', + view_mode: 'form', + flags : { + search_view: false, + sidebar : false, + views_switcher : false, + action_buttons : false, + pager: false + } + } + var element_id = _.uniqueId("act_window_dialog"); + var dialog = $('
    ', { + 'id': element_id + }).dialog({ + modal: true, + width: 'auto', + height: 'auto', + buttons: { + Cancel: function() { + $(this).dialog("destroy"); + }, + Save: function() { + var view_manager = action_manager.viewmanager; + var _dialog = this; + view_manager.views[view_manager.active_view].controller.do_save(function(r) { + $(_dialog).dialog("destroy"); + // self.start(); + self.load_scheduler(); + }) + } + } + }); + var action_manager = new openerp.base.ActionManager(this, element_id); + action_manager.start(); + action_manager.do_action(action); + //Default_get + if (!event_id) { + this.dataset.index = null; + } } }); -//openerp.base.Action = openerp.base.Action.extend({ -// do_action_window: function(action) { -// this._super.apply(this,arguments); -// for(var i = 0; i < action.views.length; i++) { -// if(action.views[i][1] == "calendar") { -// this.calendar_id = action.views[i][0]; -// break; -// } -// } -// // IF there is a view calender -// // if(this.calendar_id -// }, -//}); +openerp.base_calendar.SidebarResponsible = openerp.base.Widget.extend({ + init: function(parent, element_id, view) { + this._super(parent, element_id); + this.view = view; + }, + on_events_loaded: function(users) { + this.$element.html(QWeb.render('CalendarView.sidebar.responsible', { users : users })); + // TODO: bind checkboxes reload sheduler + } +}); + +openerp.base_calendar.SidebarNavigator = openerp.base.Widget.extend({ + init: function(parent, element_id, view) { + this._super(parent, element_id); + this.view = view; + }, + on_events_loaded: function(events) { + } +}); }; diff --git a/addons/base_calendar/static/src/xml/base_calendar.xml b/addons/base_calendar/static/src/xml/base_calendar.xml index 02d67be05bb..757c4925cd1 100644 --- a/addons/base_calendar/static/src/xml/base_calendar.xml +++ b/addons/base_calendar/static/src/xml/base_calendar.xml @@ -1,33 +1,26 @@ diff --git a/addons/base_diagram/static/src/js/diagram.js b/addons/base_diagram/static/src/js/diagram.js index af56c256ee7..b992b5cbf42 100644 --- a/addons/base_diagram/static/src/js/diagram.js +++ b/addons/base_diagram/static/src/js/diagram.js @@ -5,7 +5,7 @@ openerp.base.diagram = function (openerp) { openerp.base.views.add('diagram', 'openerp.base.DiagramView'); -openerp.base.DiagramView = openerp.base.Controller.extend({ +openerp.base.DiagramView = openerp.base.Widget.extend({ init: function(view_manager, session, element_id, dataset, view_id){ this._super(session, element_id); this.view_manager = view_manager; diff --git a/addons/base_gantt/static/src/js/gantt.js b/addons/base_gantt/static/src/js/gantt.js index cf0ce449d60..09174e25ab7 100644 --- a/addons/base_gantt/static/src/js/gantt.js +++ b/addons/base_gantt/static/src/js/gantt.js @@ -5,7 +5,7 @@ openerp.base_gantt = function (openerp) { QWeb.add_template('/base_gantt/static/src/xml/base_gantt.xml'); openerp.base.views.add('gantt', 'openerp.base_gantt.GanttView'); -openerp.base_gantt.GanttView = openerp.base.Controller.extend({ +openerp.base_gantt.GanttView = openerp.base.Widget.extend({ init: function(view_manager, session, element_id, dataset, view_id) { diff --git a/addons/web_mobile/static/src/js/chrome_mobile.js b/addons/web_mobile/static/src/js/chrome_mobile.js index 138d37136a9..622649bb5f8 100644 --- a/addons/web_mobile/static/src/js/chrome_mobile.js +++ b/addons/web_mobile/static/src/js/chrome_mobile.js @@ -1,5 +1,5 @@ openerp.web_mobile.chrome_mobile = function(openerp) { -openerp.web_mobile.Shortcuts = openerp.base.Controller.extend({ +openerp.web_mobile.Shortcuts = openerp.base.Widget.extend({ init: function(session, element_id) { this._super(session, element_id); }, @@ -19,7 +19,7 @@ openerp.web_mobile.Shortcuts = openerp.base.Controller.extend({ this.listview.start(); } }); -openerp.web_mobile.Header = openerp.base.Controller.extend({ +openerp.web_mobile.Header = openerp.base.Widget.extend({ init: function(session, element_id) { this._super(session, element_id); }, @@ -36,7 +36,7 @@ openerp.web_mobile.Header = openerp.base.Controller.extend({ } } }); -openerp.web_mobile.Secondary = openerp.base.Controller.extend({ +openerp.web_mobile.Secondary = openerp.base.Widget.extend({ init: function(session, element_id, secondary_menu_id) { this._super(session, element_id); this.data = secondary_menu_id; @@ -74,7 +74,7 @@ openerp.web_mobile.Secondary = openerp.base.Controller.extend({ } }); -openerp.web_mobile.Menu = openerp.base.Controller.extend({ +openerp.web_mobile.Menu = openerp.base.Widget.extend({ init: function(session, element_id, secondary_menu_id) { this._super(session, element_id); this.secondary_menu_id = secondary_menu_id; @@ -106,7 +106,7 @@ openerp.web_mobile.Menu = openerp.base.Controller.extend({ this.secondary.start(); } }); -openerp.web_mobile.Options = openerp.base.Controller.extend({ +openerp.web_mobile.Options = openerp.base.Widget.extend({ init: function(session, element_id) { this._super(session, element_id); }, @@ -121,7 +121,7 @@ openerp.web_mobile.Options = openerp.base.Controller.extend({ this.login.start(); } }); -openerp.web_mobile.Login = openerp.base.Controller.extend({ +openerp.web_mobile.Login = openerp.base.Widget.extend({ init: function(session, element_id) { this._super(session, element_id); }, @@ -129,7 +129,7 @@ openerp.web_mobile.Login = openerp.base.Controller.extend({ var self = this; jQuery("#oe_header").children().remove(); - this.rpc("/base/session/get_databases_list", {}, function(result) { + this.rpc("/base/database/get_list", {}, function(result) { self.db_list = result.db_list; self.$element.html(QWeb.render("Login", self)); self.$element.find('#database').click(self.on_db_select); @@ -190,7 +190,7 @@ openerp.web_mobile.Login = openerp.base.Controller.extend({ }); } }); -openerp.web_mobile.MobileWebClient = openerp.base.Controller.extend({ +openerp.web_mobile.MobileWebClient = openerp.base.Widget.extend({ init: function(element_id) { var self = this; this._super(null, element_id); @@ -214,4 +214,4 @@ openerp.web_mobile.mobilewebclient = function(element_id) { client.start(); return client; }; -} \ No newline at end of file +} diff --git a/addons/web_mobile/static/src/js/form_mobile.js b/addons/web_mobile/static/src/js/form_mobile.js index 9fa219ee86e..51bfd719c7f 100644 --- a/addons/web_mobile/static/src/js/form_mobile.js +++ b/addons/web_mobile/static/src/js/form_mobile.js @@ -1,5 +1,5 @@ openerp.web_mobile.form_mobile = function (openerp) { -openerp.web_mobile.FormView = openerp.base.Controller.extend({ +openerp.web_mobile.FormView = openerp.base.Widget.extend({ init: function(session, element_id, list_id, action) { this._super(session, element_id); this.list_id = list_id; diff --git a/addons/web_mobile/static/src/js/list_mobile.js b/addons/web_mobile/static/src/js/list_mobile.js index f7a88019e3d..809797bfe0f 100644 --- a/addons/web_mobile/static/src/js/list_mobile.js +++ b/addons/web_mobile/static/src/js/list_mobile.js @@ -1,5 +1,5 @@ openerp.web_mobile.list_mobile = function (openerp) { -openerp.web_mobile.ListView = openerp.base.Controller.extend({ +openerp.web_mobile.ListView = openerp.base.Widget.extend({ init: function(session, element_id, list_id) { this._super(session, element_id); this.list_id = list_id; diff --git a/openerpweb/openerpweb.py b/openerpweb/openerpweb.py index f3cd72fade0..ad012a0806c 100644 --- a/openerpweb/openerpweb.py +++ b/openerpweb/openerpweb.py @@ -379,7 +379,10 @@ class HttpRequest(object): akw = dict([(key, kw[key] if isinstance(kw[key], basestring) else type(kw[key])) for key in kw.keys()]) print "POST --> %s.%s %s %r" % (controller.__class__.__name__, f.__name__, request, akw) r = f(controller, self, **kw) - print "<--", r + if isinstance(r, str): + print "<--", len(r), 'bytes' + else: + print "<--", len(r), 'characters' print return r