[MERGE] trunk

bzr revid: al@openerp.com-20120725074053-e3gltrdfzm8bb7j8
This commit is contained in:
Antony Lesuisse 2012-07-25 09:40:53 +02:00
commit 1d766a2bc6
47 changed files with 6187 additions and 1415 deletions

View File

@ -20,6 +20,7 @@
"static/lib/jquery.form/jquery.form.js",
"static/lib/jquery.validate/jquery.validate.js",
"static/lib/jquery.ba-bbq/jquery.ba-bbq.js",
"static/lib/spinjs/spin.js",
"static/lib/jquery.blockUI/jquery.blockUI.js",
"static/lib/jquery.ui/js/jquery-ui-1.8.17.custom.min.js",
"static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js",
@ -33,6 +34,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 +62,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",

View File

@ -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 = werkzeug.wsgi.SharedDataMiddleware(
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):

1563
addons/web/i18n/fr_CA.po Normal file

File diff suppressed because it is too large Load Diff

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

View File

@ -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

View File

@ -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,"&lt;");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);

View File

@ -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");
@ -1448,20 +1448,31 @@
filter: alpha(opacity=50);
opacity: 0.5;
}
.openerp .oe_searchview .oe_searchview_search {
font-size: 1px;
letter-spacing: -1px;
color: transparent;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
position: absolute;
left: 3px;
top: 1px;
padding: 0;
border: none;
background: transparent;
}
.openerp .oe_searchview .oe_searchview_search:before {
font: 21px "mnmliconsRegular";
content: "r";
color: #a3a3a3;
}
.openerp .oe_searchview .oe_searchview_facets {
min-height: 22px;
}
.openerp .oe_searchview .oe_searchview_facets:before {
color: #cccccc;
font-family: "mnmliconsRegular";
content: "r";
font-size: 130%;
display: inline;
position: relative;
left: 6px;
top: 2px;
color: #a3a3a3;
padding-right: 4px;
margin-left: 15px;
}
.openerp .oe_searchview .oe_searchview_facets * {
vertical-align: top;
@ -2220,9 +2231,33 @@
height: auto;
line-height: 16px;
}
.openerp .oe_form_field_one2many .oe_list_buttons.oe_editing .oe_list_save, .openerp .oe_form_field_many2many .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%;
}
@ -2280,9 +2315,7 @@
}
.openerp .oe_list_content > tbody > tr > td.oe_list_field_cell {
padding: 3px 6px;
}
.openerp .oe_list_content > tbody > tr > td.oe_list_field_cell progress {
width: 100%;
white-space: pre-line;
}
.openerp .oe_list_content > tbody > tr > td > button, .openerp .oe_list_content > tbody > tr > th > button {
border: none;
@ -2330,9 +2363,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;
}

View File

