[MERGE] Merged from trunk.
bzr revid: jra@tinyerp.com-20120724060929-8v6t3ss5ykxi9jam
This commit is contained in:
commit
625e9b18a8
|
@ -33,6 +33,7 @@
|
|||
"static/lib/underscore/underscore.js",
|
||||
"static/lib/underscore/underscore.string.js",
|
||||
"static/lib/backbone/backbone.js",
|
||||
"static/lib/cleditor/jquery.cleditor.js",
|
||||
"static/lib/py.js/lib/py.js",
|
||||
"static/src/js/boot.js",
|
||||
"static/src/js/corelib.js",
|
||||
|
@ -60,6 +61,7 @@
|
|||
"static/src/css/base.css",
|
||||
"static/src/css/data_export.css",
|
||||
"static/src/css/data_import.css",
|
||||
"static/lib/cleditor/jquery.cleditor.css",
|
||||
],
|
||||
'qweb' : [
|
||||
"static/src/xml/*.xml",
|
||||
|
|
|
@ -28,6 +28,7 @@ import werkzeug.wsgi
|
|||
from . import nonliterals
|
||||
from . import session
|
||||
from . import openerplib
|
||||
import urlparse
|
||||
|
||||
__all__ = ['Root', 'jsonrequest', 'httprequest', 'Controller',
|
||||
'WebRequest', 'JsonRequest', 'HttpRequest']
|
||||
|
@ -417,6 +418,20 @@ class ControllerType(type):
|
|||
class Controller(object):
|
||||
__metaclass__ = ControllerType
|
||||
|
||||
class DisableCacheMiddleware(object):
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
def __call__(self, environ, start_response):
|
||||
def start_wrapped(status, headers):
|
||||
referer = environ.get('HTTP_REFERER', '')
|
||||
parsed = urlparse.urlparse(referer)
|
||||
debug = not urlparse.parse_qs(parsed.query).has_key('debug')
|
||||
filtered_headers = [(k,v) for k,v in headers if not (k=='Last-Modified' or (debug and k=='Cache-Control'))]
|
||||
if debug:
|
||||
filtered_headers.append(('Cache-Control', 'no-cache'))
|
||||
start_response(status, filtered_headers)
|
||||
return self.app(environ, start_wrapped)
|
||||
|
||||
class Root(object):
|
||||
"""Root WSGI application for the OpenERP Web Client.
|
||||
|
||||
|
@ -452,8 +467,8 @@ class Root(object):
|
|||
|
||||
static_dirs = self._load_addons(openerp_addons_namespace)
|
||||
if options.serve_static:
|
||||
self.dispatch = SuperSharedDataMiddleware(
|
||||
self.dispatch, static_dirs, cache=False)
|
||||
app = werkzeug.wsgi.SharedDataMiddleware( self.dispatch, static_dirs)
|
||||
self.dispatch = DisableCacheMiddleware(app)
|
||||
|
||||
if options.session_storage:
|
||||
if not os.path.exists(options.session_storage):
|
||||
|
@ -555,75 +570,6 @@ class Root(object):
|
|||
return m
|
||||
ps, _slash, meth = ps.rpartition('/')
|
||||
return None
|
||||
|
||||
class SuperSharedDataMiddleware(werkzeug.wsgi.SharedDataMiddleware):
|
||||
"""Redefine SharedDataMiddleware to better handle the cache = False directive.
|
||||
Also desactivate 304 Not Modified headers only when the referer has 'debug' in its
|
||||
arguments.
|
||||
"""
|
||||
def __call__(self, environ, start_response):
|
||||
import os
|
||||
import mimetypes
|
||||
import werkzeug.http
|
||||
import urlparse
|
||||
# sanitize the path for non unix systems
|
||||
cleaned_path = environ.get('PATH_INFO', '').strip('/')
|
||||
for sep in os.sep, os.altsep:
|
||||
if sep and sep != '/':
|
||||
cleaned_path = cleaned_path.replace(sep, '/')
|
||||
path = '/'.join([''] + [x for x in cleaned_path.split('/')
|
||||
if x and x != '..'])
|
||||
file_loader = None
|
||||
for search_path, loader in self.exports.iteritems():
|
||||
if search_path == path:
|
||||
real_filename, file_loader = loader(None)
|
||||
if file_loader is not None:
|
||||
break
|
||||
if not search_path.endswith('/'):
|
||||
search_path += '/'
|
||||
if path.startswith(search_path):
|
||||
real_filename, file_loader = loader(path[len(search_path):])
|
||||
if file_loader is not None:
|
||||
break
|
||||
if file_loader is None or not self.is_allowed(real_filename):
|
||||
return self.app(environ, start_response)
|
||||
|
||||
guessed_type = mimetypes.guess_type(real_filename)
|
||||
mime_type = guessed_type[0] or self.fallback_mimetype
|
||||
f, mtime, file_size = file_loader()
|
||||
|
||||
etag = self.generate_etag(mtime, file_size, real_filename)
|
||||
modified = werkzeug.http.is_resource_modified(environ, etag, last_modified=mtime)
|
||||
|
||||
headers = [('Date', werkzeug.http.http_date())]
|
||||
if self.cache:
|
||||
timeout = self.cache_timeout
|
||||
headers += [
|
||||
('Etag', '"%s"' % etag),
|
||||
('Cache-Control', 'max-age=%d, public' % timeout)
|
||||
]
|
||||
if modified:
|
||||
headers.append(('Expires', werkzeug.http.http_date(time() + timeout)))
|
||||
else:
|
||||
headers.append(('Cache-Control', 'no-cache'))
|
||||
|
||||
referer = environ.get('HTTP_REFERER', '')
|
||||
parsed = urlparse.urlparse(referer)
|
||||
debug = not urlparse.parse_qs(parsed.query).has_key('debug')
|
||||
# it's important to put it at the end
|
||||
if not debug and not modified:
|
||||
f.close()
|
||||
start_response('304 Not Modified', headers)
|
||||
return []
|
||||
|
||||
headers.extend((
|
||||
('Content-Type', mime_type),
|
||||
('Content-Length', str(file_size)),
|
||||
('Last-Modified', werkzeug.http.http_date(mtime))
|
||||
))
|
||||
start_response('200 OK', headers)
|
||||
return werkzeug.wsgi.wrap_file(environ, f)
|
||||
|
||||
|
||||
class LibException(Exception):
|
||||
""" Base of all client lib exceptions """
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 68 B |
|
@ -0,0 +1,24 @@
|
|||
.cleditorMain {border:1px solid #999; padding:0 1px 1px; background-color:white}
|
||||
.cleditorMain iframe {border:none; margin:0; padding:0}
|
||||
.cleditorMain textarea {border:none; margin:0; padding:0; overflow-y:scroll; font:10pt Arial,Verdana; resize:none; outline:none /* webkit grip focus */}
|
||||
.cleditorToolbar {background: url('images/toolbar.gif') repeat}
|
||||
.cleditorGroup {float:left; height:26px}
|
||||
.cleditorButton {float:left; width:24px; height:24px; margin:1px 0 1px 0; background: url('images/buttons.gif')}
|
||||
.cleditorDisabled {opacity:0.3; filter:alpha(opacity=30)}
|
||||
.cleditorDivider {float:left; width:1px; height:23px; margin:1px 0 1px 0; background:#CCC}
|
||||
.cleditorPopup {border:solid 1px #999; background-color:white; position:absolute; font:10pt Arial,Verdana; cursor:default; z-index:10000}
|
||||
.cleditorList div {padding:2px 4px 2px 4px}
|
||||
.cleditorList p,
|
||||
.cleditorList h1,
|
||||
.cleditorList h2,
|
||||
.cleditorList h3,
|
||||
.cleditorList h4,
|
||||
.cleditorList h5,
|
||||
.cleditorList h6,
|
||||
.cleditorList font {padding:0; margin:0; background-color:Transparent}
|
||||
.cleditorColor {width:150px; padding:1px 0 0 1px}
|
||||
.cleditorColor div {float:left; width:14px; height:14px; margin:0 1px 1px 0}
|
||||
.cleditorPrompt {background-color:#F6F7F9; padding:4px; font-size:8.5pt}
|
||||
.cleditorPrompt input,
|
||||
.cleditorPrompt textarea {font:8.5pt Arial,Verdana;}
|
||||
.cleditorMsg {background-color:#FDFCEE; width:150px; padding:4px; font-size:8.5pt}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
CLEditor WYSIWYG HTML Editor v1.3.0
|
||||
http://premiumsoftware.net/cleditor
|
||||
requires jQuery v1.4.2 or later
|
||||
|
||||
Copyright 2010, Chris Landowski, Premium Software, LLC
|
||||
Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*/
|
||||
(function(e){function aa(a){var b=this,c=a.target,d=e.data(c,x),h=s[d],f=h.popupName,i=p[f];if(!(b.disabled||e(c).attr(n)==n)){var g={editor:b,button:c,buttonName:d,popup:i,popupName:f,command:h.command,useCSS:b.options.useCSS};if(h.buttonClick&&h.buttonClick(a,g)===false)return false;if(d=="source"){if(t(b)){delete b.range;b.$area.hide();b.$frame.show();c.title=h.title}else{b.$frame.hide();b.$area.show();c.title="Show Rich Text"}setTimeout(function(){u(b)},100)}else if(!t(b))if(f){var j=e(i);if(f==
|
||||
"url"){if(d=="link"&&M(b)===""){z(b,"A selection is required when inserting a link.",c);return false}j.children(":button").unbind(q).bind(q,function(){var k=j.find(":text"),o=e.trim(k.val());o!==""&&v(b,g.command,o,null,g.button);k.val("http://");r();w(b)})}else f=="pastetext"&&j.children(":button").unbind(q).bind(q,function(){var k=j.find("textarea"),o=k.val().replace(/\n/g,"<br />");o!==""&&v(b,g.command,o,null,g.button);k.val("");r();w(b)});if(c!==e.data(i,A)){N(b,i,c);return false}return}else if(d==
|
||||
"print")b.$frame[0].contentWindow.print();else if(!v(b,g.command,g.value,g.useCSS,c))return false;w(b)}}function O(a){a=e(a.target).closest("div");a.css(H,a.data(x)?"#FFF":"#FFC")}function P(a){e(a.target).closest("div").css(H,"transparent")}function ba(a){var b=a.data.popup,c=a.target;if(!(b===p.msg||e(b).hasClass(B))){var d=e.data(b,A),h=e.data(d,x),f=s[h],i=f.command,g,j=this.options.useCSS;if(h=="font")g=c.style.fontFamily.replace(/"/g,"");else if(h=="size"){if(c.tagName=="DIV")c=c.children[0];
|
||||
g=c.innerHTML}else if(h=="style")g="<"+c.tagName+">";else if(h=="color")g=Q(c.style.backgroundColor);else if(h=="highlight"){g=Q(c.style.backgroundColor);if(l)i="backcolor";else j=true}b={editor:this,button:d,buttonName:h,popup:b,popupName:f.popupName,command:i,value:g,useCSS:j};if(!(f.popupClick&&f.popupClick(a,b)===false)){if(b.command&&!v(this,b.command,b.value,b.useCSS,d))return false;r();w(this)}}}function C(a){for(var b=1,c=0,d=0;d<a.length;++d){b=(b+a.charCodeAt(d))%65521;c=(c+b)%65521}return c<<
|
||||
16|b}function R(a,b,c,d,h){if(p[a])return p[a];var f=e(m).hide().addClass(ca).appendTo("body");if(d)f.html(d);else if(a=="color"){b=b.colors.split(" ");b.length<10&&f.width("auto");e.each(b,function(i,g){e(m).appendTo(f).css(H,"#"+g)});c=da}else if(a=="font")e.each(b.fonts.split(","),function(i,g){e(m).appendTo(f).css("fontFamily",g).html(g)});else if(a=="size")e.each(b.sizes.split(","),function(i,g){e(m).appendTo(f).html("<font size="+g+">"+g+"</font>")});else if(a=="style")e.each(b.styles,function(i,
|
||||
g){e(m).appendTo(f).html(g[1]+g[0]+g[1].replace("<","</"))});else if(a=="url"){f.html('Enter URL:<br><input type=text value="http://" size=35><br><input type=button value="Submit">');c=B}else if(a=="pastetext"){f.html("Paste your content here and click submit.<br /><textarea cols=40 rows=3></textarea><br /><input type=button value=Submit>");c=B}if(!c&&!d)c=S;f.addClass(c);l&&f.attr(I,"on").find("div,font,p,h1,h2,h3,h4,h5,h6").attr(I,"on");if(f.hasClass(S)||h===true)f.children().hover(O,P);p[a]=f[0];
|
||||
return f[0]}function T(a,b){if(b){a.$area.attr(n,n);a.disabled=true}else{a.$area.removeAttr(n);delete a.disabled}try{if(l)a.doc.body.contentEditable=!b;else a.doc.designMode=!b?"on":"off"}catch(c){}u(a)}function v(a,b,c,d,h){D(a);if(!l){if(d===undefined||d===null)d=a.options.useCSS;a.doc.execCommand("styleWithCSS",0,d.toString())}d=true;var f;if(l&&b.toLowerCase()=="inserthtml")y(a).pasteHTML(c);else{try{d=a.doc.execCommand(b,0,c||null)}catch(i){f=i.description;d=false}d||("cutcopypaste".indexOf(b)>
|
||||
-1?z(a,"For security reasons, your browser does not support the "+b+" command. Try using the keyboard shortcut or context menu instead.",h):z(a,f?f:"Error executing the "+b+" command.",h))}u(a);return d}function w(a){setTimeout(function(){t(a)?a.$area.focus():a.$frame[0].contentWindow.focus();u(a)},0)}function y(a){if(l)return J(a).createRange();return J(a).getRangeAt(0)}function J(a){if(l)return a.doc.selection;return a.$frame[0].contentWindow.getSelection()}function Q(a){var b=/rgba?\((\d+), (\d+), (\d+)/.exec(a),
|
||||
c=a.split("");if(b)for(a=(b[1]<<16|b[2]<<8|b[3]).toString(16);a.length<6;)a="0"+a;return"#"+(a.length==6?a:c[1]+c[1]+c[2]+c[2]+c[3]+c[3])}function r(){e.each(p,function(a,b){e(b).hide().unbind(q).removeData(A)})}function U(){var a=e("link[href$='jquery.cleditor.css']").attr("href");return a.substr(0,a.length-19)+"images/"}function K(a){var b=a.$main,c=a.options;a.$frame&&a.$frame.remove();var d=a.$frame=e('<iframe frameborder="0" src="javascript:true;">').hide().appendTo(b),h=d[0].contentWindow,f=
|
||||
a.doc=h.document,i=e(f);f.open();f.write(c.docType+"<html>"+(c.docCSSFile===""?"":'<head><link rel="stylesheet" type="text/css" href="'+c.docCSSFile+'" /></head>')+'<body style="'+c.bodyStyle+'"></body></html>');f.close();l&&i.click(function(){w(a)});E(a);if(l){i.bind("beforedeactivate beforeactivate selectionchange keypress",function(g){if(g.type=="beforedeactivate")a.inactive=true;else if(g.type=="beforeactivate"){!a.inactive&&a.range&&a.range.length>1&&a.range.shift();delete a.inactive}else if(!a.inactive){if(!a.range)a.range=
|
||||
[];for(a.range.unshift(y(a));a.range.length>2;)a.range.pop()}});d.focus(function(){D(a)})}(e.browser.mozilla?i:e(h)).blur(function(){V(a,true)});i.click(r).bind("keyup mouseup",function(){u(a)});L?a.$area.show():d.show();e(function(){var g=a.$toolbar,j=g.children("div:last"),k=b.width();j=j.offset().top+j.outerHeight()-g.offset().top+1;g.height(j);j=(/%/.test(""+c.height)?b.height():parseInt(c.height))-j;d.width(k).height(j);a.$area.width(k).height(ea?j-2:j);T(a,a.disabled);u(a)})}function u(a){if(!L&&
|
||||
e.browser.webkit&&!a.focused){a.$frame[0].contentWindow.focus();window.focus();a.focused=true}var b=a.doc;if(l)b=y(a);var c=t(a);e.each(a.$toolbar.find("."+W),function(d,h){var f=e(h),i=e.cleditor.buttons[e.data(h,x)],g=i.command,j=true;if(a.disabled)j=false;else if(i.getEnabled){j=i.getEnabled({editor:a,button:h,buttonName:i.name,popup:p[i.popupName],popupName:i.popupName,command:i.command,useCSS:a.options.useCSS});if(j===undefined)j=true}else if((c||L)&&i.name!="source"||l&&(g=="undo"||g=="redo"))j=
|
||||
false;else if(g&&g!="print"){if(l&&g=="hilitecolor")g="backcolor";if(!l||g!="inserthtml")try{j=b.queryCommandEnabled(g)}catch(k){j=false}}if(j){f.removeClass(X);f.removeAttr(n)}else{f.addClass(X);f.attr(n,n)}})}function D(a){l&&a.range&&a.range[0].select()}function M(a){D(a);if(l)return y(a).text;return J(a).toString()}function z(a,b,c){var d=R("msg",a.options,fa);d.innerHTML=b;N(a,d,c)}function N(a,b,c){var d,h,f=e(b);if(c){var i=e(c);d=i.offset();h=--d.left;d=d.top+i.height()}else{i=a.$toolbar;
|
||||
d=i.offset();h=Math.floor((i.width()-f.width())/2)+d.left;d=d.top+i.height()-2}r();f.css({left:h,top:d}).show();if(c){e.data(b,A,c);f.bind(q,{popup:b},e.proxy(ba,a))}setTimeout(function(){f.find(":text,textarea").eq(0).focus().select()},100)}function t(a){return a.$area.is(":visible")}function E(a,b){var c=a.$area.val(),d=a.options,h=d.updateFrame,f=e(a.doc.body);if(h){var i=C(c);if(b&&a.areaChecksum==i)return;a.areaChecksum=i}c=h?h(c):c;c=c.replace(/<(?=\/?script)/ig,"<");if(d.updateTextArea)a.frameChecksum=
|
||||
C(c);if(c!=f.html()){f.html(c);e(a).triggerHandler(F)}}function V(a,b){var c=e(a.doc.body).html(),d=a.options,h=d.updateTextArea,f=a.$area;if(h){var i=C(c);if(b&&a.frameChecksum==i)return;a.frameChecksum=i}c=h?h(c):c;if(d.updateFrame)a.areaChecksum=C(c);if(c!=f.val()){f.val(c);e(a).triggerHandler(F)}}e.cleditor={defaultOptions:{width:500,height:250,controls:"bold italic underline strikethrough subscript superscript | font size style | color highlight removeformat | bullets numbering | outdent indent | alignleft center alignright justify | undo redo | rule image link unlink | cut copy paste pastetext | print source",
|
||||
colors:"FFF FCC FC9 FF9 FFC 9F9 9FF CFF CCF FCF CCC F66 F96 FF6 FF3 6F9 3FF 6FF 99F F9F BBB F00 F90 FC6 FF0 3F3 6CC 3CF 66C C6C 999 C00 F60 FC3 FC0 3C0 0CC 36F 63F C3C 666 900 C60 C93 990 090 399 33F 60C 939 333 600 930 963 660 060 366 009 339 636 000 300 630 633 330 030 033 006 309 303",fonts:"Arial,Arial Black,Comic Sans MS,Courier New,Narrow,Garamond,Georgia,Impact,Sans Serif,Serif,Tahoma,Trebuchet MS,Verdana",sizes:"1,2,3,4,5,6,7",styles:[["Paragraph","<p>"],["Header 1","<h1>"],["Header 2","<h2>"],
|
||||
["Header 3","<h3>"],["Header 4","<h4>"],["Header 5","<h5>"],["Header 6","<h6>"]],useCSS:false,docType:'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',docCSSFile:"",bodyStyle:"margin:4px; font:10pt Arial,Verdana; cursor:text"},buttons:{init:"bold,,|italic,,|underline,,|strikethrough,,|subscript,,|superscript,,|font,,fontname,|size,Font Size,fontsize,|style,,formatblock,|color,Font Color,forecolor,|highlight,Text Highlight Color,hilitecolor,color|removeformat,Remove Formatting,|bullets,,insertunorderedlist|numbering,,insertorderedlist|outdent,,|indent,,|alignleft,Align Text Left,justifyleft|center,,justifycenter|alignright,Align Text Right,justifyright|justify,,justifyfull|undo,,|redo,,|rule,Insert Horizontal Rule,inserthorizontalrule|image,Insert Image,insertimage,url|link,Insert Hyperlink,createlink,url|unlink,Remove Hyperlink,|cut,,|copy,,|paste,,|pastetext,Paste as Text,inserthtml,|print,,|source,Show Source"},
|
||||
imagesPath:function(){return U()}};e.fn.cleditor=function(a){var b=e([]);this.each(function(c,d){if(d.tagName=="TEXTAREA"){var h=e.data(d,Y);h||(h=new cleditor(d,a));b=b.add(h)}});return b};var H="backgroundColor",A="button",x="buttonName",F="change",Y="cleditor",q="click",n="disabled",m="<div>",I="unselectable",W="cleditorButton",X="cleditorDisabled",ca="cleditorPopup",S="cleditorList",da="cleditorColor",B="cleditorPrompt",fa="cleditorMsg",l=e.browser.msie,ea=/msie\s6/i.test(navigator.userAgent),
|
||||
L=/iphone|ipad|ipod/i.test(navigator.userAgent),p={},Z,s=e.cleditor.buttons;e.each(s.init.split("|"),function(a,b){var c=b.split(","),d=c[0];s[d]={stripIndex:a,name:d,title:c[1]===""?d.charAt(0).toUpperCase()+d.substr(1):c[1],command:c[2]===""?d:c[2],popupName:c[3]===""?d:c[3]}});delete s.init;cleditor=function(a,b){var c=this;c.options=b=e.extend({},e.cleditor.defaultOptions,b);var d=c.$area=e(a).hide().data(Y,c).blur(function(){E(c,true)}),h=c.$main=e(m).addClass("cleditorMain").width(b.width).height(b.height),
|
||||
f=c.$toolbar=e(m).addClass("cleditorToolbar").appendTo(h),i=e(m).addClass("cleditorGroup").appendTo(f);e.each(b.controls.split(" "),function(g,j){if(j==="")return true;if(j=="|"){e(m).addClass("cleditorDivider").appendTo(i);i=e(m).addClass("cleditorGroup").appendTo(f)}else{var k=s[j],o=e(m).data(x,k.name).addClass(W).attr("title",k.title).bind(q,e.proxy(aa,c)).appendTo(i).hover(O,P),G={};if(k.css)G=k.css;else if(k.image)G.backgroundImage="url("+U()+k.image+")";if(k.stripIndex)G.backgroundPosition=
|
||||
k.stripIndex*-24;o.css(G);l&&o.attr(I,"on");k.popupName&&R(k.popupName,b,k.popupClass,k.popupContent,k.popupHover)}});h.insertBefore(d).append(d);if(!Z){e(document).click(function(g){g=e(g.target);g.add(g.parents()).is("."+B)||r()});Z=true}/auto|%/.test(""+b.width+b.height)&&e(window).resize(function(){K(c)});K(c)};var $=cleditor.prototype;e.each([["clear",function(a){a.$area.val("");E(a)}],["disable",T],["execCommand",v],["focus",w],["hidePopups",r],["sourceMode",t,true],["refresh",K],["select",
|
||||
function(a){setTimeout(function(){t(a)?a.$area.select():v(a,"selectall")},0)}],["selectedHTML",function(a){D(a);a=y(a);if(l)return a.htmlText;var b=e("<layer>")[0];b.appendChild(a.cloneContents());return b.innerHTML},true],["selectedText",M,true],["showMessage",z],["updateFrame",E],["updateTextArea",V]],function(a,b){$[b[0]]=function(){for(var c=[this],d=0;d<arguments.length;d++)c.push(arguments[d]);c=b[1].apply(this,c);if(b[2])return c;return this}});$.change=function(a){var b=e(this);return a?b.bind(F,
|
||||
a):b.trigger(F)}})(jQuery);
|
|
@ -1,4 +1,4 @@
|
|||
@charset "utf-8";
|
||||
@charset "UTF-8";
|
||||
@font-face {
|
||||
font-family: "mnmliconsRegular";
|
||||
src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot");
|
||||
|
@ -34,7 +34,7 @@
|
|||
background: white;
|
||||
/* http://www.quirksmode.org/dom/inputfile.html
|
||||
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
|
||||
*/ */
|
||||
*/
|
||||
}
|
||||
.openerp a {
|
||||
text-decoration: none;
|
||||
|
@ -234,14 +234,13 @@
|
|||
.openerp.ui-dialog .ui-dialog-buttonpane {
|
||||
border-top: 1px solid #e0e0e0;
|
||||
background: #f5f7f9;
|
||||
padding: 8px;
|
||||
margin: 0;
|
||||
-moz-border-radius: 0 0 2px 2px;
|
||||
-webkit-border-radius: 0 0 2px 2px;
|
||||
border-radius: 0 0 2px 2px;
|
||||
}
|
||||
.openerp.ui-dialog .ui-dialog-buttonpane button {
|
||||
margin-left: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
.openerp.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
|
||||
float: left;
|
||||
|
@ -1855,9 +1854,12 @@
|
|||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
.openerp .oe_form .oe_subtotal_footer label:after {
|
||||
content: ":";
|
||||
}
|
||||
.openerp .oe_form .oe_subtotal_footer label.oe_subtotal_footer_separator {
|
||||
font-weight: bold !important;
|
||||
padding: 2px 8px 2px 0px !important;
|
||||
padding: 2px 11px 2px 0px !important;
|
||||
}
|
||||
.openerp .oe_form .oe_subtotal_footer label.oe_form_label_help {
|
||||
font-weight: normal;
|
||||
|
@ -2061,15 +2063,6 @@
|
|||
.openerp .oe_form .oe_form_field_with_button.oe_no_button > .oe_button {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_form .oe_form_field_with_button:not(.oe_no_button) input {
|
||||
border-right: none;
|
||||
-webkit-border-top-right-radius: 0px;
|
||||
-webkit-border-bottom-right-radius: 0px;
|
||||
-moz-border-radius-topright: 0px;
|
||||
-moz-border-radius-bottomright: 0px;
|
||||
border-top-right-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
}
|
||||
.openerp .oe_form .oe_form_field_with_button:not(.oe_no_button) > .oe_button {
|
||||
float: right;
|
||||
-moz-border-radius: 0;
|
||||
|
@ -2148,9 +2141,12 @@
|
|||
}
|
||||
.openerp .oe_form_field_many2one span.oe_m2o_drop_down_button {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
top: 2px;
|
||||
right: 0px;
|
||||
}
|
||||
.openerp .oe_form_field_many2one .oe_m2o_cm_button {
|
||||
line-height: 14px;
|
||||
}
|
||||
.openerp .oe_form .oe_form_field_one2many > .oe_view_manager .oe_list_pager_single_page {
|
||||
display: none;
|
||||
}
|
||||
|
@ -2184,9 +2180,33 @@
|
|||
height: auto;
|
||||
line-height: 16px;
|
||||
}
|
||||
.openerp .oe_form_field_one2many .oe_list_buttons.oe_editing .oe_list_save {
|
||||
visibility: hidden;
|
||||
}
|
||||
.openerp .oe_form .oe_form_field_many2many > .oe_list .oe_list_pager_single_page {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_list_buttons .oe_list_save, .openerp .oe_list_buttons .oe_list_discard {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_list_buttons.oe_editing .oe_list_add, .openerp .oe_list_buttons.oe_editing .oe_list_button_import {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_list_buttons.oe_editing .oe_list_save {
|
||||
display: inline-block;
|
||||
}
|
||||
.openerp .oe_list_buttons.oe_editing .oe_list_discard {
|
||||
display: inline;
|
||||
}
|
||||
.openerp .oe_list {
|
||||
position: relative;
|
||||
}
|
||||
.openerp .oe_list .oe_form .oe_form_field {
|
||||
width: auto;
|
||||
position: absolute;
|
||||
margin: 0 !important;
|
||||
padding: 0;
|
||||
}
|
||||
.openerp .oe_list_content {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -2244,6 +2264,7 @@
|
|||
}
|
||||
.openerp .oe_list_content > tbody > tr > td.oe_list_field_cell {
|
||||
padding: 3px 6px;
|
||||
white-space: pre-line;
|
||||
}
|
||||
.openerp .oe_list_content > tbody > tr > td.oe_list_field_cell progress {
|
||||
width: 100%;
|
||||
|
@ -2294,9 +2315,6 @@
|
|||
.openerp .oe_list_content .numeric input {
|
||||
text-align: right;
|
||||
}
|
||||
.openerp .oe_list_content .oe_list_edit_row_save:before {
|
||||
content: "S";
|
||||
}
|
||||
.openerp .oe_trad_field.touched {
|
||||
border: 1px solid green !important;
|
||||
}
|
||||
|
@ -2423,6 +2441,19 @@
|
|||
.kitten-mode-activated > * {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.kitten-mode-activated .oe_footer a {
|
||||
background-image: url(http://www.risacher.com/la-rache/zfiles/la-rache.png);
|
||||
font-size: 1px;
|
||||
letter-spacing: -1px;
|
||||
color: transparent;
|
||||
display: inline-block;
|
||||
height: 15px;
|
||||
width: 80px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.kitten-mode-activated .oe_footer a span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.ui-widget-overlay {
|
||||
background: black;
|
||||
|
|
|
@ -92,9 +92,8 @@ $sheet-max-width: 860px
|
|||
letter-spacing: -1px
|
||||
color: transparent
|
||||
&:before
|
||||
font-family: "mnmliconsRegular"
|
||||
font: 21px "mnmliconsRegular"
|
||||
content: $icon-name
|
||||
font-size: 20px
|
||||
color: $color
|
||||
|
||||
// }}}
|
||||
|
@ -232,11 +231,10 @@ $sheet-max-width: 860px
|
|||
.ui-dialog-buttonpane
|
||||
border-top: 1px solid #e0e0e0
|
||||
background: #f5f7f9
|
||||
padding: 8px
|
||||
margin: 0
|
||||
@include radius(0 0 2px 2px)
|
||||
button
|
||||
margin-left: 8px
|
||||
margin: 0
|
||||
.ui-dialog-buttonset
|
||||
float: left
|
||||
.ui-dialog-titlebar-close
|
||||
|
@ -1444,9 +1442,11 @@ $sheet-max-width: 860px
|
|||
border-top: 1px solid #cacaca
|
||||
font-weight: bold
|
||||
font-size: 18px
|
||||
label:after
|
||||
content: ":"
|
||||
label.oe_subtotal_footer_separator
|
||||
font-weight: bold !important
|
||||
padding: 2px 8px 2px 0px !important
|
||||
padding: 2px 11px 2px 0px !important
|
||||
label.oe_form_label_help
|
||||
font-weight: normal
|
||||
// no sheet in popups
|
||||
|
@ -1610,14 +1610,6 @@ $sheet-max-width: 860px
|
|||
.oe_form_field_with_button.oe_no_button > .oe_button
|
||||
display: none
|
||||
.oe_form_field_with_button:not(.oe_no_button)
|
||||
input
|
||||
border-right: none
|
||||
-webkit-border-top-right-radius: 0px
|
||||
-webkit-border-bottom-right-radius: 0px
|
||||
-moz-border-radius-topright: 0px
|
||||
-moz-border-radius-bottomright: 0px
|
||||
border-top-right-radius: 0px
|
||||
border-bottom-right-radius: 0px
|
||||
> .oe_button
|
||||
float: right
|
||||
@include radius(0)
|
||||
|
@ -1691,8 +1683,10 @@ $sheet-max-width: 860px
|
|||
position: relative
|
||||
span.oe_m2o_drop_down_button
|
||||
position: absolute
|
||||
top: 1px
|
||||
top: 2px
|
||||
right: 0px
|
||||
.oe_m2o_cm_button
|
||||
line-height: 14px
|
||||
// }}}
|
||||
// FormView.one2many {{{
|
||||
.oe_form .oe_form_field_one2many > .oe_view_manager
|
||||
|
@ -1726,6 +1720,9 @@ $sheet-max-width: 860px
|
|||
li
|
||||
height: auto
|
||||
line-height: 16px
|
||||
.oe_list_buttons.oe_editing .oe_list_save
|
||||
// keep "save row" button hidden in o2m
|
||||
visibility: hidden
|
||||
// }}}
|
||||
// FormView.many2many {{{
|
||||
.oe_form .oe_form_field_many2many > .oe_list
|
||||
|
@ -1733,6 +1730,25 @@ $sheet-max-width: 860px
|
|||
display: none
|
||||
// }}}
|
||||
// ListView {{{
|
||||
.oe_list_buttons
|
||||
.oe_list_save, .oe_list_discard
|
||||
display: none
|
||||
&.oe_editing
|
||||
.oe_list_add, .oe_list_button_import
|
||||
display: none
|
||||
.oe_list_save
|
||||
display: inline-block
|
||||
.oe_list_discard
|
||||
display: inline
|
||||
|
||||
.oe_list
|
||||
position: relative
|
||||
.oe_form .oe_form_field
|
||||
width: auto
|
||||
position: absolute
|
||||
margin: 0 !important // dammit
|
||||
padding: 0
|
||||
|
||||
.oe_list_content
|
||||
width: 100%
|
||||
td:first-child, th:first-child
|
||||
|
@ -1776,8 +1792,7 @@ $sheet-max-width: 860px
|
|||
border-top: 1px solid #ddd
|
||||
> td.oe_list_field_cell
|
||||
padding: 3px 6px
|
||||
progress
|
||||
width: 100%
|
||||
white-space: pre-line
|
||||
> td, > th
|
||||
> button
|
||||
border: none
|
||||
|
@ -1805,8 +1820,6 @@ $sheet-max-width: 860px
|
|||
width: 82px
|
||||
input
|
||||
text-align: right
|
||||
.oe_list_edit_row_save:before
|
||||
content: "S"
|
||||
// }}}
|
||||
// Translation {{{
|
||||
.oe_trad_field.touched
|
||||
|
@ -1915,6 +1928,7 @@ $sheet-max-width: 860px
|
|||
background-attachment: fixed
|
||||
>*
|
||||
opacity: 0.70
|
||||
|
||||
// }}}
|
||||
|
||||
div.ui-widget-overlay
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
init: function(modules) {
|
||||
// By default only web will be loaded, the rest will be by loaded
|
||||
// by openerp.web.Session on the first session_authenticate
|
||||
modules = modules || ["web"];
|
||||
modules = _.union(['web'], modules || []);
|
||||
var new_instance = {
|
||||
// links to the global openerp
|
||||
_openerp: openerp,
|
||||
|
|
|
@ -135,8 +135,12 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
this.$element.dialog('close');
|
||||
},
|
||||
on_close: function() {
|
||||
if (this.__tmp_dialog_destroying)
|
||||
return;
|
||||
if (this.dialog_options.destroy_on_close) {
|
||||
this.__tmp_dialog_closing = true;
|
||||
this.destroy();
|
||||
this.__tmp_dialog_closing = undefined;
|
||||
}
|
||||
},
|
||||
on_resized: function() {
|
||||
|
@ -145,6 +149,11 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
_.each(this.getChildren(), function(el) {
|
||||
el.destroy();
|
||||
});
|
||||
if (! this.__tmp_dialog_closing) {
|
||||
this.__tmp_dialog_destroying = true;
|
||||
this.close();
|
||||
this.__tmp_dialog_destroying = undefined;
|
||||
}
|
||||
if (! this.isDestroyed()) {
|
||||
this.$element.dialog('destroy');
|
||||
}
|
||||
|
@ -254,7 +263,11 @@ instance.web.Loading = instance.web.Widget.extend({
|
|||
|
||||
this.count += increment;
|
||||
if (this.count > 0) {
|
||||
this.$element.text(_.str.sprintf( _t("Loading (%d)"), this.count));
|
||||
if (instance.connection.debug) {
|
||||
this.$element.text(_.str.sprintf( _t("Loading (%d)"), this.count));
|
||||
} else {
|
||||
this.$element.text(_t("Loading"));
|
||||
}
|
||||
this.$element.show();
|
||||
this.getParent().$element.addClass('oe_wait');
|
||||
} else {
|
||||
|
@ -856,44 +869,24 @@ instance.web.UserMenu = instance.web.Widget.extend({
|
|||
},
|
||||
});
|
||||
|
||||
instance.web.WebClient = instance.web.Widget.extend({
|
||||
init: function(parent) {
|
||||
var self = this;
|
||||
instance.web.Client = instance.web.Widget.extend({
|
||||
init: function(parent, origin) {
|
||||
instance.client = instance.webclient = this;
|
||||
this._super(parent);
|
||||
instance.webclient = this;
|
||||
this.querystring = '?' + jQuery.param.querystring();
|
||||
this._current_state = null;
|
||||
},
|
||||
_get_version_label: function() {
|
||||
if (this.session.openerp_entreprise) {
|
||||
return 'OpenERP';
|
||||
} else {
|
||||
return _t("OpenERP - Unsupported/Community Version");
|
||||
}
|
||||
},
|
||||
set_title: function(title) {
|
||||
title = _.str.clean(title);
|
||||
var sep = _.isEmpty(title) ? '' : ' - ';
|
||||
document.title = title + sep + 'OpenERP';
|
||||
this.origin = origin;
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this.$element.addClass("openerp openerp_webclient_container");
|
||||
if (jQuery.param !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined) {
|
||||
$("body").addClass("kitten-mode-activated");
|
||||
if ($.blockUI) {
|
||||
$.blockUI.defaults.message = '<img src="http://www.amigrave.com/kitten.gif">';
|
||||
}
|
||||
}
|
||||
this.session.session_bind().then(function() {
|
||||
self.destroy_content();
|
||||
return instance.connection.session_bind(this.origin).then(function() {
|
||||
var $e = $(QWeb.render(self._template, {}));
|
||||
self.$element.replaceWith($e);
|
||||
self.$element = $e;
|
||||
self.bind_events();
|
||||
self.show_common();
|
||||
if (!self.session.session_is_valid()) {
|
||||
self.show_login();
|
||||
} else {
|
||||
self.show_application();
|
||||
}
|
||||
});
|
||||
},
|
||||
bind_events: function() {
|
||||
var self = this;
|
||||
this.$element.on('mouseenter', '.oe_systray > div:not([data-tipsy=true])', function() {
|
||||
$(this).attr('data-tipsy', 'true').tipsy().trigger('mouseenter');
|
||||
});
|
||||
|
@ -925,6 +918,45 @@ instance.web.WebClient = instance.web.Widget.extend({
|
|||
var self = this;
|
||||
this.crashmanager = new instance.web.CrashManager();
|
||||
instance.connection.on_rpc_error.add(this.crashmanager.on_rpc_error);
|
||||
self.notification = new instance.web.Notification(this);
|
||||
self.notification.appendTo(self.$element);
|
||||
self.loading = new instance.web.Loading(self);
|
||||
self.loading.appendTo(self.$element);
|
||||
self.action_manager = new instance.web.ActionManager(self);
|
||||
self.action_manager.appendTo(self.$('.oe_application'));
|
||||
},
|
||||
});
|
||||
|
||||
instance.web.WebClient = instance.web.Client.extend({
|
||||
_template: 'WebClient',
|
||||
init: function(parent) {
|
||||
this._super(parent);
|
||||
this._current_state = null;
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
return $.when(this._super()).pipe(function() {
|
||||
if (jQuery.param !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined) {
|
||||
$("body").addClass("kitten-mode-activated");
|
||||
if ($.blockUI) {
|
||||
$.blockUI.defaults.message = '<img src="http://www.amigrave.com/kitten.gif">';
|
||||
}
|
||||
}
|
||||
if (!self.session.session_is_valid()) {
|
||||
self.show_login();
|
||||
} else {
|
||||
self.show_application();
|
||||
}
|
||||
});
|
||||
},
|
||||
set_title: function(title) {
|
||||
title = _.str.clean(title);
|
||||
var sep = _.isEmpty(title) ? '' : ' - ';
|
||||
document.title = title + sep + 'OpenERP';
|
||||
},
|
||||
show_common: function() {
|
||||
var self = this;
|
||||
this._super();
|
||||
window.onerror = function (message, file, line) {
|
||||
self.crashmanager.on_traceback({
|
||||
type: _t("Client Error"),
|
||||
|
@ -932,23 +964,18 @@ instance.web.WebClient = instance.web.Widget.extend({
|
|||
data: {debug: file + ':' + line}
|
||||
});
|
||||
};
|
||||
self.notification = new instance.web.Notification(this);
|
||||
self.notification.appendTo(self.$element);
|
||||
self.loading = new instance.web.Loading(self);
|
||||
self.loading.appendTo(self.$element);
|
||||
// TODO: deprecate and use login client action
|
||||
self.login = new instance.web.Login(self);
|
||||
self.login.on("login",self,self.show_application);
|
||||
self.$table = $(QWeb.render("WebClient", {}));
|
||||
self.action_manager = new instance.web.ActionManager(self);
|
||||
self.action_manager.appendTo(self.$table.find('.oe_application'));
|
||||
},
|
||||
show_login: function() {
|
||||
var self = this;
|
||||
self.$('.oe_topbar').hide();
|
||||
self.login.appendTo(self.$element);
|
||||
},
|
||||
show_application: function() {
|
||||
var self = this;
|
||||
self.$element.append(self.$table);
|
||||
self.$('.oe_topbar').show();
|
||||
self.login.$element.hide();
|
||||
self.menu = new instance.web.Menu(self);
|
||||
self.menu.replace(this.$element.find('.oe_menu_placeholder'));
|
||||
|
@ -960,7 +987,7 @@ instance.web.WebClient = instance.web.Widget.extend({
|
|||
self.user_menu.do_update();
|
||||
self.bind_hashchange();
|
||||
if (!self.session.openerp_entreprise) {
|
||||
var version_label = self._get_version_label();
|
||||
var version_label = _t("OpenERP - Unsupported/Community Version");
|
||||
self.$element.find('.oe_footer_powered').append(_.str.sprintf('<span> - <a href="http://www.openerp.com/support-or-publisher-warranty-contract" target="_blank">%s</a></span>', version_label));
|
||||
}
|
||||
self.set_title();
|
||||
|
@ -1063,31 +1090,36 @@ instance.web.WebClient = instance.web.Widget.extend({
|
|||
}
|
||||
});
|
||||
|
||||
instance.web.EmbeddedClient = instance.web.Widget.extend({
|
||||
template: 'EmptyComponent',
|
||||
init: function(parent, action_id, options) {
|
||||
this._super(parent);
|
||||
// TODO take the xmlid of a action instead of its id
|
||||
instance.web.EmbeddedClient = instance.web.Client.extend({
|
||||
_template: 'EmbedClient',
|
||||
init: function(parent, origin, dbname, login, key, action_id, options) {
|
||||
this._super(parent, origin);
|
||||
|
||||
this.dbname = dbname;
|
||||
this.login = login;
|
||||
this.key = key;
|
||||
this.action_id = action_id;
|
||||
this.options = options || {};
|
||||
this.am = new instance.web.ActionManager(this);
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this.am.appendTo(this.$element.addClass('openerp'));
|
||||
return this.rpc("/web/action/load", { action_id: this.action_id }, function(result) {
|
||||
var action = result.result;
|
||||
action.flags = _.extend({
|
||||
//views_switcher : false,
|
||||
search_view : false,
|
||||
action_buttons : false,
|
||||
sidebar : false
|
||||
//pager : false
|
||||
}, self.options, action.flags || {});
|
||||
return $.when(this._super()).pipe(function() {
|
||||
return instance.connection.session_authenticate(self.dbname, self.login, self.key, true).pipe(function() {
|
||||
return self.rpc("/web/action/load", { action_id: self.action_id }, function(result) {
|
||||
var action = result.result;
|
||||
action.flags = _.extend({
|
||||
//views_switcher : false,
|
||||
search_view : false,
|
||||
action_buttons : false,
|
||||
sidebar : false
|
||||
//pager : false
|
||||
}, self.options, action.flags || {});
|
||||
|
||||
self.am.do_action(action);
|
||||
self.action_manager.do_action(action);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
instance.web.embed = function (origin, dbname, login, key, action, options) {
|
||||
|
@ -1101,13 +1133,8 @@ instance.web.embed = function (origin, dbname, login, key, action, options) {
|
|||
var sc = document.getElementsByTagName('script');
|
||||
currentScript = sc[sc.length-1];
|
||||
}
|
||||
instance.connection.session_bind(origin).then(function () {
|
||||
instance.connection.session_authenticate(dbname, login, key, true).then(function () {
|
||||
var client = new instance.web.EmbeddedClient(null, action, options);
|
||||
client.insertAfter(currentScript);
|
||||
});
|
||||
});
|
||||
|
||||
var client = new instance.web.EmbeddedClient(null, origin, dbname, login, key, action, options);
|
||||
client.insertAfter(currentScript);
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -1669,6 +1669,7 @@ instance.web.search.AddToDashboard = instance.web.Widget.extend({
|
|||
return $.when(this.load_data(),this.data_loaded).pipe(this.proxy("render_data"));
|
||||
},
|
||||
load_data:function(){
|
||||
if (!instance.webclient) { return $.Deferred().reject(); }
|
||||
var self = this,dashboard_menu = instance.webclient.menu.data.data.children;
|
||||
var ir_model_data = new instance.web.Model('ir.model.data',{},[['name','=','menu_reporting_dashboard']]).query(['res_id']);
|
||||
var map_data = function(result){
|
||||
|
|
|
@ -80,7 +80,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
this.reload_mutex = new $.Mutex();
|
||||
this.__clicked_inside = false;
|
||||
this.__blur_timeout = null;
|
||||
this.rendering_engine = new instance.web.form.FormRenderingEngineReadonly(this);
|
||||
this.rendering_engine = new instance.web.form.FormRenderingEngine(this);
|
||||
this.qweb = null; // A QWeb instance will be created if the view is a QWeb template
|
||||
},
|
||||
destroy: function() {
|
||||
|
@ -239,6 +239,13 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [editable=false] whether the form should be switched to edition mode. A value of ``false`` will keep the current mode.
|
||||
* @param {Boolean} [reload=true] whether the form should reload its content on show, or use the currently loaded record
|
||||
* @return {$.Deferred}
|
||||
*/
|
||||
do_show: function (options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
|
@ -253,23 +260,24 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
}
|
||||
this.$element.show().css('visibility', 'hidden');
|
||||
this.$element.add(this.$buttons).removeClass('oe_form_dirty');
|
||||
return this.has_been_loaded.pipe(function() {
|
||||
var result;
|
||||
if (self.dataset.index === null) {
|
||||
// null index means we should start a new record
|
||||
result = self.on_button_new();
|
||||
} else {
|
||||
result = self.dataset.read_index(_.keys(self.fields_view.fields), {
|
||||
context : { 'bin_size' : true }
|
||||
}).pipe(self.on_record_loaded);
|
||||
}
|
||||
result.pipe(function() {
|
||||
if (options.editable) {
|
||||
self.set({mode: "edit"});
|
||||
|
||||
var shown = this.has_been_loaded;
|
||||
if (options.reload !== false) {
|
||||
shown = shown.pipe(function() {
|
||||
if (self.dataset.index === null) {
|
||||
// null index means we should start a new record
|
||||
return self.on_button_new();
|
||||
}
|
||||
self.$element.css('visibility', 'visible');
|
||||
return self.dataset.read_index(_.keys(self.fields_view.fields), {
|
||||
context: { 'bin_size': true }
|
||||
}).pipe(self.on_record_loaded);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return shown.pipe(function() {
|
||||
if (options.editable) {
|
||||
self.set({mode: "edit"});
|
||||
}
|
||||
self.$element.css('visibility', 'visible');
|
||||
});
|
||||
},
|
||||
do_hide: function () {
|
||||
|
@ -330,6 +338,20 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
self.$element.add(self.$buttons).removeClass('oe_form_dirty');
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Loads and sets up the default values for the model as the current
|
||||
* record
|
||||
*
|
||||
* @return {$.Deferred}
|
||||
*/
|
||||
load_defaults: function () {
|
||||
var keys = _.keys(this.fields_view.fields);
|
||||
if (keys.length) {
|
||||
return this.dataset.default_get(keys)
|
||||
.pipe(this.on_record_loaded);
|
||||
}
|
||||
return this.on_record_loaded({});
|
||||
},
|
||||
on_form_changed: function() {
|
||||
this.trigger("view_content_has_changed");
|
||||
},
|
||||
|
@ -600,22 +622,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
on_button_new: function() {
|
||||
var self = this;
|
||||
this.set({mode: "edit"});
|
||||
var def = $.Deferred();
|
||||
$.when(this.has_been_loaded).then(function() {
|
||||
return $.when(this.has_been_loaded).pipe(function() {
|
||||
if (self.can_be_discarded()) {
|
||||
var keys = _.keys(self.fields_view.fields);
|
||||
if (keys.length) {
|
||||
self.dataset.default_get(keys).pipe(self.on_record_loaded).then(function() {
|
||||
def.resolve();
|
||||
});
|
||||
} else {
|
||||
self.on_record_loaded({}).then(function() {
|
||||
def.resolve();
|
||||
});
|
||||
}
|
||||
return self.load_defaults();
|
||||
}
|
||||
});
|
||||
return def.promise();
|
||||
},
|
||||
on_button_edit: function() {
|
||||
return this.set({mode: "edit"});
|
||||
|
@ -674,13 +685,14 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
values = {},
|
||||
first_invalid_field = null;
|
||||
for (var f in self.fields) {
|
||||
if (!self.fields.hasOwnProperty(f)) { continue; }
|
||||
f = self.fields[f];
|
||||
if (!f.is_valid()) {
|
||||
form_invalid = true;
|
||||
if (!first_invalid_field) {
|
||||
first_invalid_field = f;
|
||||
}
|
||||
} else if (f.name !== 'id' && !f.get("readonly") && (!self.datarecord.id || f._dirty_flag)) {
|
||||
} else if (f.name !== 'id' && (!self.datarecord.id || (!f.get("readonly") && f._dirty_flag))) {
|
||||
// Special case 'id' field, do not save this field
|
||||
// on 'create' : save all non readonly fields
|
||||
// on 'edit' : save non readonly modified fields
|
||||
|
@ -689,8 +701,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
}
|
||||
if (form_invalid) {
|
||||
self.set({'display_invalid_fields': true});
|
||||
for (var f in self.fields) {
|
||||
self.fields[f]._check_css_flags();
|
||||
for (var g in self.fields) {
|
||||
if (!self.fields.hasOwnProperty(g)) { continue; }
|
||||
self.fields[g]._check_css_flags();
|
||||
}
|
||||
first_invalid_field.focus();
|
||||
self.on_invalid();
|
||||
|
@ -722,14 +735,15 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
});});
|
||||
},
|
||||
on_invalid: function() {
|
||||
var msg = "<ul>";
|
||||
_.each(this.fields, function(f) {
|
||||
if (!f.is_valid()) {
|
||||
msg += "<li>" + f.string + "</li>";
|
||||
}
|
||||
});
|
||||
msg += "</ul>";
|
||||
this.do_warn("The following fields are invalid :", msg);
|
||||
var warnings = _(this.fields).chain()
|
||||
.filter(function (f) { return !f.is_valid(); })
|
||||
.map(function (f) {
|
||||
return _.str.sprintf('<li>%s</li>',
|
||||
_.escape(f.string));
|
||||
}).value();
|
||||
warnings.unshift('<ul>');
|
||||
warnings.push('</ul>');
|
||||
this.do_warn("The following fields are invalid :", warnings.join(''));
|
||||
},
|
||||
on_saved: function(r, success) {
|
||||
if (!r.result) {
|
||||
|
@ -807,10 +821,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
var ids = this.get_selected_ids();
|
||||
values["id"] = ids.length > 0 ? ids[0] : false;
|
||||
_.each(this.fields, function(value_, key) {
|
||||
if (_.include(blacklist, key))
|
||||
if (_.include(blacklist, key)) {
|
||||
return;
|
||||
var val = value_.get_value();
|
||||
values[key] = val;
|
||||
}
|
||||
values[key] = value_.get_value();
|
||||
});
|
||||
return values;
|
||||
},
|
||||
|
@ -865,6 +879,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
return option[0] === value;
|
||||
})[1];
|
||||
break;
|
||||
case 'many2one':
|
||||
displayed = field.get_displayed();
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -942,6 +959,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
is_create_mode: function() {
|
||||
return !this.datarecord.id;
|
||||
},
|
||||
open_translate_dialog: function(field) {
|
||||
return this._super(field);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -985,7 +1005,6 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
|||
}
|
||||
});
|
||||
}
|
||||
selector = 'form[version!="7.0"] page,form[version!="7.0"]';
|
||||
},
|
||||
render_to: function($target) {
|
||||
var self = this;
|
||||
|
@ -1119,7 +1138,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
|||
if (found)
|
||||
return;
|
||||
|
||||
$label = $('<label/>').attr({
|
||||
var $label = $('<label/>').attr({
|
||||
'for' : name,
|
||||
"modifiers": JSON.stringify({invisible: field_modifiers.invisible}),
|
||||
"string": $field.attr('string'),
|
||||
|
@ -1333,7 +1352,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
|||
return $new_label;
|
||||
},
|
||||
handle_common_properties: function($new_element, $node) {
|
||||
var str_modifiers = $node.attr("modifiers") || "{}"
|
||||
var str_modifiers = $node.attr("modifiers") || "{}";
|
||||
var modifiers = JSON.parse(str_modifiers);
|
||||
var ic = null;
|
||||
if (modifiers.invisible !== undefined)
|
||||
|
@ -1344,12 +1363,6 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
|||
},
|
||||
});
|
||||
|
||||
instance.web.form.FormRenderingEngineReadonly = instance.web.form.FormRenderingEngine.extend({
|
||||
alter_field: function(field) {
|
||||
field.set({"force_readonly": true});
|
||||
},
|
||||
});
|
||||
|
||||
instance.web.form.FormDialog = instance.web.Dialog.extend({
|
||||
init: function(parent, options, view_id, dataset) {
|
||||
this._super(parent, options);
|
||||
|
@ -1458,24 +1471,27 @@ instance.web.form.compute_domain = function(expr, fields) {
|
|||
*/
|
||||
instance.web.form.InvisibilityChangerMixin = {
|
||||
init: function(field_manager, invisible_domain) {
|
||||
this._ic_field_manager = field_manager
|
||||
var self = this;
|
||||
this._ic_field_manager = field_manager;
|
||||
this._ic_invisible_modifier = invisible_domain;
|
||||
this._ic_field_manager.on("view_content_has_changed", this, function() {
|
||||
var result = this._ic_invisible_modifier === undefined ? false :
|
||||
instance.web.form.compute_domain(this._ic_invisible_modifier, this._ic_field_manager.fields);
|
||||
this.set({"invisible": result});
|
||||
var result = self._ic_invisible_modifier === undefined ? false :
|
||||
instance.web.form.compute_domain(
|
||||
self._ic_invisible_modifier,
|
||||
self._ic_field_manager.fields);
|
||||
self.set({"invisible": result});
|
||||
});
|
||||
this.set({invisible: this._ic_invisible_modifier === true, force_invisible: false});
|
||||
var check = function() {
|
||||
if (this.get("invisible") || this.get('force_invisible')) {
|
||||
this.set({"effective_invisible": true});
|
||||
if (self.get("invisible") || self.get('force_invisible')) {
|
||||
self.set({"effective_invisible": true});
|
||||
} else {
|
||||
this.set({"effective_invisible": false});
|
||||
self.set({"effective_invisible": false});
|
||||
}
|
||||
};
|
||||
this.on('change:invisible', this, check);
|
||||
this.on('change:force_invisible', this, check);
|
||||
_.bind(check, this)();
|
||||
check.call(this);
|
||||
},
|
||||
start: function() {
|
||||
this.on("change:effective_invisible", this, this._check_visibility);
|
||||
|
@ -1539,6 +1555,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
|
|||
var compute_domain = instance.web.form.compute_domain;
|
||||
var to_set = {};
|
||||
for (var a in this.modifiers) {
|
||||
if (!this.modifiers.hasOwnProperty(a)) { continue; }
|
||||
if (!_.include(["invisible"], a)) {
|
||||
var val = compute_domain(this.modifiers[a], this.view.fields);
|
||||
to_set[a] = val;
|
||||
|
@ -1685,11 +1702,7 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
|
|||
on_confirmed: function() {
|
||||
var self = this;
|
||||
|
||||
var context = this.node.attrs.context;
|
||||
if (context && context.__ref) {
|
||||
context = new instance.web.CompoundContext(context);
|
||||
context.set_eval_context(this._build_eval_context());
|
||||
}
|
||||
var context = this.build_context();
|
||||
|
||||
return this.view.do_execute_action(
|
||||
_.extend({}, this.node.attrs, {context: context}),
|
||||
|
@ -1742,18 +1755,18 @@ instance.web.form.FieldInterface = {
|
|||
/**
|
||||
* Get the current value of the widget.
|
||||
*
|
||||
* Must always return a syntaxically correct value to be passed to the "write" method of the osv class in
|
||||
* Must always return a syntactically correct value to be passed to the "write" method of the osv class in
|
||||
* the OpenERP server, although it is not assumed to respect the constraints applied to the field.
|
||||
* For example if the field is marqued as "required", a call to get_value() can return false.
|
||||
* For example if the field is marked as "required", a call to get_value() can return false.
|
||||
*
|
||||
* get_value() can also be called *before* a call to set_value() and, in that case, is supposed to
|
||||
* return a defaut value according to the type of field.
|
||||
* return a default value according to the type of field.
|
||||
*
|
||||
* This method is always assumed to perform synchronously, it can not return a promise.
|
||||
*
|
||||
* If there was no user interaction to modify the value of the field, it is always assumed that
|
||||
* get_value() return the same semantic value than the one passed in the last call to set_value(),
|
||||
* altough the syntax can be different. This can be the case for type of fields that have a different
|
||||
* although the syntax can be different. This can be the case for type of fields that have a different
|
||||
* syntax for "read" and "write" (example: m2o: set_value([0, "Administrator"]), get_value() => 0).
|
||||
*/
|
||||
get_value: function() {},
|
||||
|
@ -1768,7 +1781,7 @@ instance.web.form.FieldInterface = {
|
|||
*/
|
||||
is_valid: function() {},
|
||||
/**
|
||||
* Returns true if the field holds a value which is syntaxically correct, ignoring
|
||||
* Returns true if the field holds a value which is syntactically correct, ignoring
|
||||
* the potential semantic restrictions applied to the field.
|
||||
*/
|
||||
is_syntax_valid: function() {},
|
||||
|
@ -1798,6 +1811,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
|
|||
* @param node
|
||||
*/
|
||||
init: function(field_manager, node) {
|
||||
var self = this
|
||||
this._super(field_manager, node);
|
||||
this.field_manager = field_manager;
|
||||
this.name = this.node.attrs.name;
|
||||
|
@ -1810,13 +1824,13 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
|
|||
// some events to make the property "effective_readonly" sync automatically with "readonly" and
|
||||
// "force_readonly"
|
||||
this.set({"readonly": this.modifiers['readonly'] === true});
|
||||
this.set({"force_readonly": false});
|
||||
var test_effective_readonly = function() {
|
||||
this.set({"effective_readonly": this.get("readonly") || !!this.get("force_readonly")});
|
||||
self.set({"effective_readonly": self.get("readonly") || !!self.get("force_readonly")});
|
||||
};
|
||||
this.on("change:readonly", this, test_effective_readonly);
|
||||
this.on("change:force_readonly", this, test_effective_readonly);
|
||||
_.bind(test_effective_readonly, this)();
|
||||
|
||||
test_effective_readonly.call(this);
|
||||
this.on("change:value", this, function() {
|
||||
if (! this._inhibit_on_change)
|
||||
this.trigger('changed_value');
|
||||
|
@ -1893,7 +1907,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
|
|||
*/
|
||||
delay_focus: function($elem) {
|
||||
setTimeout(function() {
|
||||
$elem.focus();
|
||||
$elem[0].focus();
|
||||
}, 50);
|
||||
},
|
||||
/**
|
||||
|
@ -2236,6 +2250,11 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
|
|||
} else {
|
||||
this.$textarea.attr('disabled', 'disabled');
|
||||
}
|
||||
this.$element.keyup(function (e) {
|
||||
if (e.which === $.ui.keyCode.ENTER) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
this.setupFocus(this.$textarea);
|
||||
},
|
||||
set_value: function(value_) {
|
||||
|
@ -2288,9 +2307,58 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
|
|||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* FieldTextHtml Widget
|
||||
* Intended for FieldText widgets meant to display HTML content. This
|
||||
* widget will instantiate the CLEditor (see cleditor in static/src/lib)
|
||||
* To find more information about CLEditor configutation: go to
|
||||
* http://premiumsoftware.net/cleditor/docs/GettingStarted.html
|
||||
*/
|
||||
instance.web.form.FieldTextHtml = instance.web.form.FieldText.extend({
|
||||
|
||||
initialize_content: function() {
|
||||
this.$textarea = this.$element.find('textarea');
|
||||
var width = ((this.node.attrs || {}).editor_width || 468);
|
||||
var height = ((this.node.attrs || {}).editor_height || 100);
|
||||
this.$textarea.cleditor({
|
||||
width: width, // width not including margins, borders or padding
|
||||
height: height, // height not including margins, borders or padding
|
||||
controls: // controls to add to the toolbar
|
||||
"bold italic underline strikethrough | size " +
|
||||
"| removeformat | bullets numbering | outdent " +
|
||||
"indent | link unlink",
|
||||
sizes: // sizes in the font size popup
|
||||
"1,2,3,4,5,6,7",
|
||||
bodyStyle: // style to assign to document body contained within the editor
|
||||
"margin:4px; font:12px monospace; cursor:text; color:#1F1F1F"
|
||||
});
|
||||
this.$cleditor = this.$textarea.cleditor()[0];
|
||||
// call super now, because cleditor resets the disable attr
|
||||
this._super.apply(this, arguments);
|
||||
// propagate disabled property to cleditor
|
||||
this.$cleditor.disable(this.$textarea.prop('disabled'));
|
||||
},
|
||||
|
||||
set_value: function(value_) {
|
||||
this._super.apply(this, arguments);
|
||||
this._dirty_flag = true;
|
||||
},
|
||||
|
||||
render_value: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$cleditor.updateFrame();
|
||||
},
|
||||
|
||||
get_value: function() {
|
||||
this.$cleditor.updateTextArea();
|
||||
return this.$textarea.val();
|
||||
},
|
||||
});
|
||||
|
||||
instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
|
||||
template: 'FieldBoolean',
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
this.$checkbox = $("input", this.$element);
|
||||
this.setupFocus(this.$checkbox);
|
||||
|
@ -2298,10 +2366,10 @@ instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
|
|||
this.set({'value': this.$checkbox.is(':checked')});
|
||||
}, this));
|
||||
var check_readonly = function() {
|
||||
this.$checkbox.prop('disabled', this.get("effective_readonly"));
|
||||
self.$checkbox.prop('disabled', self.get("effective_readonly"));
|
||||
};
|
||||
this.on("change:effective_readonly", this, check_readonly);
|
||||
_.bind(check_readonly, this)();
|
||||
check_readonly.call(this);
|
||||
},
|
||||
set_value: function(value_) {
|
||||
this._super.apply(this, arguments);
|
||||
|
@ -2739,10 +2807,20 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
|||
this.$input.val(str.split("\n")[0]);
|
||||
this.current_display = this.$input.val();
|
||||
} else {
|
||||
str = _.escape(str).split("\n").join("<br />");
|
||||
var lines = _.escape(str).split("\n");
|
||||
var link = "";
|
||||
var follow = "";
|
||||
if (! this.get_definition_options().highlight_first_line) {
|
||||
link = lines.join("<br />");
|
||||
} else {
|
||||
link = lines[0];
|
||||
follow = _.rest(lines).join("<br />");
|
||||
if (follow)
|
||||
link += "<br />";
|
||||
}
|
||||
this.$element.find('a')
|
||||
.unbind('click')
|
||||
.html(str)
|
||||
.html(link)
|
||||
.click(function () {
|
||||
self.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
|
@ -2754,6 +2832,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
|||
});
|
||||
return false;
|
||||
});
|
||||
$(".oe_form_m2o_follow", this.$element).html(follow);
|
||||
}
|
||||
},
|
||||
set_value: function(value_) {
|
||||
|
@ -2770,6 +2849,9 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
|||
this._super(value_);
|
||||
this.inhibit_on_change = false;
|
||||
},
|
||||
get_displayed: function() {
|
||||
return this.display_value["" + this.get("value")];
|
||||
},
|
||||
add_id: function(id) {
|
||||
this.display_value = {};
|
||||
this.set({value: id});
|
||||
|
@ -2900,6 +2982,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
|
|||
selectable: self.multi_selection,
|
||||
sortable: false,
|
||||
import_enabled: false,
|
||||
deletable: true
|
||||
});
|
||||
if (self.get("effective_readonly")) {
|
||||
_.extend(view.options, {
|
||||
|
@ -3129,6 +3212,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
|
|||
form: 'instance.web.form.One2ManyFormView',
|
||||
kanban: 'instance.web.form.One2ManyKanbanView',
|
||||
});
|
||||
this.__ignore_blur = false;
|
||||
},
|
||||
switch_view: function(mode, unused) {
|
||||
if (mode !== 'form') {
|
||||
|
@ -3178,19 +3262,34 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
this._super(parent, dataset, view_id, _.extend(options || {}, {
|
||||
ListType: instance.web.form.One2ManyList
|
||||
}));
|
||||
this.on('edit:before', this, this.proxy('_before_edit'));
|
||||
this.on('save:before cancel:before', this, this.proxy('_before_unedit'));
|
||||
|
||||
this.records
|
||||
.bind('add', this.proxy("changed_records"))
|
||||
.bind('edit', this.proxy("changed_records"))
|
||||
.bind('remove', this.proxy("changed_records"));
|
||||
},
|
||||
start: function () {
|
||||
var ret = this._super();
|
||||
this.$element
|
||||
.off('mousedown.handleButtons')
|
||||
.on('mousedown.handleButtons', 'table button', this.proxy('_button_down'));
|
||||
return ret;
|
||||
},
|
||||
changed_records: function () {
|
||||
this.o2m.trigger_on_change();
|
||||
},
|
||||
is_valid: function () {
|
||||
var form;
|
||||
// A list not being edited is always valid
|
||||
if (!(form = this.first_edition_form())) {
|
||||
return true;
|
||||
}
|
||||
var form = this.editor.form;
|
||||
|
||||
// If the form has not been modified, the view can only be valid
|
||||
// NB: is_dirty will also be set on defaults/onchanges/whatever?
|
||||
// oe_form_dirty seems to only be set on actual user actions
|
||||
if (!form.$element.is('.oe_form_dirty')) {
|
||||
return true;
|
||||
}
|
||||
this.o2m._dirty_flag = true;
|
||||
|
||||
// Otherwise validate internal form
|
||||
return _(form.fields).chain()
|
||||
|
@ -3201,19 +3300,6 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
.all(_.identity)
|
||||
.value();
|
||||
},
|
||||
first_edition_form: function () {
|
||||
var get_form = function (group_or_list) {
|
||||
if (group_or_list.edition) {
|
||||
return group_or_list.edition_form;
|
||||
}
|
||||
return _(group_or_list.children).chain()
|
||||
.map(get_form)
|
||||
.compact()
|
||||
.first()
|
||||
.value();
|
||||
};
|
||||
return get_form(this.groups);
|
||||
},
|
||||
do_add_record: function () {
|
||||
if (this.options.editable) {
|
||||
this._super.apply(this, arguments);
|
||||
|
@ -3268,55 +3354,58 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
});
|
||||
},
|
||||
do_button_action: function (name, id, callback) {
|
||||
var _super = _.bind(this._super, this);
|
||||
|
||||
this.o2m.view.do_save().then(function () {
|
||||
_super(name, id, callback);
|
||||
});
|
||||
}
|
||||
});
|
||||
instance.web.form.One2ManyList = instance.web.ListView.List.extend({
|
||||
KEY_RETURN: 13,
|
||||
// blurring caused by hitting the [Return] key, should skip the
|
||||
// autosave-on-blur and let the handler for [Return] do its thing
|
||||
__return_blur: false,
|
||||
render_row_as_form: function () {
|
||||
if (!_.isNumber(id)) {
|
||||
instance.webclient.notification.warn(
|
||||
_t("Action Button"),
|
||||
_t("The o2m record must be saved before an action can be used"));
|
||||
return;
|
||||
}
|
||||
var parent_form = this.o2m.view;
|
||||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
// Replace the "Save Row" button with "Cancel Edition"
|
||||
self.edition_form.$element
|
||||
.undelegate('button.oe_list_edit_row_save', 'click')
|
||||
.delegate('button.oe_list_edit_row_save', 'click', function () {
|
||||
self.cancel_pending_edition();
|
||||
});
|
||||
|
||||
// Overload execute_action on the edition form to perform a simple
|
||||
// reload_record after the action is done, rather than fully
|
||||
// reload the parent view (or something)
|
||||
var _execute_action = self.edition_form.do_execute_action;
|
||||
self.edition_form.do_execute_action = function (action, dataset, record_id, _callback) {
|
||||
return _execute_action.call(this, action, dataset, record_id, function () {
|
||||
self.view.reload_record(
|
||||
self.view.records.get(record_id));
|
||||
});
|
||||
};
|
||||
|
||||
self.edition_form.on('blurred', null, function () {
|
||||
if (self.__return_blur) {
|
||||
delete self.__return_blur;
|
||||
return;
|
||||
}
|
||||
if (!self.edition_form.widget_is_stopped) {
|
||||
self.view.ensure_saved();
|
||||
}
|
||||
});
|
||||
this.ensure_saved().pipe(function () {
|
||||
return parent_form.do_save();
|
||||
}).then(function () {
|
||||
self.handle_button(name, id, callback);
|
||||
});
|
||||
},
|
||||
on_row_keyup: function (e) {
|
||||
if (e.which === this.KEY_RETURN) {
|
||||
this.__return_blur = true;
|
||||
|
||||
_before_edit: function () {
|
||||
this.__ignore_blur = false;
|
||||
this.editor.form.on('blurred', this, this._on_form_blur);
|
||||
},
|
||||
_before_unedit: function () {
|
||||
this.editor.form.off('blurred', this, this._on_form_blur);
|
||||
},
|
||||
_button_down: function () {
|
||||
// If a button is clicked (usually some sort of action button), it's
|
||||
// the button's responsibility to ensure the editable list is in the
|
||||
// correct state -> ignore form blurring
|
||||
this.__ignore_blur = true;
|
||||
},
|
||||
/**
|
||||
* Handles blurring of the nested form (saves the currently edited row),
|
||||
* unless the flag to ignore the event is set to ``true``
|
||||
*
|
||||
* Makes the internal form go away
|
||||
*/
|
||||
_on_form_blur: function () {
|
||||
if (this.__ignore_blur) {
|
||||
this.__ignore_blur = false;
|
||||
return;
|
||||
}
|
||||
this._super(e);
|
||||
// FIXME: why isn't there an API for this?
|
||||
if (this.editor.form.$element.hasClass('oe_form_dirty')) {
|
||||
this.save_edition();
|
||||
return;
|
||||
}
|
||||
this.cancel_edition();
|
||||
},
|
||||
keyup_ENTER: function () {
|
||||
// blurring caused by hitting the [Return] key, should skip the
|
||||
// autosave-on-blur and let the handler for [Return] do its thing (save
|
||||
// the current row *anyway*, then create a new one/edit the next one)
|
||||
this.__ignore_blur = true;
|
||||
this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -3485,7 +3574,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
|
|||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.addClass('oe_form_field_many2many');
|
||||
this.$element.addClass('oe_form_field oe_form_field_many2many');
|
||||
|
||||
var self = this;
|
||||
|
||||
|
@ -3860,14 +3949,16 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
|
|||
display_popup: function() {
|
||||
var self = this;
|
||||
this.renderElement();
|
||||
new instance.web.Dialog(this, {
|
||||
var dialog = new instance.web.Dialog(this, {
|
||||
min_width: '800px',
|
||||
dialogClass: 'oe_act_window',
|
||||
dialogClass: 'oe_act_window',
|
||||
close: function() {
|
||||
self.check_exit(true);
|
||||
},
|
||||
title: this.options.title || "",
|
||||
buttons: [{text:"tmp"}],
|
||||
}, this.$element).open();
|
||||
this.$buttonpane = dialog.$element.dialog("widget").find(".ui-dialog-buttonpane").html("");
|
||||
this.start();
|
||||
},
|
||||
on_write_completed: function() {},
|
||||
|
@ -3883,16 +3974,18 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
|
|||
if (this.row_id !== null) {
|
||||
options.initial_mode = this.options.readonly ? "view" : "edit";
|
||||
}
|
||||
_.extend(options, {
|
||||
$buttons: this.$buttonpane,
|
||||
});
|
||||
this.view_form = new instance.web.FormView(this, this.dataset, false, options);
|
||||
if (this.options.alternative_form_view) {
|
||||
this.view_form.set_embedded_view(this.options.alternative_form_view);
|
||||
}
|
||||
this.view_form.appendTo(this.$element.find(".oe_popup_form"));
|
||||
this.view_form.on_loaded.add_last(function() {
|
||||
var $buttons = self.view_form.$element.find(".oe_form_buttons");
|
||||
var multi_select = self.row_id === null && ! self.options.disable_multiple_selection;
|
||||
$buttons.html(QWeb.render("AbstractFormPopup.buttons", {multi_select: multi_select}));
|
||||
var $snbutton = $buttons.find(".oe_abstractformpopup-form-save-new");
|
||||
self.$buttonpane.html(QWeb.render("AbstractFormPopup.buttons", {multi_select: multi_select}));
|
||||
var $snbutton = self.$buttonpane.find(".oe_abstractformpopup-form-save-new");
|
||||
$snbutton.click(function() {
|
||||
$.when(self.view_form.do_save()).then(function() {
|
||||
self.view_form.reload_mutex.exec(function() {
|
||||
|
@ -3900,7 +3993,7 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
|
|||
});
|
||||
});
|
||||
});
|
||||
var $sbutton = $buttons.find(".oe_abstractformpopup-form-save");
|
||||
var $sbutton = self.$buttonpane.find(".oe_abstractformpopup-form-save");
|
||||
$sbutton.click(function() {
|
||||
$.when(self.view_form.do_save()).then(function() {
|
||||
self.view_form.reload_mutex.exec(function() {
|
||||
|
@ -3908,7 +4001,7 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
|
|||
});
|
||||
});
|
||||
});
|
||||
var $cbutton = $buttons.find(".oe_abstractformpopup-form-close");
|
||||
var $cbutton = self.$buttonpane.find(".oe_abstractformpopup-form-close");
|
||||
$cbutton.click(function() {
|
||||
self.check_exit();
|
||||
});
|
||||
|
@ -4017,6 +4110,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
|
|||
'selectable': !self.options.disable_multiple_selection,
|
||||
'read_only': true,
|
||||
'import_enabled': false,
|
||||
'$buttons': self.$buttonpane,
|
||||
}, self.options.list_view_options || {}));
|
||||
self.view_list.popup = self;
|
||||
self.view_list.appendTo($(".oe_popup_list", self.$element)).pipe(function() {
|
||||
|
@ -4025,16 +4119,12 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
|
|||
self.searchview.do_search();
|
||||
});
|
||||
self.view_list.on_loaded.add_last(function() {
|
||||
var $buttons = self.view_list.$element.find(".oe-actions");
|
||||
$buttons.prepend(QWeb.render("SelectCreatePopup.search.buttons"));
|
||||
var $cbutton = $buttons.find(".oe_selectcreatepopup-search-close");
|
||||
self.$buttonpane.html(QWeb.render("SelectCreatePopup.search.buttons", {widget:self}));
|
||||
var $cbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-close");
|
||||
$cbutton.click(function() {
|
||||
self.destroy();
|
||||
});
|
||||
var $sbutton = $buttons.find(".oe_selectcreatepopup-search-select");
|
||||
if(self.options.disable_multiple_selection) {
|
||||
$sbutton.hide();
|
||||
}
|
||||
var $sbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-select");
|
||||
$sbutton.click(function() {
|
||||
self.on_select_elements(self.selected_ids);
|
||||
self.destroy();
|
||||
|
@ -4512,6 +4602,7 @@ instance.web.form.widgets = new instance.web.Registry({
|
|||
'email' : 'instance.web.form.FieldEmail',
|
||||
'url' : 'instance.web.form.FieldUrl',
|
||||
'text' : 'instance.web.form.FieldText',
|
||||
'text_html' : 'instance.web.form.FieldTextHtml',
|
||||
'date' : 'instance.web.form.FieldDate',
|
||||
'datetime' : 'instance.web.form.FieldDatetime',
|
||||
'selection' : 'instance.web.form.FieldSelection',
|
||||
|
|
|
@ -142,7 +142,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
});
|
||||
},
|
||||
/**
|
||||
* View startup method, the default behavior is to set the ``oe_listw``
|
||||
* View startup method, the default behavior is to set the ``oe_list``
|
||||
* class on its root element and to perform an RPC load call.
|
||||
*
|
||||
* @returns {$.Deferred} loading promise
|
||||
|
@ -288,7 +288,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
}
|
||||
this.$buttons.find('.oe_list_add')
|
||||
.click(this.proxy('do_add_record'))
|
||||
.prop('disabled', grouped && this.options.editable);
|
||||
.prop('disabled', grouped);
|
||||
this.$buttons.on('click', '.oe_list_button_import', function() {
|
||||
self.on_sidebar_import();
|
||||
return false;
|
||||
|
@ -411,20 +411,27 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
if (column.modifiers) {
|
||||
var modifiers = JSON.parse(column.modifiers);
|
||||
column.modifiers_for = function (fields) {
|
||||
if (!modifiers.invisible) {
|
||||
return {};
|
||||
var out = {};
|
||||
|
||||
for (var attr in modifiers) {
|
||||
if (!modifiers.hasOwnProperty(attr)) { continue; }
|
||||
var modifier = modifiers[attr];
|
||||
out[attr] = _.isBoolean(modifier)
|
||||
? modifier
|
||||
: domain_computer(modifier, fields);
|
||||
}
|
||||
return {
|
||||
'invisible': domain_computer(modifiers.invisible, fields)
|
||||
};
|
||||
|
||||
return out;
|
||||
};
|
||||
if (modifiers['tree_invisible']) {
|
||||
column.invisible = '1';
|
||||
} else {
|
||||
delete column.invisible;
|
||||
}
|
||||
column.modifiers = modifiers;
|
||||
} else {
|
||||
column.modifiers_for = noop;
|
||||
column.modifiers = {};
|
||||
}
|
||||
return column;
|
||||
};
|
||||
|
@ -436,10 +443,12 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
if (grouped) {
|
||||
this.columns.unshift({
|
||||
id: '_group', tag: '', string: _t("Group"), meta: true,
|
||||
modifiers_for: function () { return {}; }
|
||||
modifiers_for: function () { return {}; },
|
||||
modifiers: {}
|
||||
}, {
|
||||
id: '_count', tag: '', string: '#', meta: true,
|
||||
modifiers_for: function () { return {}; }
|
||||
modifiers_for: function () { return {}; },
|
||||
modifiers: {}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -667,6 +676,19 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
* @param {Function} callback should be called after the action is executed, if non-null
|
||||
*/
|
||||
do_button_action: function (name, id, callback) {
|
||||
this.handle_button(name, id, callback);
|
||||
},
|
||||
/**
|
||||
* Base handling of buttons, can be called when overriding do_button_action
|
||||
* in order to bypass parent overrides.
|
||||
*
|
||||
* This method should not be overridden.
|
||||
*
|
||||
* @param {String} name action name
|
||||
* @param {Object} id id of the record the action should be called on
|
||||
* @param {Function} callback should be called after the action is executed, if non-null
|
||||
*/
|
||||
handle_button: function (name, id, callback) {
|
||||
var action = _.detect(this.columns, function (field) {
|
||||
return field.name === name;
|
||||
});
|
||||
|
@ -911,27 +933,42 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
|
||||
this.record_callbacks = {
|
||||
'remove': function (event, record) {
|
||||
var $row = self.$current.find(
|
||||
'[data-id=' + record.get('id') + ']');
|
||||
var $row = self.$current.children(
|
||||
'[data-id=' + record.get('id') + ']');
|
||||
var index = $row.data('index');
|
||||
$row.remove();
|
||||
self.refresh_zebra(index);
|
||||
},
|
||||
'reset': function () { return self.on_records_reset(); },
|
||||
'change': function (event, record) {
|
||||
var $row = self.$current.find('[data-id=' + record.get('id') + ']');
|
||||
'change': function (event, record, attribute, value, old_value) {
|
||||
var $row;
|
||||
if (attribute === 'id') {
|
||||
if (old_value) {
|
||||
throw new Error("Setting 'id' attribute on existing record "
|
||||
+ JSON.stringify(record.attributes));
|
||||
}
|
||||
if (!_.contains(self.dataset.ids, value)) {
|
||||
// add record to dataset if not already in (added by
|
||||
// the form view?)
|
||||
self.dataset.ids.splice(
|
||||
self.records.indexOf(record), 0, value);
|
||||
}
|
||||
// Set id on new record
|
||||
$row = self.$current.children('[data-id=false]');
|
||||
} else {
|
||||
$row = self.$current.children(
|
||||
'[data-id=' + record.get('id') + ']');
|
||||
}
|
||||
$row.replaceWith(self.render_record(record));
|
||||
},
|
||||
'add': function (ev, records, record, index) {
|
||||
var $new_row = $('<tr>').attr({
|
||||
'data-id': record.get('id')
|
||||
});
|
||||
var $new_row = $(self.render_record(record));
|
||||
|
||||
if (index === 0) {
|
||||
$new_row.prependTo(self.$current);
|
||||
} else {
|
||||
var previous_record = records.at(index-1),
|
||||
$previous_sibling = self.$current.find(
|
||||
$previous_sibling = self.$current.children(
|
||||
'[data-id=' + previous_record.get('id') + ']');
|
||||
$new_row.insertAfter($previous_sibling);
|
||||
}
|
||||
|
@ -975,11 +1012,11 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
e.stopPropagation();
|
||||
})
|
||||
.delegate('tr', 'click', function (e) {
|
||||
e.stopPropagation();
|
||||
var row_id = self.row_id(e.currentTarget);
|
||||
if (row_id !== undefined) {
|
||||
if (row_id) {
|
||||
e.stopPropagation();
|
||||
if (!self.dataset.select_id(row_id)) {
|
||||
throw "Could not find id in dataset"
|
||||
throw new Error("Could not find id in dataset");
|
||||
}
|
||||
self.row_clicked(e);
|
||||
}
|
||||
|
@ -1139,6 +1176,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
* @returns {String} QWeb rendering of the selected record
|
||||
*/
|
||||
render_record: function (record) {
|
||||
var self = this;
|
||||
var index = this.records.indexOf(record);
|
||||
return QWeb.render('ListView.row', {
|
||||
columns: this.columns,
|
||||
|
@ -1147,7 +1185,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
row_parity: (index % 2 === 0) ? 'even' : 'odd',
|
||||
view: this.view,
|
||||
render_cell: function () {
|
||||
return this.render_cell.apply(this, arguments); }
|
||||
return self.render_cell.apply(self, arguments); }
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
@ -1831,7 +1869,7 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
|
|||
* @returns this
|
||||
*/
|
||||
remove: function (record) {
|
||||
var index = _(this.records).indexOf(record);
|
||||
var index = this.indexOf(record);
|
||||
if (index === -1) {
|
||||
_(this._proxies).each(function (proxy) {
|
||||
proxy.remove(record);
|
||||
|
@ -1847,13 +1885,42 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
|
|||
return this;
|
||||
},
|
||||
|
||||
_onRecordEvent: function (event, record, options) {
|
||||
_onRecordEvent: function (event) {
|
||||
switch(event) {
|
||||
// don't propagate reset events
|
||||
if (event === 'reset') { return; }
|
||||
case 'reset': return;
|
||||
case 'change:id':
|
||||
var record = arguments[1];
|
||||
var new_value = arguments[2];
|
||||
var old_value = arguments[3];
|
||||
// [change:id, record, new_value, old_value]
|
||||
if (this._byId[old_value] === record) {
|
||||
delete this._byId[old_value];
|
||||
this._byId[new_value] = record;
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.trigger.apply(this, arguments);
|
||||
},
|
||||
|
||||
// underscore-type methods
|
||||
find: function (callback) {
|
||||
var record;
|
||||
for(var section in this._proxies) {
|
||||
if (!this._proxies.hasOwnProperty(section)) {
|
||||
continue
|
||||
}
|
||||
if ((record = this._proxies[section].find(callback))) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
for(var i=0; i<this.length; ++i) {
|
||||
record = this.records[i];
|
||||
if (callback(record)) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
},
|
||||
each: function (callback) {
|
||||
for(var section in this._proxies) {
|
||||
if (this._proxies.hasOwnProperty(section)) {
|
||||
|
@ -1878,6 +1945,46 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
|
|||
},
|
||||
indexOf: function (record) {
|
||||
return _(this.records).indexOf(record);
|
||||
},
|
||||
succ: function (record, options) {
|
||||
options = options || {wraparound: false};
|
||||
var result;
|
||||
for(var section in this._proxies) {
|
||||
if (!this._proxies.hasOwnProperty(section)) {
|
||||
continue;
|
||||
}
|
||||
if ((result = this._proxies[section].succ(record, options))) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
var index = this.indexOf(record);
|
||||
if (index === -1) { return null; }
|
||||
var next_index = index + 1;
|
||||
if (options.wraparound && (next_index === this.length)) {
|
||||
return this.at(0);
|
||||
}
|
||||
return this.at(next_index);
|
||||
},
|
||||
pred: function (record, options) {
|
||||
options = options || {wraparound: false};
|
||||
|
||||
var result;
|
||||
for (var section in this._proxies) {
|
||||
if (!this._proxies.hasOwnProperty(section)) {
|
||||
continue;
|
||||
}
|
||||
if ((result = this._proxies[section].pred(record, options))) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
var index = this.indexOf(record);
|
||||
if (index === -1) { return null; }
|
||||
var next_index = index - 1;
|
||||
if (options.wraparound && (next_index === -1)) {
|
||||
return this.at(this.length - 1);
|
||||
}
|
||||
return this.at(next_index);
|
||||
}
|
||||
});
|
||||
Collection.include(Events);
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
* @namespace
|
||||
*/
|
||||
openerp.web.list_editable = function (instance) {
|
||||
var KEY_RETURN = 13,
|
||||
KEY_ESCAPE = 27;
|
||||
var QWeb = instance.web.qweb;
|
||||
|
||||
// editability status of list rows
|
||||
instance.web.ListView.prototype.defaults.editable = null;
|
||||
|
||||
|
@ -15,6 +11,13 @@ openerp.web.list_editable = function (instance) {
|
|||
init: function () {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.editor = this.make_editor();
|
||||
// Stores records of {field, cell}, allows for re-rendering fields
|
||||
// depending on cell state during and after resize events
|
||||
this.fields_for_resize = [];
|
||||
instance.web.bus.on('resize', this, this.resize_fields);
|
||||
|
||||
$(this.groups).bind({
|
||||
'edit': function (e, id, dataset) {
|
||||
self.do_edit(dataset.index, id, dataset);
|
||||
|
@ -26,7 +29,24 @@ openerp.web.list_editable = function (instance) {
|
|||
self.configure_pager(self.dataset);
|
||||
self.compute_aggregates();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
this.records.bind('remove', function () {
|
||||
if (self.editor.is_editing()) {
|
||||
self.cancel_edition();
|
||||
}
|
||||
});
|
||||
|
||||
this.on('edit:after', this, function () {
|
||||
self.$element.add(self.$buttons).addClass('oe_editing');
|
||||
});
|
||||
this.on('save:after cancel:after', this, function () {
|
||||
self.$element.add(self.$buttons).removeClass('oe_editing');
|
||||
});
|
||||
},
|
||||
destroy: function () {
|
||||
instance.web.bus.off('resize', this, this.resize_fields);
|
||||
this._super();
|
||||
},
|
||||
/**
|
||||
* Handles the activation of a record in editable mode (making a record
|
||||
|
@ -49,6 +69,7 @@ openerp.web.list_editable = function (instance) {
|
|||
* @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
|
||||
*/
|
||||
set_editable: function (force) {
|
||||
// TODO: fix handling of editability status to be simpler & clearer & more coherent
|
||||
// If ``force``, set editability to bottom
|
||||
// otherwise rely on view default
|
||||
// view' @editable is handled separately as we have not yet
|
||||
|
@ -71,15 +92,60 @@ openerp.web.list_editable = function (instance) {
|
|||
if (this.options.editable) {
|
||||
this.$element.find('table:first').show();
|
||||
this.$element.find('.oe_view_nocontent').remove();
|
||||
this.groups.new_record();
|
||||
this.start_edition();
|
||||
} else {
|
||||
this._super();
|
||||
}
|
||||
},
|
||||
on_loaded: function (data, grouped) {
|
||||
var self = this;
|
||||
if (this.editor) {
|
||||
this.editor.destroy();
|
||||
}
|
||||
// tree/@editable takes priority on everything else if present.
|
||||
this.options.editable = ! this.options.read_only && (data.arch.attrs.editable || this.options.editable);
|
||||
return this._super(data, grouped);
|
||||
var result = this._super(data, grouped);
|
||||
if (this.options.editable) {
|
||||
// FIXME: any hook available to ensure this is only done once?
|
||||
this.$buttons
|
||||
.off('click', '.oe_list_save')
|
||||
.on('click', '.oe_list_save', this.proxy('save_edition'))
|
||||
.off('click', '.oe_list_discard')
|
||||
.on('click', '.oe_list_discard', function (e) {
|
||||
e.preventDefault();
|
||||
self.cancel_edition();
|
||||
});
|
||||
this.$element
|
||||
.off('click', 'tbody td:not(.oe_list_field_cell)')
|
||||
.on('click', 'tbody td:not(.oe_list_field_cell)', function () {
|
||||
if (!self.editor.is_editing()) {
|
||||
self.start_edition();
|
||||
}
|
||||
});
|
||||
// Editor is not restartable due to formview not being
|
||||
// restartable
|
||||
this.editor = this.make_editor();
|
||||
var editor_ready = this.editor.prependTo(this.$element)
|
||||
.then(this.proxy('setup_events'));
|
||||
|
||||
return $.when(result, editor_ready);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
* Builds a new editor object
|
||||
*
|
||||
* @return {instance.web.list.Editor}
|
||||
*/
|
||||
make_editor: function () {
|
||||
return new instance.web.list.Editor(this);
|
||||
},
|
||||
do_button_action: function () {
|
||||
var self = this, args = arguments;
|
||||
this.ensure_saved().then(function () {
|
||||
self.handle_button.apply(self, args);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Ensures the editable list is saved (saves any pending edition if
|
||||
|
@ -90,27 +156,487 @@ openerp.web.list_editable = function (instance) {
|
|||
* @returns {$.Deferred}
|
||||
*/
|
||||
ensure_saved: function () {
|
||||
return this.groups.ensure_saved();
|
||||
if (!this.editor.is_editing()) {
|
||||
return $.when();
|
||||
}
|
||||
return this.save_edition();
|
||||
},
|
||||
/**
|
||||
* Set up the edition of a record of the list view "inline"
|
||||
*
|
||||
* @param {instance.web.list.Record} [record] record to edit, leave empty to create a new record
|
||||
* @param {Object} [options]
|
||||
* @param {String} [options.focus_field] field to focus at start of edition
|
||||
* @return {jQuery.Deferred}
|
||||
*/
|
||||
start_edition: function (record, options) {
|
||||
var self = this;
|
||||
var item = false;
|
||||
if (record) {
|
||||
item = record.attributes;
|
||||
} else {
|
||||
var attrs = {id: false};
|
||||
_(this.columns).chain()
|
||||
.filter(function (x) { return x.tag === 'field'})
|
||||
.pluck('name')
|
||||
.each(function (field) { attrs[field] = false; });
|
||||
record = new instance.web.list.Record(attrs);
|
||||
this.records.add(record, {
|
||||
at: this.prepends_on_create() ? 0 : null});
|
||||
}
|
||||
var $recordRow = this.groups.get_row_for(record);
|
||||
var cells = this.get_cells_for($recordRow);
|
||||
|
||||
return this.ensure_saved().pipe(function () {
|
||||
self.fields_for_resize.splice(0, self.fields_for_resize.length);
|
||||
return self.with_event('edit', {
|
||||
record: record.attributes,
|
||||
cancel: false
|
||||
}, function () {
|
||||
return self.editor.edit(item, function (field_name, field) {
|
||||
var cell = cells[field_name];
|
||||
if (!cell || field.get('effective_readonly')) {
|
||||
// Readonly fields can just remain the list's,
|
||||
// form's usually don't have backgrounds &al
|
||||
field.set({invisible: true});
|
||||
return;
|
||||
}
|
||||
|
||||
self.fields_for_resize.push({field: field, cell: cell});
|
||||
}, options).pipe(function () {
|
||||
$recordRow.addClass('oe_edition');
|
||||
self.resize_fields();
|
||||
return record.attributes;
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
get_cells_for: function ($row) {
|
||||
var cells = {};
|
||||
$row.children('td').each(function (index, el) {
|
||||
cells[el.getAttribute('data-field')] = el
|
||||
});
|
||||
return cells;
|
||||
},
|
||||
/**
|
||||
* If currently editing a row, resizes all registered form fields based
|
||||
* on the corresponding row cell
|
||||
*/
|
||||
resize_fields: function () {
|
||||
if (!this.editor.is_editing()) { return; }
|
||||
for(var i=0, len=this.fields_for_resize.length; i<len; ++i) {
|
||||
var item = this.fields_for_resize[i];
|
||||
this.resize_field(item.field, item.cell);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Resizes a field's root element based on the corresponding cell of
|
||||
* a listview row
|
||||
*
|
||||
* @param {instance.web.form.AbstractField} field
|
||||
* @param {jQuery} cell
|
||||
*/
|
||||
resize_field: function (field, cell) {
|
||||
var $cell = $(cell);
|
||||
var position = $cell.position();
|
||||
|
||||
field.$element.css({
|
||||
top: position.top,
|
||||
left: position.left,
|
||||
width: $cell.outerWidth(),
|
||||
minHeight: $cell.outerHeight()
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @return {jQuery.Deferred}
|
||||
*/
|
||||
save_edition: function () {
|
||||
var self = this;
|
||||
return this.with_event('save', {
|
||||
editor: this.editor,
|
||||
form: this.editor.form,
|
||||
cancel: false
|
||||
}, function () {
|
||||
return this.editor.save().pipe(function (attrs) {
|
||||
var created = false;
|
||||
var record = self.records.get(attrs.id);
|
||||
if (!record) {
|
||||
// new record
|
||||
created = true;
|
||||
record = self.records.find(function (r) {
|
||||
return !r.get('id');
|
||||
}).set('id', attrs.id);
|
||||
}
|
||||
// onwrite callback could be altering & reloading the
|
||||
// record which has *just* been saved, so first perform all
|
||||
// onwrites then do a final reload of the record
|
||||
return self.handle_onwrite(record)
|
||||
.pipe(function () {
|
||||
return self.reload_record(record); })
|
||||
.pipe(function () {
|
||||
return { created: created, record: record }; });
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @return {jQuery.Deferred}
|
||||
*/
|
||||
cancel_edition: function () {
|
||||
var self = this;
|
||||
return this.with_event('cancel', {
|
||||
editor: this.editor,
|
||||
form: this.editor.form,
|
||||
cancel: false
|
||||
}, function () {
|
||||
return this.editor.cancel().pipe(function (attrs) {
|
||||
if (attrs.id) {
|
||||
var record = self.records.get(attrs.id);
|
||||
if (!record) {
|
||||
// Record removed by third party during edition
|
||||
return
|
||||
}
|
||||
return self.reload_record(record);
|
||||
}
|
||||
var to_delete = self.records.find(function (r) {
|
||||
return !r.get('id');
|
||||
});
|
||||
if (to_delete) {
|
||||
self.records.remove(to_delete);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Executes an action on the view's editor bracketed by a cancellable
|
||||
* event of the name provided.
|
||||
*
|
||||
* The event name provided will be post-fixed with ``:before`` and
|
||||
* ``:after``, the ``event`` parameter will be passed alongside the
|
||||
* ``:before`` variant and if the parameter's ``cancel`` key is set to
|
||||
* ``true`` the action *will not be called* and the method will return
|
||||
* a rejection
|
||||
*
|
||||
* @param {String} event_name name of the event
|
||||
* @param {Object} event event object, provided to ``:before`` sub-event
|
||||
* @param {Function} action callable, called with the view's editor as its context
|
||||
* @param {Array} [args] supplementary arguments provided to the action
|
||||
* @param {Array} [trigger_params] supplementary arguments provided to the ``:after`` sub-event, before anything fetched by the ``action`` function
|
||||
* @return {jQuery.Deferred}
|
||||
*/
|
||||
with_event: function (event_name, event, action) {
|
||||
var self = this;
|
||||
event = event || {};
|
||||
this.trigger(event_name + ':before', event);
|
||||
if (event.cancel) {
|
||||
return $.Deferred().reject({
|
||||
message: _.str.sprintf("Event %s:before cancelled",
|
||||
event_name)});
|
||||
}
|
||||
return $.when(action.call(this)).then(function () {
|
||||
self.trigger.apply(self, [event_name + ':after']
|
||||
.concat(_.toArray(arguments)));
|
||||
});
|
||||
},
|
||||
edition_view: function (editor) {
|
||||
var view = $.extend(true, {}, this.fields_view);
|
||||
view.arch.tag = 'form';
|
||||
_.extend(view.arch.attrs, {
|
||||
'class': 'oe_form_container',
|
||||
version: '7.0'
|
||||
});
|
||||
_(view.arch.children).each(function (widget) {
|
||||
var modifiers = JSON.parse(widget.attrs.modifiers || '{}');
|
||||
widget.attrs.nolabel = true;
|
||||
if (modifiers['tree_invisible'] || widget.tag === 'button') {
|
||||
modifiers.invisible = true;
|
||||
}
|
||||
widget.attrs.modifiers = JSON.stringify(modifiers);
|
||||
});
|
||||
return view;
|
||||
},
|
||||
handle_onwrite: function (source_record) {
|
||||
var self = this;
|
||||
var on_write_callback = self.fields_view.arch.attrs.on_write;
|
||||
if (!on_write_callback) { return $.when(); }
|
||||
return this.dataset.call(on_write_callback, [source_record.get('id')])
|
||||
.pipe(function (ids) {
|
||||
return $.when.apply(
|
||||
null, _(ids).map(
|
||||
_.bind(self.handle_onwrite_record, self, source_record)));
|
||||
});
|
||||
},
|
||||
handle_onwrite_record: function (source_record, id) {
|
||||
var record = this.records.get(id);
|
||||
if (!record) {
|
||||
// insert after the source record
|
||||
var index = this.records.indexOf(source_record) + 1;
|
||||
record = new instance.web.list.Record({id: id});
|
||||
this.records.add(record, {at: index});
|
||||
this.dataset.ids.splice(index, 0, id);
|
||||
}
|
||||
return this.reload_record(record);
|
||||
},
|
||||
prepends_on_create: function () {
|
||||
return this.options.editable === 'top';
|
||||
},
|
||||
setup_events: function () {
|
||||
var self = this;
|
||||
this.editor.$element.on('keyup keydown', function (e) {
|
||||
if (!self.editor.is_editing()) { return; }
|
||||
var key = _($.ui.keyCode).chain()
|
||||
.map(function (v, k) { return {name: k, code: v}; })
|
||||
.find(function (o) { return o.code === e.which; })
|
||||
.value();
|
||||
if (!key) { return; }
|
||||
var method = e.type + '_' + key.name;
|
||||
if (!(method in self)) { return; }
|
||||
self[method](e);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Saves the current record, and goes to the next one (creation or
|
||||
* edition)
|
||||
*
|
||||
* @private
|
||||
* @param {String} [next_record='succ'] method to call on the records collection to get the next record to edit
|
||||
* @return {*}
|
||||
*/
|
||||
_next: function (next_record) {
|
||||
next_record = next_record || 'succ';
|
||||
var self = this;
|
||||
return this.save_edition().pipe(function (saveInfo) {
|
||||
if (saveInfo.created) {
|
||||
return self.start_edition();
|
||||
}
|
||||
return self.start_edition(
|
||||
self.records[next_record](
|
||||
saveInfo.record, {wraparound: true}));
|
||||
});
|
||||
},
|
||||
keyup_ENTER: function () {
|
||||
return this._next();
|
||||
},
|
||||
keyup_ESCAPE: function () {
|
||||
return this.cancel_edition();
|
||||
},
|
||||
_text_selection_range: function (el) {
|
||||
if (el.selectionStart !== undefined) {
|
||||
return {
|
||||
start: el.selectionStart,
|
||||
end: el.selectionEnd
|
||||
};
|
||||
} else if(document.body.createTextRange) {
|
||||
throw new Error("Implement text range handling for MSIE");
|
||||
var sel = document.body.createTextRange();
|
||||
if (sel.parentElement() === el) {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
_text_cursor: function (el) {
|
||||
var selection = this._text_selection_range(el);
|
||||
if (selection.start !== selection.end) {
|
||||
return null;
|
||||
}
|
||||
return selection.start;
|
||||
},
|
||||
keydown_UP: function (e) {
|
||||
if (!this.editor.is_editing('edit')) { return $.when(); }
|
||||
// FIXME: assumes editable widgets are input-type elements
|
||||
var index = this._text_cursor(e.target);
|
||||
// If selecting or not at the start of the input
|
||||
if (index === null || index !== 0) { return $.when(); }
|
||||
|
||||
e.preventDefault();
|
||||
return this._next('pred');
|
||||
},
|
||||
keydown_DOWN: function (e) {
|
||||
if (!this.editor.is_editing('edit')) { return $.when(); }
|
||||
// FIXME: assumes editable widgets are input-type elements
|
||||
var index = this._text_cursor(e.target);
|
||||
// If selecting or not at the end of the input
|
||||
if (index === null || index !== e.target.value.length) { return $.when(); }
|
||||
|
||||
e.preventDefault();
|
||||
return this._next();
|
||||
},
|
||||
keydown_TAB: function (e) {
|
||||
var form = this.editor.form;
|
||||
var last_field = _(form.fields_order).chain()
|
||||
.map(function (name) { return form.fields[name]; })
|
||||
.filter(function (field) { return field.$element.is(':visible'); })
|
||||
.last()
|
||||
.value();
|
||||
// tabbed from last field in form
|
||||
if (last_field && last_field.$element.has(e.target).length) {
|
||||
e.preventDefault();
|
||||
return this._next();
|
||||
}
|
||||
return $.when();
|
||||
}
|
||||
});
|
||||
|
||||
instance.web.list.Editor = instance.web.Widget.extend({
|
||||
/**
|
||||
* @constructs instance.web.list.Editor
|
||||
* @extends instance.web.Widget
|
||||
*
|
||||
* Adapter between listview and formview for editable-listview purposes
|
||||
*
|
||||
* @param {instance.web.Widget} parent
|
||||
* @param {Object} options
|
||||
* @param {instance.web.FormView} [options.formView=instance.web.FormView]
|
||||
* @param {Object} [options.delegate]
|
||||
*/
|
||||
init: function (parent, options) {
|
||||
this._super(parent);
|
||||
this.options = options || {};
|
||||
_.defaults(this.options, {
|
||||
formView: instance.web.FormView,
|
||||
delegate: this.getParent()
|
||||
});
|
||||
this.delegate = this.options.delegate;
|
||||
|
||||
this.record = null;
|
||||
|
||||
this.form = new (this.options.formView)(
|
||||
this, this.delegate.dataset, false, {
|
||||
initial_mode: 'edit',
|
||||
$buttons: $(),
|
||||
$pager: $()
|
||||
});
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
var _super = this._super();
|
||||
this.form.embedded_view = this._validate_view(
|
||||
this.delegate.edition_view(this));
|
||||
var form_ready = this.form.appendTo(this.$element).then(
|
||||
self.form.proxy('do_hide'));
|
||||
return $.when(_super, form_ready);
|
||||
},
|
||||
_validate_view: function (edition_view) {
|
||||
if (!edition_view) {
|
||||
throw new Error("editor delegate's #edition_view must return "
|
||||
+ "a view descriptor");
|
||||
}
|
||||
var arch = edition_view.arch;
|
||||
if (!(arch && arch.children instanceof Array)) {
|
||||
throw new Error("Editor delegate's #edition_view must have a" +
|
||||
" non-empty arch")
|
||||
}
|
||||
if (!(arch.tag === "form")) {
|
||||
throw new Error("Editor delegate's #edition_view must have a" +
|
||||
" 'form' root node");
|
||||
}
|
||||
if (!(arch.attrs && arch.attrs.version === "7.0")) {
|
||||
throw new Error("Editor delegate's #edition_view must be a" +
|
||||
" version 7 view");
|
||||
}
|
||||
if (!/\boe_form_container\b/.test(arch.attrs['class'])) {
|
||||
throw new Error("Editor delegate's #edition_view must have the" +
|
||||
" class 'oe_form_container' on its root" +
|
||||
" element");
|
||||
}
|
||||
|
||||
return edition_view;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} [state] either ``new`` or ``edit``
|
||||
* @return {Boolean}
|
||||
*/
|
||||
is_editing: function (state) {
|
||||
if (!this.record) {
|
||||
return false;
|
||||
}
|
||||
switch(state) {
|
||||
case null: case undefined:
|
||||
return true;
|
||||
case 'new': return !this.record.id;
|
||||
case 'edit': return !!this.record.id;
|
||||
}
|
||||
throw new Error("is_editing's state filter must be either `new` or" +
|
||||
" `edit` if provided");
|
||||
},
|
||||
_focus_setup: function (focus_field) {
|
||||
var form = this.form;
|
||||
|
||||
var field;
|
||||
// If a field to focus was specified
|
||||
if (focus_field
|
||||
// Is actually in the form
|
||||
&& (field = form.fields[focus_field])
|
||||
// And is visible
|
||||
&& field.$element.is(':visible')) {
|
||||
// focus it
|
||||
field.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
_(form.fields_order).detect(function (name) {
|
||||
// look for first visible field in fields_order, focus it
|
||||
var field = form.fields[name];
|
||||
if (!field.$element.is(':visible')) {
|
||||
return false;
|
||||
}
|
||||
field.focus();
|
||||
// Stop as soon as a field got focused
|
||||
return true;
|
||||
});
|
||||
},
|
||||
edit: function (record, configureField, options) {
|
||||
// TODO: specify sequence of edit calls
|
||||
var self = this;
|
||||
var form = self.form;
|
||||
var loaded = record
|
||||
? form.on_record_loaded(_.extend({}, record))
|
||||
: form.load_defaults();
|
||||
|
||||
return loaded.pipe(function () {
|
||||
return form.do_show({reload: false});
|
||||
}).pipe(function () {
|
||||
self.record = form.datarecord;
|
||||
_(form.fields).each(function (field, name) {
|
||||
configureField(name, field);
|
||||
});
|
||||
self._focus_setup(options && options.focus_field);
|
||||
return form;
|
||||
});
|
||||
},
|
||||
save: function () {
|
||||
var self = this;
|
||||
return this.form
|
||||
.do_save(null, this.delegate.prepends_on_create())
|
||||
.pipe(function (result) {
|
||||
var created = result.created && !self.record.id;
|
||||
if (created) {
|
||||
self.record.id = result.result;
|
||||
}
|
||||
return self.cancel();
|
||||
});
|
||||
},
|
||||
cancel: function () {
|
||||
var record = this.record;
|
||||
this.record = null;
|
||||
if (!this.form.can_be_discarded()) {
|
||||
return $.Deferred().reject({
|
||||
message: "The form's data can not be discarded"}).promise();
|
||||
}
|
||||
this.form.do_hide();
|
||||
return $.when(record);
|
||||
}
|
||||
});
|
||||
|
||||
instance.web.ListView.Groups.include(/** @lends instance.web.ListView.Groups# */{
|
||||
passtrough_events: instance.web.ListView.Groups.prototype.passtrough_events + " edit saved",
|
||||
new_record: function () {
|
||||
// TODO: handle multiple children
|
||||
this.children[null].new_record();
|
||||
},
|
||||
/**
|
||||
* Ensures descendant editable List instances are all saved if they have
|
||||
* pending editions.
|
||||
*
|
||||
* @returns {$.Deferred}
|
||||
*/
|
||||
ensure_saved: function () {
|
||||
return $.when.apply(null,
|
||||
_.invoke(
|
||||
_.values(this.children),
|
||||
'ensure_saved'));
|
||||
get_row_for: function (record) {
|
||||
return _(this.children).chain()
|
||||
.invoke('get_row_for', record)
|
||||
.compact()
|
||||
.first()
|
||||
.value();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -119,325 +645,27 @@ openerp.web.list_editable = function (instance) {
|
|||
if (!this.options.editable) {
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
this.edit_record($(event.currentTarget).data('id'));
|
||||
},
|
||||
/**
|
||||
* Checks if a record is being edited, and if so cancels it
|
||||
*/
|
||||
cancel_pending_edition: function () {
|
||||
var self = this, cancelled;
|
||||
if (!this.edition) {
|
||||
return $.when();
|
||||
}
|
||||
|
||||
if (this.edition_id) {
|
||||
cancelled = this.reload_record(this.records.get(this.edition_id));
|
||||
} else {
|
||||
cancelled = $.when();
|
||||
}
|
||||
cancelled.then(function () {
|
||||
self.view.unpad_columns();
|
||||
self.edition_form.destroy();
|
||||
self.edition_form.$element.remove();
|
||||
delete self.edition_form;
|
||||
self.dataset.index = null;
|
||||
delete self.edition_id;
|
||||
delete self.edition;
|
||||
var record_id = $(event.currentTarget).data('id');
|
||||
this.view.start_edition(
|
||||
record_id ? this.records.get(record_id) : null, {
|
||||
focus_field: $(event.target).data('field')
|
||||
});
|
||||
this.pad_table_to(5);
|
||||
return cancelled;
|
||||
},
|
||||
/**
|
||||
* Adapts this list's view description to be suitable to the inner form
|
||||
* view of a row being edited.
|
||||
* If a row mapping to the record (@data-id matching the record's id or
|
||||
* no @data-id if the record has no id), returns it. Otherwise returns
|
||||
* ``null``.
|
||||
*
|
||||
* @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
|
||||
* @param {Record} record the record to get a row for
|
||||
* @return {jQuery|null}
|
||||
*/
|
||||
get_form_fields_view: function () {
|
||||
// deep copy of view
|
||||
var view = $.extend(true, {}, this.group.view.fields_view);
|
||||
_(view.arch.children).each(function (widget) {
|
||||
widget.attrs.nolabel = true;
|
||||
if (widget.tag === 'button') {
|
||||
delete widget.attrs.string;
|
||||
}
|
||||
});
|
||||
view.arch.attrs.col = 2 * view.arch.children.length;
|
||||
return view;
|
||||
},
|
||||
on_row_keyup: function (e) {
|
||||
var self = this;
|
||||
switch (e.which) {
|
||||
case KEY_RETURN:
|
||||
$(e.target).blur();
|
||||
e.preventDefault();
|
||||
//e.stopImmediatePropagation();
|
||||
setTimeout(function () {
|
||||
self.save_row().then(function (result) {
|
||||
if (result.created) {
|
||||
self.new_record();
|
||||
return;
|
||||
}
|
||||
|
||||
var next_record_id,
|
||||
next_record = self.records.at(
|
||||
self.records.indexOf(result.edited_record) + 1);
|
||||
if (next_record) {
|
||||
next_record_id = next_record.get('id');
|
||||
self.dataset.index = _(self.dataset.ids)
|
||||
.indexOf(next_record_id);
|
||||
} else {
|
||||
self.dataset.index = 0;
|
||||
next_record_id = self.records.at(0).get('id');
|
||||
}
|
||||
self.edit_record(next_record_id);
|
||||
}, 0);
|
||||
});
|
||||
break;
|
||||
case KEY_ESCAPE:
|
||||
this.cancel_edition();
|
||||
break;
|
||||
get_row_for: function (record) {
|
||||
var id;
|
||||
var $row = this.$current.children('[data-id=' + record.get('id') + ']');
|
||||
if ($row.length) {
|
||||
return $row;
|
||||
}
|
||||
},
|
||||
render_row_as_form: function (row) {
|
||||
var self = this;
|
||||
return this.ensure_saved().pipe(function () {
|
||||
var record_id = $(row).data('id');
|
||||
var $new_row = $('<tr>', {
|
||||
id: _.uniqueId('oe-editable-row-'),
|
||||
'data-id': record_id,
|
||||
'class': (row ? $(row).attr('class') : ''),
|
||||
click: function (e) {e.stopPropagation();}
|
||||
})
|
||||
.addClass('oe_form oe_form_container')
|
||||
.delegate('button.oe_list_edit_row_save', 'click', function () {
|
||||
self.save_row();
|
||||
})
|
||||
.delegate('button', 'keyup', function (e) {
|
||||
e.stopImmediatePropagation();
|
||||
})
|
||||
.keyup(function () {
|
||||
return self.on_row_keyup.apply(self, arguments); })
|
||||
.keydown(function (e) { e.stopPropagation(); })
|
||||
.keypress(function (e) {
|
||||
if (e.which === KEY_RETURN) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (row) {
|
||||
$new_row.replaceAll(row);
|
||||
} else if (self.options.editable) {
|
||||
var $last_child = self.$current.children('tr:last');
|
||||
if (self.records.length) {
|
||||
if (self.options.editable === 'top') {
|
||||
$new_row.insertBefore(
|
||||
self.$current.children('[data-id]:first'));
|
||||
} else {
|
||||
$new_row.insertAfter(
|
||||
self.$current.children('[data-id]:last'));
|
||||
}
|
||||
} else {
|
||||
$new_row.prependTo(self.$current);
|
||||
}
|
||||
if ($last_child.is(':not([data-id])')) {
|
||||
$last_child.remove();
|
||||
}
|
||||
}
|
||||
self.edition = true;
|
||||
self.edition_id = record_id;
|
||||
self.dataset.index = _(self.dataset.ids).indexOf(record_id);
|
||||
if (self.dataset.index === -1) {
|
||||
self.dataset.index = null;
|
||||
}
|
||||
self.edition_form = _.extend(new instance.web.ListEditableFormView(self.view, self.dataset, false), {
|
||||
$element: $new_row,
|
||||
editable_list: self
|
||||
});
|
||||
// put in $.when just in case FormView.on_loaded becomes asynchronous
|
||||
return $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
|
||||
$new_row.find('> td')
|
||||
.end()
|
||||
.find('td:last').removeClass('oe_list_field_cell').end();
|
||||
// pad in case of groupby
|
||||
_(self.columns).each(function (column) {
|
||||
if (column.meta) {
|
||||
$new_row.prepend('<td>');
|
||||
}
|
||||
});
|
||||
// Add column for the save, if
|
||||
// there is none in the list
|
||||
if (!self.options.deletable) {
|
||||
self.view.pad_columns(
|
||||
1, {except: $new_row});
|
||||
}
|
||||
|
||||
self.edition_form.do_show();
|
||||
});
|
||||
});
|
||||
},
|
||||
handle_onwrite: function (source_record_id) {
|
||||
var self = this;
|
||||
var on_write_callback = self.view.fields_view.arch.attrs.on_write;
|
||||
if (!on_write_callback) { return; }
|
||||
this.dataset.call(on_write_callback, [source_record_id], function (ids) {
|
||||
_(ids).each(function (id) {
|
||||
var record = self.records.get(id);
|
||||
if (!record) {
|
||||
// insert after the source record
|
||||
var index = self.records.indexOf(
|
||||
self.records.get(source_record_id)) + 1;
|
||||
record = new instance.web.list.Record({id: id});
|
||||
self.records.add(record, {at: index});
|
||||
self.dataset.ids.splice(index, 0, id);
|
||||
}
|
||||
self.reload_record(record);
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Saves the current row, and returns a Deferred resolving to an object
|
||||
* with the following properties:
|
||||
*
|
||||
* ``created``
|
||||
* Boolean flag indicating whether the record saved was being created
|
||||
* (``true`` or edited (``false``)
|
||||
* ``edited_record``
|
||||
* The result of saving the record (either the newly created record,
|
||||
* or the post-edition record), after insertion in the Collection if
|
||||
* needs be.
|
||||
*
|
||||
* @returns {$.Deferred<{created: Boolean, edited_record: Record}>}
|
||||
*/
|
||||
save_row: function () {
|
||||
//noinspection JSPotentiallyInvalidConstructorUsage
|
||||
var self = this;
|
||||
return this.edition_form
|
||||
.do_save(null, this.options.editable === 'top')
|
||||
.pipe(function (result) {
|
||||
if (result.created && !self.edition_id) {
|
||||
self.records.add({id: result.result},
|
||||
{at: self.options.editable === 'top' ? 0 : null});
|
||||
self.edition_id = result.result;
|
||||
}
|
||||
var edited_record = self.records.get(self.edition_id);
|
||||
|
||||
return $.when(
|
||||
self.handle_onwrite(self.edition_id),
|
||||
self.cancel_pending_edition().then(function () {
|
||||
$(self).trigger('saved', [self.dataset]);
|
||||
})).pipe(function () {
|
||||
return {
|
||||
created: result.created || false,
|
||||
edited_record: edited_record
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* If the current list is being edited, ensures it's saved
|
||||
*/
|
||||
ensure_saved: function () {
|
||||
if (this.edition) {
|
||||
// kinda-hack-ish: if the user has entered data in a field,
|
||||
// oe_form_dirty will be set on the form so save, otherwise
|
||||
// discard the current (entirely empty) line
|
||||
if (this.edition_form.$element.is('.oe_form_dirty')) {
|
||||
return this.save_row();
|
||||
}
|
||||
return this.cancel_pending_edition();
|
||||
}
|
||||
//noinspection JSPotentiallyInvalidConstructorUsage
|
||||
return $.when();
|
||||
},
|
||||
/**
|
||||
* Cancels the edition of the row for the current dataset index
|
||||
*/
|
||||
cancel_edition: function () {
|
||||
this.cancel_pending_edition();
|
||||
},
|
||||
/**
|
||||
* Edits record currently selected via dataset
|
||||
*/
|
||||
edit_record: function (record_id) {
|
||||
this.render_row_as_form(
|
||||
this.$current.find('[data-id=' + record_id + ']'));
|
||||
$(this).trigger(
|
||||
'edit',
|
||||
[record_id, this.dataset]);
|
||||
},
|
||||
new_record: function () {
|
||||
this.render_row_as_form();
|
||||
},
|
||||
render_record: function (record) {
|
||||
var index = this.records.indexOf(record),
|
||||
self = this;
|
||||
// FIXME: context dict should probably be extracted cleanly
|
||||
return QWeb.render('ListView.row', {
|
||||
columns: this.columns,
|
||||
options: this.options,
|
||||
record: record,
|
||||
row_parity: (index % 2 === 0) ? 'even' : 'odd',
|
||||
view: this.view,
|
||||
render_cell: function () {
|
||||
return self.render_cell.apply(self, arguments); },
|
||||
edited: !!this.edition_form
|
||||
});
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
instance.web.ListEditableFormView = instance.web.FormView.extend({
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.rendering_engine = new instance.web.ListEditableRenderingEngine(this);
|
||||
this.options.initial_mode = "edit";
|
||||
},
|
||||
renderElement: function() {}
|
||||
});
|
||||
|
||||
instance.web.ListEditableRenderingEngine = instance.web.form.FormRenderingEngineInterface.extend({
|
||||
init: function(view) {
|
||||
this.view = view;
|
||||
},
|
||||
set_fields_view: function(fields_view) {
|
||||
this.fvg = fields_view;
|
||||
},
|
||||
set_tags_registry: function(tags_registry) {
|
||||
this.tags_registry = tags_registry;
|
||||
},
|
||||
set_fields_registry: function(fields_registry) {
|
||||
this.fields_registry = fields_registry;
|
||||
},
|
||||
render_to: function($element) {
|
||||
var self = this;
|
||||
|
||||
var xml = instance.web.json_node_to_xml(this.fvg.arch);
|
||||
var $xml = $(xml);
|
||||
|
||||
if (this.view.editable_list.options.selectable)
|
||||
$("<td>").appendTo($element);
|
||||
|
||||
$xml.children().each(function(i, el) {
|
||||
var modifiers = JSON.parse($(el).attr("modifiers") || "{}");
|
||||
var $td = $("<td>");
|
||||
if (modifiers.tree_invisible === true)
|
||||
$td.hide();
|
||||
var tag_name = el.tagName.toLowerCase();
|
||||
var w;
|
||||
if (tag_name === "field") {
|
||||
var name = $(el).attr("name");
|
||||
var key = $(el).attr('widget') || self.fvg.fields[name].type;
|
||||
var obj = self.view.fields_registry.get_object(key);
|
||||
w = new (obj)(self.view, instance.web.xml_to_json(el));
|
||||
self.view.register_field(w, $(el).attr("name"));
|
||||
} else {
|
||||
var obj = self.tags_registry.get_object(tag_name);
|
||||
w = new (obj)(self.view, instance.web.xml_to_json(el));
|
||||
}
|
||||
w.appendTo($td);
|
||||
$td.appendTo($element);
|
||||
});
|
||||
$(QWeb.render('ListView.row.save')).appendTo($element);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1074,11 +1074,13 @@ instance.web.TranslateDialog = instance.web.Dialog.extend({
|
|||
if (self.view.translatable_fields && self.view.translatable_fields.length) {
|
||||
self.do_load_fields_values(function() {
|
||||
sup.call(self);
|
||||
// desactivated because it created an exception, plus it does not seem very useful
|
||||
/*
|
||||
if (field) {
|
||||
var $field_input = self.$element.find('tr[data-field="' + field.name + '"] td:nth-child(2) *:first-child');
|
||||
self.$element.scrollTo($field_input);
|
||||
$field_input.focus();
|
||||
}
|
||||
}*/
|
||||
});
|
||||
} else {
|
||||
sup.call(self);
|
||||
|
@ -1346,7 +1348,9 @@ instance.web.json_node_to_xml = function(node, human_readable, indent) {
|
|||
if (typeof(node) === 'string') {
|
||||
return sindent + node;
|
||||
} else if (typeof(node.tag) !== 'string' || !node.children instanceof Array || !node.attrs instanceof Object) {
|
||||
throw("Node a json node");
|
||||
throw new Error(
|
||||
_.str.sprintf("Node [%s] is not a JSONified XML node",
|
||||
JSON.stringify(node)));
|
||||
}
|
||||
for (var attr in node.attrs) {
|
||||
var vattr = node.attrs[attr];
|
||||
|
|
|
@ -440,6 +440,7 @@
|
|||
</t>
|
||||
|
||||
<t t-name="WebClient">
|
||||
<div class="openerp openerp_webclient_container">
|
||||
<table class="oe_webclient">
|
||||
<tr>
|
||||
<td colspan="2" class="oe_topbar">
|
||||
|
@ -463,6 +464,13 @@
|
|||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="EmbedClient">
|
||||
<div class="openerp">
|
||||
<div class="oe_application"></div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="ViewManager">
|
||||
|
@ -691,9 +699,9 @@
|
|||
<button type="button" class="oe_button oe_list_add oe_highlight">
|
||||
<t t-esc="widget.options.addable"/>
|
||||
</button>
|
||||
<t t-if="widget.options.import_enabled">
|
||||
<span class="oe_alternative" t-if="widget.options.import_enabled">
|
||||
<span class="oe_fade">or</span> <a href="#" class="oe_bold oe_list_button_import">Import</a>
|
||||
</t>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
<t t-name="ListView.pager">
|
||||
|
@ -713,6 +721,7 @@
|
|||
<tr t-name="ListView.row" t-att-class="row_parity"
|
||||
t-att-data-id="record.get('id')"
|
||||
t-att-style="view.style_for(record)">
|
||||
<t t-set="asData" t-value="record.toForm().data"/>
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<td t-if="column.meta">
|
||||
|
||||
|
@ -724,23 +733,26 @@
|
|||
<input t-if="!options.radio" type="checkbox" name="radiogroup" t-att-checked="checked"/>
|
||||
</th>
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<t t-set="align" t-value="column.type === 'integer' or column.type == 'float'"/>
|
||||
<t t-set="number" t-value="column.type === 'integer' or column.type == 'float'"/>
|
||||
<t t-set="modifiers" t-value="column.modifiers_for(asData)"/>
|
||||
<td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help"
|
||||
t-att-class="'oe_list_field_cell' + (align ? ' oe_number' : '')
|
||||
+ (column.tag === 'button' ? ' oe_button' : '')"
|
||||
t-att-data-field="column.id">
|
||||
<t t-raw="render_cell(record, column)"/>
|
||||
</td>
|
||||
t-attf-class="oe_list_field_cell oe_list_field_#{column.widget or column.type} #{number ? 'oe_number' : ''} #{column.tag === 'button' ? 'oe-button' : ''} #{modifiers.readonly ? 'oe_readonly' : ''}"
|
||||
t-att-data-field="column.id"
|
||||
><t t-raw="render_cell(record, column)"/></td>
|
||||
</t>
|
||||
<td t-if="options.deletable" class='oe_list_record_delete' width="1">
|
||||
<button type="button" name="delete" class="oe_i">d</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<t t-name="ListView.row.save">
|
||||
<td>
|
||||
<button class='oe_i oe_list_edit_row_save' type='button' name='save'/>
|
||||
</td>
|
||||
<t t-extend="ListView.buttons">
|
||||
<t t-jquery="button.oe_list_add" t-operation="after">
|
||||
<button class="oe_button oe_list_save oe_highlight"
|
||||
type="button">Save</button>
|
||||
</t>
|
||||
<t t-jquery="a.oe_list_button_import" t-operation="after">
|
||||
<a href="#" class="oe_bold oe_list_discard">discard</a>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="FormView">
|
||||
|
@ -1012,11 +1024,12 @@
|
|||
</t>
|
||||
<t t-name="FieldMany2One">
|
||||
<span class="oe_form_field oe_form_field_many2one oe_form_field_with_button" t-att-style="widget.node.attrs.style">
|
||||
<a t-if="widget.get('effective_readonly')" href="#" class="oe_form_uri"/>
|
||||
<t t-if="widget.get('effective_readonly')">
|
||||
<a href="#" class="oe_form_uri"/>
|
||||
<span class="oe_form_m2o_follow"/>
|
||||
</t>
|
||||
<t t-if="!widget.get('effective_readonly')">
|
||||
<button class="oe_button oe_m2o_cm_button" title="Open Resource">
|
||||
<img t-att-src='_s + "/web/static/src/img/icons/terp-folder-yellow.png"'/>
|
||||
</button>
|
||||
<a href="#" tabindex="-1" class="oe_m2o_cm_button oe_e oe_right">/</a>
|
||||
<div>
|
||||
<input type="text"
|
||||
t-att-id="widget.id_for_label"
|
||||
|
@ -1536,8 +1549,11 @@
|
|||
</div>
|
||||
</t>
|
||||
<t t-name="SelectCreatePopup.search.buttons">
|
||||
<button type="button" class="oe_button oe_selectcreatepopup-search-select" disabled="disabled">Select</button>
|
||||
<button type="button" class="oe_button oe_selectcreatepopup-search-close">Cancel</button>
|
||||
<t t-if="! widget.options.disable_multiple_selection">
|
||||
<button type="button" class="oe_button oe_selectcreatepopup-search-select" disabled="disabled">Select</button>
|
||||
or
|
||||
</t>
|
||||
<a class="oe_button oe_selectcreatepopup-search-close oe_bold oe_form_button_cancel" href="javascript:void(0)">Cancel</a>
|
||||
</t>
|
||||
<t t-name="AbstractFormPopup.buttons">
|
||||
<t t-if="! multi_select">
|
||||
|
@ -1547,7 +1563,7 @@
|
|||
<button type="button" class="oe_button oe_abstractformpopup-form-save-new oe_highlight">Save & New</button>
|
||||
<button type="button" class="oe_button oe_abstractformpopup-form-save oe_highlight">Save & Close</button>
|
||||
</t>
|
||||
<button type="button" class="oe_button oe_abstractformpopup-form-close">Cancel</button>
|
||||
or <a class="oe_button oe_abstractformpopup-form-close oe_bold oe_form_button_cancel" href="javascript:void(0)">Cancel</a>
|
||||
</t>
|
||||
<t t-extend="ListView.row">
|
||||
<!-- adds back padding to row being rendered after edition, if necessary
|
||||
|
@ -1555,7 +1571,7 @@
|
|||
missing columns
|
||||
-->
|
||||
<t t-jquery="> :last" t-operation="after">
|
||||
<td t-if="edited and !options.deletable" class="oe_list_padding"/>
|
||||
<td t-if="edited and !options.deletable" class="oe-listview-padding"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
$(document).ready(function () {
|
||||
var $fix = $('#qunit-fixture');
|
||||
|
||||
var instance;
|
||||
var baseSetup = function () {
|
||||
instance = openerp.testing.instanceFor('list_editable');
|
||||
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} [attrs]
|
||||
* @param {String} [attrs.type="char"]
|
||||
* @param {Boolean} [attrs.required]
|
||||
* @param {Boolean} [attrs.invisible]
|
||||
* @param {Boolean} [attrs.readonly]
|
||||
* @return {Object}
|
||||
*/
|
||||
function field(name, attrs) {
|
||||
attrs = attrs || {};
|
||||
attrs.name = name;
|
||||
return _.defaults(attrs, {
|
||||
type: 'char'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} fields
|
||||
* @return {Object}
|
||||
*/
|
||||
function makeFormView(fields) {
|
||||
var fobj = {};
|
||||
_(fields).each(function (field) {
|
||||
fobj[field.name] = {
|
||||
type: field.type,
|
||||
string: field.string
|
||||
};
|
||||
});
|
||||
var children = _(fields).map(function (field) {
|
||||
return {
|
||||
tag: 'field',
|
||||
attrs: {
|
||||
name: field.name,
|
||||
modifiers: JSON.stringify({
|
||||
required: field.required,
|
||||
invisible: field.invisible,
|
||||
readonly: field.readonly
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
arch: {
|
||||
tag: 'form',
|
||||
attrs: {
|
||||
version: '7.0',
|
||||
'class': 'oe_form_container'
|
||||
},
|
||||
children: children
|
||||
},
|
||||
fields: fobj
|
||||
};
|
||||
}
|
||||
|
||||
module('editor', {
|
||||
setup: baseSetup
|
||||
});
|
||||
asyncTest('base-state', 2, function () {
|
||||
var e = new instance.web.list.Editor({
|
||||
dataset: {},
|
||||
edition_view: function () {
|
||||
return makeFormView();
|
||||
}
|
||||
});
|
||||
e.appendTo($fix)
|
||||
.always(start)
|
||||
.fail(function (error) { ok(false, error && error.message); })
|
||||
.done(function () {
|
||||
ok(!e.is_editing(), "should not be editing");
|
||||
ok(e.form instanceof instance.web.FormView,
|
||||
"should use default form type");
|
||||
});
|
||||
});
|
||||
asyncTest('toggle-edition-save', 4, function () {
|
||||
instance.connection.responses['/web/dataset/call_kw:create'] = function () {
|
||||
return { result: 42 };
|
||||
};
|
||||
instance.connection.responses['/web/dataset/call_kw:read'] = function () {
|
||||
return { result: [{
|
||||
id: 42,
|
||||
a: false,
|
||||
b: false,
|
||||
c: false
|
||||
}]};
|
||||
};
|
||||
var e = new instance.web.list.Editor({
|
||||
dataset: new instance.web.DataSetSearch(),
|
||||
prepends_on_create: function () { return false; },
|
||||
edition_view: function () {
|
||||
return makeFormView([ field('a'), field('b'), field('c') ]);
|
||||
}
|
||||
});
|
||||
var counter = 0;
|
||||
e.appendTo($fix)
|
||||
.pipe(function () {
|
||||
return e.edit({}, function () {
|
||||
++counter;
|
||||
});
|
||||
})
|
||||
.pipe(function (form) {
|
||||
ok(e.is_editing(), "should be editing");
|
||||
equal(counter, 3, "should have configured all fields");
|
||||
return e.save();
|
||||
})
|
||||
.always(start)
|
||||
.fail(function (error) { ok(false, error && error.message); })
|
||||
.done(function (record) {
|
||||
ok(!e.is_editing(), "should have stopped editing");
|
||||
equal(record.id, 42, "should have newly created id");
|
||||
})
|
||||
});
|
||||
asyncTest('toggle-edition-cancel', 2, function () {
|
||||
instance.connection.responses['/web/dataset/call_kw:create'] = function () {
|
||||
return { result: 42 };
|
||||
};
|
||||
var e = new instance.web.list.Editor({
|
||||
dataset: new instance.web.DataSetSearch(),
|
||||
prepends_on_create: function () { return false; },
|
||||
edition_view: function () {
|
||||
return makeFormView([ field('a'), field('b'), field('c') ]);
|
||||
}
|
||||
});
|
||||
var counter = 0;
|
||||
e.appendTo($fix)
|
||||
.pipe(function () {
|
||||
return e.edit({}, function () {
|
||||
++counter;
|
||||
});
|
||||
})
|
||||
.pipe(function (form) {
|
||||
return e.cancel();
|
||||
})
|
||||
.always(start)
|
||||
.fail(function (error) { ok(false, error && error.message); })
|
||||
.done(function (record) {
|
||||
ok(!e.is_editing(), "should have stopped editing");
|
||||
ok(!record.id, "should have no id");
|
||||
})
|
||||
});
|
||||
asyncTest('toggle-save-required', 2, function () {
|
||||
instance.connection.responses['/web/dataset/call_kw:create'] = function () {
|
||||
return { result: 42 };
|
||||
};
|
||||
var e = new instance.web.list.Editor({
|
||||
do_warn: function () {
|
||||
warnings++;
|
||||
},
|
||||
dataset: new instance.web.DataSetSearch(),
|
||||
prepends_on_create: function () { return false; },
|
||||
edition_view: function () {
|
||||
return makeFormView([
|
||||
field('a', {required: true}), field('b'), field('c') ]);
|
||||
}
|
||||
});
|
||||
var counter = 0;
|
||||
var warnings = 0;
|
||||
e.appendTo($fix)
|
||||
.pipe(function () {
|
||||
return e.edit({}, function () {
|
||||
++counter;
|
||||
});
|
||||
})
|
||||
.pipe(function (form) {
|
||||
return e.save();
|
||||
})
|
||||
.always(start)
|
||||
.done(function () { ok(false, "cancel should not succeed"); })
|
||||
.fail(function () {
|
||||
equal(warnings, 1, "should have been warned");
|
||||
ok(e.is_editing(), "should have kept editing");
|
||||
})
|
||||
});
|
||||
|
||||
module('list-edition', {
|
||||
setup: function () {
|
||||
baseSetup();
|
||||
|
||||
var records = {};
|
||||
_.extend(instance.connection.responses, {
|
||||
'/web/listview/load': function () {
|
||||
return {result: {
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"},
|
||||
b: {type: 'char', string: "B"},
|
||||
c: {type: 'char', string: "C"}
|
||||
},
|
||||
arch: {
|
||||
tag: 'tree',
|
||||
attrs: {},
|
||||
children: [
|
||||
{tag: 'field', attrs: {name: 'a'}},
|
||||
{tag: 'field', attrs: {name: 'b'}},
|
||||
{tag: 'field', attrs: {name: 'c'}}
|
||||
]
|
||||
}
|
||||
}};
|
||||
},
|
||||
'/web/dataset/call_kw:create': function (params) {
|
||||
records[42] = _.extend({}, params.params.args[0]);
|
||||
return {result: 42};
|
||||
},
|
||||
'/web/dataset/call_kw:read': function (params) {
|
||||
var id = params.params.args[0][0];
|
||||
if (id in records) {
|
||||
return {result: [records[id]]};
|
||||
}
|
||||
return {result: []};
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
asyncTest('newrecord', 6, function () {
|
||||
var got_defaults = false;
|
||||
instance.connection.responses['/web/dataset/call_kw:default_get'] = function (params) {
|
||||
var fields = params.params.args[0];
|
||||
deepEqual(
|
||||
fields, ['a', 'b', 'c'],
|
||||
"should ask defaults for all fields");
|
||||
got_defaults = true;
|
||||
return {result: {
|
||||
a: "qux",
|
||||
b: "quux"
|
||||
}};
|
||||
};
|
||||
|
||||
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
||||
var l = new instance.web.ListView({}, ds);
|
||||
l.set_editable(true);
|
||||
|
||||
l.appendTo($fix)
|
||||
.pipe(l.proxy('reload_content'))
|
||||
.pipe(function () {
|
||||
return l.start_edition();
|
||||
})
|
||||
.always(start)
|
||||
.pipe(function () {
|
||||
ok(got_defaults, "should have fetched default values for form");
|
||||
return l.save_edition();
|
||||
})
|
||||
.pipe(function (result) {
|
||||
ok(result.created, "should yield newly created record");
|
||||
equal(result.record.get('a'), "qux",
|
||||
"should have used default values");
|
||||
equal(result.record.get('b'), "quux",
|
||||
"should have used default values");
|
||||
ok(!result.record.get('c'),
|
||||
"should have no value if there was no default");
|
||||
})
|
||||
.fail(function (e) { ok(false, e && e.message || e); });
|
||||
});
|
||||
|
||||
module('list-edition-events', {
|
||||
setup: function () {
|
||||
baseSetup();
|
||||
_.extend(instance.connection.responses, {
|
||||
'/web/listview/load': function () {
|
||||
return {result: {
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"},
|
||||
b: {type: 'char', string: "B"},
|
||||
c: {type: 'char', string: "C"}
|
||||
},
|
||||
arch: {
|
||||
tag: 'tree',
|
||||
attrs: {},
|
||||
children: [
|
||||
{tag: 'field', attrs: {name: 'a'}},
|
||||
{tag: 'field', attrs: {name: 'b'}},
|
||||
{tag: 'field', attrs: {name: 'c'}}
|
||||
]
|
||||
}
|
||||
}};
|
||||
},
|
||||
'/web/dataset/call_kw:read': function (params) {
|
||||
return {result: [{
|
||||
id: 1,
|
||||
a: 'foo',
|
||||
b: 'bar',
|
||||
c: 'baz'
|
||||
}]};
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
asyncTest('edition events', 4, function () {
|
||||
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
||||
var o = {
|
||||
counter: 0,
|
||||
onEvent: function (e) { this.counter++; }
|
||||
};
|
||||
var l = new instance.web.ListView({}, ds);
|
||||
l.set_editable(true);
|
||||
l.on('edit:before edit:after', o, o.onEvent);
|
||||
l.appendTo($fix)
|
||||
.pipe(l.proxy('reload_content'))
|
||||
.always(start)
|
||||
.pipe(function () {
|
||||
ok(l.options.editable, "should be editable");
|
||||
equal(o.counter, 0, "should have seen no event yet");
|
||||
return l.start_edition(l.records.get(1));
|
||||
})
|
||||
.pipe(function () {
|
||||
ok(l.editor.is_editing(), "should be editing");
|
||||
equal(o.counter, 2, "should have seen two edition events");
|
||||
})
|
||||
.fail(function (e) { ok(false, e && e.message); });
|
||||
});
|
||||
|
||||
asyncTest('edition events: cancelling', 3, function () {
|
||||
var edit_after = false;
|
||||
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
||||
var l = new instance.web.ListView({}, ds);
|
||||
l.set_editable(true);
|
||||
l.on('edit:before', {}, function (e) {
|
||||
e.cancel = true;
|
||||
});
|
||||
l.on('edit:after', {}, function () {
|
||||
edit_after = true;
|
||||
});
|
||||
l.appendTo($fix)
|
||||
.pipe(l.proxy('reload_content'))
|
||||
.always(start)
|
||||
.pipe(function () {
|
||||
ok(l.options.editable, "should be editable");
|
||||
return l.start_edition();
|
||||
})
|
||||
// cancelling an event rejects the deferred
|
||||
.pipe($.Deferred().reject(), function () {
|
||||
ok(!l.editor.is_editing(), "should not be editing");
|
||||
ok(!edit_after, "should not have fired the edit:after event");
|
||||
return $.when();
|
||||
})
|
||||
.fail(function (e) { ok(false, e && e.message || e); });
|
||||
});
|
||||
});
|
|
@ -133,7 +133,7 @@ $(document).ready(function () {
|
|||
strictEqual(changed, 1);
|
||||
});
|
||||
|
||||
module('list-collections-degenerate', {
|
||||
module('list-collections', {
|
||||
setup: function () {
|
||||
openerp = window.openerp.init([]);
|
||||
window.openerp.web.corelib(openerp);
|
||||
|
@ -145,7 +145,7 @@ $(document).ready(function () {
|
|||
window.openerp.web.list(openerp);
|
||||
}
|
||||
});
|
||||
test('Fetch from collection', function () {
|
||||
test('degenerate-fetch', function () {
|
||||
var c = new openerp.web.list.Collection();
|
||||
strictEqual(c.length, 0);
|
||||
c.add({id: 1, value: 2});
|
||||
|
@ -163,7 +163,7 @@ $(document).ready(function () {
|
|||
strictEqual(r2.get('id'), 1);
|
||||
strictEqual(r2.get('value'), 2);
|
||||
});
|
||||
test('Add at index', function () {
|
||||
test('degenerate-indexed-add', function () {
|
||||
var c = new openerp.web.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
|
@ -175,7 +175,7 @@ $(document).ready(function () {
|
|||
strictEqual(c.at(1).get('value'), 55);
|
||||
strictEqual(c.at(3).get('value'), 20);
|
||||
});
|
||||
test('Remove record', function () {
|
||||
test('degenerate-remove', function () {
|
||||
var c = new openerp.web.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
|
@ -188,7 +188,7 @@ $(document).ready(function () {
|
|||
equal(c.get(2), undefined);
|
||||
strictEqual(c.at(1).get('value'), 20);
|
||||
});
|
||||
test('Remove unbind', function () {
|
||||
test('degenerate-remove-bound', function () {
|
||||
var changed = false,
|
||||
c = new openerp.web.list.Collection([ {id: 1, value: 5} ]);
|
||||
c.bind('change', function () { changed = true; });
|
||||
|
@ -198,7 +198,7 @@ $(document).ready(function () {
|
|||
ok(!changed, 'removed records should not trigger events in their ' +
|
||||
'parent collection');
|
||||
});
|
||||
test('Reset', function () {
|
||||
test('degenerate-reset', function () {
|
||||
var event, obj, c = new openerp.web.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
|
@ -218,7 +218,7 @@ $(document).ready(function () {
|
|||
strictEqual(c.length, 1);
|
||||
strictEqual(c.get(42).get('value'), 55);
|
||||
});
|
||||
test('Reset unbind', function () {
|
||||
test('degenerate-reset-bound', function () {
|
||||
var changed = false,
|
||||
c = new openerp.web.list.Collection([ {id: 1, value: 5} ]);
|
||||
c.bind('change', function () { changed = true; });
|
||||
|
@ -229,7 +229,7 @@ $(document).ready(function () {
|
|||
'parent collection');
|
||||
});
|
||||
|
||||
test('Events propagation', function () {
|
||||
test('degenerate-propagations', function () {
|
||||
var values = [];
|
||||
var c = new openerp.web.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
|
@ -260,6 +260,82 @@ $(document).ready(function () {
|
|||
c.at(1).set('wealth', 5);
|
||||
strictEqual(total, 47);
|
||||
});
|
||||
test('degenerate-successor', function () {
|
||||
var root = new openerp.web.list.Collection([
|
||||
{id: 1, value: 1},
|
||||
{id: 2, value: 2},
|
||||
{id: 3, value: 3},
|
||||
{id: 4, value: 5},
|
||||
{id: 5, value: 8}
|
||||
]);
|
||||
|
||||
deepEqual(root.succ(root.at(2)).attributes,
|
||||
root.at(3).attributes,
|
||||
"should return the record at (index + 1) from the pivot");
|
||||
equal(root.succ(root.at(4)), null,
|
||||
"should return null as successor to last record");
|
||||
deepEqual(root.succ(root.at(4), {wraparound: true}).attributes,
|
||||
root.at(0).attributes,
|
||||
"should return index 0 as successor to last record if" +
|
||||
" wraparound is set");
|
||||
deepEqual(root.succ(root.at(2), {wraparound: true}).attributes,
|
||||
root.at(3).attributes,
|
||||
"wraparound should have no effect if not succ(last_record)");
|
||||
});
|
||||
test('successor', function () {
|
||||
var root = new openerp.web.list.Collection();
|
||||
root.proxy('first').add([{id: 1, value: 1}, {id: 2, value: 2}]);
|
||||
root.proxy('second').add([{id: 3, value: 3}, {id: 4, value: 5}]);
|
||||
root.proxy('third').add([{id: 5, value: 8}, {id: 6, value: 13}]);
|
||||
|
||||
deepEqual(root.succ(root.get(3)).attributes,
|
||||
root.get(4).attributes,
|
||||
"should get successor");
|
||||
equal(root.succ(root.get(4)),
|
||||
null,
|
||||
"successors do not cross collections");
|
||||
deepEqual(root.succ(root.get(4), {wraparound: true}).attributes,
|
||||
root.get(3).attributes,
|
||||
"should wraparound within a collection");
|
||||
});
|
||||
test('degenerate-predecessor', function () {
|
||||
var root = new openerp.web.list.Collection([
|
||||
{id: 1, value: 1},
|
||||
{id: 2, value: 2},
|
||||
{id: 3, value: 3},
|
||||
{id: 4, value: 5},
|
||||
{id: 5, value: 8}
|
||||
]);
|
||||
|
||||
deepEqual(root.pred(root.at(2)).attributes,
|
||||
root.at(1).attributes,
|
||||
"should return the record at (index - 1) from the pivot");
|
||||
equal(root.pred(root.at(0)), null,
|
||||
"should return null as predecessor to first record");
|
||||
deepEqual(root.pred(root.at(0), {wraparound: true}).attributes,
|
||||
root.at(4).attributes,
|
||||
"should return last record as predecessor to first record" +
|
||||
" if wraparound is set");
|
||||
deepEqual(root.pred(root.at(1), {wraparound: true}).attributes,
|
||||
root.at(0).attributes,
|
||||
"wraparound should have no effect if not pred(first_record)");
|
||||
});
|
||||
test('predecessor', function () {
|
||||
var root = new openerp.web.list.Collection();
|
||||
root.proxy('first').add([{id: 1, value: 1}, {id: 2, value: 2}]);
|
||||
root.proxy('second').add([{id: 3, value: 3}, {id: 4, value: 5}]);
|
||||
root.proxy('third').add([{id: 5, value: 8}, {id: 6, value: 13}]);
|
||||
|
||||
deepEqual(root.pred(root.get(4)).attributes,
|
||||
root.get(3).attributes,
|
||||
"should get predecessor");
|
||||
equal(root.pred(root.get(3)),
|
||||
null,
|
||||
"predecessor do not cross collections");
|
||||
deepEqual(root.pred(root.get(3), {wraparound: true}).attributes,
|
||||
root.get(4).attributes,
|
||||
"should wraparound within a collection");
|
||||
});
|
||||
|
||||
module('list-hofs', {
|
||||
setup: function () {
|
||||
|
@ -338,4 +414,33 @@ $(document).ready(function () {
|
|||
ids, [1, 2, 3, 10, 20, 30],
|
||||
'tree collections should be deeply iterated');
|
||||
});
|
||||
|
||||
module("list-weirds", {
|
||||
setup: function () {
|
||||
openerp = window.openerp.init([]);
|
||||
window.openerp.web.corelib(openerp);
|
||||
window.openerp.web.coresetup(openerp);
|
||||
window.openerp.web.chrome(openerp);
|
||||
// views loader stuff
|
||||
window.openerp.web.data(openerp);
|
||||
window.openerp.web.views(openerp);
|
||||
window.openerp.web.list(openerp);
|
||||
}
|
||||
});
|
||||
test('set-from-noid', function () {
|
||||
var root = new openerp.web.list.Collection();
|
||||
root.add({v: 3});
|
||||
root.at(0).set('id', 42);
|
||||
var record = root.get(42);
|
||||
equal(root.length, 1);
|
||||
equal(record.get('v'), 3, "should have fetched the original record");
|
||||
});
|
||||
test('set-from-previd', function () {
|
||||
var root = new openerp.web.list.Collection();
|
||||
root.add({id: 1, v: 2});
|
||||
root.get(1).set('id', 42);
|
||||
var record = root.get(42);
|
||||
equal(root.length, 1);
|
||||
equal(record.get('v'), 2, "should have fetched the original record");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,21 +9,6 @@ $(document).ready(function () {
|
|||
openerp.web.Foo2 = {};
|
||||
}
|
||||
});
|
||||
test('key fetch', function () {
|
||||
var reg = new openerp.web.Registry({
|
||||
foo: 'openerp.web.Foo',
|
||||
bar: 'openerp.web.Bar',
|
||||
quux: 'openerp.web.Quux'
|
||||
});
|
||||
|
||||
strictEqual(reg.get_object('foo'), openerp.web.Foo);
|
||||
raises(function () { reg.get_object('qux'); },
|
||||
openerp.web.KeyNotFound,
|
||||
"Unknown keys should raise KeyNotFound");
|
||||
raises(function () { reg.get_object('quux'); },
|
||||
openerp.web.ObjectNotFound,
|
||||
"Incorrect file paths should raise ObjectNotFound");
|
||||
});
|
||||
test('key set', function () {
|
||||
var reg = new openerp.web.Registry();
|
||||
|
||||
|
|
|
@ -1,37 +1,8 @@
|
|||
$(document).ready(function () {
|
||||
var xhr = QWeb2.Engine.prototype.get_xhr();
|
||||
xhr.open('GET', '/web/static/src/xml/base.xml', false);
|
||||
xhr.send(null);
|
||||
var doc = xhr.responseXML;
|
||||
|
||||
var noop = function () {};
|
||||
/**
|
||||
* Make connection RPC responses mockable by setting keys on the
|
||||
* Connection#responses object (key is the URL, value is the function to
|
||||
* call with the RPC request payload)
|
||||
*
|
||||
* @param {openerp.web.Connection} connection connection instance to mockify
|
||||
* @param {Object} [responses] url:function mapping to seed the mock connection
|
||||
*/
|
||||
var mockifyRPC = function (connection, responses) {
|
||||
connection.responses = responses || {};
|
||||
connection.rpc_function = function (url, payload) {
|
||||
if (!(url.url in this.responses)) {
|
||||
return $.Deferred().reject({}, 'failed', _.str.sprintf("Url %s not found in mock responses", url.url)).promise();
|
||||
}
|
||||
return $.when(this.responses[url.url](payload));
|
||||
};
|
||||
};
|
||||
|
||||
var instance;
|
||||
module('query', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
}
|
||||
});
|
||||
test('Adding a facet to the query creates a facet and a value', function () {
|
||||
|
@ -167,16 +138,11 @@ $(document).ready(function () {
|
|||
|
||||
module('defaults', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -404,18 +370,11 @@ $(document).ready(function () {
|
|||
|
||||
module('completions', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
// date complete
|
||||
window.openerp.web.formats(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('calling', 4, function () {
|
||||
|
@ -432,10 +391,7 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
view.appendTo($('#qunit-fixture'))
|
||||
.always(start)
|
||||
.fail(function (error) { ok(false, error.message); })
|
||||
.done(function () {
|
||||
stop();
|
||||
view.complete_global_search({term: "dum"}, function (completions) {
|
||||
start();
|
||||
equal(completions.length, 1, "should have a single completion");
|
||||
|
@ -454,7 +410,11 @@ $(document).ready(function () {
|
|||
var completion = {
|
||||
label: "Dummy",
|
||||
facet: {
|
||||
field: {get_domain: noop, get_context: noop, get_groupby: noop},
|
||||
field: {
|
||||
get_domain: openerp.testing.noop,
|
||||
get_context: openerp.testing.noop,
|
||||
get_groupby: openerp.testing.noop
|
||||
},
|
||||
category: 'Dummy',
|
||||
values: [{label: 'dummy', value: 42}]
|
||||
}
|
||||
|
@ -476,7 +436,11 @@ $(document).ready(function () {
|
|||
});
|
||||
});
|
||||
asyncTest('facet selection: new value existing facet', 3, function () {
|
||||
var field = {get_domain: noop, get_context: noop, get_groupby: noop};
|
||||
var field = {
|
||||
get_domain: openerp.testing.noop,
|
||||
get_context: openerp.testing.noop,
|
||||
get_groupby: openerp.testing.noop
|
||||
};
|
||||
var completion = {
|
||||
label: "Dummy",
|
||||
facet: {
|
||||
|
@ -663,16 +627,11 @@ $(document).ready(function () {
|
|||
|
||||
module('search-serialization', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('No facet, no call', 6, function () {
|
||||
|
@ -940,16 +899,11 @@ $(document).ready(function () {
|
|||
|
||||
module('removal', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('clear button', function () {
|
||||
|
@ -975,16 +929,11 @@ $(document).ready(function () {
|
|||
|
||||
module('drawer', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('is-drawn', 2, function () {
|
||||
|
@ -1003,16 +952,11 @@ $(document).ready(function () {
|
|||
|
||||
module('filters', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection, {
|
||||
openerp.testing.mockifyRPC(instance, {
|
||||
'/web/searchview/load': function () {
|
||||
// view with a single group of filters
|
||||
return {result: {fields_view: {
|
||||
|
@ -1117,17 +1061,11 @@ $(document).ready(function () {
|
|||
|
||||
module('saved_filters', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.formats(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('checkboxing', 6, function () {
|
||||
|
@ -1183,17 +1121,11 @@ $(document).ready(function () {
|
|||
|
||||
module('advanced', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.formats(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('single-advanced', 6, function () {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<script src="/web/static/lib/backbone/backbone.js" type="text/javascript"></script>
|
||||
|
||||
<!-- jquery -->
|
||||
<script src="/web/static/lib/jquery/jquery-1.7.2b1.js"></script>
|
||||
<script src="/web/static/lib/jquery/jquery-1.7.2.js"></script>
|
||||
<script src="/web/static/lib/jquery.ui/js/jquery-ui-1.8.17.custom.min.js"></script>
|
||||
<script src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
|
||||
|
||||
|
@ -38,6 +38,12 @@
|
|||
<script src="/web/static/src/js/search.js"></script>
|
||||
<script src="/web/static/src/js/view_form.js"></script>
|
||||
<script src="/web/static/src/js/view_list.js"></script>
|
||||
<script src="/web/static/src/js/view_list_editable.js"></script>
|
||||
|
||||
<script src="/web/static/test/testing.js"></script>
|
||||
<script type="text/javascript">
|
||||
QUnit.config.testTimeout = 500;
|
||||
</script>
|
||||
</head>
|
||||
<body id="oe" class="openerp">
|
||||
<h1 id="qunit-header">OpenERP web Test Suite</h1>
|
||||
|
@ -55,4 +61,5 @@
|
|||
<script type="text/javascript" src="/web/static/test/rpc.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/evals.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/search.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/list-editable.js"></script>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
// Test support structures and methods for OpenERP
|
||||
openerp.testing = (function () {
|
||||
var xhr = QWeb2.Engine.prototype.get_xhr();
|
||||
xhr.open('GET', '/web/static/src/xml/base.xml', false);
|
||||
xhr.send(null);
|
||||
var doc = xhr.responseXML;
|
||||
|
||||
var dependencies = {
|
||||
corelib: [],
|
||||
coresetup: ['corelib'],
|
||||
data: ['corelib', 'coresetup'],
|
||||
dates: [],
|
||||
formats: ['coresetup', 'dates'],
|
||||
chrome: ['corelib', 'coresetup'],
|
||||
views: ['corelib', 'coresetup', 'data', 'chrome'],
|
||||
search: ['data', 'coresetup', 'formats'],
|
||||
list: ['views', 'data'],
|
||||
form: ['data', 'views', 'list', 'formats'],
|
||||
list_editable: ['list', 'form', 'data'],
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Function which does not do anything
|
||||
*/
|
||||
noop: function () { },
|
||||
/**
|
||||
* Loads 'base.xml' template file into qweb for the provided instance
|
||||
*
|
||||
* @param instance openerp instance being initialized, to load the template file in
|
||||
*/
|
||||
loadTemplate: function (instance) {
|
||||
instance.web.qweb.add_template(doc);
|
||||
},
|
||||
/**
|
||||
* Alter provided instance's ``connection`` attribute to make response
|
||||
* mockable:
|
||||
*
|
||||
* * The ``responses`` parameter can be used to provide a map of (RPC)
|
||||
* paths (e.g. ``/web/view/load``) to a function returning a response
|
||||
* to the query.
|
||||
* * ``instance,connection`` grows a ``responses`` attribute which is
|
||||
* a map of the same (and is in fact initialized to the ``responses``
|
||||
* parameter if one is provided)
|
||||
*
|
||||
* Note that RPC requests to un-mocked URLs will be rejected with an
|
||||
* error message: only explicitly specified urls will get a response.
|
||||
*
|
||||
* Mocked connections will *never* perform an actual RPC connection.
|
||||
*
|
||||
* @param instance openerp instance being initialized
|
||||
* @param {Object} [responses]
|
||||
*/
|
||||
mockifyRPC: function (instance, responses) {
|
||||
var connection = instance.connection;
|
||||
connection.responses = responses || {};
|
||||
connection.rpc_function = function (url, payload) {
|
||||
var fn = this.responses[url.url + ':' + payload.params.method]
|
||||
|| this.responses[url.url];
|
||||
|
||||
if (!fn) {
|
||||
return $.Deferred().reject({}, 'failed',
|
||||
_.str.sprintf("Url %s not found in mock responses, with arguments %s",
|
||||
url.url, JSON.stringify(payload.params))
|
||||
).promise();
|
||||
}
|
||||
return $.when(fn(payload));
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Creates an openerp web instance loading the specified module after
|
||||
* all of its dependencies.
|
||||
*
|
||||
* @param {String} module
|
||||
* @returns OpenERP Web instance
|
||||
*/
|
||||
instanceFor: function (module) {
|
||||
var instance = openerp.init([]);
|
||||
this._load(instance, module);
|
||||
return instance;
|
||||
},
|
||||
_load: function (instance, module, loaded) {
|
||||
if (!loaded) { loaded = []; }
|
||||
|
||||
var deps = dependencies[module];
|
||||
if (!deps) { throw new Error("Unknown dependencies for " + module); }
|
||||
|
||||
var to_load = _.difference(deps, loaded);
|
||||
while (!_.isEmpty(to_load)) {
|
||||
this._load(instance, to_load[0], loaded);
|
||||
to_load = _.difference(deps, loaded);
|
||||
}
|
||||
openerp.web[module](instance);
|
||||
loaded.push(module);
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -8,135 +8,135 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-02-22 02:18+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-07-19 01:27+0000\n"
|
||||
"Last-Translator: Akira Hiyama <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15531)\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:12
|
||||
#: addons/web_calendar/static/src/js/calendar.js:11
|
||||
msgid "Calendar"
|
||||
msgstr "カレンダー"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:73
|
||||
#: addons/web_calendar/static/src/js/calendar.js:70
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:139
|
||||
msgid "Today"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:140
|
||||
msgid "Day"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:141
|
||||
msgid "Week"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:142
|
||||
msgid "Month"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:143
|
||||
msgid "New event"
|
||||
msgstr ""
|
||||
msgstr "フィルタ"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:144
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
msgid "Today"
|
||||
msgstr "本日"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:145
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
msgid "Day"
|
||||
msgstr "日"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:146
|
||||
msgid "Details"
|
||||
msgstr ""
|
||||
msgid "Week"
|
||||
msgstr "週"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:147
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgid "Month"
|
||||
msgstr "月"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:148
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
msgid "New event"
|
||||
msgstr "新規イベント"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:149
|
||||
msgid "Save"
|
||||
msgstr "保存"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:150
|
||||
msgid "Event will be deleted permanently, are you sure?"
|
||||
msgstr ""
|
||||
msgid "Cancel"
|
||||
msgstr "キャンセル"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:151
|
||||
#: addons/web_calendar/static/src/js/calendar.js:164
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
msgid "Details"
|
||||
msgstr "詳細"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:152
|
||||
msgid "Time period"
|
||||
msgstr ""
|
||||
msgid "Edit"
|
||||
msgstr "編集"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:153
|
||||
msgid "Full day"
|
||||
msgstr ""
|
||||
msgid "Delete"
|
||||
msgstr "削除"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:155
|
||||
msgid "Event will be deleted permanently, are you sure?"
|
||||
msgstr "イベントは完全に削除されます。よろしいですか?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:156
|
||||
msgid "Do you want to edit the whole set of repeated events?"
|
||||
msgstr ""
|
||||
#: addons/web_calendar/static/src/js/calendar.js:169
|
||||
msgid "Description"
|
||||
msgstr "詳細"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:157
|
||||
msgid "Repeat event"
|
||||
msgstr ""
|
||||
msgid "Time period"
|
||||
msgstr "期間"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:158
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
msgid "Full day"
|
||||
msgstr "終日"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:159
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
#: addons/web_calendar/static/src/js/calendar.js:161
|
||||
msgid "Do you want to edit the whole set of repeated events?"
|
||||
msgstr "繰り返しイベントのセット全体を編集しますか?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:162
|
||||
#: addons/web_calendar/static/src/js/calendar.js:170
|
||||
msgid "Agenda"
|
||||
msgstr ""
|
||||
msgid "Repeat event"
|
||||
msgstr "繰り返しイベント"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:163
|
||||
msgid "Date"
|
||||
msgstr ""
|
||||
msgid "Disabled"
|
||||
msgstr "無効"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:164
|
||||
msgid "Enabled"
|
||||
msgstr "有効"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:167
|
||||
msgid "Year"
|
||||
msgstr ""
|
||||
#: addons/web_calendar/static/src/js/calendar.js:175
|
||||
msgid "Agenda"
|
||||
msgstr "議事"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:8
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:9
|
||||
#: addons/web_calendar/static/src/js/calendar.js:168
|
||||
msgid "Date"
|
||||
msgstr "日付"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/js/calendar.js:172
|
||||
msgid "Year"
|
||||
msgstr "年"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:5
|
||||
#: addons/web_calendar/static/src/xml/web_calendar.xml:6
|
||||
msgid " "
|
||||
msgstr " "
|
||||
|
||||
|
|
|
@ -8,29 +8,29 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-03-26 22:14+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-07-19 01:29+0000\n"
|
||||
"Last-Translator: Akira Hiyama <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15531)\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:61
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:60
|
||||
msgid "Edit Layout"
|
||||
msgstr "レイアウトを編集"
|
||||
msgstr "レイアウトの編集"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:107
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:106
|
||||
msgid "Are you sure you want to remove this item ?"
|
||||
msgstr "この項目を取り除きますか?"
|
||||
msgstr "この項目を削除しますか?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
msgstr "レイアウトをリセット"
|
||||
msgstr "レイアウトのリセット"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6
|
||||
|
@ -40,12 +40,12 @@ msgstr "リセット"
|
|||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8
|
||||
msgid "Change Layout.."
|
||||
msgstr "レイアウトを変更…"
|
||||
msgstr "レイアウトの変更…"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10
|
||||
msgid "Change Layout"
|
||||
msgstr "レイアウトを変更"
|
||||
msgstr "レイアウトの変更"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27
|
||||
|
@ -60,7 +60,7 @@ msgstr "作成"
|
|||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39
|
||||
msgid "Choose dashboard layout"
|
||||
msgstr "ダッシュボードのレイアウトを選択"
|
||||
msgstr "ダッシュボードレイアウトの選択"
|
||||
|
||||
#, python-format
|
||||
#~ msgid "Execute task \"%s\""
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# Ukrainian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-07-22 09:32+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Ukrainian <uk@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-23 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 15654)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:60
|
||||
msgid "Edit Layout"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:106
|
||||
msgid "Are you sure you want to remove this item ?"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6
|
||||
msgid "Reset"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8
|
||||
msgid "Change Layout.."
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10
|
||||
msgid "Change Layout"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39
|
||||
msgid "Choose dashboard layout"
|
||||
msgstr ""
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-03-26 22:02+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-07-19 01:37+0000\n"
|
||||
"Last-Translator: Akira Hiyama <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15531)\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:11
|
||||
|
@ -25,7 +25,7 @@ msgstr "ダイアグラム"
|
|||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:165
|
||||
msgid "Are you sure?"
|
||||
msgstr ""
|
||||
msgstr "本当によろしいですか?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:195
|
||||
|
@ -35,6 +35,10 @@ msgid ""
|
|||
"\n"
|
||||
"Are you sure ?"
|
||||
msgstr ""
|
||||
"このノードの削除は元に戻すことができません。\n"
|
||||
"また、接続している全ての取引を削除します。\n"
|
||||
"\n"
|
||||
"本当によろしいですか?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:213
|
||||
|
@ -43,18 +47,21 @@ msgid ""
|
|||
"\n"
|
||||
"Are you sure ?"
|
||||
msgstr ""
|
||||
"この取引の削除は元に戻すことができません。\n"
|
||||
"\n"
|
||||
"本当によろしいですか?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:224
|
||||
#: addons/web_diagram/static/src/js/diagram.js:257
|
||||
msgid "Activity"
|
||||
msgstr "アクティビティ"
|
||||
msgstr "活動"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:232
|
||||
#: addons/web_diagram/static/src/js/diagram.js:296
|
||||
msgid "Open: "
|
||||
msgstr "開く: "
|
||||
msgstr "開く: "
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/js/diagram.js:262
|
||||
|
@ -66,9 +73,9 @@ msgstr "作成:"
|
|||
#: addons/web_diagram/static/src/js/diagram.js:289
|
||||
#: addons/web_diagram/static/src/js/diagram.js:308
|
||||
msgid "Transition"
|
||||
msgstr "変遷"
|
||||
msgstr "遷移"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_diagram/static/src/xml/base_diagram.xml:6
|
||||
msgid "New Node"
|
||||
msgstr "新しいノード"
|
||||
msgstr "新規ノード"
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# Hungarian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-07-19 06:30+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Hungarian <hu@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_gantt/static/src/js/gantt.js:11
|
||||
msgid "Gantt"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_gantt/static/src/xml/web_gantt.xml:10
|
||||
msgid "Create"
|
||||
msgstr "Létrehozás"
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-03-26 21:57+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-07-19 01:37+0000\n"
|
||||
"Last-Translator: Akira Hiyama <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15531)\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_gantt/static/src/js/gantt.js:11
|
||||
|
@ -25,4 +25,4 @@ msgstr "ガント"
|
|||
#. openerp-web
|
||||
#: addons/web_gantt/static/src/xml/web_gantt.xml:10
|
||||
msgid "Create"
|
||||
msgstr "作成する"
|
||||
msgstr "作成"
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
# Hungarian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-07-19 06:27+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Hungarian <hu@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/js/graph.js:19
|
||||
msgid "Graph"
|
||||
msgstr "Grafikon"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:5
|
||||
msgid "Graph Options"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:7
|
||||
msgid "Graph Mode"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:11
|
||||
msgid "Pie"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:12
|
||||
msgid "Bars"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:14
|
||||
msgid "Lines"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:15
|
||||
msgid "Areas"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:18
|
||||
msgid "Radar"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:20
|
||||
msgid "Legend"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:24
|
||||
msgid "Hidden"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:25
|
||||
msgid "Inside"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:26
|
||||
msgid "Top"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:28
|
||||
msgid "Actions"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:32
|
||||
msgid "Switch Axis"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:33
|
||||
msgid "Show Data"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:34
|
||||
msgid "Download as PNG"
|
||||
msgstr ""
|
|
@ -8,91 +8,91 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-02-22 02:21+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-07-19 03:25+0000\n"
|
||||
"Last-Translator: Akira Hiyama <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15531)\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/js/graph.js:22
|
||||
#: addons/web_graph/static/src/js/graph.js:19
|
||||
msgid "Graph"
|
||||
msgstr "グラフ"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:5
|
||||
msgid "Graph Options"
|
||||
msgstr ""
|
||||
msgstr "グラフオプション"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:7
|
||||
msgid "Graph Mode"
|
||||
msgstr ""
|
||||
msgstr "グラフモード"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:11
|
||||
msgid "Pie"
|
||||
msgstr ""
|
||||
msgstr "パイ"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:12
|
||||
msgid "Bars"
|
||||
msgstr ""
|
||||
msgstr "バー"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:14
|
||||
msgid "Lines"
|
||||
msgstr ""
|
||||
msgstr "ライン"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:15
|
||||
msgid "Areas"
|
||||
msgstr ""
|
||||
msgstr "エリア"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:18
|
||||
msgid "Radar"
|
||||
msgstr ""
|
||||
msgstr "レーダー"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:20
|
||||
msgid "Legend"
|
||||
msgstr ""
|
||||
msgstr "凡例"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:24
|
||||
msgid "Hidden"
|
||||
msgstr ""
|
||||
msgstr "非表示"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:25
|
||||
msgid "Inside"
|
||||
msgstr ""
|
||||
msgstr "内側"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:26
|
||||
msgid "Top"
|
||||
msgstr ""
|
||||
msgstr "上"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:28
|
||||
msgid "Actions"
|
||||
msgstr ""
|
||||
msgstr "アクション"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:32
|
||||
msgid "Switch Axis"
|
||||
msgstr ""
|
||||
msgstr "Axisに切替"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:33
|
||||
msgid "Show Data"
|
||||
msgstr ""
|
||||
msgstr "データの表示"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_graph/static/src/xml/web_graph.xml:34
|
||||
msgid "Download as PNG"
|
||||
msgstr ""
|
||||
msgstr "PNGとしてダウンロード"
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
# Hungarian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-07-19 06:26+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Hungarian <hu@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/js/kanban.js:10
|
||||
msgid "Kanban"
|
||||
msgstr "Kanban"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/js/kanban.js:293
|
||||
msgid "Undefined"
|
||||
msgstr "Nem definiált"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/js/kanban.js:468
|
||||
msgid "Are you sure you want to delete this record ?"
|
||||
msgstr "Biztosan törölni szeretné ezt a bejegyzést?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/js/kanban.js:839
|
||||
msgid "Create: "
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:41
|
||||
msgid "Show more... ("
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:41
|
||||
msgid "remaining)"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:71
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:71
|
||||
msgid "or"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:72
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid "Create"
|
||||
#~ msgstr "Létrehozás"
|
|
@ -8,59 +8,59 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-03-06 06:35+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-07-19 03:34+0000\n"
|
||||
"Last-Translator: Akira Hiyama <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15531)\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/js/kanban.js:10
|
||||
msgid "Kanban"
|
||||
msgstr "看板"
|
||||
msgstr "かんばん"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/js/kanban.js:372
|
||||
#: addons/web_kanban/static/src/js/kanban.js:293
|
||||
msgid "Undefined"
|
||||
msgstr "未定義"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/js/kanban.js:684
|
||||
#: addons/web_kanban/static/src/js/kanban.js:468
|
||||
msgid "Are you sure you want to delete this record ?"
|
||||
msgstr "このレコードを削除しますか?"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/js/kanban.js:839
|
||||
msgid "Create: "
|
||||
msgstr ""
|
||||
msgstr "作成: "
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:53
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:41
|
||||
msgid "Show more... ("
|
||||
msgstr "もっと表示する…("
|
||||
msgstr "さらに表示…("
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:53
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:41
|
||||
msgid "remaining)"
|
||||
msgstr "残り)"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:71
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
msgstr "追加"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:71
|
||||
msgid "or"
|
||||
msgstr ""
|
||||
msgstr "または"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_kanban/static/src/xml/web_kanban.xml:72
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
msgstr "キャンセル"
|
||||
|
||||
#~ msgid "</tr><tr>"
|
||||
#~ msgstr "</tr><tr>"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
@charset "utf-8";
|
||||
.openerp .oe_kanban_view {
|
||||
background: url(data:image/pngbase64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAKElEQVQIHWP8DwTv379nAAFBQUEGhnfv3oHEwADEZgJLIRGMIClkLQCr3x2Htp/lLwAAAABJRU5ErkJggg==);
|
||||
background: url(/web/static/src/img/form_sheetbg.png);
|
||||
height: inherit;
|
||||
}
|
||||
.openerp .oe_kanban_view .ui-sortable-placeholder {
|
||||
|
@ -239,6 +238,9 @@
|
|||
.openerp .oe_kanban_view .oe_kanban_grouped .oe_kanban_show_more .oe_button {
|
||||
width: 100%;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_ungrouped {
|
||||
background: white;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_ungrouped .oe_kanban_record {
|
||||
float: left;
|
||||
padding: 2px;
|
||||
|
@ -399,17 +401,23 @@
|
|||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_project_times li {
|
||||
float: left;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_status {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
display: inline-block;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
margin: 4px auto;
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
|
||||
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
|
||||
-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
|
||||
background-position: center center;
|
||||
background-image: -webkit-radial-gradient(circle, #eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
|
||||
background-image: -moz-radial-gradient(#eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
|
||||
background-image: -ms-radial-gradient(#eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
|
||||
background-image: radial-gradient(#eeeeee 0%, #cccccc 40%, #bbbbbb 100%);
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_status_green {
|
||||
background: green;
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
|
||||
.openerp .oe_kanban_view
|
||||
// KanbanView {{{
|
||||
background: url(data:image/pngbase64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAKElEQVQIHWP8DwTv379nAAFBQUEGhnfv3oHEwADEZgJLIRGMIClkLQCr3x2Htp/lLwAAAABJRU5ErkJggg==)
|
||||
//background: url(data:image/pngbase64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAKElEQVQIHWP8DwTv379nAAFBQUEGhnfv3oHEwADEZgJLIRGMIClkLQCr3x2Htp/lLwAAAABJRU5ErkJggg==)
|
||||
background: url(/web/static/src/img/form_sheetbg.png)
|
||||
height: inherit
|
||||
.ui-sortable-placeholder
|
||||
border: 1px dotted black
|
||||
|
@ -214,12 +215,14 @@
|
|||
text-align: center
|
||||
.oe_kanban_grouped .oe_kanban_show_more .oe_button
|
||||
width: 100%
|
||||
.oe_kanban_ungrouped .oe_kanban_record
|
||||
float: left
|
||||
padding: 2px
|
||||
box-sizing: border-box
|
||||
-moz-box-sizing: border-box
|
||||
-webkit-box-sizing: border-box
|
||||
.oe_kanban_ungrouped
|
||||
background: white
|
||||
.oe_kanban_record
|
||||
float: left
|
||||
padding: 2px
|
||||
box-sizing: border-box
|
||||
-moz-box-sizing: border-box
|
||||
-webkit-box-sizing: border-box
|
||||
.oe_kanban_action_button
|
||||
height: 22px
|
||||
margin: 0
|
||||
|
@ -333,14 +336,18 @@
|
|||
float: right
|
||||
position: relative
|
||||
top: 2px
|
||||
|
||||
.oe_kanban_project_times li
|
||||
float: left
|
||||
|
||||
.oe_kanban_status
|
||||
position: relative
|
||||
top: 4px
|
||||
display: inline-block
|
||||
height: 12px
|
||||
width: 12px
|
||||
margin: 4px auto
|
||||
@include radius(6px)
|
||||
@include box-shadow(0 1px 3px rgba(0,0,0,0.6))
|
||||
@include radial-gradient((#eee 0%, #ccc 40%, #bbb 100%))
|
||||
.oe_kanban_status_green
|
||||
background: green
|
||||
@include radial-gradient((#55dd55 0%, #44aa44 40%, #339933 100%))
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-03-31 18:42+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-07-19 04:00+0000\n"
|
||||
"Last-Translator: Akira Hiyama <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15531)\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_mobile/static/src/xml/web_mobile.xml:17
|
||||
|
@ -25,7 +25,7 @@ msgstr "OpenERP"
|
|||
#. openerp-web
|
||||
#: addons/web_mobile/static/src/xml/web_mobile.xml:22
|
||||
msgid "Database:"
|
||||
msgstr "データベース:"
|
||||
msgstr "データベース:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_mobile/static/src/xml/web_mobile.xml:30
|
||||
|
@ -65,7 +65,7 @@ msgstr "お気に入り"
|
|||
#. openerp-web
|
||||
#: addons/web_mobile/static/src/xml/web_mobile.xml:58
|
||||
msgid "Preference"
|
||||
msgstr "優先"
|
||||
msgstr "プリファレンス"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_mobile/static/src/xml/web_mobile.xml:123
|
||||
|
@ -75,7 +75,7 @@ msgstr "ログアウト"
|
|||
#. openerp-web
|
||||
#: addons/web_mobile/static/src/xml/web_mobile.xml:132
|
||||
msgid "There are no records to show."
|
||||
msgstr "表示するレコードはありません"
|
||||
msgstr "表示するレコードはありません。"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_mobile/static/src/xml/web_mobile.xml:183
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
# Hungarian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-07-19 06:28+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Hungarian <hu@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/js/process.js:261
|
||||
msgid "Cancel"
|
||||
msgstr "Mégsem"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/js/process.js:262
|
||||
msgid "Save"
|
||||
msgstr "Mentés"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:6
|
||||
msgid "Process View"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:19
|
||||
msgid "Documentation"
|
||||
msgstr "Dokumentáció"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:19
|
||||
msgid "Read Documentation Online"
|
||||
msgstr "Olvassa a dokumentációt online"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:25
|
||||
msgid "Forum"
|
||||
msgstr "Fórum"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:25
|
||||
msgid "Community Discussion"
|
||||
msgstr "Közösségi vitafórum"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:31
|
||||
msgid "Books"
|
||||
msgstr "Könyvek"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:31
|
||||
msgid "Get the books"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:37
|
||||
msgid "OpenERP Enterprise"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:37
|
||||
msgid "Purchase OpenERP Enterprise"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:52
|
||||
msgid "Process"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:56
|
||||
msgid "Notes:"
|
||||
msgstr "Megjegyzések:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:59
|
||||
msgid "Last modified by:"
|
||||
msgstr "Utoljára módosította:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:59
|
||||
msgid "N/A"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:62
|
||||
msgid "Subflows:"
|
||||
msgstr "Alfolyamatok:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:75
|
||||
msgid "Related:"
|
||||
msgstr "Kapcsolódó:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:88
|
||||
msgid "Select Process"
|
||||
msgstr "Folyamat kiválasztása"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:98
|
||||
msgid "Select"
|
||||
msgstr "Kiválaszt"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:109
|
||||
msgid "Edit Process"
|
||||
msgstr ""
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-03-26 22:04+0000\n"
|
||||
"Last-Translator: Masaki Yamaya <Unknown>\n"
|
||||
"PO-Revision-Date: 2012-07-19 04:29+0000\n"
|
||||
"Last-Translator: Akira Hiyama <Unknown>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15531)\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-20 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 15644)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/js/process.js:261
|
||||
|
@ -30,7 +30,7 @@ msgstr "保存"
|
|||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:6
|
||||
msgid "Process View"
|
||||
msgstr "プロセス一覧"
|
||||
msgstr "プロセスビュー"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:19
|
||||
|
@ -40,7 +40,7 @@ msgstr "ドキュメンテーション"
|
|||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:19
|
||||
msgid "Read Documentation Online"
|
||||
msgstr "オンラインのドキュメンテーションを読んでください。"
|
||||
msgstr "オンラインのドキュメントを読んで下さい"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:25
|
||||
|
@ -55,12 +55,12 @@ msgstr "コミュニティの議論"
|
|||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:31
|
||||
msgid "Books"
|
||||
msgstr "帳簿"
|
||||
msgstr "本"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:31
|
||||
msgid "Get the books"
|
||||
msgstr "帳簿を取る"
|
||||
msgstr "本を手に入れる"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:37
|
||||
|
@ -85,7 +85,7 @@ msgstr "注記"
|
|||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:59
|
||||
msgid "Last modified by:"
|
||||
msgstr "最後に変更:"
|
||||
msgstr "最終更新者:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:59
|
||||
|
@ -105,14 +105,14 @@ msgstr "関係:"
|
|||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:88
|
||||
msgid "Select Process"
|
||||
msgstr "プロセスを選んでください"
|
||||
msgstr "プロセスの選択"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:98
|
||||
msgid "Select"
|
||||
msgstr "選択する"
|
||||
msgstr "選択"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:109
|
||||
msgid "Edit Process"
|
||||
msgstr "プロセスを編集"
|
||||
msgstr "プロセスの編集"
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
Notes on the usage of the Form View as a sub-widget
|
||||
===================================================
|
||||
|
||||
Undocumented stuff
|
||||
------------------
|
||||
|
||||
* ``initial_mode`` *option* defines the starting mode of the form
|
||||
view, one of ``view`` and ``edit`` (?). Default value is ``view``
|
||||
(non-editable form).
|
||||
|
||||
* ``embedded_view`` *attribute* has to be set separately when
|
||||
providing a view directly, no option available for that usage.
|
||||
|
||||
* View arch **must** contain node with
|
||||
``@class="oe_form_container"``, otherwise everything will break
|
||||
without any info
|
||||
|
||||
* Root element of view arch not being ``form`` may or may not work
|
||||
correctly, no idea.
|
||||
|
||||
* Freeform views => ``@version="7.0"``
|
||||
|
||||
* Form is not entirely loaded (some widgets may not appear) unless
|
||||
``on_record_loaded`` is called (or ``do_show``, which itself calls
|
||||
``on_record_loaded``).
|
||||
|
||||
* "Empty" form => ``on_button_new`` (...), or manually call
|
||||
``default_get`` + ``on_record_loaded``
|
||||
|
||||
* Form fields default to width: 100%, padding, !important margin, can
|
||||
be reached via ``.oe_form_field``
|
||||
|
||||
* Form *will* render buttons and a pager, offers options to locate
|
||||
both outside of form itself (``$buttons`` and ``$pager``), providing
|
||||
empty jquery objects (``$()``) seems to stop displaying both but not
|
||||
sure if there are deleterious side-effects.
|
||||
|
||||
Other options:
|
||||
|
||||
* Pass in ``$(document.createDocumentFragment)`` to ensure it's a
|
||||
DOM-compatible tree completely outside of the actual DOM.
|
||||
|
||||
* ???
|
||||
|
||||
* readonly fields probably don't have a background, beware if need of
|
||||
overlay
|
||||
|
||||
* What is the difference between ``readonly`` and
|
||||
``effective_readonly``?
|
|
@ -18,6 +18,9 @@ Contents:
|
|||
|
||||
search-view
|
||||
|
||||
list-view
|
||||
form-notes
|
||||
|
||||
Older stuff
|
||||
-----------
|
||||
|
||||
|
|
|
@ -0,0 +1,455 @@
|
|||
List View
|
||||
=========
|
||||
|
||||
Style Hooks
|
||||
-----------
|
||||
|
||||
The list view provides a few style hook classes for re-styling of list views in
|
||||
various situations:
|
||||
|
||||
``.oe_list``
|
||||
|
||||
The root element of the list view, styling rules should be rooted
|
||||
on that class.
|
||||
|
||||
``table.oe_list_content``
|
||||
|
||||
The root table for the listview, accessory components may be
|
||||
generated or added outside this section, this is the list view
|
||||
"proper".
|
||||
|
||||
``.oe_list_buttons``
|
||||
|
||||
The action buttons array for the list view, with its sub-elements
|
||||
|
||||
``.oe_list_add``
|
||||
|
||||
The default "Create"/"Add" button of the list view
|
||||
|
||||
``.oe_alternative``
|
||||
|
||||
The "alternative choice" for the list view, by default text
|
||||
along the lines of "or import" with a link.
|
||||
|
||||
``.oe_list_field_cell``
|
||||
|
||||
The cell (``td``) for a given field of the list view, cells which
|
||||
are *not* fields (e.g. name of a group, or number of items in a
|
||||
group) will not have this class. The field cell can be further
|
||||
specified:
|
||||
|
||||
``.oe_number``
|
||||
|
||||
Numeric cell types (integer and float)
|
||||
|
||||
``.oe_button``
|
||||
|
||||
Action button (``button`` tag in the view) inside the cell
|
||||
|
||||
``.oe_readonly``
|
||||
|
||||
Readonly field cell
|
||||
|
||||
``.oe_list_field_$type``
|
||||
|
||||
Additional class for the precise type of the cell, ``$type``
|
||||
is the field's @widget if there is one, otherwise it's the
|
||||
field's type.
|
||||
|
||||
``.oe_list_record_selector``
|
||||
|
||||
Selector cells
|
||||
|
||||
Editable list view
|
||||
++++++++++++++++++
|
||||
|
||||
The editable list view module adds a few supplementary style hook
|
||||
classes, for edition situations:
|
||||
|
||||
``.oe_editing``
|
||||
|
||||
Added to both ``.oe_list`` and ``.oe_list_button`` (as the
|
||||
buttons may be outside of the list view) when a row of the list is
|
||||
currently being edited.
|
||||
|
||||
``tr.oe_edition``
|
||||
|
||||
Class set on the row being edited itself. Note that the edition
|
||||
form is *not* contained within the row, this allows for styling or
|
||||
modifying the row while it's being edited separately. Mostly for
|
||||
fields which can not be edited (e.g. read-only fields).
|
||||
|
||||
|
||||
Editable list view
|
||||
------------------
|
||||
|
||||
List view edition is an extension to the base listview providing the
|
||||
capability of inline record edition by delegating to an embedded form
|
||||
view.
|
||||
|
||||
.. todo::
|
||||
|
||||
cleanup options and settings for editability configuration. Right
|
||||
now there are:
|
||||
|
||||
``defaults.editable``
|
||||
|
||||
``null``, ``"top"`` or ``"bottom"``, generally broken and
|
||||
useless
|
||||
|
||||
``context.set_editable``
|
||||
|
||||
forces ``options.editable`` to ``"bottom"``
|
||||
|
||||
``view.arch.attrs.editable``
|
||||
|
||||
same as ``defaults.editable``, but applied separately (after
|
||||
reloading the view), if absent delegates to
|
||||
``options.editable`` which may have been set previously.
|
||||
|
||||
``options.read_only``
|
||||
|
||||
force options.editable to false, or something?
|
||||
|
||||
.. note:: can probably be replaced by cancelling ``edit:before``
|
||||
|
||||
and :js:func:`~openerp.web.ListView.set_editable` which
|
||||
ultimately behaves weird-as-fuck-ly.
|
||||
|
||||
The editable list view module adds a number of methods to the list
|
||||
view, on top of implementing the :js:class:`EditorDelegate` protocol:
|
||||
|
||||
Interaction Methods
|
||||
+++++++++++++++++++
|
||||
|
||||
.. js:function:: openerp.web.ListView.ensure_saved
|
||||
|
||||
Attempts to resolve the pending edition, if any, by saving the
|
||||
edited row's current state.
|
||||
|
||||
:returns: delegate resolving to all editions having been saved, or
|
||||
rejected if a pending edition could not be saved
|
||||
(e.g. validation failure)
|
||||
|
||||
.. js:function:: openerp.web.ListView.start_edition([record][, options])
|
||||
|
||||
Starts editing the provided record inline, through an overlay form
|
||||
view of editable fields in the record.
|
||||
|
||||
If no record is provided, creates a new one according to the
|
||||
editability configuration of the list view.
|
||||
|
||||
This method resolves any pending edition when invoked, before
|
||||
starting a new edition.
|
||||
|
||||
:param record: record to edit, or null to create a new record
|
||||
:type record: :js:class:`~openerp.web.list.Record`
|
||||
:param EditOptions options:
|
||||
:returns: delegate to the form used for the edition
|
||||
|
||||
.. js:function:: openerp.web.ListView.save_edition
|
||||
|
||||
Resolves the pending edition.
|
||||
|
||||
:returns: delegate to the save being completed, resolves to an
|
||||
object with two attributes ``created`` (flag indicating
|
||||
whether the saved record was just created or was
|
||||
updated) and ``record`` the reloaded record having been
|
||||
edited.
|
||||
|
||||
.. js:function:: openerp.web.ListView.cancel_edition
|
||||
|
||||
Cancels pending edition, cleans up the list view in case of
|
||||
creation (removes the empty record being created).
|
||||
|
||||
Utility Methods
|
||||
+++++++++++++++
|
||||
|
||||
.. js:function:: openerp.web.ListView.get_cells_for(row)
|
||||
|
||||
Extracts the cells from a listview row, and puts them in a
|
||||
{fieldname: cell} mapping for analysis and manipulation.
|
||||
|
||||
:param jQuery row:
|
||||
:rtype: Object
|
||||
|
||||
.. js:function:: openerp.web.ListView.with_event(event_name, event, action[, args][, trigger_params])
|
||||
|
||||
Executes ``action`` in the context of the view's editor,
|
||||
bracketing it with cancellable event signals.
|
||||
|
||||
:param String event_name: base name for the bracketing event, will
|
||||
be postfixed by ``:before`` and
|
||||
``:after`` before being called
|
||||
(respectively before and after
|
||||
``action`` is executed)
|
||||
:param Object event: object passed to the ``:before`` event
|
||||
handlers.
|
||||
:param Function action: function called with the view's editor as
|
||||
its ``this``. May return a deferred.
|
||||
:param Array args: arguments passed to ``action``
|
||||
:param Array trigger_params: arguments passed to the ``:after``
|
||||
event handler alongside the results
|
||||
of ``action``
|
||||
|
||||
Behavioral Customizations
|
||||
+++++++++++++++++++++++++
|
||||
|
||||
.. js:function:: openerp.web.ListView.handle_onwrite(record)
|
||||
|
||||
Implements the handling of the ``onwrite`` listview attribute:
|
||||
calls the RPC methods specified by ``@onwrite``, and if that
|
||||
method returns an array of ids loads or reloads the records
|
||||
corresponding to those ids.
|
||||
|
||||
:param record: record being written having triggered the
|
||||
``onwrite`` callback
|
||||
:type record: openerp.web.list.Record
|
||||
:returns: deferred to all reloadings being done
|
||||
|
||||
Events
|
||||
++++++
|
||||
|
||||
For simpler interactions by/with external users of the listview, the
|
||||
view provides a number of dedicated events to its lifecycle.
|
||||
|
||||
.. note:: if an event is defined as *cancellable*, it means its first
|
||||
parameter is an object on which the ``cancel`` attribute can
|
||||
be set. If the ``cancel`` attribute is set, the view will
|
||||
abort its current behavior as soon as possible, and rollback
|
||||
any state modification.
|
||||
|
||||
``edit:before`` *cancellable*
|
||||
|
||||
Invoked before the list view starts editing a record.
|
||||
|
||||
Provided with an event object with a single property ``record``,
|
||||
holding the attributes of the record being edited (``record`` is
|
||||
empty *but not null* for a new record)
|
||||
|
||||
``edit:after``
|
||||
|
||||
Invoked after the list view has gone into an edition state,
|
||||
provided with the attributes of the record being edited (see
|
||||
``edit:before``) as first parameter and the form used for the
|
||||
edition as second parameter.
|
||||
|
||||
``save:before`` *cancellable*
|
||||
|
||||
Invoked right before saving a pending edition, provided with an
|
||||
event object holding the listview's editor (``editor``) and the
|
||||
edition form (``form``)
|
||||
|
||||
``save:after``
|
||||
|
||||
Invoked after a save has been completed
|
||||
|
||||
``cancel:before`` *cancellable*
|
||||
|
||||
Invoked before cancelling a pending edition, provided with the
|
||||
same information as ``save:before``.
|
||||
|
||||
``cancel:after``
|
||||
|
||||
Invoked after a pending edition has been cancelled.
|
||||
|
||||
DOM events
|
||||
++++++++++
|
||||
|
||||
The list view has grown hooks for the ``keyup`` event on its edition
|
||||
form (during edition): any such event bubbling out of the edition form
|
||||
will be forwarded to a method ``keyup_EVENTNAME``, where ``EVENTNAME``
|
||||
is the name of the key in ``$.ui.keyCode``.
|
||||
|
||||
The method will also get the event object (originally passed to the
|
||||
``keyup`` handler) as its sole parameter.
|
||||
|
||||
The base editable list view has handlers for the ``ENTER`` and
|
||||
``ESCAPE`` keys.
|
||||
|
||||
Editor
|
||||
------
|
||||
|
||||
The list-edition modules does not generally interact with the embedded
|
||||
formview, delegating instead to its
|
||||
:js:class:`~openerp.web.list.Editor`.
|
||||
|
||||
.. js:class:: openerp.web.list.Editor(parent[, options])
|
||||
|
||||
The editor object provides a more convenient interface to form
|
||||
views, and simplifies the usage of form views for semi-arbitrary
|
||||
edition of stuff.
|
||||
|
||||
However, the editor does *not* task itself with being internally
|
||||
consistent at this point: calling
|
||||
e.g. :js:func:`~openerp.web.list.Editor.edit` multiple times in a
|
||||
row without saving or cancelling each edit is undefined.
|
||||
|
||||
:param parent:
|
||||
:type parent: :js:class:`~openerp.web.Widget`
|
||||
:param EditorOptions options:
|
||||
|
||||
.. js:function:: openerp.web.list.Editor.is_editing([record_state])
|
||||
|
||||
Indicates whether the editor is currently in the process of
|
||||
providing edition for a record.
|
||||
|
||||
Can be filtered by the state of the record being edited
|
||||
(whether it's a record being *created* or a record being
|
||||
*altered*), in which case it asserts both that an edition is
|
||||
underway and that the record being edited respectively does
|
||||
not yet exist in the database or already exists there.
|
||||
|
||||
:param record_state: state of the record being edited.
|
||||
Either ``"new"`` or ``"edit"``.
|
||||
:type record_state: String
|
||||
:rtype: Boolean
|
||||
|
||||
.. js:function:: openerp.web.list.Editor.edit(record, configureField[, options])
|
||||
|
||||
Loads the provided record into the internal form view and
|
||||
displays the form view.
|
||||
|
||||
Will also attempt to focus the first visible field of the form
|
||||
view.
|
||||
|
||||
:param Object record: record to load into the form view
|
||||
(key:value mapping similar to the result
|
||||
of a ``read``)
|
||||
:param configureField: function called with each field of the
|
||||
form view right after the form is
|
||||
displayed, lets whoever called this
|
||||
method do some last-minute
|
||||
configuration of form fields.
|
||||
:type configureField: Function<String, openerp.web.form.Field>
|
||||
:param EditOptions options:
|
||||
:returns: jQuery delegate to the form object
|
||||
|
||||
.. js:function:: openerp.web.list.Editor.save
|
||||
|
||||
Attempts to save the internal form, then hide it
|
||||
|
||||
:returns: delegate to the record under edition (with ``id``
|
||||
added for a creation). The record is not updated
|
||||
from when it was passed in, aside from the ``id``
|
||||
attribute.
|
||||
|
||||
.. js:function:: openerp.web.list.Editor.cancel
|
||||
|
||||
Attemps to cancel the edition of the internal form, then hide
|
||||
the form
|
||||
|
||||
:returns: delegate to the record under edition
|
||||
|
||||
.. js:class:: EditorOptions
|
||||
|
||||
.. js:attribute:: EditorOptions.formView
|
||||
|
||||
Form view (sub)-class to instantiate and delegate edition to.
|
||||
|
||||
By default, :js:class:`~openerp.web.FormView`
|
||||
|
||||
.. js:attribute:: EditorOptions.delegate
|
||||
|
||||
Object used to get various bits of information about how to
|
||||
display stuff.
|
||||
|
||||
By default, uses the editor's parent widget. See
|
||||
:js:class:`EditorDelegate` for the methods and attributes to
|
||||
provide.
|
||||
|
||||
.. js:class:: EditorDelegate
|
||||
|
||||
Informal protocol defining the methods and attributes expected of
|
||||
the :js:class:`~openerp.web.list.Editor`'s delegate.
|
||||
|
||||
.. js:attribute:: EditorDelegate.dataset
|
||||
|
||||
The dataset passed to the form view to synchronize the form
|
||||
view and the outer widget.
|
||||
|
||||
.. js:function:: EditorDelegate.edition_view(editor)
|
||||
|
||||
Called by the :js:class:`~openerp.web.list.Editor` object to
|
||||
get a form view (JSON) to pass along to the form view it
|
||||
created.
|
||||
|
||||
The result should be a valid form view, see :doc:`Form Notes
|
||||
<form-notes>` for various peculiarities of the form view
|
||||
format.
|
||||
|
||||
:param editor: editor object asking for the view
|
||||
:type editor: :js:class:`~openerp.web.list.Editor`
|
||||
:returns: form view
|
||||
:rtype: Object
|
||||
|
||||
.. js:function:: EditorDelegate.prepends_on_create
|
||||
|
||||
By default, the :js:class:`~openerp.web.list.Editor` will
|
||||
append the ids of newly created records to the
|
||||
:js:attr:`EditorDelegate.dataset`. If this method returns
|
||||
``true``, it will prepend these ids instead.
|
||||
|
||||
:returns: whether new records should be prepended to the
|
||||
dataset (instead of appended)
|
||||
:rtype: Boolean
|
||||
|
||||
|
||||
.. js:class:: EditOptions
|
||||
|
||||
Options object optionally passed into a method starting an edition
|
||||
to configure its setup and behavior
|
||||
|
||||
.. js:attribute:: focus_field
|
||||
|
||||
Name of the field to set focus on after setting up the edition
|
||||
of the record.
|
||||
|
||||
If this option is not provided, or the requested field can not
|
||||
be focused (invisible, readonly or not in the view), the first
|
||||
visible non-readonly field is focused.
|
||||
|
||||
Changes from 6.1
|
||||
----------------
|
||||
|
||||
* The editable listview behavior has been rewritten pretty much from
|
||||
scratch, any code touching on editability will have to be modified
|
||||
|
||||
* The overloading of :js:class:`~openerp.web.ListView.Groups` and
|
||||
:js:class:`~openerp.web.ListView.List` for editability has been
|
||||
drastically simplified, and most of the behavior has been moved to
|
||||
the list view itself. Only
|
||||
:js:func:`~openerp.web.ListView.List.row_clicked` is still
|
||||
overridden.
|
||||
|
||||
* A new method ``get_row_for(record) -> jQuery(tr) | null`` has been
|
||||
added to both ListView.List and ListView.Group, it can be called
|
||||
from the list view to get the table row matching a record (if such
|
||||
a row exists).
|
||||
|
||||
* :js:func:`~openerp.web.ListView.do_button_action`'s core behavior
|
||||
has been split away to
|
||||
:js:func:`~openerp.web.ListView.handle_button`. This allows bypassing
|
||||
overrides of :js:func:`~openerp.web.ListView.do_button_action` in a
|
||||
parent class.
|
||||
|
||||
Ideally, :js:func:`~openerp.web.ListView.handle_button` should not be
|
||||
overridden.
|
||||
|
||||
* Modifiers handling has been improved (all modifiers information
|
||||
should now be available through :js:func:`~Column.modifiers_for`,
|
||||
not just ``invisible``)
|
||||
|
||||
* Changed some handling of the list view's record: a record may now
|
||||
have no id, and the listview will handle that correctly (for new
|
||||
records being created) as well as correctly handle the ``id`` being
|
||||
set.
|
||||
|
||||
* Extended the internal collections structure of the list view with
|
||||
`#find`_, `#succ`_ and `#pred`_.
|
||||
|
||||
.. _#find: http://underscorejs.org/#find
|
||||
|
||||
.. _#succ: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:succ
|
||||
|
||||
.. _#pred: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:pred
|
Loading…
Reference in New Issue