[MERGE] Merge from openerp-web.
bzr revid: jra@tinyerp.com-20110927133232-9gakcgfto5v7yoah
This commit is contained in:
@@ -10,15 +10,14 @@ class Options(object):
|
||||
pass
|
||||
|
||||
def wsgi_postload():
|
||||
import openerp.wsgi
|
||||
import openerp.tools
|
||||
import openerp
|
||||
import os
|
||||
import tempfile
|
||||
_logger.info("embedded mode")
|
||||
o = Options()
|
||||
o.dbfilter = openerp.tools.config['dbfilter']
|
||||
o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions")
|
||||
o.addons_path = os.path.dirname(os.path.dirname(__file__))
|
||||
o.addons_path = openerp.modules.module.ad_paths
|
||||
o.serve_static = True
|
||||
o.backend = 'local'
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
"name" : "web",
|
||||
"depends" : [],
|
||||
'active': True,
|
||||
'post_load' : 'wsgi_postload',
|
||||
'web_preload' : True,
|
||||
'js' : [
|
||||
"static/lib/datejs/globalization/en-US.js",
|
||||
"static/lib/datejs/core.js",
|
||||
@@ -19,6 +21,7 @@
|
||||
"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/jquery.deferred-queue/jquery.deferred-queue.js",
|
||||
"static/lib/json/json2.js",
|
||||
"static/lib/qweb/qweb2.js",
|
||||
"static/lib/underscore/underscore.js",
|
||||
@@ -32,6 +35,7 @@
|
||||
"static/src/js/views.js",
|
||||
"static/src/js/data.js",
|
||||
"static/src/js/data_export.js",
|
||||
"static/src/js/data_import.js",
|
||||
"static/src/js/search.js",
|
||||
"static/src/js/view_form.js",
|
||||
"static/src/js/view_list.js",
|
||||
@@ -44,6 +48,6 @@
|
||||
"static/lib/jquery.ui.notify/css/ui.notify.css",
|
||||
"static/src/css/base.css",
|
||||
"static/src/css/data_export.css",
|
||||
"static/src/css/data_import.css",
|
||||
],
|
||||
'post_load' : 'wsgi_postload',
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ _logger = logging.getLogger(__name__)
|
||||
# Globals (wont move into a pool)
|
||||
#-----------------------------------------------------------
|
||||
|
||||
applicationsession = {}
|
||||
addons_module = {}
|
||||
addons_manifest = {}
|
||||
controllers_class = {}
|
||||
@@ -52,10 +51,6 @@ class WebRequest(object):
|
||||
:type request: :class:`werkzeug.wrappers.BaseRequest`
|
||||
:param config: configuration object
|
||||
|
||||
.. attribute:: applicationsession
|
||||
|
||||
an application-wide :class:`~collections.Mapping`
|
||||
|
||||
.. attribute:: httprequest
|
||||
|
||||
the original :class:`werkzeug.wrappers.Request` object provided to the
|
||||
@@ -94,7 +89,6 @@ class WebRequest(object):
|
||||
``bool``, indicates whether the debug mode is active on the client
|
||||
"""
|
||||
def __init__(self, request, config):
|
||||
self.applicationsession = applicationsession
|
||||
self.httprequest = request
|
||||
self.httpresponse = None
|
||||
self.httpsession = request.session
|
||||
@@ -381,20 +375,21 @@ class Root(object):
|
||||
static URLs to the corresponding directories
|
||||
"""
|
||||
statics = {}
|
||||
addons_path = self.config.addons_path
|
||||
if addons_path not in sys.path:
|
||||
sys.path.insert(0, addons_path)
|
||||
for module in os.listdir(addons_path):
|
||||
if module not in addons_module:
|
||||
manifest_path = os.path.join(addons_path, module, '__openerp__.py')
|
||||
path_static = os.path.join(addons_path, module, 'static')
|
||||
if os.path.isfile(manifest_path) and os.path.isdir(path_static):
|
||||
manifest = ast.literal_eval(open(manifest_path).read())
|
||||
_logger.info("Loading %s", module)
|
||||
m = __import__(module)
|
||||
addons_module[module] = m
|
||||
addons_manifest[module] = manifest
|
||||
statics['/%s/static' % module] = path_static
|
||||
for addons_path in self.config.addons_path:
|
||||
if addons_path not in sys.path:
|
||||
sys.path.insert(0, addons_path)
|
||||
for module in os.listdir(addons_path):
|
||||
if module not in addons_module:
|
||||
manifest_path = os.path.join(addons_path, module, '__openerp__.py')
|
||||
path_static = os.path.join(addons_path, module, 'static')
|
||||
if os.path.isfile(manifest_path) and os.path.isdir(path_static):
|
||||
manifest = ast.literal_eval(open(manifest_path).read())
|
||||
manifest['addons_path'] = addons_path
|
||||
_logger.info("Loading %s", module)
|
||||
m = __import__(module)
|
||||
addons_module[module] = m
|
||||
addons_manifest[module] = manifest
|
||||
statics['/%s/static' % module] = path_static
|
||||
for k, v in controllers_class.items():
|
||||
if k not in controllers_object:
|
||||
o = v()
|
||||
|
||||
+152
-24
@@ -68,15 +68,30 @@ class Xml2Json:
|
||||
# OpenERP Web web Controllers
|
||||
#----------------------------------------------------------
|
||||
|
||||
def manifest_glob(addons_path, addons, key):
|
||||
def manifest_preload():
|
||||
modules = [k for k,v in openerpweb.addons_manifest.items() if v.get('web_preload')]
|
||||
return modules
|
||||
|
||||
def manifest_addons(addons):
|
||||
if addons==None:
|
||||
addons = manifest_preload()
|
||||
else:
|
||||
addons = addons.split(',')
|
||||
return addons
|
||||
|
||||
def manifest_glob(addons, key):
|
||||
addons = manifest_addons(addons)
|
||||
files = []
|
||||
for addon in addons:
|
||||
globlist = openerpweb.addons_manifest.get(addon, {}).get(key, [])
|
||||
manifest = openerpweb.addons_manifest.get(addon, {})
|
||||
addons_path = manifest['addons_path']
|
||||
globlist = manifest.get(key, [])
|
||||
for pattern in globlist:
|
||||
for path in glob.glob(os.path.join(addons_path, addon, pattern)):
|
||||
files.append(path[len(addons_path):])
|
||||
return files
|
||||
|
||||
# TODO change into concat_file(addons,key) taking care of addons_path
|
||||
def concat_files(addons_path, file_list):
|
||||
""" Concatenate file content
|
||||
return (concat,timestamp)
|
||||
@@ -104,7 +119,7 @@ home_template = textwrap.dedent("""<!DOCTYPE html>
|
||||
%(javascript)s
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
var c = new openerp.init();
|
||||
var c = new openerp.init(%(modules)s);
|
||||
var wc = new c.web.WebClient("oe");
|
||||
wc.start();
|
||||
});
|
||||
@@ -117,23 +132,23 @@ class WebClient(openerpweb.Controller):
|
||||
_cp_path = "/web/webclient"
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def csslist(self, req, mods='web'):
|
||||
return manifest_glob(req.config.addons_path, mods.split(','), 'css')
|
||||
def csslist(self, req, mods=None):
|
||||
return manifest_glob(mods, 'css')
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def jslist(self, req, mods='web'):
|
||||
return manifest_glob(req.config.addons_path, mods.split(','), 'js')
|
||||
def jslist(self, req, mods=None):
|
||||
return manifest_glob(mods, 'js')
|
||||
|
||||
@openerpweb.httprequest
|
||||
def css(self, req, mods='web'):
|
||||
files = manifest_glob(req.config.addons_path, mods.split(','), 'css')
|
||||
def css(self, req, mods=None):
|
||||
files = manifest_glob(mods, 'css')
|
||||
content,timestamp = concat_files(req.config.addons_path, files)
|
||||
# TODO request set the Date of last modif and Etag
|
||||
return req.make_response(content, [('Content-Type', 'text/css')])
|
||||
|
||||
@openerpweb.httprequest
|
||||
def js(self, req, mods='web'):
|
||||
files = manifest_glob(req.config.addons_path, mods.split(','), 'js')
|
||||
def js(self, req, mods=None):
|
||||
files = manifest_glob(mods, 'js')
|
||||
content,timestamp = concat_files(req.config.addons_path, files)
|
||||
# TODO request set the Date of last modif and Etag
|
||||
return req.make_response(content, [('Content-Type', 'application/javascript')])
|
||||
@@ -143,17 +158,19 @@ class WebClient(openerpweb.Controller):
|
||||
# script tags
|
||||
jslist = ['/web/webclient/js']
|
||||
if req.debug:
|
||||
jslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(req.config.addons_path, ['web'], 'js')]
|
||||
jslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(None, 'js')]
|
||||
js = "\n ".join(['<script type="text/javascript" src="%s"></script>'%i for i in jslist])
|
||||
|
||||
# css tags
|
||||
csslist = ['/web/webclient/css']
|
||||
if req.debug:
|
||||
csslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(req.config.addons_path, ['web'], 'css')]
|
||||
csslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(None, 'css')]
|
||||
css = "\n ".join(['<link rel="stylesheet" href="%s">'%i for i in csslist])
|
||||
|
||||
r = home_template % {
|
||||
'javascript': js,
|
||||
'css': css
|
||||
'css': css,
|
||||
'modules': simplejson.dumps(manifest_preload()),
|
||||
}
|
||||
return r
|
||||
|
||||
@@ -166,20 +183,21 @@ class WebClient(openerpweb.Controller):
|
||||
"grouping", "decimal_point", "thousands_sep"])
|
||||
else:
|
||||
lang_obj = None
|
||||
|
||||
|
||||
if lang.count("_") > 0:
|
||||
separator = "_"
|
||||
else:
|
||||
separator = "@"
|
||||
langs = lang.split(separator)
|
||||
langs = [separator.join(langs[:x]) for x in range(1, len(langs) + 1)]
|
||||
|
||||
|
||||
transs = {}
|
||||
for addon_name in mods:
|
||||
transl = {"messages":[]}
|
||||
transs[addon_name] = transl
|
||||
for l in langs:
|
||||
f_name = os.path.join(req.config.addons_path, addon_name, "po", l + ".po")
|
||||
addons_path = openerpweb.addons_manifest[addon_name]['addons_path']
|
||||
f_name = os.path.join(addons_path, addon_name, "po", l + ".po")
|
||||
if not os.path.exists(f_name):
|
||||
continue
|
||||
try:
|
||||
@@ -240,7 +258,7 @@ class Database(openerpweb.Controller):
|
||||
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:
|
||||
@@ -299,6 +317,7 @@ class Session(openerpweb.Controller):
|
||||
"context": ctx,
|
||||
"db": req.session._db
|
||||
}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def get_session_info(self, req):
|
||||
req.session.assert_valid(force=True)
|
||||
@@ -307,6 +326,7 @@ class Session(openerpweb.Controller):
|
||||
"context": req.session.get_context() if req.session._uid else False,
|
||||
"db": req.session._db
|
||||
}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def change_password (self,req,fields):
|
||||
old_password, new_password,confirm_password = operator.itemgetter('old_pwd', 'new_password','confirm_pwd')(
|
||||
@@ -322,6 +342,7 @@ class Session(openerpweb.Controller):
|
||||
except:
|
||||
return {'error': 'Original password incorrect, your password was not changed.', 'title': 'Change Password'}
|
||||
return {'error': 'Error, password not changed !', 'title': 'Change Password'}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def sc_list(self, req):
|
||||
return req.session.model('ir.ui.view_sc').get_sc(
|
||||
@@ -336,13 +357,14 @@ class Session(openerpweb.Controller):
|
||||
}
|
||||
except Exception, e:
|
||||
return {"error": e, "title": "Languages"}
|
||||
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def modules(self, req):
|
||||
# TODO query server for installed web modules
|
||||
mods = []
|
||||
for name, manifest in openerpweb.addons_manifest.items():
|
||||
if name != 'web' and manifest.get('active', True):
|
||||
# TODO replace by ir.module.module installed web
|
||||
if not manifest.get('web_preload') and manifest.get('active', True):
|
||||
mods.append(name)
|
||||
return mods
|
||||
|
||||
@@ -715,7 +737,7 @@ class DataSet(openerpweb.Controller):
|
||||
args[domain_id] = d
|
||||
if context_id and len(args) - 1 >= context_id:
|
||||
args[context_id] = c
|
||||
|
||||
|
||||
for i in xrange(len(args)):
|
||||
if isinstance(args[i], web.common.nonliterals.BaseContext):
|
||||
args[i] = req.session.eval_context(args[i])
|
||||
@@ -963,7 +985,7 @@ 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")
|
||||
@@ -972,7 +994,7 @@ class SearchView(View):
|
||||
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")
|
||||
@@ -1382,7 +1404,7 @@ class Reports(View):
|
||||
report_data['form'] = action['datas']['form']
|
||||
if 'ids' in action['datas']:
|
||||
report_ids = action['datas']['ids']
|
||||
|
||||
|
||||
report_id = report_srv.report(
|
||||
req.session._db, req.session._uid, req.session._password,
|
||||
action["report_name"], report_ids,
|
||||
@@ -1394,6 +1416,7 @@ class Reports(View):
|
||||
req.session._db, req.session._uid, req.session._password, report_id)
|
||||
if report_struct["state"]:
|
||||
break
|
||||
|
||||
time.sleep(self.POLLING_DELAY)
|
||||
|
||||
report = base64.b64decode(report_struct['result'])
|
||||
@@ -1407,3 +1430,108 @@ class Reports(View):
|
||||
('Content-Type', report_mimetype),
|
||||
('Content-Length', len(report))],
|
||||
cookies={'fileToken': int(token)})
|
||||
|
||||
|
||||
class Import(View):
|
||||
_cp_path = "/web/import"
|
||||
|
||||
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.httprequest
|
||||
def detect_data(self, req, csvfile, csvsep, csvdel, csvcode, jsonp):
|
||||
try:
|
||||
data = list(csv.reader(
|
||||
csvfile, quotechar=str(csvdel), delimiter=str(csvsep)))
|
||||
except csv.Error, e:
|
||||
csvfile.seek(0)
|
||||
return '<script>window.top.%s(%s);</script>' % (
|
||||
jsonp, simplejson.dumps({'error': {
|
||||
'message': 'Error parsing CSV file: %s' % e,
|
||||
# decodes each byte to a unicode character, which may or
|
||||
# may not be printable, but decoding will succeed.
|
||||
# Otherwise simplejson will try to decode the `str` using
|
||||
# utf-8, which is very likely to blow up on characters out
|
||||
# of the ascii range (in range [128, 256))
|
||||
'preview': csvfile.read(200).decode('iso-8859-1')}}))
|
||||
|
||||
try:
|
||||
return '<script>window.top.%s(%s);</script>' % (
|
||||
jsonp, simplejson.dumps(
|
||||
{'records': data[:10]}, encoding=csvcode))
|
||||
except UnicodeDecodeError:
|
||||
return '<script>window.top.%s(%s);</script>' % (
|
||||
jsonp, simplejson.dumps({
|
||||
'message': u"Failed to decode CSV file using encoding %s, "
|
||||
u"try switching to a different encoding" % csvcode
|
||||
}))
|
||||
|
||||
@openerpweb.httprequest
|
||||
def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp,
|
||||
meta):
|
||||
modle_obj = req.session.model(model)
|
||||
skip, indices, fields = operator.itemgetter('skip', 'indices', 'fields')(
|
||||
simplejson.loads(meta))
|
||||
|
||||
error = None
|
||||
if not (csvdel and len(csvdel) == 1):
|
||||
error = u"The CSV delimiter must be a single character"
|
||||
|
||||
if not indices and fields:
|
||||
error = u"You must select at least one field to import"
|
||||
|
||||
if error:
|
||||
return '<script>window.top.%s(%s);</script>' % (
|
||||
jsonp, simplejson.dumps({'error': {'message': error}}))
|
||||
|
||||
# skip ignored records
|
||||
data_record = itertools.islice(
|
||||
csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)),
|
||||
skip, None)
|
||||
|
||||
# if only one index, itemgetter will return an atom rather than a tuple
|
||||
if len(indices) == 1: mapper = lambda row: [row[indices[0]]]
|
||||
else: mapper = operator.itemgetter(*indices)
|
||||
|
||||
data = None
|
||||
error = None
|
||||
try:
|
||||
# decode each data row
|
||||
data = [
|
||||
[record.decode(csvcode) for record in row]
|
||||
for row in itertools.imap(mapper, data_record)
|
||||
# don't insert completely empty rows (can happen due to fields
|
||||
# filtering in case of e.g. o2m content rows)
|
||||
if any(row)
|
||||
]
|
||||
except UnicodeDecodeError:
|
||||
error = u"Failed to decode CSV file using encoding %s" % csvcode
|
||||
except csv.Error, e:
|
||||
error = u"Could not process CSV file: %s" % e
|
||||
|
||||
# If the file contains nothing,
|
||||
if not data:
|
||||
error = u"File to import is empty"
|
||||
if error:
|
||||
return '<script>window.top.%s(%s);</script>' % (
|
||||
jsonp, simplejson.dumps({'error': {'message': error}}))
|
||||
|
||||
try:
|
||||
(code, record, message, _nope) = modle_obj.import_data(
|
||||
fields, data, 'init', '', False,
|
||||
req.session.eval_context(req.context))
|
||||
except xmlrpclib.Fault, e:
|
||||
error = {"message": u"%s, %s" % (e.faultCode, e.faultString)}
|
||||
return '<script>window.top.%s(%s);</script>' % (
|
||||
jsonp, simplejson.dumps({'error':error}))
|
||||
|
||||
if code != -1:
|
||||
return '<script>window.top.%s(%s);</script>' % (
|
||||
jsonp, simplejson.dumps({'success':True}))
|
||||
|
||||
msg = u"Error during import: %s\n\nTrying to import record %r" % (
|
||||
message, record)
|
||||
return '<script>window.top.%s(%s);</script>' % (
|
||||
jsonp, simplejson.dumps({'error': {'message':msg}}))
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
Copyright 2011 Xavier Morel. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
of conditions and the following disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY XAVIER MOREL ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,59 @@
|
||||
.. -*- restructuredtext -*-
|
||||
|
||||
In jQuery 1.5, jQuery has introduced a Deferred object in order to
|
||||
better handle callbacks.
|
||||
|
||||
Along with Deferred, it introduced ``jQuery.when`` which allows, among
|
||||
other things, for multiplexing deferreds (waiting on multiple
|
||||
deferreds at the same time).
|
||||
|
||||
While this is very nice if all deferreds are available at the same
|
||||
point, it doesn't really work if the resolution of a deferred can
|
||||
generate more deferreds, or for collections of deferreds coming from
|
||||
multiple sources (which may not be aware of one another).
|
||||
|
||||
Deferred.queue tries to be a solution to this. It is based on the
|
||||
principle of FIFO multiple-producers multiple-consumers tasks queues,
|
||||
such as Python's `queue.Queue`_. Any code with a reference to the
|
||||
queue can add a promise to the queue, and the queue (which is a
|
||||
promise itself) will only be resolved once all promises within are
|
||||
resolved.
|
||||
|
||||
Quickstart
|
||||
----------
|
||||
|
||||
Deferred.queue has a very simple life cycle: it is dormant when
|
||||
created, it starts working as soon as promises get added to it (via
|
||||
``.push``, or by providing them directly to its constructor), and as
|
||||
soon as the last promise in the queue is resolved it resolves itself.
|
||||
|
||||
If any promise in the queue fails, the queue itself will be rejected
|
||||
(without waiting for further promises).
|
||||
|
||||
Once a queue has been resolved or rejected, adding new promises to it
|
||||
results in an error.
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
``jQuery.Deferred.queue([promises...])``
|
||||
Creates a new deferred queue. Can be primed with a series of promise
|
||||
objects.
|
||||
|
||||
``.push(promise...)``
|
||||
Adds promises to the queue. Returns the queue itself (can be
|
||||
chained).
|
||||
|
||||
If the queue has already been resolved or rejected, raises an error.
|
||||
|
||||
``.then([doneCallbacks][, failCallbacks])``
|
||||
Promise/A ``then`` method.
|
||||
|
||||
``.done(doneCallbacks)``
|
||||
jQuery ``done`` extension to promise objects
|
||||
|
||||
``.fail(failCallbacks)``
|
||||
jQuery ``fail`` extension to promise objects
|
||||
|
||||
.. _queue.Queue:
|
||||
http://docs.python.org/dev/library/queue.html
|
||||
@@ -0,0 +1,34 @@
|
||||
(function ($) {
|
||||
"use strict";
|
||||
$.extend($.Deferred, {
|
||||
queue: function () {
|
||||
var queueDeferred = $.Deferred();
|
||||
var promises = 0;
|
||||
|
||||
function resolve() {
|
||||
if (--promises > 0) {
|
||||
return;
|
||||
}
|
||||
setTimeout($.proxy(queueDeferred, 'resolve'), 0);
|
||||
}
|
||||
|
||||
var promise = $.extend(queueDeferred.promise(), {
|
||||
push: function () {
|
||||
if (this.isResolved() || this.isRejected()) {
|
||||
throw new Error("Can not add promises to a resolved "
|
||||
+ "or rejected promise queue");
|
||||
}
|
||||
|
||||
promises += 1;
|
||||
$.when.apply(null, arguments).then(
|
||||
resolve, $.proxy(queueDeferred, 'reject'));
|
||||
return this;
|
||||
}
|
||||
});
|
||||
if (arguments.length) {
|
||||
promise.push.apply(promise, arguments);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
});
|
||||
})(jQuery)
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
.openerp .oe_import_grid {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.openerp .oe_import_grid-header .oe_import_grid-cell {
|
||||
background: url(../img/gradientlinebg.gif) repeat-x #CCCCCC;
|
||||
border-bottom: 1px solid #E3E3E3;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
.openerp .oe_import_grid-row .oe_import_grid-cell {
|
||||
border-bottom: 1px solid #E3E3E3;
|
||||
}
|
||||
.openerp .separator.horizontal {
|
||||
font-weight: bold;
|
||||
border-bottom-width: 1px;
|
||||
margin: 6px 4px 6px 1px;
|
||||
height: 20px;
|
||||
}
|
||||
.openerp .duplicate_fld{
|
||||
background-color:#FF6666;
|
||||
}
|
||||
.openerp .select_fld{
|
||||
background: none repeat scroll 0 0 white;
|
||||
}
|
||||
.openerp .ui-autocomplete {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
padding-right: 20px;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 182 B |
@@ -19,9 +19,9 @@
|
||||
/**
|
||||
* OpenERP instance constructor
|
||||
*
|
||||
* @param {Boolean} skip_init if true, skips the built-in initialization
|
||||
* @param {Array} modules list of modules to initialize
|
||||
*/
|
||||
init: function(skip_init) {
|
||||
init: function(modules) {
|
||||
var new_instance = {
|
||||
// links to the global openerp
|
||||
_openerp: openerp,
|
||||
@@ -35,8 +35,9 @@
|
||||
web_mobile: {}
|
||||
};
|
||||
openerp.sessions[new_instance._session_id] = new_instance;
|
||||
if (!skip_init){
|
||||
openerp.web(new_instance);
|
||||
modules = modules || ["web"];
|
||||
for(var i=0; i < modules.length; i++) {
|
||||
openerp[modules[i]](new_instance);
|
||||
}
|
||||
return new_instance;
|
||||
}
|
||||
@@ -58,7 +59,7 @@ openerp.web = function(instance) {
|
||||
openerp.web.formats(instance);
|
||||
openerp.web.chrome(instance);
|
||||
openerp.web.data(instance);
|
||||
var files = ["views","search","list","form","list_editable","web_mobile","view_tree","data_export"];
|
||||
var files = ["views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import"];
|
||||
for(var i=0; i<files.length; i++) {
|
||||
if(openerp.web[files[i]]) {
|
||||
openerp.web[files[i]](instance);
|
||||
|
||||
@@ -218,9 +218,8 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
||||
this.$element.closest(".openerp")
|
||||
.removeClass("login-mode")
|
||||
.addClass("database_block");
|
||||
|
||||
|
||||
var self = this;
|
||||
|
||||
var fetch_db = this.rpc("/web/database/get_list", {}, function(result) {
|
||||
self.db_list = result.db_list;
|
||||
});
|
||||
@@ -232,7 +231,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
||||
self.lang_list = result.lang_list;
|
||||
});
|
||||
$.when(fetch_db, fetch_langs).then(function () {self.do_create();});
|
||||
|
||||
|
||||
this.$element.find('#db-create').click(this.do_create);
|
||||
this.$element.find('#db-drop').click(this.do_drop);
|
||||
this.$element.find('#db-backup').click(this.do_backup);
|
||||
@@ -399,7 +398,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
||||
do_restore: function() {
|
||||
var self = this;
|
||||
self.$option_id.html(QWeb.render("RestoreDB", self));
|
||||
|
||||
|
||||
self.$option_id.find("form[name=restore_db_form]").validate({
|
||||
submitHandler: function (form) {
|
||||
$.blockUI({message:'<img src="/web/static/src/img/throbber2.gif">'});
|
||||
@@ -467,10 +466,11 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
|
||||
/**
|
||||
* @constructs openerp.web.Login
|
||||
* @extends openerp.web.Widget
|
||||
*
|
||||
*
|
||||
* @param parent
|
||||
* @param element_id
|
||||
*/
|
||||
|
||||
init: function(parent, element_id) {
|
||||
this._super(parent, element_id);
|
||||
this.has_local_storage = typeof(localStorage) != 'undefined';
|
||||
@@ -496,7 +496,7 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
|
||||
},
|
||||
display: function() {
|
||||
var self = this;
|
||||
|
||||
|
||||
this.$element.html(QWeb.render("Login", this));
|
||||
this.database = new openerp.web.Database(
|
||||
this, "oe_database", "oe_db_options");
|
||||
@@ -574,7 +574,7 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||
/**
|
||||
* @constructs openerp.web.Header
|
||||
* @extends openerp.web.Widget
|
||||
*
|
||||
*
|
||||
* @param parent
|
||||
*/
|
||||
init: function(parent) {
|
||||
@@ -665,7 +665,7 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
on_action: function(action) {
|
||||
},
|
||||
on_preferences: function(){
|
||||
@@ -713,7 +713,7 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# *
|
||||
action_manager.appendTo(this.dialog);
|
||||
action_manager.render(this.dialog);
|
||||
},
|
||||
|
||||
|
||||
change_password :function() {
|
||||
var self = this;
|
||||
this.dialog = new openerp.web.Dialog(this,{
|
||||
@@ -758,7 +758,7 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
|
||||
/**
|
||||
* @constructs openerp.web.Menu
|
||||
* @extends openerp.web.Widget
|
||||
*
|
||||
*
|
||||
* @param parent
|
||||
* @param element_id
|
||||
* @param secondary_menu_id
|
||||
@@ -910,7 +910,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||
/**
|
||||
* @constructs openerp.web.WebClient
|
||||
* @extends openerp.web.Widget
|
||||
*
|
||||
*
|
||||
* @param element_id
|
||||
*/
|
||||
init: function(element_id) {
|
||||
@@ -1000,7 +1000,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
|
||||
self.execute_home_action(home_action[0], ds);
|
||||
})
|
||||
},
|
||||
default_home: function () {
|
||||
default_home: function () {
|
||||
},
|
||||
/**
|
||||
* Bundles the execution of the home action
|
||||
|
||||
@@ -608,15 +608,23 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.reset_ids([]);
|
||||
this.last_default_get = {};
|
||||
},
|
||||
default_get: function(fields, callback) {
|
||||
return this._super(fields).then(this.on_default_get).then(callback);
|
||||
},
|
||||
on_default_get: function(res) {
|
||||
this.last_default_get = res;
|
||||
},
|
||||
create: function(data, callback, error_callback) {
|
||||
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data};
|
||||
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
|
||||
defaults: this.last_default_get};
|
||||
this.to_create.push(cached);
|
||||
this.cache.push(cached);
|
||||
this.on_change();
|
||||
var to_return = $.Deferred().then(callback);
|
||||
to_return.resolve({result: cached.id});
|
||||
return to_return.promise();
|
||||
var prom = $.Deferred().then(callback);
|
||||
setTimeout(function() {prom.resolve({result: cached.id});}, 0);
|
||||
return prom.promise();
|
||||
},
|
||||
write: function (id, data, options, callback) {
|
||||
var self = this;
|
||||
@@ -676,7 +684,8 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
|
||||
var cached = _.detect(self.cache, function(x) {return x.id === id;});
|
||||
var created = _.detect(self.to_create, function(x) {return x.id === id;});
|
||||
if (created) {
|
||||
_.each(fields, function(x) {if (cached.values[x] === undefined) cached.values[x] = false;});
|
||||
_.each(fields, function(x) {if (cached.values[x] === undefined)
|
||||
cached.values[x] = created.defaults[x] || false;});
|
||||
} else {
|
||||
if (!cached || !_.all(fields, function(x) {return cached.values[x] !== undefined}))
|
||||
to_get.push(id);
|
||||
@@ -716,6 +725,10 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
|
||||
}
|
||||
});
|
||||
openerp.web.ReadOnlyDataSetSearch = openerp.web.DataSetSearch.extend({
|
||||
default_get: function(fields, callback) {
|
||||
return this._super(fields, callback).then(this.on_default_get);
|
||||
},
|
||||
on_default_get: function(result) {},
|
||||
create: function(data, callback, error_callback) {
|
||||
this.on_create(data);
|
||||
var to_return = $.Deferred().then(callback);
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
openerp.web.data_import = function(openerp) {
|
||||
var QWeb = openerp.web.qweb;
|
||||
/**
|
||||
* Safari does not deal well at all with raw JSON data being returned. As a
|
||||
* result, we're going to cheat by using a pseudo-jsonp: instead of getting
|
||||
* JSON data in the iframe, we're getting a ``script`` tag which consists of a
|
||||
* function call and the returned data (the json dump).
|
||||
*
|
||||
* The function is an auto-generated name bound to ``window``, which calls
|
||||
* back into the callback provided here.
|
||||
*
|
||||
* @param {Object} form the form element (DOM or jQuery) to use in the call
|
||||
* @param {Object} attributes jquery.form attributes object
|
||||
* @param {Function} callback function to call with the returned data
|
||||
*/
|
||||
function jsonp(form, attributes, callback) {
|
||||
attributes = attributes || {};
|
||||
var options = {jsonp: _.uniqueId('import_callback_')};
|
||||
window[options.jsonp] = function () {
|
||||
delete window[options.jsonp];
|
||||
callback.apply(null, arguments);
|
||||
};
|
||||
if ('data' in attributes) {
|
||||
_.extend(attributes.data, options);
|
||||
} else {
|
||||
_.extend(attributes, {data: options});
|
||||
}
|
||||
$(form).ajaxSubmit(attributes);
|
||||
}
|
||||
|
||||
openerp.web.DataImport = openerp.web.Dialog.extend({
|
||||
template: 'ImportDataView',
|
||||
dialog_title: "Import Data",
|
||||
init: function(parent, dataset){
|
||||
var self = this;
|
||||
this._super(parent, {});
|
||||
this.model = parent.model;
|
||||
this.fields = [];
|
||||
this.all_fields = [];
|
||||
this.required_fields = null;
|
||||
|
||||
var convert_fields = function (root, prefix) {
|
||||
prefix = prefix || '';
|
||||
_(root.fields).each(function (f) {
|
||||
self.all_fields.push(prefix + f.name);
|
||||
if (f.fields) {
|
||||
convert_fields(f, prefix + f.id + '/');
|
||||
}
|
||||
});
|
||||
};
|
||||
this.ready = $.Deferred.queue().then(function () {
|
||||
self.required_fields = _(self.fields).chain()
|
||||
.filter(function (field) { return field.required; })
|
||||
.pluck('name')
|
||||
.value();
|
||||
convert_fields(self);
|
||||
self.all_fields.sort();
|
||||
});
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super();
|
||||
this.open({
|
||||
modal: true,
|
||||
width: '70%',
|
||||
height: 'auto',
|
||||
position: 'top',
|
||||
buttons: [
|
||||
{text: "Close", click: function() { self.stop(); }},
|
||||
{text: "Import File", click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'}
|
||||
],
|
||||
close: function(event, ui) {
|
||||
self.stop();
|
||||
}
|
||||
});
|
||||
this.toggle_import_button(false);
|
||||
this.$element.find('#csvfile').change(this.on_autodetect_data);
|
||||
this.$element.find('fieldset').change(this.on_autodetect_data);
|
||||
this.$element.find('fieldset legend').click(function() {
|
||||
$(this).next().toggle();
|
||||
});
|
||||
this.ready.push(new openerp.web.DataSet(this, this.model).call(
|
||||
'fields_get', [], function (fields) {
|
||||
self.graft_fields(fields);
|
||||
}));
|
||||
},
|
||||
graft_fields: function (fields, parent, level) {
|
||||
parent = parent || this;
|
||||
level = level || 0;
|
||||
|
||||
var self = this;
|
||||
_(fields).each(function (field, field_name) {
|
||||
var f = {
|
||||
id: field_name,
|
||||
name: field_name,
|
||||
string: field.string,
|
||||
required: field.required
|
||||
};
|
||||
|
||||
switch (field.type) {
|
||||
case 'many2many':
|
||||
case 'many2one':
|
||||
f.name += '/id';
|
||||
break;
|
||||
case 'one2many':
|
||||
f.name += '/id';
|
||||
f.fields = [];
|
||||
// only fetch sub-fields to a depth of 2 levels
|
||||
if (level < 2) {
|
||||
self.ready.push(new openerp.web.DataSet(self, field.relation).call(
|
||||
'fields_get', [], function (fields) {
|
||||
self.graft_fields(fields, f, level+1);
|
||||
}));
|
||||
}
|
||||
break;
|
||||
}
|
||||
parent.fields.push(f);
|
||||
});
|
||||
},
|
||||
toggle_import_button: function (newstate) {
|
||||
this.$dialog.dialog('widget')
|
||||
.find('.oe-dialog-import-button')
|
||||
.button('option', 'disabled', !newstate);
|
||||
},
|
||||
do_import: function() {
|
||||
if(!this.$element.find('#csvfile').val()) { return; }
|
||||
var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10);
|
||||
var with_headers = this.$element.find('#file_has_headers').prop('checked');
|
||||
if (!lines_to_skip && with_headers) {
|
||||
lines_to_skip = 1;
|
||||
}
|
||||
var indices = [], fields = [];
|
||||
this.$element.find(".sel_fields").each(function(index, element) {
|
||||
var val = element.value;
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
indices.push(index);
|
||||
fields.push(val);
|
||||
});
|
||||
|
||||
jsonp(this.$element.find('#import_data'), {
|
||||
url: '/web/import/import_data',
|
||||
data: {
|
||||
model: this.model,
|
||||
meta: JSON.stringify({
|
||||
skip: lines_to_skip,
|
||||
indices: indices,
|
||||
fields: fields
|
||||
})
|
||||
}
|
||||
}, this.on_import_results);
|
||||
},
|
||||
on_autodetect_data: function() {
|
||||
if(!this.$element.find('#csvfile').val()) { return; }
|
||||
jsonp(this.$element.find('#import_data'), {
|
||||
url: '/web/import/detect_data'
|
||||
}, this.on_import_results);
|
||||
},
|
||||
on_import_results: function(results) {
|
||||
this.$element.find('#result').empty();
|
||||
var headers, result_node = this.$element.find("#result");
|
||||
|
||||
if (results['records']) {
|
||||
var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10),
|
||||
with_headers = this.$element.find('#file_has_headers').prop('checked');
|
||||
headers = with_headers ? results.records[0] : null;
|
||||
|
||||
result_node.append(QWeb.render('ImportView.result', {
|
||||
'headers': headers,
|
||||
'records': lines_to_skip ? results.records.slice(lines_to_skip)
|
||||
: with_headers ? results.records.slice(1)
|
||||
: results.records
|
||||
}));
|
||||
} else if (results['error']) {
|
||||
result_node.append(QWeb.render('ImportView.error', {
|
||||
'error': results['error']}));
|
||||
} else if (results['success']) {
|
||||
if (this.widget_parent.widget_parent.active_view == "list") {
|
||||
this.widget_parent.reload_content();
|
||||
}
|
||||
this.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.ready.then(function () {
|
||||
var $fields = self.$element.find('.sel_fields').bind('blur', function () {
|
||||
if (this.value && !_(self.all_fields).contains(this.value)) {
|
||||
this.value = '';
|
||||
}
|
||||
}).autocomplete({
|
||||
minLength: 0,
|
||||
source: self.all_fields,
|
||||
change: self.on_check_field_values
|
||||
}).focus(function () {
|
||||
$(this).autocomplete('search');
|
||||
});
|
||||
// Column auto-detection
|
||||
_(headers).each(function (header, index) {
|
||||
var f =_(self.fields).detect(function (field) {
|
||||
// TODO: levenshtein between header and field.string
|
||||
return field.name === header || field.string.toLowerCase() === header;
|
||||
});
|
||||
if (f) {
|
||||
$fields.eq(index).val(f.name);
|
||||
}
|
||||
});
|
||||
self.on_check_field_values();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Looks through all the field selections, and tries to find if two
|
||||
* (or more) columns were matched to the same model field.
|
||||
*
|
||||
* Returns a map of the multiply-mapped fields to an array of offending
|
||||
* columns (not actually columns, but the inputs containing the same field
|
||||
* names).
|
||||
*
|
||||
* Also has the side-effect of marking the discovered inputs with the class
|
||||
* ``duplicate_fld``.
|
||||
*
|
||||
* @returns {Object<String, Array<String>>} map of duplicate field matches to same-valued inputs
|
||||
*/
|
||||
find_duplicate_fields: function() {
|
||||
// Maps values to DOM nodes, in order to discover duplicates
|
||||
var values = {}, duplicates = {};
|
||||
this.$element.find(".sel_fields").each(function(index, element) {
|
||||
var value = element.value;
|
||||
var $element = $(element).removeClass('duplicate_fld');
|
||||
if (!value) { return; }
|
||||
|
||||
if (!(value in values)) {
|
||||
values[value] = element;
|
||||
} else {
|
||||
var same_valued_field = values[value];
|
||||
if (value in duplicates) {
|
||||
duplicates[value].push(element);
|
||||
} else {
|
||||
duplicates[value] = [same_valued_field, element];
|
||||
}
|
||||
$element.add(same_valued_field).addClass('duplicate_fld');
|
||||
}
|
||||
});
|
||||
return duplicates;
|
||||
},
|
||||
on_check_field_values: function () {
|
||||
this.$element.find("#message, #msg").remove();
|
||||
|
||||
var required_valid = this.check_required();
|
||||
|
||||
var duplicates = this.find_duplicate_fields();
|
||||
if (_.isEmpty(duplicates)) {
|
||||
this.toggle_import_button(required_valid);
|
||||
} else {
|
||||
var $err = $('<div id="msg" style="color: red;">Destination fields should only be selected once, some fields are selected more than once:</div>').insertBefore(this.$element.find('#result'));
|
||||
var $dupes = $('<dl>').appendTo($err);
|
||||
_(duplicates).each(function(elements, value) {
|
||||
$('<dt>').text(value).appendTo($dupes);
|
||||
_(elements).each(function(element) {
|
||||
var cell = $(element).closest('td');
|
||||
$('<dd>').text(cell.parent().children().index(cell)).appendTo($dupes);
|
||||
});
|
||||
});
|
||||
this.toggle_import_button(false);
|
||||
}
|
||||
|
||||
},
|
||||
check_required: function() {
|
||||
if (!this.required_fields.length) { return true; }
|
||||
|
||||
var selected_fields = _(this.$element.find('.sel_fields').get()).chain()
|
||||
.pluck('value')
|
||||
.compact()
|
||||
.value();
|
||||
|
||||
var missing_fields = _.difference(this.required_fields, selected_fields);
|
||||
if (missing_fields.length) {
|
||||
this.$element.find("#result").before('<div id="message" style="color:red">*Required Fields are not selected : ' + missing_fields + '.</div>');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
stop: function() {
|
||||
$(this.$dialog).remove();
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -345,7 +345,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
do_save: function(success, prepend_on_create) {
|
||||
var self = this;
|
||||
if (!this.ready) {
|
||||
return false;
|
||||
return $.Deferred().reject();
|
||||
}
|
||||
var form_dirty = false,
|
||||
form_invalid = false,
|
||||
@@ -367,17 +367,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
if (form_invalid) {
|
||||
first_invalid_field.focus();
|
||||
this.on_invalid();
|
||||
return false;
|
||||
return $.Deferred().reject();
|
||||
} else {
|
||||
console.log("About to save", values);
|
||||
if (!this.datarecord.id) {
|
||||
return this.dataset.create(values, function(r) {
|
||||
self.on_created(r, success, prepend_on_create);
|
||||
});
|
||||
return this.dataset.create(values).pipe(function(r) {
|
||||
return self.on_created(r, undefined, prepend_on_create);
|
||||
}).then(success);
|
||||
} else {
|
||||
return this.dataset.write(this.datarecord.id, values, {}, function(r) {
|
||||
self.on_saved(r, success);
|
||||
});
|
||||
return this.dataset.write(this.datarecord.id, values, {}).pipe(function(r) {
|
||||
return self.on_saved(r);
|
||||
}).then(success);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -402,11 +402,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
on_saved: function(r, success) {
|
||||
if (!r.result) {
|
||||
// should not happen in the server, but may happen for internal purpose
|
||||
return $.Deferred().reject();
|
||||
} else {
|
||||
if (success) {
|
||||
success(r);
|
||||
}
|
||||
this.reload();
|
||||
return $.Deferred().then(success).resolve(r);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -425,6 +424,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
on_created: function(r, success, prepend_on_create) {
|
||||
if (!r.result) {
|
||||
// should not happen in the server, but may happen for internal purpose
|
||||
return $.Deferred().reject();
|
||||
} else {
|
||||
this.datarecord.id = r.result;
|
||||
if (!prepend_on_create) {
|
||||
@@ -439,10 +439,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
this.sidebar.attachments.do_update();
|
||||
}
|
||||
console.debug("The record has been created with id #" + this.datarecord.id);
|
||||
if (success) {
|
||||
success(_.extend(r, {created: true}));
|
||||
}
|
||||
this.reload();
|
||||
return $.Deferred().then(success).resolve(_.extend(r, {created: true}));
|
||||
}
|
||||
},
|
||||
do_search: function (domains, contexts, groupbys) {
|
||||
@@ -472,6 +470,13 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
||||
get_selected_ids: function() {
|
||||
var id = this.dataset.ids[this.dataset.index];
|
||||
return id ? [id] : [];
|
||||
},
|
||||
recursive_save: function() {
|
||||
var self = this;
|
||||
return $.when(this.do_save()).pipe(function(res) {
|
||||
if (self.dataset.parent_view)
|
||||
return self.dataset.parent_view.recursive_save();
|
||||
});
|
||||
}
|
||||
});
|
||||
openerp.web.FormDialog = openerp.web.Dialog.extend({
|
||||
@@ -830,30 +835,31 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.click(this.on_click);
|
||||
},
|
||||
on_click: function(saved) {
|
||||
on_click: function() {
|
||||
var self = this;
|
||||
if ((!this.node.attrs.special && this.view.dirty_for_user && saved !== true) || !this.view.datarecord.id) {
|
||||
this.view.do_save(function() {
|
||||
self.on_click(true);
|
||||
});
|
||||
} else {
|
||||
if (this.node.attrs.confirm) {
|
||||
var dialog = $('<div>' + this.node.attrs.confirm + '</div>').dialog({
|
||||
var exec_action = function() {
|
||||
if (self.node.attrs.confirm) {
|
||||
var dialog = $('<div>' + self.node.attrs.confirm + '</div>').dialog({
|
||||
title: 'Confirm',
|
||||
modal: true,
|
||||
buttons: {
|
||||
Ok: function() {
|
||||
self.on_confirmed();
|
||||
$(this).dialog("close");
|
||||
$(self).dialog("close");
|
||||
},
|
||||
Cancel: function() {
|
||||
$(this).dialog("close");
|
||||
$(self).dialog("close");
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.on_confirmed();
|
||||
self.on_confirmed();
|
||||
}
|
||||
};
|
||||
if ((!this.node.attrs.special && this.view.dirty_for_user) || !this.view.datarecord.id) {
|
||||
this.view.recursive_save().then(exec_action);
|
||||
} else {
|
||||
exec_action();
|
||||
}
|
||||
},
|
||||
on_confirmed: function() {
|
||||
@@ -1467,7 +1473,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||
this.$input = this.$element.find("input");
|
||||
this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
|
||||
this.$menu_btn = this.$element.find(".oe-m2o-cm-button");
|
||||
|
||||
|
||||
// context menu
|
||||
var init_context_menu_def = $.Deferred().then(function(e) {
|
||||
var rdataset = new openerp.web.DataSetStatic(self, "ir.values", self.build_context());
|
||||
@@ -1475,7 +1481,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||
[[self.field.relation, false]], false, rdataset.get_context()], false, 0)
|
||||
.then(function(result) {
|
||||
self.related_entries = result;
|
||||
|
||||
|
||||
var $cmenu = $("#" + self.cm_id);
|
||||
$cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
|
||||
var bindings = {};
|
||||
@@ -1945,11 +1951,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
||||
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
|
||||
if (this.viewmanager.active_view === "form") {
|
||||
var res = $.when(view.do_save());
|
||||
if (res === false) {
|
||||
// ignore
|
||||
} else if (res.isRejected()) {
|
||||
throw "Save or create on one2many dataset is not supposed to fail.";
|
||||
} else if (!res.isResolved()) {
|
||||
if (!res.isResolved() && !res.isRejected()) {
|
||||
throw "Asynchronous get_value() is not supported in form view.";
|
||||
}
|
||||
return res;
|
||||
@@ -2002,6 +2004,7 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
|
||||
} else {
|
||||
var self = this;
|
||||
var pop = new openerp.web.form.SelectCreatePopup(this);
|
||||
pop.on_default_get.add(self.dataset.on_default_get);
|
||||
pop.select_element(self.o2m.field.relation,{
|
||||
initial_view: "form",
|
||||
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
|
||||
@@ -2163,6 +2166,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
||||
this.dataset = new openerp.web.ReadOnlyDataSetSearch(this, this.model,
|
||||
this.context);
|
||||
this.dataset.parent_view = this.options.parent_view;
|
||||
this.dataset.on_default_get.add(this.on_default_get);
|
||||
if (this.options.initial_view == "search") {
|
||||
this.setup_search_view();
|
||||
} else { // "form"
|
||||
@@ -2285,7 +2289,8 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
||||
this.on_select_elements(this.created_elements);
|
||||
}
|
||||
this.stop();
|
||||
}
|
||||
},
|
||||
on_default_get: function(res) {}
|
||||
});
|
||||
|
||||
openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({
|
||||
|
||||
@@ -862,6 +862,8 @@ db.web.View = db.web.Widget.extend(/** @lends db.web.View# */{
|
||||
console.log('Todo');
|
||||
},
|
||||
on_sidebar_import: function() {
|
||||
var import_view = new db.web.DataImport(this, this.dataset);
|
||||
import_view.start();
|
||||
},
|
||||
on_sidebar_export: function() {
|
||||
var export_view = new db.web.DataExport(this, this.dataset);
|
||||
|
||||
@@ -1383,6 +1383,79 @@
|
||||
</table>
|
||||
</form>
|
||||
</t>
|
||||
|
||||
<t t-name="ImportView">
|
||||
<a id="importview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Import</a>
|
||||
</t>
|
||||
<t t-name="ImportDataView">
|
||||
<form name="import_data" id="import_data" action="" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="session_id" t-att-value="session.session_id"/>
|
||||
<h2 class="separator horizontal">1. Import a .CSV file</h2>
|
||||
<p>Select a .CSV file to import. If you need a sample of file to import,
|
||||
you should use the export tool with the "Import Compatible" option.
|
||||
</p>
|
||||
<p>
|
||||
<label for="csvfile">CSV File:</label>
|
||||
<input type="file" id="csvfile" size="50" name="csvfile"/>
|
||||
</p>
|
||||
<h2 class="separator horizontal">2. Check your file format</h2>
|
||||
<div id="result"></div>
|
||||
<fieldset>
|
||||
<legend style="cursor:pointer;">Import Options</legend>
|
||||
<table style="display:none">
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<label for="file_has_headers">Does your file have titles?</label>
|
||||
<input type="checkbox" checked="checked"
|
||||
id="file_has_headers"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="csv_separator">Separator:</label></td>
|
||||
<td><input type="text" name="csvsep" id="csv_separator" value=","/></td>
|
||||
<td><label for="csv_delimiter">Delimiter:</label></td>
|
||||
<td><input type="text" name="csvdel" id="csv_delimiter" value='"'/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="csv_encoding">Encoding:</label></td>
|
||||
<td>
|
||||
<select name="csvcode" id="csv_encoding">
|
||||
<option value="utf-8">UTF-8</option>
|
||||
<option value="latin1">Latin 1</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><label for="csv_skip" title="For use if CSV files have titles on multiple lines, skips more than a single line during import">
|
||||
Lines to skip<sup>?</sup>:</label></td>
|
||||
<td><input type="number" id="csv_skip" value="0" min="0"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
</form>
|
||||
</t>
|
||||
<table t-name="ImportView.result"
|
||||
class="oe_import_grid" width="100%" style="margin: 5px 0;">
|
||||
<tr t-if="headers" class="oe_import_grid-header">
|
||||
<td t-foreach="headers" t-as="header" class="oe_import_grid-cell">
|
||||
<t t-esc="header"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td t-foreach="records[0]" t-as="column">
|
||||
<input class="sel_fields"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr t-foreach="records" t-as="record" class="oe_import_grid-row">
|
||||
<td t-foreach="record" t-as="cell" class="oe_import_grid-cell">
|
||||
<t t-esc="cell"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<t t-name="ImportView.error">
|
||||
<p style="white-space:pre-line;">The import failed due to:<t t-esc="error.message"/></p>
|
||||
<t t-if="error.preview">
|
||||
<p>Here is a preview of the file we could not import:</p>
|
||||
<pre><t t-esc="error.preview"/></pre>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="About-Page">
|
||||
<div>
|
||||
<h1>OpenERP Web</h1>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
import controllers
|
||||
@@ -1 +0,0 @@
|
||||
import main
|
||||
@@ -1,11 +0,0 @@
|
||||
from web.controllers.main import View
|
||||
import web.common as openerpweb
|
||||
|
||||
class GraphView(View):
|
||||
_cp_path = "/web_graph/graphview"
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def load(self, req, model, view_id):
|
||||
fields_view = self.fields_view_get(req, model, view_id, 'graph')
|
||||
all_fields = req.session.model(model).fields_get()
|
||||
return {'fields_view': fields_view, 'all_fields':all_fields}
|
||||
@@ -33,8 +33,20 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
this.$element.hide();
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super();
|
||||
return this.rpc("/web_graph/graphview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
|
||||
return $.when(
|
||||
new openerp.web.DataSet(this, this.model).call('fields_get', []),
|
||||
this.rpc('/web/view/load', {
|
||||
model: this.model,
|
||||
view_id: this.view_id,
|
||||
view_type: 'graph'
|
||||
})).then(function (fields_result, view_result) {
|
||||
self.on_loaded({
|
||||
all_fields: fields_result[0],
|
||||
fields_view: view_result[0]
|
||||
});
|
||||
});
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
this.all_fields = data.all_fields;
|
||||
@@ -49,8 +61,6 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
this.operator = [];
|
||||
this.group_field = [];
|
||||
this.orientation = this.fields_view.arch.attrs.orientation || '';
|
||||
this.elem_id = this.$element[0]['id'];
|
||||
|
||||
_.each(this.fields_view.arch.children, function (field) {
|
||||
if (field.attrs.operator) {
|
||||
this.operator.push(field.attrs.name);
|
||||
@@ -98,7 +108,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
},
|
||||
|
||||
schedule_chart: function(results) {
|
||||
this.$element.html(QWeb.render("GraphView", {"fields_view": this.fields_view, "chart": this.chart,'elem_id': this.elem_id}));
|
||||
this.$element.html(QWeb.render("GraphView", {"fields_view": this.fields_view, "chart": this.chart,'element_id': this.element_id}));
|
||||
|
||||
_.each(results, function (result) {
|
||||
_.each(result, function (field_value, field_name) {
|
||||
@@ -304,7 +314,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
|
||||
var bar_chart = new dhtmlXChart({
|
||||
view: view_chart,
|
||||
container: self.elem_id+"-barchart",
|
||||
container: self.element_id+"-barchart",
|
||||
value:"#"+group_list[0]+"#",
|
||||
gradient: "3d",
|
||||
border: false,
|
||||
@@ -374,7 +384,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
});
|
||||
}
|
||||
bar_chart.parse(_.values(abscissa_data), "json");
|
||||
jQuery("#"+self.elem_id+"-barchart").height(jQuery("#"+self.elem_id+"-barchart").height()+50);
|
||||
jQuery("#"+self.element_id+"-barchart").height(jQuery("#"+self.element_id+"-barchart").height()+50);
|
||||
bar_chart.attachEvent("onItemClick", function(id) {
|
||||
self.open_list_view(bar_chart.get(id));
|
||||
});
|
||||
@@ -383,7 +393,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({
|
||||
var self = this;
|
||||
var chart = new dhtmlXChart({
|
||||
view:"pie3D",
|
||||
container:self.elem_id+"-piechart",
|
||||
container:self.element_id+"-piechart",
|
||||
value:"#"+self.operator_field+"#",
|
||||
pieInnerText:function(obj) {
|
||||
var sum = chart.sum("#"+self.operator_field+"#");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<div t-name="GraphView" t-att-id="elem_id+'-'+chart+'chart'"
|
||||
<div t-name="GraphView" t-att-id="element_id+'-'+chart+'chart'"
|
||||
style="height:300px;position:relative;"/>
|
||||
</template>
|
||||
@@ -5,4 +5,5 @@
|
||||
"js": ["static/*/*.js", "static/*/js/*.js"],
|
||||
"css": [],
|
||||
'active': False,
|
||||
'web_preload': True,
|
||||
}
|
||||
|
||||
@@ -13,6 +13,13 @@ openerp.web.SearchView = openerp.web.SearchView.extend({
|
||||
|
||||
// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them
|
||||
|
||||
openerp.web.Login = openerp.web.Login.extend({
|
||||
start: function() {
|
||||
console.log('Hello there');
|
||||
this._super.apply(this,arguments);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// vim:et fdc=0 fdl=0:
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
.openerp .oe_kanban_view .oe_column {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.openerp .oe_vertical_text {
|
||||
writing-mode:tb-rl;
|
||||
-webkit-transform:rotate(90deg);
|
||||
-moz-transform:rotate(90deg);
|
||||
-o-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
display:block;
|
||||
width:30px;
|
||||
height:20px;
|
||||
align:center;
|
||||
font-size:24px;
|
||||
}
|
||||
|
||||
.openerp .oe_kanban_view .ui-sortable-placeholder {
|
||||
border: 1px dotted black;
|
||||
visibility: visible !important;
|
||||
@@ -14,6 +27,10 @@
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.openerp .oe_kanban_view .fold-columns-icon {
|
||||
float: left;
|
||||
padding: 2px 2px 0 0;
|
||||
}
|
||||
.openerp .oe_kanban_action_button {
|
||||
height: 22px;
|
||||
margin: 0;
|
||||
@@ -200,4 +217,3 @@
|
||||
.openerp .oe_kanban_color_alert .oe_kanban_color_border {
|
||||
border-color: #c00 !important;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 761 B |
@@ -18,6 +18,8 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
this.all_display_data = false;
|
||||
this.groups = [];
|
||||
this.qweb = new QWeb2.Engine();
|
||||
this.aggregates = {};
|
||||
this.NO_OF_COLUMNS = 3;
|
||||
if (this.options.action_views_ids.form) {
|
||||
this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start();
|
||||
this.form_dialog.on_form_dialog_saved.add_last(this.on_record_saved);
|
||||
@@ -36,8 +38,17 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
}
|
||||
},
|
||||
add_qweb_template: function() {
|
||||
var group_operator = ["avg", "max", "min", "sum", "count"]
|
||||
for (var i=0, ii=this.fields_view.arch.children.length; i < ii; i++) {
|
||||
var child = this.fields_view.arch.children[i];
|
||||
if (child.tag === "field") {
|
||||
for(j=0, jj=group_operator.length; j < jj; j++) {
|
||||
if (child.attrs[group_operator[j]]) {
|
||||
this.aggregates[child.attrs.name] = child.attrs[group_operator[j]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (child.tag === "templates") {
|
||||
this.transform_qweb_template(child);
|
||||
this.qweb.add_template(openerp.web.json_node_to_xml(child));
|
||||
@@ -118,14 +129,33 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
}
|
||||
}
|
||||
},
|
||||
on_show_data: function(data) {
|
||||
sort_group: function (first, second) {
|
||||
if (first.header && second.header)
|
||||
{
|
||||
first = first.header.toLowerCase();
|
||||
second = second.header.toLowerCase();
|
||||
if (first > second) return 1;
|
||||
else if (first < second) return -1;
|
||||
else return 0;
|
||||
}
|
||||
else return 0;
|
||||
},
|
||||
on_show_data: function() {
|
||||
var self = this;
|
||||
this.$element.html(QWeb.render("KanbanView", {"data": data}));
|
||||
if (!this.group_by.length) {
|
||||
this.do_record_group();
|
||||
}
|
||||
self.all_display_data.sort(this.sort_group);
|
||||
this.$element.html(QWeb.render("KanbanView", {"data": self.all_display_data}));
|
||||
this.on_reload_kanban();
|
||||
this.$element.find(".oe_vertical_text").hide();
|
||||
var drag_handel = false;
|
||||
if (this.$element.find(".oe_kanban_draghandle").length > 0) {
|
||||
drag_handel = ".oe_kanban_draghandle";
|
||||
}
|
||||
if (!this.group_by.length) {
|
||||
drag_handel = true;
|
||||
}
|
||||
this.$element.find(".oe_column").sortable({
|
||||
connectWith: ".oe_column",
|
||||
handle : drag_handel,
|
||||
@@ -134,36 +164,44 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
self.source_index['column'] = ui.item.parent().attr('id');
|
||||
},
|
||||
stop: self.on_receive_record,
|
||||
scroll: false
|
||||
});
|
||||
this.$element.find(".oe_column").disableSelection()
|
||||
this.$element.find('button.oe_kanban_button_new').click(this.do_add_record);
|
||||
this.$element.find(".fold-columns-icon").click(function(event) {
|
||||
self.do_fold_unfold_columns(event, this.id);
|
||||
});
|
||||
},
|
||||
on_button_click: function (button_attrs, record_id) {
|
||||
var self = this;
|
||||
if (this.groups.length) {
|
||||
_.each(this.groups, function (group) {
|
||||
group.list([],
|
||||
function (groups) {},
|
||||
function (dataset) {
|
||||
dataset.read_slice([], {}, function(records) {
|
||||
var index = parseInt(_.indexOf(dataset.ids, record_id));
|
||||
if(index >= 0) {
|
||||
self.on_confirm_click(dataset, button_attrs, index, record_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
var index = parseInt(_.indexOf(self.dataset.ids, record_id));
|
||||
if (index >= 0) {
|
||||
_.extend(self.dataset, {domain: self.domain, context: self.context});
|
||||
self.on_confirm_click(self.dataset, button_attrs, index, record_id);
|
||||
do_fold_unfold_columns: function(event, element_id) {
|
||||
var column_id = "column_" + element_id;
|
||||
var column_element = this.$element.find("#" + column_id + " .oe_fold_column");
|
||||
if (column_element.is(":hidden")) {
|
||||
this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/minus-icon.png');
|
||||
column_element.show();
|
||||
this.$element.find("#" + column_id + ".oe_table_column").css("width",Math.round(99 / this.all_display_data.length) + "%");
|
||||
this.$element.find("#" + column_id + ".oe_vertical_text").hide();
|
||||
}
|
||||
else{
|
||||
this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/plus-icon.png');
|
||||
column_element.hide();
|
||||
this.$element.find("#" + column_id + ".oe_table_column").css("width","0.5%");
|
||||
this.$element.find("#" + column_id + ".oe_vertical_text").show();
|
||||
}
|
||||
|
||||
},
|
||||
do_record_group: function() {
|
||||
if (this.NO_OF_COLUMNS && this.all_display_data.length > 0) {
|
||||
var records = this.all_display_data[0].records;
|
||||
var record_per_group = Math.round((records).length / this.NO_OF_COLUMNS);
|
||||
this.all_display_data = [];
|
||||
for (var i=0, ii=this.NO_OF_COLUMNS; i < ii; i++) {
|
||||
this.all_display_data.push({'records': records.slice(0,record_per_group), 'value':i, 'header' : false, 'ids':[]});
|
||||
records.splice(0,record_per_group);
|
||||
}
|
||||
}
|
||||
},
|
||||
on_confirm_click: function (dataset, button_attrs, index, record_id) {
|
||||
this.on_execute_button_click(dataset, button_attrs, record_id);
|
||||
on_button_click: function (button_attrs, record_id) {
|
||||
this.on_execute_button_click(this.dataset, button_attrs, record_id);
|
||||
},
|
||||
do_add_record: function() {
|
||||
this.dataset.index = null;
|
||||
@@ -179,8 +217,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
},
|
||||
on_record_saved: function(r) {
|
||||
var id = this.form_dialog.form.datarecord.id;
|
||||
// TODO fme: reload record instead of all. need refactoring
|
||||
this.do_actual_search();
|
||||
this.on_reload_record(id);
|
||||
},
|
||||
do_change_color: function(record_id, $e) {
|
||||
var self = this,
|
||||
@@ -198,12 +235,41 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
var data = {};
|
||||
data[$e.data('name')] = $(this).data('color');
|
||||
self.dataset.write(id, data, {}, function() {
|
||||
// TODO fme: reload record instead of all. need refactoring
|
||||
self.do_actual_search();
|
||||
self.on_reload_record(id);
|
||||
});
|
||||
$cpicker.remove();
|
||||
});
|
||||
},
|
||||
/**
|
||||
Reload one record in view.
|
||||
record_id : reload record id.
|
||||
*/
|
||||
on_reload_record: function (record_id){
|
||||
var self = this;
|
||||
this.dataset.read_ids([record_id], [], function (records) {
|
||||
if (records.length > 0) {
|
||||
for (var i=0, ii=self.all_display_data.length; i < ii; i++) {
|
||||
for(j=0, jj=self.all_display_data[i].records.length; j < jj; j++) {
|
||||
if (self.all_display_data[i].records[j].id == record_id) {
|
||||
_.extend(self.all_display_data[i].records[j], records[0]);
|
||||
self.$element.find("#main_" + record_id).children().remove();
|
||||
self.$element.find("#main_" + record_id).append(self.qweb.render('kanban-box', {
|
||||
record: self.do_transform_record(self.all_display_data[i].records[j]),
|
||||
kanban_color: self.kanban_color,
|
||||
kanban_gravatar: self.kanban_gravatar
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.$element.find("#main_" + record_id + " .oe_kanban_action").click(self.on_action_clicked);
|
||||
self.$element.find("#main_" + record_id + " .oe_kanban_box_show_onclick_trigger").click(function() {
|
||||
$(this).parent("#main_" + record_id + " .oe_kanban_box").find(".oe_kanban_box_show_onclick").toggle();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
do_delete: function (id) {
|
||||
var self = this;
|
||||
return $.when(this.dataset.unlink([id])).then(function () {
|
||||
@@ -228,16 +294,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
this.do_execute_action(
|
||||
button_attrs, dataset,
|
||||
record_id, function () {
|
||||
var count = 1;
|
||||
_.each(self.all_display_data, function(data, index) {
|
||||
self.dataset.read_ids( data.ids, [], function(records){
|
||||
self.all_display_data[index].records = records;
|
||||
if(self.all_display_data.length == count) {
|
||||
self.do_actual_search();
|
||||
}
|
||||
count++;
|
||||
});
|
||||
});
|
||||
self.do_actual_search();
|
||||
}
|
||||
);
|
||||
},
|
||||
@@ -249,9 +306,8 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
if (!ui.item.attr("id")) {
|
||||
return false;
|
||||
}
|
||||
// TODO fme: check what was this sequence
|
||||
if (self.fields_view.fields.sequence && (self.source_index.index >= 0 && self.source_index.index != from) ||
|
||||
(self.source_index.column && self.source_index.column != ui.item.parent().attr('id'))) {
|
||||
if (self.fields_view.fields.sequence != undefined && ((self.source_index.index >= 0 && self.source_index.index != from) ||
|
||||
(self.source_index.column && self.source_index.column != ui.item.parent().attr('id')))) {
|
||||
var child_record = ui.item.parent().children();
|
||||
var data, sequence = 1, index = to;
|
||||
child_record.splice(0, to);
|
||||
@@ -338,9 +394,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
kanban_gravatar: self.kanban_gravatar
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
self.$element.find("#column_" + data.value).remove();
|
||||
self.all_display_data.splice(index, 1);
|
||||
}
|
||||
});
|
||||
this.$element.find('.oe_kanban_action').click(this.on_action_clicked);
|
||||
@@ -377,8 +430,8 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
do_search: function (domains, contexts, group_by) {
|
||||
var self = this;
|
||||
this.rpc('/web/session/eval_domain_and_context', {
|
||||
domains: domains,
|
||||
contexts: contexts,
|
||||
domains: [this.dataset.get_domain()].concat(domains),
|
||||
contexts: [this.dataset.get_context()].concat(contexts),
|
||||
group_by_seq: group_by
|
||||
}, function (results) {
|
||||
self.domain = results.domain;
|
||||
@@ -392,23 +445,27 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
group_by = self.group_by;
|
||||
if (!group_by.length && this.fields_view.arch.attrs.default_group_by) {
|
||||
group_by = [this.fields_view.arch.attrs.default_group_by];
|
||||
self.group_by = group_by;
|
||||
}
|
||||
self.datagroup = new openerp.web.DataGroup(self, self.model, self.domain, self.context, group_by);
|
||||
self.dataset.context = self.context;
|
||||
self.dataset.domain = self.domain;
|
||||
self.datagroup.list([],
|
||||
self.datagroup.list(_.keys(self.fields_view.fields),
|
||||
function (groups) {
|
||||
self.groups = groups;
|
||||
self.do_render_group(groups);
|
||||
if (groups.length) {
|
||||
self.do_render_group(groups);
|
||||
}
|
||||
else {
|
||||
self.all_display_data = [];
|
||||
self.on_show_data();
|
||||
}
|
||||
},
|
||||
function (dataset) {
|
||||
self.domain = dataset.domain;
|
||||
self.context = dataset.context;
|
||||
self.groups = [];
|
||||
self.dataset.read_slice([], {}, function(records) {
|
||||
self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}];
|
||||
self.dataset.read_slice([], {'domain': self.domain, 'context': self.context}, function(records) {
|
||||
if (records.length) self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}];
|
||||
else self.all_display_data = [];
|
||||
self.$element.find(".oe_kanban_view").remove();
|
||||
self.on_show_data(self.all_display_data);
|
||||
self.on_show_data();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -417,8 +474,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
this.all_display_data = [];
|
||||
var self = this;
|
||||
_.each(datagroups, function (group) {
|
||||
self.dataset.context = group.context;
|
||||
self.dataset.domain = group.domain;
|
||||
var group_name = group.value;
|
||||
var group_value = group.value;
|
||||
if (!group.value) {
|
||||
@@ -428,11 +483,15 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({
|
||||
group_name = group.value[1];
|
||||
group_value = group.value[0];
|
||||
}
|
||||
self.dataset.read_slice([], {}, function(records) {
|
||||
self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids});
|
||||
var group_aggregates = {};
|
||||
_.each(self.aggregates, function(value, key) {
|
||||
group_aggregates[value] = group.aggregates[key];
|
||||
});
|
||||
self.dataset.read_slice([], {'domain': group.domain, 'context': group.context}, function(records) {
|
||||
self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids, 'aggregates': group_aggregates});
|
||||
if (datagroups.length == self.all_display_data.length) {
|
||||
self.$element.find(".oe_kanban_view").remove();
|
||||
self.on_show_data(self.all_display_data);
|
||||
self.on_show_data();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,15 +9,22 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td t-foreach="data" t-as="columns" class="oe_table_column oe_column_heading" t-att-id="'column_' + columns.value">
|
||||
<t t-if="columns.value" t-esc="columns.header"/>
|
||||
<td t-foreach="data" t-as="columns" class="oe_table_column" t-att-id="'column_' + columns.value">
|
||||
<img t-att-id="columns.value" class="fold-columns-icon" src="/web_kanban/static/src/img/minus-icon.png"/>
|
||||
<div class="oe_fold_column" t-att-id="'column_' + columns.value">
|
||||
<div class="oe_column_heading" t-if="columns.value and columns.header">
|
||||
<t t-esc="columns.header"/>
|
||||
</div>
|
||||
<div t-foreach="columns.aggregates or {}" t-as="aggregate">
|
||||
<i><u><t t-esc="aggregate"/>:</u> <t t-esc="aggregate_value"/></i>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td t-foreach="data" t-as="columns" class="oe_table_column" t-att-id="'column_' + columns.value" t-attf-style="width: #{Math.round(99 / data.length)}%">
|
||||
<div class="oe_column" t-att-id="'column_' + columns.value">
|
||||
<div t-foreach="columns.records" t-as="record" class="oe_kanban_record" t-att-id="'main_' + record.id"/>
|
||||
</div>
|
||||
<td t-foreach="data" t-as="columns" class="oe_column oe_table_column" t-att-id="'column_' + columns.value" t-attf-style="width: #{Math.round(99 / data.length)}%">
|
||||
<p t-if="columns.header" class="oe_vertical_text" t-att-id="'column_' + columns.value"><t t-esc="columns.header"/></p>
|
||||
<div t-foreach="columns.records" t-as="record" class="oe_fold_column oe_kanban_record" t-att-id="'main_' + record.id"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ optparser.add_option("--server-port", dest="server_port", default=8069,
|
||||
help="OpenERP server port", type="int", metavar="NUMBER")
|
||||
optparser.add_option("--db-filter", dest="dbfilter", default='.*',
|
||||
help="Filter listed database", metavar="REGEXP")
|
||||
optparser.add_option('--addons-path', dest='addons_path', default=path_addons,
|
||||
optparser.add_option('--addons-path', dest='addons_path', default=[path_addons], action='append',
|
||||
help="Path do addons directory", metavar="PATH")
|
||||
|
||||
server_options = optparse.OptionGroup(optparser, "Server configuration")
|
||||
|
||||
Reference in New Issue
Block a user