@ -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
// }}}
@ -1126,19 +1125,20 @@ $sheet-max-width: 860px
border-right: 5px solid transparent
@include opacity()
.oe_searchview_search
@include text-to-icon("r", #a3a3a3)
@include box-shadow(none)
@include radius(0)
position: absolute
left: 3px
top: 1px
padding: 0
border: none
background: transparent
.oe_searchview_facets
min-height: 22px
&:before
color: #ccc
font-family: "mnmliconsRegular"
content: "r"
font-size: 130%
display: inline
position: relative
left: 6px
top: 2px
color: #a3a3a3
padding-right: 4px
margin-left: 15px
*
vertical-align: top
display: inline-block
@ -1745,6 +1745,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
@ -1752,6 +1755,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
@ -1795,8 +1817,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
@ -1824,8 +1845,6 @@ $sheet-max-width: 860px
width: 82px
input
text-align: right
.oe_list_edit_row_save:before
content: "S"
// }}}
// Translation {{{
.oe_trad_field.touched
@ -1934,6 +1953,7 @@ $sheet-max-width: 860px
background-attachment: fixed
>*
opacity: 0.70
// }}}
div.ui-widget-overlay

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -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,

View File

@ -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');
}
@ -248,13 +257,17 @@ instance.web.Loading = instance.web.Widget.extend({
// Block UI after 3s
this.long_running_timer = setTimeout(function () {
self.blocked_ui = true;
$.blockUI();
instance.web.blockUI();
}, 3000);
}
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 {
@ -263,7 +276,7 @@ instance.web.Loading = instance.web.Widget.extend({
// Don't unblock if blocked by somebody else
if (self.blocked_ui) {
this.blocked_ui = false;
$.unblockUI();
instance.web.unblockUI();
}
this.$element.fadeOut();
this.getParent().$element.removeClass('oe_wait');
@ -274,7 +287,7 @@ instance.web.Loading = instance.web.Widget.extend({
instance.web.DatabaseManager = instance.web.Widget.extend({
init: function(parent) {
this._super(parent);
this.unblockUIFunction = $.unblockUI;
this.unblockUIFunction = instance.web.unblockUI;
$.validator.addMethod('matches', function (s, _, re) {
return new RegExp(re).test(s);
}, _t("Invalid database name"));
@ -341,16 +354,16 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
* from unblocking the UI
*/
blockUI: function () {
$.blockUI();
$.unblockUI = function () {};
instance.web.blockUI();
instance.web.unblockUI = function () {};
},
/**
* Reinstates $.unblockUI so third parties can play with blockUI, and
* unblocks the UI
*/
unblockUI: function () {
$.unblockUI = this.unblockUIFunction;
$.unblockUI();
instance.web.unblockUI = this.unblockUIFunction;
instance.web.unblockUI();
},
/**
* Displays an error dialog resulting from the various RPC communications
@ -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);
};
};

View File

@ -550,7 +550,39 @@ $.async_when = function() {
/** Setup blockui */
if ($.blockUI) {
$.blockUI.defaults.baseZ = 1100;
$.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">';
$.blockUI.defaults.message = '<div class="oe_blockui_spin" style="height: 50px">';
$.blockUI.defaults.css.border = '0';
$.blockUI.defaults.css["background-color"] = '';
$.blockUI.spinners = [];
}
instance.web.blockUI = function() {
var tmp = $.blockUI.apply($, arguments);
var target = $(".oe_blockui_spin")[0];
var opts = {
lines: 13, // The number of lines to draw
length: 7, // The length of each line
width: 4, // The line thickness
radius: 10, // The radius of the inner circle
rotate: 0, // The rotation offset
color: '#FFF', // #rgb or #rrggbb
speed: 1, // Rounds per second
trail: 60, // Afterglow percentage
shadow: false, // Whether to render a shadow
hwaccel: false, // Whether to use hardware acceleration
className: 'spinner', // The CSS class to assign to the spinner
zIndex: 2e9, // The z-index (defaults to 2000000000)
top: 'auto', // Top position relative to parent in px
left: 'auto' // Left position relative to parent in px
};
var spinner = new Spinner(opts).spin(target);
$.blockUI.spinners.push(spinner);
return tmp;
}
instance.web.unblockUI = function() {
_.each($.blockUI.spinners, function(el) {
el.stop();
});
return $.unblockUI.apply($, arguments);
}
/** Setup default session */

View File

@ -376,7 +376,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
exported_fields.unshift({name: 'id', label: 'External ID'});
var export_format = this.$element.find("#export_format").val();
$.blockUI();
instance.web.blockUI();
this.session.get_file({
url: '/web/export/' + export_format,
data: {data: JSON.stringify({
@ -387,7 +387,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
import_compat: Boolean(
this.$element.find("#import_compat").val())
})},
complete: $.unblockUI
complete: instance.web.unblockUI
});
},
close: function() {

View File

@ -330,6 +330,12 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
});
}
// Launch a search on clicking the oe_searchview_search button
this.$element.on('click', 'button.oe_searchview_search', function (e) {
e.stopImmediatePropagation();
self.do_search();
});
this.$element.on('keydown',
'.oe_searchview_input, .oe_searchview_facet', function (e) {
switch(e.which) {
@ -1475,7 +1481,17 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
facet_for: function (value) {
var self = this;
if (value instanceof Array) {
return $.when(facet_from(this, value));
if (value.length === 2 && _.isString(value[1])) {
return $.when(facet_from(this, value));
}
if (value.length > 1) {
// more than one search_default m2o id? Should we OR them?
throw new Error(
_("M2O search fields do not currently handle multiple default values"));
}
// there are many cases of {search_default_$m2ofield: [id]}, need
// to handle this as if it were a single value.
value = value[0];
}
return this.model.call('name_get', [value], {}).pipe(function (names) {
if (_(names).isEmpty()) { return null; }
@ -1669,6 +1685,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){

View File

@ -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'),
@ -1212,12 +1231,6 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
$child = $td.children(':first');
switch ($child[0].tagName.toLowerCase()) {
case 'separator':
if ($child.attr('orientation') === 'vertical') {
$td.addClass('oe_vertical_separator').attr('width', '1');
$td.empty();
row_cols-= $td.attr('colspan') || 1;
total--;
}
break;
case 'label':
if ($child.attr('for')) {
@ -1339,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)
@ -1350,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);
@ -1464,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);
@ -1545,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;
@ -1691,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}),
@ -1748,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() {},
@ -1774,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() {},
@ -1804,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;
@ -1816,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');
@ -1899,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);
},
/**
@ -2242,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_) {
@ -2294,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);
@ -2304,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);
@ -2745,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',
@ -2760,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_) {
@ -2776,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});
@ -2906,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, {
@ -2950,8 +3027,11 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
controller.o2m = self;
if (view_type == "list") {
if (self.get("effective_readonly"))
controller.set_editable(false);
if (self.get("effective_readonly")) {
controller.on('edit:before', self, function (e) {
e.cancel = true;
});
}
} else if (view_type === "form") {
if (self.get("effective_readonly")) {
$(".oe_form_buttons", controller.$element).children().remove();
@ -3135,6 +3215,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') {
@ -3184,19 +3265,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()
@ -3207,21 +3303,8 @@ 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) {
if (this.editable()) {
this._super.apply(this, arguments);
} else {
var self = this;
@ -3274,55 +3357,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);
}
});
@ -3491,7 +3577,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;
@ -3866,14 +3952,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() {},
@ -3889,16 +3977,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() {
@ -3906,7 +3996,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() {
@ -3914,7 +4004,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();
});
@ -4021,9 +4111,12 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
self.dataset, false,
_.extend({'deletable': false,
'selectable': !self.options.disable_multiple_selection,
'read_only': true,
'import_enabled': false,
'$buttons': self.$buttonpane,
}, self.options.list_view_options || {}));
self.view_list.on('edit:before', self, function (e) {
e.cancel = true;
});
self.view_list.popup = self;
self.view_list.appendTo($(".oe_popup_list", self.$element)).pipe(function() {
self.view_list.do_show();
@ -4031,16 +4124,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();
@ -4259,7 +4348,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
//link.target = '_blank';
link.href = "data:application/octet-stream;base64," + value;
} else {
$.blockUI();
instance.web.blockUI();
this.session.get_file({
url: '/web/binary/saveas_ajax',
data: {data: JSON.stringify({
@ -4269,7 +4358,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
filename_field: (this.node.attrs.filename || ''),
context: this.view.dataset.get_context()
})},
complete: $.unblockUI,
complete: instance.web.unblockUI,
error: instance.webclient.crashmanager.on_rpc_error
});
ev.stopPropagation();
@ -4518,6 +4607,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',

View File

@ -21,9 +21,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
// whether the view rows can be reordered (via vertical drag & drop)
'reorderable': true,
'action_buttons': true,
// if true, the view can't be editable, ignoring the view's and the context's
// instructions
'read_only': false,
// if true, the 'Import', 'Export', etc... buttons will be shown
'import_enabled': true,
},
@ -142,7 +139,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 +285,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 +408,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 +440,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 +673,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 +930,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 +1009,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 +1173,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 +1182,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 +1866,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 +1882,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 +1942,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);

File diff suppressed because it is too large Load Diff

View File

@ -274,7 +274,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
},
ir_actions_report_xml: function(action, on_closed) {
var self = this;
$.blockUI();
instance.web.blockUI();
self.rpc("/web/session/eval_domain_and_context", {
contexts: [action.context],
domains: []
@ -284,7 +284,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
self.session.get_file({
url: '/web/report',
data: {action: JSON.stringify(action)},
complete: $.unblockUI,
complete: instance.web.unblockUI,
success: function(){
if (!self.dialog && on_closed) {
on_closed();
@ -657,32 +657,6 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
this.$element.find('.oe_debug_view').change(this.on_debug_changed);
this.$element.addClass("oe_view_manager_" + (this.action.target || 'current'));
if (this.action.help && !this.flags.low_profile) {
var Users = new instance.web.DataSet(self, 'res.users'),
$tips = this.$element.find('.oe_view_manager_menu_tips');
$tips.delegate('blockquote button', 'click', function() {
var $this = $(this);
//noinspection FallthroughInSwitchStatementJS
switch ($this.attr('name')) {
case 'disable':
Users.write(self.session.uid, {menu_tips:false});
case 'hide':
$this.closest('blockquote').hide();
self.session.hidden_menutips[self.action.id] = true;
}
});
if (!(self.action.id in self.session.hidden_menutips)) {
Users.read_ids([this.session.uid], ['menu_tips']).then(function(users) {
var user = users[0];
if (!(user && user.id === self.session.uid)) {
return;
}
$tips.find('blockquote').toggle(user.menu_tips);
});
}
}
return manager_ready;
},
on_debug_changed: function (evt) {
@ -867,7 +841,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
} else {
self.do_attachement_update(self.dataset, self.model_id);
}
$.unblockUI();
instance.web.unblockUI();
});
},
start: function() {
@ -1006,7 +980,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
$e.parent().find('input[type=file]').prop('disabled', true);
$e.parent().find('button').prop('disabled', true).find('img, span').toggle();
this.$('.oe_sidebar_add_attachment span').text(_t('Uploading...'));
$.blockUI();
instance.web.blockUI();
}
},
on_attachment_delete: function(e) {
@ -1100,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);
@ -1372,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];

View File

@ -370,6 +370,7 @@
</t>
<t t-name="WebClient">
<div class="openerp openerp_webclient_container">
<table class="oe_webclient">
<tr>
<td colspan="2" class="oe_topbar">
@ -393,6 +394,13 @@
</td>
</tr>
</table>
</div>
</t>
<t t-name="EmbedClient">
<div class="openerp">
<div class="oe_application"></div>
</div>
</t>
<t t-name="ViewManager">
@ -621,9 +629,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">
@ -643,6 +651,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">
@ -654,23 +663,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">
@ -786,7 +798,7 @@
</div>
</t>
<t t-name="FormRenderingSeparator">
<div t-attf-class="oe_horizontal_separator #{classnames}">
<div t-attf-class="oe_horizontal_separator oe_clear #{classnames}">
<t t-esc="string"/>
</div>
</t>
@ -942,9 +954,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')">
<a href="#" class="oe_m2o_cm_button oe_e oe_right">/</a>
<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"
@ -1204,6 +1219,8 @@
<div class="oe_searchview_clear"/>
<div class="oe_searchview_unfold_drawer" title="Advanced Search..."/>
<div class="oe_searchview_drawer"/>
<button type="button" class="oe_searchview_search"
title="Search Again">Search</button>
</div>
<div t-name="SearchView.InputView"
@ -1464,8 +1481,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">
@ -1475,7 +1495,7 @@
<button type="button" class="oe_button oe_abstractformpopup-form-save-new oe_highlight">Save &amp; New</button>
<button type="button" class="oe_button oe_abstractformpopup-form-save oe_highlight">Save &amp; 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
@ -1483,7 +1503,7 @@
missing columns
-->
<t t-jquery="&gt; :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>

View File

@ -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); });
});
});

View File

@ -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");
});
});

View File

@ -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();

View File

@ -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 () {

View File

@ -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>

View File

@ -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);
}
}
})();

View File

@ -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 "&nbsp;"
msgstr "&nbsp;"

View File

@ -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\""

View File

@ -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 "&nbsp;"
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 ""

View File

@ -37,11 +37,12 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
delete(action.attrs.width);
delete(action.attrs.height);
delete(action.attrs.colspan);
self.rpc('/web/action/load', {
action_id: parseInt(action.attrs.name, 10)
}, function(result) {
self.on_load_action(result, column_index + '_' + action_index, action.attrs);
});
var action_id = _.str.toNumber(action.attrs.name);
if (!_.isNaN(action_id)) {
self.rpc('/web/action/load', {action_id: action_id}, function(result) {
self.on_load_action(result, column_index + '_' + action_index, action.attrs);
});
}
});
});
},

View File

@ -48,7 +48,7 @@
</div>
</t>
<t t-name="DashBoard.xml">
<form t-att-string="form_title">
<form t-att-string="form_title" version="7.0">
<board t-att-style="style">
<column t-foreach="columns" t-as="column">
<action t-foreach="column" t-as="action" t-att="action"/>

View File

@ -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 "新ノード"

View File

@ -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"

View File

@ -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 "作成"

View File

@ -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 ""

View File

@ -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としてダウンロード"

View File

@ -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"

View File

@ -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>"

View File

@ -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

View File

@ -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 ""

View File

@ -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 "プロセス編集"

View File

@ -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-29 11:39+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"PO-Revision-Date: 2012-07-24 06:25+0000\n"
"Last-Translator: Tor Syversen <sol-moe@online.no>\n"
"Language-Team: Norwegian Bokmal <nb@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-25 04:52+0000\n"
"X-Generator: Launchpad (build 15679)\n"
#. openerp-web
#: addons/web_process/static/src/js/process.js:261
@ -85,12 +85,12 @@ msgstr "Notater:"
#. openerp-web
#: addons/web_process/static/src/xml/web_process.xml:59
msgid "Last modified by:"
msgstr ""
msgstr "Sist revidert av:"
#. openerp-web
#: addons/web_process/static/src/xml/web_process.xml:59
msgid "N/A"
msgstr ""
msgstr "N/A"
#. openerp-web
#: addons/web_process/static/src/xml/web_process.xml:62
@ -105,7 +105,7 @@ msgstr ""
#. openerp-web
#: addons/web_process/static/src/xml/web_process.xml:88
msgid "Select Process"
msgstr ""
msgstr "Velg prosess"
#. openerp-web
#: addons/web_process/static/src/xml/web_process.xml:98
@ -115,4 +115,4 @@ msgstr "Velg"
#. openerp-web
#: addons/web_process/static/src/xml/web_process.xml:109
msgid "Edit Process"
msgstr ""
msgstr "Rediger prosess"

55
doc/form-notes.rst Normal file
View File

@ -0,0 +1,55 @@
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``?
* No facilities for DOM events handling/delegations e.g. handling
keyup/keydown/keypress from a form fields into the form's user.
* Also no way to reverse from a DOM node (e.g. DOMEvent#target) back to a
form view field easily

View File

@ -18,6 +18,9 @@ Contents:
search-view
list-view
form-notes
Older stuff
-----------

466
doc/list-view.rst Normal file
View File

@ -0,0 +1,466 @@
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.
Editability status
++++++++++++++++++
The editability status of a list view can be queried through the
:js:func:`~openerp.web.ListView.editable` method, will return a falsy
value if the listview is not currently editable.
The editability status is based on three flags:
``tree/@editable``
If present, can be either ``"top"`` or ``"bottom"``. Either will
make the list view editable, with new records being respectively
created at the top or at the bottom of the view.
``context.set_editable``
Boolean flag extracted from a search context (during the
:js:func:`~openerp.web.ListView.do_search`` handler), ``true``
will make the view editable (from the top), ``false`` or the
absence of the flag is a noop.
``defaults.editable``
Like ``tree/@editable``, one of absent (``null``)), ``"top"`` or
``"bottom"``, fallback for the list view if none of the previous
two flags are set.
These three flags can only *make* a listview editable, they can *not*
override a previously set flag. To do that, a listview user should
instead cancel :ref:`the edit:before event <listview-edit-before>`.
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.
Generally speaking, an event should only be cancelled (by
setting the ``cancel`` flag to ``true``), uncancelling an
event is undefined as event handlers are executed on a
first-come-first-serve basis and later handlers may
re-cancel an uncancelled event.
.. _listview-edit-before:
``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