Merge lp:~openerp-dev/openobject-addons/trunk-website_sign into lp:openobject-addons

Proposed by Kunal Chavda
Status: Work in progress
Proposed branch: lp:~openerp-dev/openobject-addons/trunk-website_sign
Merge into: lp:openobject-addons
Diff against target: 1449 lines (+1186/-108)
17 files modified
mail/static/src/xml/mail.xml (+26/-24)
website/static/lib/jSignature/jSignature.min.js (+77/-0)
website/views/website_templates.xml (+1/-1)
website_quote/static/lib/jSignature/jSignature.min.js (+0/-77)
website_quote/views/website_quotation.xml (+6/-6)
website_sign/__init__.py (+25/-0)
website_sign/__openerp__.py (+46/-0)
website_sign/controllers/__init__.py (+3/-0)
website_sign/controllers/main.py (+183/-0)
website_sign/data/website_sign_data.xml (+66/-0)
website_sign/models/__init__.py (+1/-0)
website_sign/models/website_sign.py (+257/-0)
website_sign/security/ir.model.access.csv (+3/-0)
website_sign/static/src/css/website_sign.css (+83/-0)
website_sign/static/src/js/sign.js (+200/-0)
website_sign/static/src/xml/sign.xml (+58/-0)
website_sign/views/website_sign.xml (+151/-0)
To merge this branch: bzr merge lp:~openerp-dev/openobject-addons/trunk-website_sign
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+213410@code.launchpad.net

Description of the change

Hello,
I have developed website sign module which will add functionality on attachment for digital signature of requested partner. We can request to those partner who are followers of that record only.
Also this module contains track of signers like how many person requested to sign, on which date he has signed and how much sign requests are pending.

Thanks,
Kunal Chavda

To post a comment you must log in.
9244. By Kunal Chavda

[MERGE]with latest.

9245. By Kunal Chavda

[IMP]Improved varible names.

9246. By Kunal Chavda

[FIX]forgot to change in getting partner ids.

9247. By Kunal Chavda

[FIX]when there is log an note than request signature icon should not be shown on attachment.

9248. By Kunal Chavda

[IMP]Improved code for see request signature link on attachment while full mail composer wizard opened.

9249. By Kunal Chavda

[MERGE]with latest trunk.

9250. By Kunal Chavda

[FIX]passing integer instead of string ids.

9251. By Kunal Chavda

[MERGE]with latest trunk.

9252. By Kunal Chavda

[IMP]Improved code for web assets moved from manifests to ir.ui.view bundles as per trunk changes.

9253. By Kunal Chavda

[MERGE]with latest trunk.

9254. By Kunal Chavda

[MERGE]with latest trunk.

9255. By Ronak Baxi (OpenERP)

[MERGE]with trunk revno. 9457

9256. By Ronak Baxi (OpenERP)

[MERGE]with revno 9459

9257. By Ronak Baxi (OpenERP)

[MERGE]with latest revno 9460

9258. By Ronak Baxi (OpenERP)

[FIX]foreach enumurator error

Unmerged revisions

9258. By Ronak Baxi (OpenERP)

[FIX]foreach enumurator error

9257. By Ronak Baxi (OpenERP)

[MERGE]with latest revno 9460

9256. By Ronak Baxi (OpenERP)

[MERGE]with revno 9459

9255. By Ronak Baxi (OpenERP)

[MERGE]with trunk revno. 9457

9254. By Kunal Chavda

[MERGE]with latest trunk.

9253. By Kunal Chavda

[MERGE]with latest trunk.

9252. By Kunal Chavda

[IMP]Improved code for web assets moved from manifests to ir.ui.view bundles as per trunk changes.

9251. By Kunal Chavda

[MERGE]with latest trunk.

9250. By Kunal Chavda

[FIX]passing integer instead of string ids.

9249. By Kunal Chavda

[MERGE]with latest trunk.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'mail/static/src/xml/mail.xml'
2--- mail/static/src/xml/mail.xml 2014-05-07 16:51:26 +0000
3+++ mail/static/src/xml/mail.xml 2014-05-15 14:05:22 +0000
4@@ -88,30 +88,32 @@
5 -->
6 <t t-name="mail.thread.message.attachments">
7 <t t-foreach='widget.attachment_ids' t-as='attachment'>
8- <t t-if="attachment.file_type_icon !== 'webimage'">
9- <div t-attf-class="oe_attachment #{attachment.upload ? 'oe_uploading' : ''}">
10- <a t-att-href='attachment.url' target="_blank">
11- <img t-att-src="'/mail/static/src/img/mimetypes/' + attachment.file_type_icon + '.png'"></img>
12- <div class='oe_name'><t t-raw='attachment.name' /></div>
13- </a>
14- <div class='oe_delete oe_e' title="Delete this attachment" t-att-data-id="attachment.id">[</div>
15- <div class='oe_progress_bar'>
16- uploading
17- </div>
18- </div>
19- </t>
20- <t t-if="attachment.file_type_icon === 'webimage'">
21- <div t-attf-class="oe_attachment oe_preview #{attachment.upload ? 'oe_uploading' : ''}">
22- <a t-att-href='attachment.url' target="_blank">
23- <img t-att-src="widget.attachments_resize_image(attachment.id, [100,80])"></img>
24- <div class='oe_name'><t t-raw='attachment.name' /></div>
25- </a>
26- <div class='oe_delete oe_e' title="Delete this attachment" t-att-data-id="attachment.id">[</div>
27- <div class='oe_progress_bar'>
28- uploading
29- </div>
30- </div>
31- </t>
32+ <div id="attachments" style="display:inline-block;">
33+ <t t-if="attachment.file_type_icon !== 'webimage'">
34+ <div t-attf-class="oe_attachment #{attachment.upload ? 'oe_uploading' : ''}">
35+ <a t-att-href='attachment.url' target="_blank">
36+ <img t-att-src="'/mail/static/src/img/mimetypes/' + attachment.file_type_icon + '.png'"></img>
37+ <div class='oe_name'><t t-raw='attachment.name' /></div>
38+ </a>
39+ <div class='oe_delete oe_e' title="Delete this attachment" t-att-data-id="attachment.id">[</div>
40+ <div class='oe_progress_bar'>
41+ uploading
42+ </div>
43+ </div>
44+ </t>
45+ <t t-if="attachment.file_type_icon === 'webimage'">
46+ <div t-attf-class="oe_attachment oe_preview #{attachment.upload ? 'oe_uploading' : ''}">
47+ <a t-att-href='attachment.url' target="_blank">
48+ <img t-att-src="widget.attachments_resize_image(attachment.id, [100,80])"></img>
49+ <div class='oe_name'><t t-raw='attachment.name' /></div>
50+ </a>
51+ <div class='oe_delete oe_e' title="Delete this attachment" t-att-data-id="attachment.id">[</div>
52+ <div class='oe_progress_bar'>
53+ uploading
54+ </div>
55+ </div>
56+ </t>
57+ </div>
58 </t>
59 </t>
60
61
62=== added directory 'website/static/lib/jSignature'
63=== added file 'website/static/lib/jSignature/jSignature.min.js'
64--- website/static/lib/jSignature/jSignature.min.js 1970-01-01 00:00:00 +0000
65+++ website/static/lib/jSignature/jSignature.min.js 2014-05-15 14:05:22 +0000
66@@ -0,0 +1,77 @@
67+/*
68+
69+jSignature v2 "2012-11-01T22:48" "commit ID 1c15dfafecc75925c3b7d529356a558b59220edb"
70+Copyright (c) 2012 Willow Systems Corp http://willow-systems.com
71+Copyright (c) 2010 Brinley Ang http://www.unbolt.net
72+MIT License <http://www.opensource.org/licenses/mit-license.php>
73+
74+
75+Simplify.js BSD
76+(c) 2012, Vladimir Agafonkin
77+mourner.github.com/simplify-js
78+
79+
80+base64 encoder
81+MIT, GPL
82+http://phpjs.org/functions/base64_encode
83++ original by: Tyler Akins (http://rumkin.com)
84++ improved by: Bayron Guevara
85++ improved by: Thunder.m
86++ improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
87++ bugfixed by: Pellentesque Malesuada
88++ improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
89++ improved by: Rafal Kukawski (http://kukawski.pl)
90+
91+
92+jSignature v2 jSignature's Undo Button and undo functionality plugin
93+
94+
95+jSignature v2 jSignature's custom "base30" format export and import plugins.
96+
97+
98+jSignature v2 SVG export plugin.
99+
100+*/
101+(function(){function q(b){for(var j,s=b.css("color"),g,b=b[0],c=!1;b&&!g&&!c;){try{j=$(b).css("background-color")}catch(d){j="transparent"}"transparent"!==j&&"rgba(0, 0, 0, 0)"!==j&&(g=j);c=b.body;b=b.parentNode}var b=/rgb[a]*\((\d+),\s*(\d+),\s*(\d+)/,c=/#([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})/,a;j=void 0;(j=s.match(b))?a={r:parseInt(j[1],10),g:parseInt(j[2],10),b:parseInt(j[3],10)}:(j=s.match(c))&&(a={r:parseInt(j[1],16),g:parseInt(j[2],16),b:parseInt(j[3],16)});var f;g?
102+(j=void 0,(j=g.match(b))?f={r:parseInt(j[1],10),g:parseInt(j[2],10),b:parseInt(j[3],10)}:(j=g.match(c))&&(f={r:parseInt(j[1],16),g:parseInt(j[2],16),b:parseInt(j[3],16)})):f=a?127<Math.max.apply(null,[a.r,a.g,a.b])?{r:0,g:0,b:0}:{r:255,g:255,b:255}:{r:255,g:255,b:255};j=function(b){return"rgb("+[b.r,b.g,b.b].join(", ")+")"};a&&f?(b=Math.max.apply(null,[a.r,a.g,a.b]),a=Math.max.apply(null,[f.r,f.g,f.b]),a=Math.round(a+-0.75*(a-b)),a={r:a,g:a,b:a}):a?(a=Math.max.apply(null,[a.r,a.g,a.b]),b=1,127<a&&
103+(b=-1),a=Math.round(a+96*b),a={r:a,g:a,b:a}):a={r:191,g:191,b:191};return{color:s,"background-color":f?j(f):g,"decor-color":j(a)}}function m(b,j){this.x=b;this.y=j;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var s=function(b){return Math.round(b/Math.abs(b))};this.resizeTo=function(b){if(0===this.x&&0===this.y)this._length=0;else if(0===
104+this.x)this._length=b,this.y=b*s(this.y);else if(0===this.y)this._length=b,this.x=b*s(this.x);else{var j=Math.abs(this.y/this.x),a=Math.sqrt(Math.pow(b,2)/(1+Math.pow(j,2))),j=j*a;this._length=b;this.x=a*s(this.x);this.y=j*s(this.y)}return this};this.angleTo=function(b){var j=this.getLength()*b.getLength();return 0===j?0:Math.acos(Math.min(Math.max((this.x*b.x+this.y*b.y)/j,-1),1))/Math.PI}}function k(b,j){this.x=b;this.y=j;this.getVectorToCoordinates=function(b,j){return new m(b-this.x,j-this.y)};
105+this.getVectorFromCoordinates=function(b,j){return this.getVectorToCoordinates(b,j).reverse()};this.getVectorToPoint=function(b){return new m(b.x-this.x,b.y-this.y)};this.getVectorFromPoint=function(b){return this.getVectorToPoint(b).reverse()}}function t(b,j,a,g,c){this.data=b;this.context=j;if(b.length)for(var d=b.length,f,l,i=0;i<d;i++){f=b[i];l=f.x.length;a.call(j,f);for(var e=1;e<l;e++)g.call(j,f,e);c.call(j,f)}this.changed=function(){};this.startStrokeFn=a;this.addToStrokeFn=g;this.endStrokeFn=
106+c;this.inStroke=!1;this._stroke=this._lastPoint=null;this.startStroke=function(b){if(b&&"number"==typeof b.x&&"number"==typeof b.y){this._stroke={x:[b.x],y:[b.y]};this.data.push(this._stroke);this._lastPoint=b;this.inStroke=!0;var j=this._stroke,a=this.startStrokeFn,g=this.context;setTimeout(function(){a.call(g,j)},3);return b}return null};this.addToStroke=function(b){if(this.inStroke&&"number"===typeof b.x&&"number"===typeof b.y&&4<Math.abs(b.x-this._lastPoint.x)+Math.abs(b.y-this._lastPoint.y)){var j=
107+this._stroke.x.length;this._stroke.x.push(b.x);this._stroke.y.push(b.y);this._lastPoint=b;var a=this._stroke,g=this.addToStrokeFn,s=this.context;setTimeout(function(){g.call(s,a,j)},3);return b}return null};this.endStroke=function(){var b=this.inStroke;this.inStroke=!1;this._lastPoint=null;if(b){var j=this._stroke,a=this.endStrokeFn,g=this.context,s=this.changed;setTimeout(function(){a.call(g,j);s.call(g)},3);return!0}return null}}function r(b,j,a){var g=this.$parent=$(b),b=this.eventTokens={};this.events=
108+new v(this);var c=$.fn[e]("globalEvents"),d={width:"ratio",height:"ratio",sizeRatio:4,color:"#000","background-color":"#fff","decor-color":"#eee",lineWidth:0,minFatFingerCompensation:-10,showUndoButton:!1,data:[]};$.extend(d,q(g));j&&$.extend(d,j);this.settings=d;for(var f in a)a.hasOwnProperty(f)&&a[f].call(this,f);this.events.publish(e+".initializing");this.$controlbarUpper=$('<div style="padding:0 !important;margin:0 !important;width: 100% !important; height: 0 !important;margin-top:-1em !important;margin-bottom:1em !important;"></div>').appendTo(g);
109+this.isCanvasEmulator=!1;a=this.canvas=this.initializeCanvas(d);j=$(a);this.$controlbarLower=$('<div style="padding:0 !important;margin:0 !important;width: 100% !important; height: 0 !important;margin-top:-1.5em !important;margin-bottom:1.5em !important;"></div>').appendTo(g);this.canvasContext=a.getContext("2d");j.data(e+".this",this);g=(g=d.lineWidth)?g:Math.max(Math.round(a.width/400),2);d.lineWidth=g;this.lineCurveThreshold=3*d.lineWidth;d.cssclass&&""!=$.trim(d.cssclass)&&j.addClass(d.cssclass);
110+this.fatFingerCompensation=0;var g=function(b){var j,a,g=function(g){g=g.changedTouches&&0<g.changedTouches.length?g.changedTouches[0]:g;return new k(Math.round(g.pageX+j),Math.round(g.pageY+a)+b.fatFingerCompensation)},d=new y(750,function(){b.dataEngine.endStroke()});this.drawEndHandler=function(j){try{j.preventDefault()}catch(a){}d.clear();b.dataEngine.endStroke()};this.drawStartHandler=function(c){c.preventDefault();var s=$(b.canvas).offset();j=-1*s.left;a=-1*s.top;b.dataEngine.startStroke(g(c));
111+d.kick()};this.drawMoveHandler=function(j){j.preventDefault();b.dataEngine.inStroke&&(b.dataEngine.addToStroke(g(j)),d.kick())};return this}.call({},this),l=g.drawEndHandler,i=g.drawStartHandler,p=g.drawMoveHandler,h=this.canvas,j=$(h);this.isCanvasEmulator?(j.bind("mousemove."+e,p),j.bind("mouseup."+e,l),j.bind("mousedown."+e,i)):(h.ontouchstart=function(b){h.onmousedown=void 0;h.onmouseup=void 0;h.onmousemove=void 0;this.fatFingerCompensation=d.minFatFingerCompensation&&-3*d.lineWidth>d.minFatFingerCompensation?
112+-3*d.lineWidth:d.minFatFingerCompensation;i(b);h.ontouchend=l;h.ontouchstart=i;h.ontouchmove=p},h.onmousedown=function(b){h.ontouchstart=void 0;h.ontouchend=void 0;h.ontouchmove=void 0;i(b);h.onmousedown=i;h.onmouseup=l;h.onmousemove=p});b[e+".windowmouseup"]=c.subscribe(e+".windowmouseup",g.drawEndHandler);this.events.publish(e+".attachingEventHandlers");var n=this,b=d.width.toString(10),w=e;if("ratio"===b||"%"===b.split("")[b.length-1])this.eventTokens[w+".parentresized"]=c.subscribe(w+".parentresized",
113+function(b,j,a){return function(){var g=j.width();if(g!==a){for(var d in b)b.hasOwnProperty(d)&&(c.unsubscribe(b[d]),delete b[d]);var s=n.settings;n.$parent.children().remove();for(d in n)n.hasOwnProperty(d)&&delete n[d];d=s.data;var g=1*g/a,f=[],l,i,e,h,k,p;i=0;for(e=d.length;i<e;i++){p=d[i];l={x:[],y:[]};h=0;for(k=p.x.length;h<k;h++)l.x.push(p.x[h]*g),l.y.push(p.y[h]*g);f.push(l)}s.data=f;j[w](s)}}}(this.eventTokens,this.$parent,this.$parent.width(),1*this.canvas.width/this.canvas.height));this.resetCanvas(d.data);
114+this.events.publish(e+".initialized");return this}var e="jSignature",y=function(b,j){var a;this.kick=function(){clearTimeout(a);a=setTimeout(j,b)};this.clear=function(){clearTimeout(a)};return this},v=function(b){this.topics={};this.context=b?b:this;this.publish=function(b,a,g,d){if(this.topics[b]){var c=this.topics[b],f=Array.prototype.slice.call(arguments,1),l=[],i,e,h,p;e=0;for(h=c.length;e<h;e++)p=c[e],i=p[0],p[1]&&(p[0]=function(){},l.push(e)),i.apply(this.context,f);e=0;for(h=l.length;e<h;e++)c.splice(l[e],
115+1)}};this.subscribe=function(b,a,g){this.topics[b]?this.topics[b].push([a,g]):this.topics[b]=[[a,g]];return{topic:b,callback:a}};this.unsubscribe=function(b){if(this.topics[b.topic])for(var a=this.topics[b.topic],g=0,d=a.length;g<d;g++)a[g][0]===b.callback&&a.splice(g,1)}},z=function(b){var a=this.canvasContext,d=b.x[0],b=b.y[0],g=this.settings.lineWidth,c=a.fillStyle;a.fillStyle=a.strokeStyle;a.fillRect(d+g/-2,b+g/-2,g,g);a.fillStyle=c},u=function(b,a){var d=new k(b.x[a-1],b.y[a-1]),g=new k(b.x[a],
116+b.y[a]),c=d.getVectorToPoint(g);if(1<a){var f=new k(b.x[a-2],b.y[a-2]),l=f.getVectorToPoint(d),i;if(l.getLength()>this.lineCurveThreshold){i=2<a?(new k(b.x[a-3],b.y[a-3])).getVectorToPoint(f):new m(0,0);var e=0.35*l.getLength(),h=l.angleTo(i.reverse()),p=c.angleTo(l.reverse());i=(new m(i.x+l.x,i.y+l.y)).resizeTo(Math.max(0.05,h)*e);var n=(new m(l.x+c.x,l.y+c.y)).reverse().resizeTo(Math.max(0.05,p)*e),l=this.canvasContext,e=f.x,p=f.y,h=d.x,w=d.y,A=f.x+i.x,f=f.y+i.y;i=d.x+n.x;n=d.y+n.y;l.beginPath();
117+l.moveTo(e,p);l.bezierCurveTo(A,f,i,n,h,w);l.stroke()}}c.getLength()<=this.lineCurveThreshold&&(c=this.canvasContext,f=d.x,d=d.y,i=g.x,g=g.y,c.beginPath(),c.moveTo(f,d),c.lineTo(i,g),c.stroke())},x=function(b){var a=b.x.length-1;if(0<a){var d=new k(b.x[a],b.y[a]),g=new k(b.x[a-1],b.y[a-1]),c=g.getVectorToPoint(d);if(c.getLength()>this.lineCurveThreshold){if(1<a){var b=(new k(b.x[a-2],b.y[a-2])).getVectorToPoint(g),f=(new m(b.x+c.x,b.y+c.y)).resizeTo(c.getLength()/2),c=this.canvasContext,b=g.x,a=g.y,
118+l=d.x,i=d.y,e=g.x+f.x,g=g.y+f.y,f=d.x,d=d.y;c.beginPath();c.moveTo(b,a);c.bezierCurveTo(e,g,f,d,l,i)}else c=this.canvasContext,b=g.x,g=g.y,a=d.x,d=d.y,c.beginPath(),c.moveTo(b,g),c.lineTo(a,d);c.stroke()}}};r.prototype.resetCanvas=function(b){var a=this.canvas,d=this.settings,c=this.canvasContext,f=this.isCanvasEmulator,l=a.width,i=a.height;c.clearRect(0,0,l+30,i+30);c.shadowColor=c.fillStyle=d["background-color"];f&&c.fillRect(0,0,l+30,i+30);c.lineWidth=Math.ceil(parseInt(d.lineWidth,10));c.lineCap=
119+c.lineJoin="round";c.strokeStyle=d["decor-color"];c.shadowOffsetX=0;c.shadowOffsetY=0;var h=Math.round(i/5),p=1.5*h,k=i-h,l=l-1.5*h,i=i-h;c.beginPath();c.moveTo(p,k);c.lineTo(l,i);c.stroke();c.strokeStyle=d.color;f||(c.shadowColor=c.strokeStyle,c.shadowOffsetX=0.5*c.lineWidth,c.shadowOffsetY=-0.6*c.lineWidth,c.shadowBlur=0);b||(b=[]);c=this.dataEngine=new t(b,this,z,u,x);d.data=b;$(a).data(e+".data",b).data(e+".settings",d);var n=this.$parent,w=this.events,A=e;c.changed=function(){w.publish(A+".change");
120+n.trigger("change")};c.changed();return!0};r.prototype.initializeCanvas=function(b){var a=document.createElement("canvas"),c=$(a);b.width===b.height&&"ratio"===b.height&&(b.width="100%");c.css("margin",0).css("padding",0).css("border","none").css("height","ratio"===b.height||!b.height?1:b.height.toString(10)).css("width","ratio"===b.width||!b.width?1:b.width.toString(10));c.appendTo(this.$parent);"ratio"===b.height?c.css("height",Math.round(c.width()/b.sizeRatio)):"ratio"===b.width&&c.css("width",
121+Math.round(c.height()*b.sizeRatio));c.addClass(e);a.width=c.width();a.height=c.height();b=a;if(b.getContext)b=!1;else{var c=b.ownerDocument.parentWindow,d=c.FlashCanvas?b.ownerDocument.parentWindow.FlashCanvas:"undefined"===typeof FlashCanvas?void 0:FlashCanvas;if(d){b=d.initElement(b);d=1;c&&(c.screen&&c.screen.deviceXDPI&&c.screen.logicalXDPI)&&(d=1*c.screen.deviceXDPI/c.screen.logicalXDPI);if(1!==d)try{$(b).children("object").get(0).resize(Math.ceil(b.width*d),Math.ceil(b.height*d)),b.getContext("2d").scale(d,
122+d)}catch(f){}b=!0}else throw Error("Canvas element does not support 2d context. jSignature cannot proceed.");}this.isCanvasEmulator=b;a.onselectstart=function(b){b&&b.preventDefault&&b.preventDefault();b&&b.stopPropagation&&b.stopPropagation();return!1};return a};var p=window,h=function(b,a){var c=new Image,d=this;c.onload=function(){d.getContext("2d").drawImage(c,0,0,c.width<d.width?c.width:d.width,c.height<d.height?c.height:d.height)};c.src="data:"+a+","+b},a=function(b){this.find("canvas."+e).add(this.filter("canvas."+
123+e)).data(e+".this").resetCanvas(b);return this},f=function(b,a){if(void 0===a&&("string"===typeof b&&"data:"===b.substr(0,5))&&(a=b.slice(5).split(",")[0],b=b.slice(6+a.length),a===b))return;var c=this.find("canvas."+e).add(this.filter("canvas."+e));if(n.hasOwnProperty(a))0!==c.length&&n[a].call(c[0],b,a,function(b){return function(){return b.resetCanvas.apply(b,arguments)}}(c.data(e+".this")));else throw Error(e+" is unable to find import plugin with for format '"+String(a)+"'");return this},d=new v,
124+c=e,l,i=function(){d.publish(c+".parentresized")};$(p).bind("resize."+c,function(){l&&clearTimeout(l);l=setTimeout(i,500)}).bind("mouseup."+c,function(){d.publish(c+".windowmouseup")});var w={},A={"default":function(){return this.toDataURL()},"native":function(b){return b},image:function(){var b=this.toDataURL();if("string"===typeof b&&4<b.length&&"data:"===b.slice(0,5)&&-1!==b.indexOf(",")){var a=b.indexOf(",");return[b.slice(5,a),b.substr(a+1)]}return[]}},n={"native":function(b,a,c){c(b)},image:h,
125+"image/png;base64":h,"image/jpeg;base64":h,"image/jpg;base64":h},B={"export":A,"import":n,instance:w},C={init:function(b){return this.each(function(){var a,c=!1;for(a=this.parentNode;a&&!c;)c=a.body,a=a.parentNode;!c||new r(this,b,w)})},getSettings:function(){return this.find("canvas."+e).add(this.filter("canvas."+e)).data(e+".this").settings},clear:a,reset:a,addPlugin:function(b,a,c){B.hasOwnProperty(b)&&(B[b][a]=c);return this},listPlugins:function(b){var a=[];if(B.hasOwnProperty(b)){var b=B[b],
126+c;for(c in b)b.hasOwnProperty(c)&&a.push(c)}return a},getData:function(b){var a=this.find("canvas."+e).add(this.filter("canvas."+e));void 0===b&&(b="default");if(0!==a.length&&A.hasOwnProperty(b))return A[b].call(a.get(0),a.data(e+".data"))},importData:f,setData:f,globalEvents:function(){return d},events:function(){return this.find("canvas."+e).add(this.filter("canvas."+e)).data(e+".this").events}};$.fn[e]=function(b){if(!b||"object"===typeof b)return C.init.apply(this,arguments);if("string"===typeof b&&
127+C[b])return C[b].apply(this,Array.prototype.slice.call(arguments,1));$.error("Method "+String(b)+" does not exist on jQuery."+e)}})();
128+(function(){$.fn.jSignature("addPlugin","instance","UndoButton",function(q){this.events.subscribe("jSignature.attachingEventHandlers",function(){if(this.settings[q]){var m=this.settings[q];"function"!==typeof m&&(m=function(){var e=$('<input type="button" value="Undo last stroke" style="position:absolute;display:none;margin:0 !important;top:auto" />').appendTo(this.$controlbarLower),k=e.width();e.css("left",Math.round((this.canvas.width-k)/2));k!==e.width()&&e.width(k);return e});var k=m.call(this),
129+t=this;t.events.subscribe("jSignature.change",function(){t.dataEngine.data.length?k.show():k.hide()});var r=this,e=(this.events.topics.hasOwnProperty("jSignature.undo")?q:"jSignature")+".undo";k.bind("click",function(){r.events.publish(e)});r.events.subscribe(e,function(){var e=r.dataEngine.data;e.length&&(e.pop(),r.resetCanvas(e))})}})})})();
130+(function(){for(var q={},m={},k="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX".split(""),t=k.length/2,r=t-1;-1<r;r--)q[k[r]]=k[r+t],m[k[r+t]]=k[r];var e=function(e){for(var e=e.split(""),h=e.length,a=1;a<h;a++)e[a]=q[e[a]];return e.join("")},y=function(k){for(var h=[],a=0,f=1,d=k.length,c,l,i=0;i<d;i++)c=Math.round(k[i]),l=c-a,a=c,0>l&&0<f?(f=-1,h.push("Z")):0<l&&0>f&&(f=1,h.push("Y")),c=Math.abs(l),c>=t?h.push(e(c.toString(t))):h.push(c.toString(t));return h.join("")},v=function(e){for(var h=
131+[],e=e.split(""),a=e.length,f,d=1,c=[],l=0,i=0;i<a;i++)f=e[i],f in q||"Z"===f||"Y"===f?(0!==c.length&&(c=parseInt(c.join(""),t)*d+l,h.push(c),l=c),"Z"===f?(d=-1,c=[]):"Y"===f?(d=1,c=[]):c=[f]):c.push(m[f]);h.push(parseInt(c.join(""),t)*d+l);return h},z=function(e){for(var h=[],a=e.length,f,d=0;d<a;d++)f=e[d],h.push(y(f.x)),h.push(y(f.y));return h.join("_")},u=function(e){for(var h=[],e=e.split("_"),a=e.length/2,f=0;f<a;f++)h.push({x:v(e[2*f]),y:v(e[2*f+1])});return h},k=function(e){return["image/jsignature;base30",
132+z(e)]},r=function(e,h,a){"string"===typeof e&&("image/jsignature;base30"===e.substring(0,23).toLowerCase()&&(e=e.substring(24)),a(u(e)))};if(null==this.jQuery)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");var x=this.jQuery.fn.jSignature;x("addPlugin","export","base30",k);x("addPlugin","export","image/jsignature;base30",k);x("addPlugin","import","base30",r);x("addPlugin","import","image/jsignature;base30",r);this.jSignatureDebug&&(this.jSignatureDebug.base30=
133+{remapTailChars:e,compressstrokeleg:y,uncompressstrokeleg:v,compressstrokes:z,uncompressstrokes:u,charmap:q})}).call("undefined"!==typeof window?window:this);
134+(function(){function q(a,f){this.x=a;this.y=f;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var d=function(a){return Math.round(a/Math.abs(a))};this.resizeTo=function(a){if(0===this.x&&0===this.y)this._length=0;else if(0===this.x)this._length=a,this.y=a*d(this.y);else if(0===this.y)this._length=a,this.x=a*d(this.x);else{var f=Math.abs(this.y/
135+this.x),e=Math.sqrt(Math.pow(a,2)/(1+Math.pow(f,2))),f=f*e;this._length=a;this.x=e*d(this.x);this.y=f*d(this.y)}return this};this.angleTo=function(a){var d=this.getLength()*a.getLength();return 0===d?0:Math.acos(Math.min(Math.max((this.x*a.x+this.y*a.y)/d,-1),1))/Math.PI}}function m(a,f){this.x=a;this.y=f;this.getVectorToCoordinates=function(a,c){return new q(a-this.x,c-this.y)};this.getVectorFromCoordinates=function(a,c){return this.getVectorToCoordinates(a,c).reverse()};this.getVectorToPoint=function(a){return new q(a.x-
136+this.x,a.y-this.y)};this.getVectorFromPoint=function(a){return this.getVectorToPoint(a).reverse()}}function k(a,f){var d=Math.pow(10,f);return Math.round(a*d)/d}function t(a,f,d){var f=f+1,c=new m(a.x[f-1],a.y[f-1]),e=new m(a.x[f],a.y[f]),e=c.getVectorToPoint(e),i=new m(a.x[f-2],a.y[f-2]),c=i.getVectorToPoint(c);return c.getLength()>d?(d=2<f?(new m(a.x[f-3],a.y[f-3])).getVectorToPoint(i):new q(0,0),a=0.35*c.getLength(),i=c.angleTo(d.reverse()),f=e.angleTo(c.reverse()),d=(new q(d.x+c.x,d.y+c.y)).resizeTo(Math.max(0.05,
137+i)*a),e=(new q(c.x+e.x,c.y+e.y)).reverse().resizeTo(Math.max(0.05,f)*a),e=new q(c.x+e.x,c.y+e.y),["c",k(d.x,2),k(d.y,2),k(e.x,2),k(e.y,2),k(c.x,2),k(c.y,2)]):["l",k(c.x,2),k(c.y,2)]}function r(a,f){var d=a.x.length-1,c=new m(a.x[d],a.y[d]),e=new m(a.x[d-1],a.y[d-1]),c=e.getVectorToPoint(c);if(1<d&&c.getLength()>f){var d=(new m(a.x[d-2],a.y[d-2])).getVectorToPoint(e),e=c.angleTo(d.reverse()),i=0.35*c.getLength(),d=(new q(d.x+c.x,d.y+c.y)).resizeTo(Math.max(0.05,e)*i);return["c",k(d.x,2),k(d.y,2),k(c.x,
138+2),k(c.y,2),k(c.x,2),k(c.y,2)]}return["l",k(c.x,2),k(c.y,2)]}function e(a,e,d){for(var e=["M",k(a.x[0]-e,2),k(a.y[0]-d,2)],d=1,c=a.x.length-1;d<c;d++)e.push.apply(e,t(a,d,1));0<c?e.push.apply(e,r(a,d,1)):0===c&&e.push.apply(e,["l",1,1]);return e.join(" ")}function y(a){var f=['<?xml version="1.0" encoding="UTF-8" standalone="no"?>','<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'],d,c=a.length,l,i=[],h=[],k=l=d=0,n=0,p=[];if(0!==c){for(d=0;d<c;d++){k=
139+a[d];n=[];l={x:[],y:[]};for(var m=void 0,b=void 0,m=0,b=k.x.length;m<b;m++)n.push({x:k.x[m],y:k.y[m]});n=simplify(n,0.7,!0);m=0;for(b=n.length;m<b;m++)l.x.push(n[m].x),l.y.push(n[m].y);p.push(l);i=i.concat(l.x);h=h.concat(l.y)}a=Math.min.apply(null,i)-1;c=Math.max.apply(null,i)+1;i=Math.min.apply(null,h)-1;h=Math.max.apply(null,h)+1;k=0>a?0:a;n=0>i?0:i;d=c-a;l=h-i}f.push('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'+d.toString()+'" height="'+l.toString()+'">');d=0;for(c=p.length;d<
140+c;d++)l=p[d],f.push('<path fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="'+e(l,k,n)+'"/>');f.push("</svg>");return f.join("")}function v(a){return[p,y(a)]}function z(a){return[h,x(y(a))]}var u=window;"use strict";("undefined"!=typeof exports?exports:u).simplify=function(a,e,d){e=void 0!==e?e*e:1;if(!d){for(var c=a.length,l,i=a[0],h=[i],d=1;d<c;d++){l=a[d];var k=l.x-i.x,n=l.y-i.y;k*k+n*n>e&&(h.push(l),i=l)}a=(i!==l&&h.push(l),h)}l=a;var d=l.length,
141+c=new ("undefined"!=typeof Uint8Array?Uint8Array:Array)(d),i=0,h=d-1,m,p,b=[],j=[],s=[];for(c[i]=c[h]=1;h;){n=0;for(k=i+1;k<h;k++){m=l[k];var g=l[i],r=l[h],q=g.x,t=g.y,g=r.x-q,u=r.y-t,v=void 0;if(0!==g||0!==u)v=((m.x-q)*g+(m.y-t)*u)/(g*g+u*u),1<v?(q=r.x,t=r.y):0<v&&(q+=g*v,t+=u*v);m=(g=m.x-q,u=m.y-t,g*g+u*u);m>n&&(p=k,n=m)}n>e&&(c[p]=1,b.push(i),j.push(p),b.push(p),j.push(h));i=b.pop();h=j.pop()}for(k=0;k<d;k++)c[k]&&s.push(l[k]);return a=s,a};if("function"!==typeof x)var x=function(a){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(""),
142+d,c,h,i,k=0,m=0,n="",n=[];do d=a.charCodeAt(k++),c=a.charCodeAt(k++),h=a.charCodeAt(k++),i=d<<16|c<<8|h,d=i>>18&63,c=i>>12&63,h=i>>6&63,i&=63,n[m++]=e[d]+e[c]+e[h]+e[i];while(k<a.length);n=n.join("");a=a.length%3;return(a?n.slice(0,a-3):n)+"===".slice(a||3)};var p="image/svg+xml",h="image/svg+xml;base64";if("undefined"===typeof $)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");u=$.fn.jSignature;u("addPlugin","export","svg",v);u("addPlugin",
143+"export",p,v);u("addPlugin","export","svgbase64",z);u("addPlugin","export",h,z)})();
144\ No newline at end of file
145
146=== modified file 'website/views/website_templates.xml'
147--- website/views/website_templates.xml 2014-05-13 08:18:31 +0000
148+++ website/views/website_templates.xml 2014-05-15 14:05:22 +0000
149@@ -178,7 +178,7 @@
150 </p>
151 </div>
152 <ul class="list-inline js_language_selector mt16" t-if="(request.website_multilang and len(languages) &gt; 1) or editable">
153- <li t-foreach="languages" t-as="lg">
154+ <li t-foreach="website.get_languages()" t-as="lg">
155 <a t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang=lg[0])"
156 t-att-data-default-lang="editable and 'true' if lg[0] == website.default_lang_code else None">
157 <t t-esc="lg[1].split('/').pop()"/>
158
159=== removed directory 'website_quote/static/lib'
160=== removed directory 'website_quote/static/lib/jSignature'
161=== removed file 'website_quote/static/lib/jSignature/jSignature.min.js'
162--- website_quote/static/lib/jSignature/jSignature.min.js 2014-01-18 10:46:34 +0000
163+++ website_quote/static/lib/jSignature/jSignature.min.js 1970-01-01 00:00:00 +0000
164@@ -1,77 +0,0 @@
165-/*
166-
167-jSignature v2 "2012-11-01T22:48" "commit ID 1c15dfafecc75925c3b7d529356a558b59220edb"
168-Copyright (c) 2012 Willow Systems Corp http://willow-systems.com
169-Copyright (c) 2010 Brinley Ang http://www.unbolt.net
170-MIT License <http://www.opensource.org/licenses/mit-license.php>
171-
172-
173-Simplify.js BSD
174-(c) 2012, Vladimir Agafonkin
175-mourner.github.com/simplify-js
176-
177-
178-base64 encoder
179-MIT, GPL
180-http://phpjs.org/functions/base64_encode
181-+ original by: Tyler Akins (http://rumkin.com)
182-+ improved by: Bayron Guevara
183-+ improved by: Thunder.m
184-+ improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
185-+ bugfixed by: Pellentesque Malesuada
186-+ improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
187-+ improved by: Rafal Kukawski (http://kukawski.pl)
188-
189-
190-jSignature v2 jSignature's Undo Button and undo functionality plugin
191-
192-
193-jSignature v2 jSignature's custom "base30" format export and import plugins.
194-
195-
196-jSignature v2 SVG export plugin.
197-
198-*/
199-(function(){function q(b){for(var j,s=b.css("color"),g,b=b[0],c=!1;b&&!g&&!c;){try{j=$(b).css("background-color")}catch(d){j="transparent"}"transparent"!==j&&"rgba(0, 0, 0, 0)"!==j&&(g=j);c=b.body;b=b.parentNode}var b=/rgb[a]*\((\d+),\s*(\d+),\s*(\d+)/,c=/#([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})/,a;j=void 0;(j=s.match(b))?a={r:parseInt(j[1],10),g:parseInt(j[2],10),b:parseInt(j[3],10)}:(j=s.match(c))&&(a={r:parseInt(j[1],16),g:parseInt(j[2],16),b:parseInt(j[3],16)});var f;g?
200-(j=void 0,(j=g.match(b))?f={r:parseInt(j[1],10),g:parseInt(j[2],10),b:parseInt(j[3],10)}:(j=g.match(c))&&(f={r:parseInt(j[1],16),g:parseInt(j[2],16),b:parseInt(j[3],16)})):f=a?127<Math.max.apply(null,[a.r,a.g,a.b])?{r:0,g:0,b:0}:{r:255,g:255,b:255}:{r:255,g:255,b:255};j=function(b){return"rgb("+[b.r,b.g,b.b].join(", ")+")"};a&&f?(b=Math.max.apply(null,[a.r,a.g,a.b]),a=Math.max.apply(null,[f.r,f.g,f.b]),a=Math.round(a+-0.75*(a-b)),a={r:a,g:a,b:a}):a?(a=Math.max.apply(null,[a.r,a.g,a.b]),b=1,127<a&&
201-(b=-1),a=Math.round(a+96*b),a={r:a,g:a,b:a}):a={r:191,g:191,b:191};return{color:s,"background-color":f?j(f):g,"decor-color":j(a)}}function m(b,j){this.x=b;this.y=j;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var s=function(b){return Math.round(b/Math.abs(b))};this.resizeTo=function(b){if(0===this.x&&0===this.y)this._length=0;else if(0===
202-this.x)this._length=b,this.y=b*s(this.y);else if(0===this.y)this._length=b,this.x=b*s(this.x);else{var j=Math.abs(this.y/this.x),a=Math.sqrt(Math.pow(b,2)/(1+Math.pow(j,2))),j=j*a;this._length=b;this.x=a*s(this.x);this.y=j*s(this.y)}return this};this.angleTo=function(b){var j=this.getLength()*b.getLength();return 0===j?0:Math.acos(Math.min(Math.max((this.x*b.x+this.y*b.y)/j,-1),1))/Math.PI}}function k(b,j){this.x=b;this.y=j;this.getVectorToCoordinates=function(b,j){return new m(b-this.x,j-this.y)};
203-this.getVectorFromCoordinates=function(b,j){return this.getVectorToCoordinates(b,j).reverse()};this.getVectorToPoint=function(b){return new m(b.x-this.x,b.y-this.y)};this.getVectorFromPoint=function(b){return this.getVectorToPoint(b).reverse()}}function t(b,j,a,g,c){this.data=b;this.context=j;if(b.length)for(var d=b.length,f,l,i=0;i<d;i++){f=b[i];l=f.x.length;a.call(j,f);for(var e=1;e<l;e++)g.call(j,f,e);c.call(j,f)}this.changed=function(){};this.startStrokeFn=a;this.addToStrokeFn=g;this.endStrokeFn=
204-c;this.inStroke=!1;this._stroke=this._lastPoint=null;this.startStroke=function(b){if(b&&"number"==typeof b.x&&"number"==typeof b.y){this._stroke={x:[b.x],y:[b.y]};this.data.push(this._stroke);this._lastPoint=b;this.inStroke=!0;var j=this._stroke,a=this.startStrokeFn,g=this.context;setTimeout(function(){a.call(g,j)},3);return b}return null};this.addToStroke=function(b){if(this.inStroke&&"number"===typeof b.x&&"number"===typeof b.y&&4<Math.abs(b.x-this._lastPoint.x)+Math.abs(b.y-this._lastPoint.y)){var j=
205-this._stroke.x.length;this._stroke.x.push(b.x);this._stroke.y.push(b.y);this._lastPoint=b;var a=this._stroke,g=this.addToStrokeFn,s=this.context;setTimeout(function(){g.call(s,a,j)},3);return b}return null};this.endStroke=function(){var b=this.inStroke;this.inStroke=!1;this._lastPoint=null;if(b){var j=this._stroke,a=this.endStrokeFn,g=this.context,s=this.changed;setTimeout(function(){a.call(g,j);s.call(g)},3);return!0}return null}}function r(b,j,a){var g=this.$parent=$(b),b=this.eventTokens={};this.events=
206-new v(this);var c=$.fn[e]("globalEvents"),d={width:"ratio",height:"ratio",sizeRatio:4,color:"#000","background-color":"#fff","decor-color":"#eee",lineWidth:0,minFatFingerCompensation:-10,showUndoButton:!1,data:[]};$.extend(d,q(g));j&&$.extend(d,j);this.settings=d;for(var f in a)a.hasOwnProperty(f)&&a[f].call(this,f);this.events.publish(e+".initializing");this.$controlbarUpper=$('<div style="padding:0 !important;margin:0 !important;width: 100% !important; height: 0 !important;margin-top:-1em !important;margin-bottom:1em !important;"></div>').appendTo(g);
207-this.isCanvasEmulator=!1;a=this.canvas=this.initializeCanvas(d);j=$(a);this.$controlbarLower=$('<div style="padding:0 !important;margin:0 !important;width: 100% !important; height: 0 !important;margin-top:-1.5em !important;margin-bottom:1.5em !important;"></div>').appendTo(g);this.canvasContext=a.getContext("2d");j.data(e+".this",this);g=(g=d.lineWidth)?g:Math.max(Math.round(a.width/400),2);d.lineWidth=g;this.lineCurveThreshold=3*d.lineWidth;d.cssclass&&""!=$.trim(d.cssclass)&&j.addClass(d.cssclass);
208-this.fatFingerCompensation=0;var g=function(b){var j,a,g=function(g){g=g.changedTouches&&0<g.changedTouches.length?g.changedTouches[0]:g;return new k(Math.round(g.pageX+j),Math.round(g.pageY+a)+b.fatFingerCompensation)},d=new y(750,function(){b.dataEngine.endStroke()});this.drawEndHandler=function(j){try{j.preventDefault()}catch(a){}d.clear();b.dataEngine.endStroke()};this.drawStartHandler=function(c){c.preventDefault();var s=$(b.canvas).offset();j=-1*s.left;a=-1*s.top;b.dataEngine.startStroke(g(c));
209-d.kick()};this.drawMoveHandler=function(j){j.preventDefault();b.dataEngine.inStroke&&(b.dataEngine.addToStroke(g(j)),d.kick())};return this}.call({},this),l=g.drawEndHandler,i=g.drawStartHandler,p=g.drawMoveHandler,h=this.canvas,j=$(h);this.isCanvasEmulator?(j.bind("mousemove."+e,p),j.bind("mouseup."+e,l),j.bind("mousedown."+e,i)):(h.ontouchstart=function(b){h.onmousedown=void 0;h.onmouseup=void 0;h.onmousemove=void 0;this.fatFingerCompensation=d.minFatFingerCompensation&&-3*d.lineWidth>d.minFatFingerCompensation?
210--3*d.lineWidth:d.minFatFingerCompensation;i(b);h.ontouchend=l;h.ontouchstart=i;h.ontouchmove=p},h.onmousedown=function(b){h.ontouchstart=void 0;h.ontouchend=void 0;h.ontouchmove=void 0;i(b);h.onmousedown=i;h.onmouseup=l;h.onmousemove=p});b[e+".windowmouseup"]=c.subscribe(e+".windowmouseup",g.drawEndHandler);this.events.publish(e+".attachingEventHandlers");var n=this,b=d.width.toString(10),w=e;if("ratio"===b||"%"===b.split("")[b.length-1])this.eventTokens[w+".parentresized"]=c.subscribe(w+".parentresized",
211-function(b,j,a){return function(){var g=j.width();if(g!==a){for(var d in b)b.hasOwnProperty(d)&&(c.unsubscribe(b[d]),delete b[d]);var s=n.settings;n.$parent.children().remove();for(d in n)n.hasOwnProperty(d)&&delete n[d];d=s.data;var g=1*g/a,f=[],l,i,e,h,k,p;i=0;for(e=d.length;i<e;i++){p=d[i];l={x:[],y:[]};h=0;for(k=p.x.length;h<k;h++)l.x.push(p.x[h]*g),l.y.push(p.y[h]*g);f.push(l)}s.data=f;j[w](s)}}}(this.eventTokens,this.$parent,this.$parent.width(),1*this.canvas.width/this.canvas.height));this.resetCanvas(d.data);
212-this.events.publish(e+".initialized");return this}var e="jSignature",y=function(b,j){var a;this.kick=function(){clearTimeout(a);a=setTimeout(j,b)};this.clear=function(){clearTimeout(a)};return this},v=function(b){this.topics={};this.context=b?b:this;this.publish=function(b,a,g,d){if(this.topics[b]){var c=this.topics[b],f=Array.prototype.slice.call(arguments,1),l=[],i,e,h,p;e=0;for(h=c.length;e<h;e++)p=c[e],i=p[0],p[1]&&(p[0]=function(){},l.push(e)),i.apply(this.context,f);e=0;for(h=l.length;e<h;e++)c.splice(l[e],
213-1)}};this.subscribe=function(b,a,g){this.topics[b]?this.topics[b].push([a,g]):this.topics[b]=[[a,g]];return{topic:b,callback:a}};this.unsubscribe=function(b){if(this.topics[b.topic])for(var a=this.topics[b.topic],g=0,d=a.length;g<d;g++)a[g][0]===b.callback&&a.splice(g,1)}},z=function(b){var a=this.canvasContext,d=b.x[0],b=b.y[0],g=this.settings.lineWidth,c=a.fillStyle;a.fillStyle=a.strokeStyle;a.fillRect(d+g/-2,b+g/-2,g,g);a.fillStyle=c},u=function(b,a){var d=new k(b.x[a-1],b.y[a-1]),g=new k(b.x[a],
214-b.y[a]),c=d.getVectorToPoint(g);if(1<a){var f=new k(b.x[a-2],b.y[a-2]),l=f.getVectorToPoint(d),i;if(l.getLength()>this.lineCurveThreshold){i=2<a?(new k(b.x[a-3],b.y[a-3])).getVectorToPoint(f):new m(0,0);var e=0.35*l.getLength(),h=l.angleTo(i.reverse()),p=c.angleTo(l.reverse());i=(new m(i.x+l.x,i.y+l.y)).resizeTo(Math.max(0.05,h)*e);var n=(new m(l.x+c.x,l.y+c.y)).reverse().resizeTo(Math.max(0.05,p)*e),l=this.canvasContext,e=f.x,p=f.y,h=d.x,w=d.y,A=f.x+i.x,f=f.y+i.y;i=d.x+n.x;n=d.y+n.y;l.beginPath();
215-l.moveTo(e,p);l.bezierCurveTo(A,f,i,n,h,w);l.stroke()}}c.getLength()<=this.lineCurveThreshold&&(c=this.canvasContext,f=d.x,d=d.y,i=g.x,g=g.y,c.beginPath(),c.moveTo(f,d),c.lineTo(i,g),c.stroke())},x=function(b){var a=b.x.length-1;if(0<a){var d=new k(b.x[a],b.y[a]),g=new k(b.x[a-1],b.y[a-1]),c=g.getVectorToPoint(d);if(c.getLength()>this.lineCurveThreshold){if(1<a){var b=(new k(b.x[a-2],b.y[a-2])).getVectorToPoint(g),f=(new m(b.x+c.x,b.y+c.y)).resizeTo(c.getLength()/2),c=this.canvasContext,b=g.x,a=g.y,
216-l=d.x,i=d.y,e=g.x+f.x,g=g.y+f.y,f=d.x,d=d.y;c.beginPath();c.moveTo(b,a);c.bezierCurveTo(e,g,f,d,l,i)}else c=this.canvasContext,b=g.x,g=g.y,a=d.x,d=d.y,c.beginPath(),c.moveTo(b,g),c.lineTo(a,d);c.stroke()}}};r.prototype.resetCanvas=function(b){var a=this.canvas,d=this.settings,c=this.canvasContext,f=this.isCanvasEmulator,l=a.width,i=a.height;c.clearRect(0,0,l+30,i+30);c.shadowColor=c.fillStyle=d["background-color"];f&&c.fillRect(0,0,l+30,i+30);c.lineWidth=Math.ceil(parseInt(d.lineWidth,10));c.lineCap=
217-c.lineJoin="round";c.strokeStyle=d["decor-color"];c.shadowOffsetX=0;c.shadowOffsetY=0;var h=Math.round(i/5),p=1.5*h,k=i-h,l=l-1.5*h,i=i-h;c.beginPath();c.moveTo(p,k);c.lineTo(l,i);c.stroke();c.strokeStyle=d.color;f||(c.shadowColor=c.strokeStyle,c.shadowOffsetX=0.5*c.lineWidth,c.shadowOffsetY=-0.6*c.lineWidth,c.shadowBlur=0);b||(b=[]);c=this.dataEngine=new t(b,this,z,u,x);d.data=b;$(a).data(e+".data",b).data(e+".settings",d);var n=this.$parent,w=this.events,A=e;c.changed=function(){w.publish(A+".change");
218-n.trigger("change")};c.changed();return!0};r.prototype.initializeCanvas=function(b){var a=document.createElement("canvas"),c=$(a);b.width===b.height&&"ratio"===b.height&&(b.width="100%");c.css("margin",0).css("padding",0).css("border","none").css("height","ratio"===b.height||!b.height?1:b.height.toString(10)).css("width","ratio"===b.width||!b.width?1:b.width.toString(10));c.appendTo(this.$parent);"ratio"===b.height?c.css("height",Math.round(c.width()/b.sizeRatio)):"ratio"===b.width&&c.css("width",
219-Math.round(c.height()*b.sizeRatio));c.addClass(e);a.width=c.width();a.height=c.height();b=a;if(b.getContext)b=!1;else{var c=b.ownerDocument.parentWindow,d=c.FlashCanvas?b.ownerDocument.parentWindow.FlashCanvas:"undefined"===typeof FlashCanvas?void 0:FlashCanvas;if(d){b=d.initElement(b);d=1;c&&(c.screen&&c.screen.deviceXDPI&&c.screen.logicalXDPI)&&(d=1*c.screen.deviceXDPI/c.screen.logicalXDPI);if(1!==d)try{$(b).children("object").get(0).resize(Math.ceil(b.width*d),Math.ceil(b.height*d)),b.getContext("2d").scale(d,
220-d)}catch(f){}b=!0}else throw Error("Canvas element does not support 2d context. jSignature cannot proceed.");}this.isCanvasEmulator=b;a.onselectstart=function(b){b&&b.preventDefault&&b.preventDefault();b&&b.stopPropagation&&b.stopPropagation();return!1};return a};var p=window,h=function(b,a){var c=new Image,d=this;c.onload=function(){d.getContext("2d").drawImage(c,0,0,c.width<d.width?c.width:d.width,c.height<d.height?c.height:d.height)};c.src="data:"+a+","+b},a=function(b){this.find("canvas."+e).add(this.filter("canvas."+
221-e)).data(e+".this").resetCanvas(b);return this},f=function(b,a){if(void 0===a&&("string"===typeof b&&"data:"===b.substr(0,5))&&(a=b.slice(5).split(",")[0],b=b.slice(6+a.length),a===b))return;var c=this.find("canvas."+e).add(this.filter("canvas."+e));if(n.hasOwnProperty(a))0!==c.length&&n[a].call(c[0],b,a,function(b){return function(){return b.resetCanvas.apply(b,arguments)}}(c.data(e+".this")));else throw Error(e+" is unable to find import plugin with for format '"+String(a)+"'");return this},d=new v,
222-c=e,l,i=function(){d.publish(c+".parentresized")};$(p).bind("resize."+c,function(){l&&clearTimeout(l);l=setTimeout(i,500)}).bind("mouseup."+c,function(){d.publish(c+".windowmouseup")});var w={},A={"default":function(){return this.toDataURL()},"native":function(b){return b},image:function(){var b=this.toDataURL();if("string"===typeof b&&4<b.length&&"data:"===b.slice(0,5)&&-1!==b.indexOf(",")){var a=b.indexOf(",");return[b.slice(5,a),b.substr(a+1)]}return[]}},n={"native":function(b,a,c){c(b)},image:h,
223-"image/png;base64":h,"image/jpeg;base64":h,"image/jpg;base64":h},B={"export":A,"import":n,instance:w},C={init:function(b){return this.each(function(){var a,c=!1;for(a=this.parentNode;a&&!c;)c=a.body,a=a.parentNode;!c||new r(this,b,w)})},getSettings:function(){return this.find("canvas."+e).add(this.filter("canvas."+e)).data(e+".this").settings},clear:a,reset:a,addPlugin:function(b,a,c){B.hasOwnProperty(b)&&(B[b][a]=c);return this},listPlugins:function(b){var a=[];if(B.hasOwnProperty(b)){var b=B[b],
224-c;for(c in b)b.hasOwnProperty(c)&&a.push(c)}return a},getData:function(b){var a=this.find("canvas."+e).add(this.filter("canvas."+e));void 0===b&&(b="default");if(0!==a.length&&A.hasOwnProperty(b))return A[b].call(a.get(0),a.data(e+".data"))},importData:f,setData:f,globalEvents:function(){return d},events:function(){return this.find("canvas."+e).add(this.filter("canvas."+e)).data(e+".this").events}};$.fn[e]=function(b){if(!b||"object"===typeof b)return C.init.apply(this,arguments);if("string"===typeof b&&
225-C[b])return C[b].apply(this,Array.prototype.slice.call(arguments,1));$.error("Method "+String(b)+" does not exist on jQuery."+e)}})();
226-(function(){$.fn.jSignature("addPlugin","instance","UndoButton",function(q){this.events.subscribe("jSignature.attachingEventHandlers",function(){if(this.settings[q]){var m=this.settings[q];"function"!==typeof m&&(m=function(){var e=$('<input type="button" value="Undo last stroke" style="position:absolute;display:none;margin:0 !important;top:auto" />').appendTo(this.$controlbarLower),k=e.width();e.css("left",Math.round((this.canvas.width-k)/2));k!==e.width()&&e.width(k);return e});var k=m.call(this),
227-t=this;t.events.subscribe("jSignature.change",function(){t.dataEngine.data.length?k.show():k.hide()});var r=this,e=(this.events.topics.hasOwnProperty("jSignature.undo")?q:"jSignature")+".undo";k.bind("click",function(){r.events.publish(e)});r.events.subscribe(e,function(){var e=r.dataEngine.data;e.length&&(e.pop(),r.resetCanvas(e))})}})})})();
228-(function(){for(var q={},m={},k="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX".split(""),t=k.length/2,r=t-1;-1<r;r--)q[k[r]]=k[r+t],m[k[r+t]]=k[r];var e=function(e){for(var e=e.split(""),h=e.length,a=1;a<h;a++)e[a]=q[e[a]];return e.join("")},y=function(k){for(var h=[],a=0,f=1,d=k.length,c,l,i=0;i<d;i++)c=Math.round(k[i]),l=c-a,a=c,0>l&&0<f?(f=-1,h.push("Z")):0<l&&0>f&&(f=1,h.push("Y")),c=Math.abs(l),c>=t?h.push(e(c.toString(t))):h.push(c.toString(t));return h.join("")},v=function(e){for(var h=
229-[],e=e.split(""),a=e.length,f,d=1,c=[],l=0,i=0;i<a;i++)f=e[i],f in q||"Z"===f||"Y"===f?(0!==c.length&&(c=parseInt(c.join(""),t)*d+l,h.push(c),l=c),"Z"===f?(d=-1,c=[]):"Y"===f?(d=1,c=[]):c=[f]):c.push(m[f]);h.push(parseInt(c.join(""),t)*d+l);return h},z=function(e){for(var h=[],a=e.length,f,d=0;d<a;d++)f=e[d],h.push(y(f.x)),h.push(y(f.y));return h.join("_")},u=function(e){for(var h=[],e=e.split("_"),a=e.length/2,f=0;f<a;f++)h.push({x:v(e[2*f]),y:v(e[2*f+1])});return h},k=function(e){return["image/jsignature;base30",
230-z(e)]},r=function(e,h,a){"string"===typeof e&&("image/jsignature;base30"===e.substring(0,23).toLowerCase()&&(e=e.substring(24)),a(u(e)))};if(null==this.jQuery)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");var x=this.jQuery.fn.jSignature;x("addPlugin","export","base30",k);x("addPlugin","export","image/jsignature;base30",k);x("addPlugin","import","base30",r);x("addPlugin","import","image/jsignature;base30",r);this.jSignatureDebug&&(this.jSignatureDebug.base30=
231-{remapTailChars:e,compressstrokeleg:y,uncompressstrokeleg:v,compressstrokes:z,uncompressstrokes:u,charmap:q})}).call("undefined"!==typeof window?window:this);
232-(function(){function q(a,f){this.x=a;this.y=f;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var d=function(a){return Math.round(a/Math.abs(a))};this.resizeTo=function(a){if(0===this.x&&0===this.y)this._length=0;else if(0===this.x)this._length=a,this.y=a*d(this.y);else if(0===this.y)this._length=a,this.x=a*d(this.x);else{var f=Math.abs(this.y/
233-this.x),e=Math.sqrt(Math.pow(a,2)/(1+Math.pow(f,2))),f=f*e;this._length=a;this.x=e*d(this.x);this.y=f*d(this.y)}return this};this.angleTo=function(a){var d=this.getLength()*a.getLength();return 0===d?0:Math.acos(Math.min(Math.max((this.x*a.x+this.y*a.y)/d,-1),1))/Math.PI}}function m(a,f){this.x=a;this.y=f;this.getVectorToCoordinates=function(a,c){return new q(a-this.x,c-this.y)};this.getVectorFromCoordinates=function(a,c){return this.getVectorToCoordinates(a,c).reverse()};this.getVectorToPoint=function(a){return new q(a.x-
234-this.x,a.y-this.y)};this.getVectorFromPoint=function(a){return this.getVectorToPoint(a).reverse()}}function k(a,f){var d=Math.pow(10,f);return Math.round(a*d)/d}function t(a,f,d){var f=f+1,c=new m(a.x[f-1],a.y[f-1]),e=new m(a.x[f],a.y[f]),e=c.getVectorToPoint(e),i=new m(a.x[f-2],a.y[f-2]),c=i.getVectorToPoint(c);return c.getLength()>d?(d=2<f?(new m(a.x[f-3],a.y[f-3])).getVectorToPoint(i):new q(0,0),a=0.35*c.getLength(),i=c.angleTo(d.reverse()),f=e.angleTo(c.reverse()),d=(new q(d.x+c.x,d.y+c.y)).resizeTo(Math.max(0.05,
235-i)*a),e=(new q(c.x+e.x,c.y+e.y)).reverse().resizeTo(Math.max(0.05,f)*a),e=new q(c.x+e.x,c.y+e.y),["c",k(d.x,2),k(d.y,2),k(e.x,2),k(e.y,2),k(c.x,2),k(c.y,2)]):["l",k(c.x,2),k(c.y,2)]}function r(a,f){var d=a.x.length-1,c=new m(a.x[d],a.y[d]),e=new m(a.x[d-1],a.y[d-1]),c=e.getVectorToPoint(c);if(1<d&&c.getLength()>f){var d=(new m(a.x[d-2],a.y[d-2])).getVectorToPoint(e),e=c.angleTo(d.reverse()),i=0.35*c.getLength(),d=(new q(d.x+c.x,d.y+c.y)).resizeTo(Math.max(0.05,e)*i);return["c",k(d.x,2),k(d.y,2),k(c.x,
236-2),k(c.y,2),k(c.x,2),k(c.y,2)]}return["l",k(c.x,2),k(c.y,2)]}function e(a,e,d){for(var e=["M",k(a.x[0]-e,2),k(a.y[0]-d,2)],d=1,c=a.x.length-1;d<c;d++)e.push.apply(e,t(a,d,1));0<c?e.push.apply(e,r(a,d,1)):0===c&&e.push.apply(e,["l",1,1]);return e.join(" ")}function y(a){var f=['<?xml version="1.0" encoding="UTF-8" standalone="no"?>','<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'],d,c=a.length,l,i=[],h=[],k=l=d=0,n=0,p=[];if(0!==c){for(d=0;d<c;d++){k=
237-a[d];n=[];l={x:[],y:[]};for(var m=void 0,b=void 0,m=0,b=k.x.length;m<b;m++)n.push({x:k.x[m],y:k.y[m]});n=simplify(n,0.7,!0);m=0;for(b=n.length;m<b;m++)l.x.push(n[m].x),l.y.push(n[m].y);p.push(l);i=i.concat(l.x);h=h.concat(l.y)}a=Math.min.apply(null,i)-1;c=Math.max.apply(null,i)+1;i=Math.min.apply(null,h)-1;h=Math.max.apply(null,h)+1;k=0>a?0:a;n=0>i?0:i;d=c-a;l=h-i}f.push('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'+d.toString()+'" height="'+l.toString()+'">');d=0;for(c=p.length;d<
238-c;d++)l=p[d],f.push('<path fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="'+e(l,k,n)+'"/>');f.push("</svg>");return f.join("")}function v(a){return[p,y(a)]}function z(a){return[h,x(y(a))]}var u=window;"use strict";("undefined"!=typeof exports?exports:u).simplify=function(a,e,d){e=void 0!==e?e*e:1;if(!d){for(var c=a.length,l,i=a[0],h=[i],d=1;d<c;d++){l=a[d];var k=l.x-i.x,n=l.y-i.y;k*k+n*n>e&&(h.push(l),i=l)}a=(i!==l&&h.push(l),h)}l=a;var d=l.length,
239-c=new ("undefined"!=typeof Uint8Array?Uint8Array:Array)(d),i=0,h=d-1,m,p,b=[],j=[],s=[];for(c[i]=c[h]=1;h;){n=0;for(k=i+1;k<h;k++){m=l[k];var g=l[i],r=l[h],q=g.x,t=g.y,g=r.x-q,u=r.y-t,v=void 0;if(0!==g||0!==u)v=((m.x-q)*g+(m.y-t)*u)/(g*g+u*u),1<v?(q=r.x,t=r.y):0<v&&(q+=g*v,t+=u*v);m=(g=m.x-q,u=m.y-t,g*g+u*u);m>n&&(p=k,n=m)}n>e&&(c[p]=1,b.push(i),j.push(p),b.push(p),j.push(h));i=b.pop();h=j.pop()}for(k=0;k<d;k++)c[k]&&s.push(l[k]);return a=s,a};if("function"!==typeof x)var x=function(a){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(""),
240-d,c,h,i,k=0,m=0,n="",n=[];do d=a.charCodeAt(k++),c=a.charCodeAt(k++),h=a.charCodeAt(k++),i=d<<16|c<<8|h,d=i>>18&63,c=i>>12&63,h=i>>6&63,i&=63,n[m++]=e[d]+e[c]+e[h]+e[i];while(k<a.length);n=n.join("");a=a.length%3;return(a?n.slice(0,a-3):n)+"===".slice(a||3)};var p="image/svg+xml",h="image/svg+xml;base64";if("undefined"===typeof $)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");u=$.fn.jSignature;u("addPlugin","export","svg",v);u("addPlugin",
241-"export",p,v);u("addPlugin","export","svgbase64",z);u("addPlugin","export",h,z)})();
242\ No newline at end of file
243
244=== modified file 'website_quote/views/website_quotation.xml'
245--- website_quote/views/website_quotation.xml 2014-01-28 20:50:17 +0000
246+++ website_quote/views/website_quotation.xml 2014-05-15 14:05:22 +0000
247@@ -163,7 +163,7 @@
248 <t t-call="website.layout">
249 <t t-set="head">
250 <script type="text/javascript" src="/website_quote/static/src/js/website_quotation.js"></script>
251- <script type="text/javascript" src="/website_quote/static/lib/jSignature/jSignature.min.js"></script>
252+ <script type="text/javascript" src="/website/static/lib/jSignature/jSignature.min.js"></script>
253 <link rel='stylesheet' href='/website_quote/static/src/css/website_quotation.css'/>
254 <t t-raw="head or ''"/>
255 </t>
256@@ -183,7 +183,7 @@
257 <div class="mt8" t-if="order_valid">
258 <a type="submit" href="#discussion">
259 Ask Changes
260- </a> or
261+ </a> or
262 <a data-toggle="modal" data-target="#modeldecline">
263 Reject
264 </a>
265@@ -210,7 +210,7 @@
266
267 <div class="text-center mb16" t-if="quotation.amount_undiscounted &gt; quotation.amount_total">
268 <p class="text-muted mb8">Your advantage:</p>
269- <strong t-field="quotation.amount_total"
270+ <strong t-field="quotation.amount_total"
271 t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'/>
272 <strong t-field="quotation.amount_undiscounted"
273 t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'
274@@ -257,7 +257,7 @@
275 <p>
276 I agree that by signing this proposal, I
277 accept it on the behalf of <b t-field="quotation.company_id"/>,
278- for an amount of
279+ for an amount of
280 <b data-id="total_amount" t-field="quotation.amount_total"
281 t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'/>
282 with payment terms: <b t-field="quotation.payment_term"/>.
283@@ -361,12 +361,12 @@
284 <a t-att-id="line.id"/>
285 <div t-field="line.website_description"/>
286 </t>
287-
288+
289 <div class="oe_structure"/>
290
291 <a id="pricing"/>
292 <t t-call="website_quote.pricing"/>
293-
294+
295 <a id="options"/>
296 <t t-call="website_quote.optional_products"/>
297
298
299=== added directory 'website_sign'
300=== added file 'website_sign/__init__.py'
301--- website_sign/__init__.py 1970-01-01 00:00:00 +0000
302+++ website_sign/__init__.py 2014-05-15 14:05:22 +0000
303@@ -0,0 +1,25 @@
304+# -*- coding: utf-8 -*-
305+##############################################################################
306+#
307+# OpenERP, Open Source Management Solution
308+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
309+#
310+# This program is free software: you can redistribute it and/or modify
311+# it under the terms of the GNU Affero General Public License as
312+# published by the Free Software Foundation, either version 3 of the
313+# License, or (at your option) any later version.
314+#
315+# This program is distributed in the hope that it will be useful,
316+# but WITHOUT ANY WARRANTY; without even the implied warranty of
317+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
318+# GNU Affero General Public License for more details.
319+#
320+# You should have received a copy of the GNU Affero General Public License
321+# along with this program. If not, see <http://www.gnu.org/licenses/>.
322+#
323+##############################################################################
324+import controllers
325+import models
326+
327+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
328+
329
330=== added file 'website_sign/__openerp__.py'
331--- website_sign/__openerp__.py 1970-01-01 00:00:00 +0000
332+++ website_sign/__openerp__.py 2014-05-15 14:05:22 +0000
333@@ -0,0 +1,46 @@
334+# -*- coding: utf-8 -*-
335+##############################################################################
336+#
337+# OpenERP, Open Source Management Solution
338+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
339+#
340+# This program is free software: you can redistribute it and/or modify
341+# it under the terms of the GNU Affero General Public License as
342+# published by the Free Software Foundation, either version 3 of the
343+# License, or (at your option) any later version.
344+#
345+# This program is distributed in the hope that it will be useful,
346+# but WITHOUT ANY WARRANTY; without even the implied warranty of
347+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
348+# GNU Affero General Public License for more details.
349+#
350+# You should have received a copy of the GNU Affero General Public License
351+# along with this program. If not, see <http://www.gnu.org/licenses/>.
352+#
353+##############################################################################
354+
355+
356+{
357+ 'name': 'Digital Sign on Document',
358+ 'version': '1.0',
359+ 'category': 'Website',
360+ 'description': """
361+Digital sign on attached Document.
362+===================================
363+
364+ """,
365+ 'author': 'OpenERP SA',
366+ 'website': 'http://www.openerp.com',
367+ 'depends': ['website'],
368+ 'data': [
369+ 'security/ir.model.access.csv',
370+ 'views/website_sign.xml',
371+ 'data/website_sign_data.xml',
372+ ],
373+ 'demo': [],
374+ 'qweb': [
375+ 'static/src/xml/*.xml'
376+ ],
377+ 'installable': True,
378+}
379+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
380
381=== added directory 'website_sign/controllers'
382=== added file 'website_sign/controllers/__init__.py'
383--- website_sign/controllers/__init__.py 1970-01-01 00:00:00 +0000
384+++ website_sign/controllers/__init__.py 2014-05-15 14:05:22 +0000
385@@ -0,0 +1,3 @@
386+import main
387+
388+# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
389
390=== added file 'website_sign/controllers/main.py'
391--- website_sign/controllers/main.py 1970-01-01 00:00:00 +0000
392+++ website_sign/controllers/main.py 2014-05-15 14:05:22 +0000
393@@ -0,0 +1,183 @@
394+# -*- coding: utf-8 -*-
395+##############################################################################
396+#
397+# OpenERP, Open Source Management Solution
398+# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
399+#
400+# This program is free software: you can redistribute it and/or modify
401+# it under the terms of the GNU Affero General Public License as
402+# published by the Free Software Foundation, either version 3 of the
403+# License, or (at your option) any later version.
404+#
405+# This program is distributed in the hope that it will be useful,
406+# but WITHOUT ANY WARRANTY; without even the implied warranty of
407+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
408+# GNU Affero General Public License for more details.
409+#
410+# You should have received a copy of the GNU Affero General Public License
411+# along with this program. If not, see <http://www.gnu.org/licenses/>.
412+#
413+##############################################################################
414+
415+from openerp import SUPERUSER_ID
416+from openerp.addons.web import http
417+from openerp.addons.web.http import request
418+from openerp.addons.website.models import website
419+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
420+import time
421+from openerp.addons.web.controllers.main import login_redirect
422+from openerp.tools.translate import _
423+
424+class website_sign(http.Controller):
425+ @http.route([
426+ "/sign/document/<int:id>",
427+ "/sign/document/<int:id>/<token>"
428+ ], type='http', auth="public", website=True)
429+ def request_sign(self, id, token=None, message=False, **post):
430+ if not request.session.uid:
431+ return login_redirect()
432+ current_sign = None
433+ ir_attachment_signature = request.registry.get('ir.attachment.signature')
434+ ir_attachment = request.registry.get('ir.attachment')
435+
436+ if token:
437+ signature_id = ir_attachment_signature.search(request.cr, request.uid,[('document_id', '=', id),('access_token', '=', token)], context=request.context)
438+ if not signature_id:
439+ return request.website.render('website.404')
440+ current_sign = ir_attachment_signature.browse(request.cr, request.uid, signature_id[0])
441+
442+ # list out partners and their signatures who are requested to sign.
443+ request_sign = ir_attachment_signature.search(request.cr, request.uid,[('document_id', '=', id)], context=request.context)
444+ signatures = ir_attachment_signature.browse(request.cr, SUPERUSER_ID, request_sign)
445+ req_count = [signs.id for signs in signatures if signs.state == 'draft']
446+ attachment = ir_attachment.browse(request.cr, SUPERUSER_ID, id)
447+ record = request.registry.get(attachment.res_model).browse(request.cr, token and SUPERUSER_ID or request.uid, attachment.res_id)
448+
449+ values = {
450+ 'attachment_id': id,
451+ 'signatures': signatures,
452+ 'current_sign': current_sign,
453+ 'token': token,
454+ 'attachment': attachment,
455+ 'record': record,
456+ 'sign_req': req_count,
457+ 'message': message and int(message) or False
458+ }
459+
460+ return request.website.render('website_sign.doc_sign', values)
461+
462+ @http.route(['/website_sign/signed'], type='json', auth="public", website=True)
463+ def signed(self, res_id=None, token=None, sign=None, signer=None, **post):
464+ ir_attachment_signature = request.registry.get('ir.attachment.signature')
465+ signature_id = ir_attachment_signature.search(request.cr, request.uid,[('document_id', '=', int(res_id)),('access_token', '=', token)], context=request.context)
466+ ir_attachment_signature.write(request.cr, request.uid, signature_id[0], {'state': 'closed', 'signing_date': time.strftime(DEFAULT_SERVER_DATE_FORMAT), 'sign': sign, 'signer_name': signer}, context=request.context)
467+
468+ #send mail and notification in chatter about signed by user.
469+ model = request.registry.get('ir.attachment').search_read(request.cr, request.uid, [('id', '=', int(res_id))], ['res_model', 'res_id', 'name'], context=request.context)[0]
470+ thread_model = model['res_model']
471+ thread_id = model['res_id']
472+ message = _('Document <b>%s</b> signed by %s') % (model['name'], signer)
473+ self.__message_post(message, thread_model, thread_id, type='comment', subtype='mt_comment')
474+
475+ return True
476+
477+ def __message_post(self, message, thread_model, thread_id, type='comment', subtype=False, attachments=[]):
478+ request.session.body = message
479+ cr, uid, context = request.cr, request.uid, request.context
480+ user = request.registry['res.users'].browse(cr, SUPERUSER_ID, uid, context=context)
481+ if 'body' in request.session and request.session.body:
482+ context.update({'notify_author': True})
483+ request.registry.get(thread_model).message_post(cr, SUPERUSER_ID, thread_id,
484+ body=request.session.body,
485+ type=type,
486+ subtype=subtype,
487+ author_id=user.partner_id.id,
488+ partner_ids=[user.partner_id.id],
489+ context=context,
490+ )
491+ request.session.body = False
492+ return True
493+
494+ @http.route(['/website_sign/get_followers'], type='json', auth="public", website=True)
495+ def get_followers(self, thread_id=None, attachment_id=None, model=None, **post):
496+ partner_id = (request.registry.get('res.users').browse(request.cr, request.uid, request.uid)).partner_id.id
497+
498+ fol_obj = request.registry.get('mail.followers')
499+ fol_ids = fol_obj.search(request.cr, SUPERUSER_ID, [('res_model', '=', model), ('res_id', 'in', [thread_id])])
500+
501+ # get already selected signers
502+ sel_fol_obj = request.registry.get('ir.attachment.signature')
503+ sel_follower = sel_fol_obj.search_read(request.cr, SUPERUSER_ID,[('document_id','=', attachment_id)],['partner_id'], context=request.context)
504+ sel_fol_ids = map(lambda d: d['partner_id'][0], sel_follower)
505+
506+ res = []
507+ followers_data = {}
508+ for follower in fol_obj.browse(request.cr, SUPERUSER_ID, fol_ids):
509+ if not partner_id == follower.partner_id.id:
510+ if follower.partner_id.id in sel_fol_ids:
511+ res.append({'followers_id': follower.partner_id.id, 'name': follower.partner_id.name, 'email': follower.partner_id.email, 'selected':'checked'})
512+ else:
513+ res.append({'followers_id': follower.partner_id.id, 'name': follower.partner_id.name, 'email': follower.partner_id.email, 'selected': None})
514+ followers_data['signer_data'] = res
515+
516+ #get title and comments of attachment
517+ doc_data = request.registry.get('ir.attachment').search_read(request.cr, SUPERUSER_ID,[('id','=', attachment_id)],['name','description'], context=request.context)
518+ followers_data['doc_data'] = doc_data
519+
520+ return followers_data
521+
522+ @http.route(['/website_sign/set_signer'], type='json', auth="public", website=True)
523+ def set_signer(self, attachment_id=None, signer_id=None, title=None, comments=None, **post):
524+ ir_attachment_signature = request.registry.get('ir.attachment.signature')
525+ vals, att_vals = {}, {}
526+
527+ set_fol = ir_attachment_signature.search_read(request.cr, SUPERUSER_ID,[('document_id','=', attachment_id)],['partner_id'], context=request.context)
528+ set_fol_ids = map(lambda d: d['partner_id'][0], set_fol)
529+
530+ attach_data = request.registry.get('ir.attachment').search_read(request.cr, SUPERUSER_ID,[('id','=', attachment_id)],['name','description'], context=request.context)[0]
531+ if attach_data['name'] != title:
532+ att_vals['name'] = title
533+ if attach_data['description'] != comments:
534+ att_vals['description'] = comments
535+
536+ if att_vals:
537+ request.registry.get('ir.attachment').write(request.cr, request.uid, attachment_id, att_vals, context=request.context)
538+
539+ if not set(signer_id) == set(set_fol_ids):
540+ for partner in set_fol_ids:
541+ for doc_id in set_fol:
542+ if doc_id['partner_id'][0] == partner:
543+ ir_attachment_signature.unlink(request.cr, SUPERUSER_ID, [doc_id['id']], context=request.context)
544+
545+ for signer in signer_id:
546+ vals['partner_id'] = signer
547+ vals['document_id'] = attachment_id
548+ vals['state'] = 'draft'
549+ vals['date'] = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
550+ ir_attachment_signature.create(request.cr, request.uid, vals, context=request.context)
551+
552+ return True
553+
554+ @http.route(['/website_sign/get_signer'], type='json', auth="public", website=True)
555+ def get_signer(self, attachment_ids=None, **post):
556+ ir_attachment_signature = request.registry.get('ir.attachment.signature')
557+ signer_obj = ir_attachment_signature.search(request.cr, request.uid, [('document_id', 'in', attachment_ids)], context=request.context)
558+ signers = ir_attachment_signature.browse(request.cr, SUPERUSER_ID, signer_obj)
559+ signer_ids = map(lambda d: d.partner_id.id, signers)
560+
561+ signers_data = {}
562+ for sign_id in signer_ids:
563+ signers_data[sign_id] = []
564+ for doc in signers:
565+ if sign_id == doc.partner_id.id:
566+ signers_data[sign_id].append({'id': doc.document_id.id,'name': doc.document_id.name, 'token': doc.access_token, 'fname': doc.document_id.datas_fname})
567+ return signers_data
568+
569+ @http.route(['/sign/document/<int:id>/<token>/note'], type='http', auth="public", website=True)
570+ def post_note(self, id, token, **post):
571+ record = request.registry.get('ir.attachment').search_read(request.cr, request.uid,[('id', '=', id)], ['res_id', 'res_model'], context=request.context)[0]
572+ message = post.get('comment')
573+
574+ if message:
575+ self.__message_post(message, record['res_model'], record['res_id'], type='comment', subtype='mt_comment')
576+ return request.redirect("/sign/document/%s/%s?message=1" % (id, token))
577
578=== added directory 'website_sign/data'
579=== added file 'website_sign/data/website_sign_data.xml'
580--- website_sign/data/website_sign_data.xml 1970-01-01 00:00:00 +0000
581+++ website_sign/data/website_sign_data.xml 2014-05-15 14:05:22 +0000
582@@ -0,0 +1,66 @@
583+<?xml version="1.0"?>
584+<openerp>
585+ <data>
586+ <record id="request_sign_template" model="email.template">
587+ <field name="name">Document Sign Request</field>
588+ <field name="model_id" ref="website_sign.model_ir_attachment_signature"/>
589+ <field name="auto_delete" eval="True"/>
590+ <field name="email_from">${ctx['email_from']}</field>
591+ <field name="email_to" >${ctx['email_to']}</field>
592+ <field name="body_html"><![CDATA[
593+ <center>
594+ <table width = "60%">
595+ <tbody>
596+ <tr>
597+ <td style="background-color:#ffffff;border:1px solid #ccc;padding:10px 10px 5px 10px" bgcolor="FFFFFF" align="center">
598+ <h1 style="font-size:14px">
599+ ${ctx['email_from_usr']} has requested your signature
600+ </h1>
601+ </td>
602+ </tr>
603+ </tbody>
604+ </table>
605+ <table width="60%">
606+ <tbody>
607+ <tr>
608+ <td style="background-color:#ffffff;border:1px solid #ccc;padding:40px 40px 30px 40px" bgcolor="FFFFFF">
609+ <h1 style="font-size:18px;margin:0 0 10px 0;font-weight:bold">${ctx['docnames']}</h1>
610+ <p style="line-height:1.4em;font-size:14px;margin:10px 0px">
611+ <span style="color:#8c8c8c">From: ${ctx['email_from_usr']} -
612+ <a href="mailto:${ctx['email_from']}?subject=RE:" style="color:#368bc6;text-decoration:none" target="_blank">Reply</a>
613+ </span>
614+ </p>
615+ % if ctx['msgbody']:
616+ <hr style="color:#cccccc;background-color:#cccccc;min-height:1px;border:none"/>
617+ <p style="margin:20px 0px 20px 0px">
618+ ${ctx['msgbody'] | safe}
619+ </p>
620+ % endif
621+ <hr style="color:#cccccc;background-color:#cccccc;min-height:1px;border:none"/>
622+ <div style="margin:20px 0px 20px 0px">
623+ <table>
624+ <tbody>
625+ <tr>
626+ % for link in ctx['links']:
627+ <td style='padding:9px 16px 9px 16px;font-size:16px;color:#f7fbfd;background:#0088cc'>
628+ <a style='text-decoration:none;font-size:16px;color:#f7fbfd' href=${link[0]}>
629+ <span style='color:#f7fbfd'>Sign on ${link[1]}</span>
630+ </a>
631+ </td>
632+ % endfor
633+ </tr>
634+ </tbody>
635+ </table>
636+ </div>
637+ <hr style="color:#cccccc;background-color:#cccccc;min-height:1px;border:none">
638+ <p style="margin:10px 0px;font-size:14px;line-height:1.4em;color:#ff0000">Warning: do not forward this email to others or else they will be able to access your document.</p>
639+ </td>
640+ </tr>
641+ </tbody>
642+ </table>
643+ </center>
644+ ]]>
645+ </field>
646+ </record>
647+ </data>
648+</openerp>
649\ No newline at end of file
650
651=== added directory 'website_sign/models'
652=== added file 'website_sign/models/__init__.py'
653--- website_sign/models/__init__.py 1970-01-01 00:00:00 +0000
654+++ website_sign/models/__init__.py 2014-05-15 14:05:22 +0000
655@@ -0,0 +1,1 @@
656+import website_sign
657
658=== added file 'website_sign/models/website_sign.py'
659--- website_sign/models/website_sign.py 1970-01-01 00:00:00 +0000
660+++ website_sign/models/website_sign.py 2014-05-15 14:05:22 +0000
661@@ -0,0 +1,257 @@
662+# -*- coding: utf-8 -*-
663+##############################################################################
664+#
665+# OpenERP, Open Source Management Solution
666+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
667+#
668+# This program is free software: you can redistribute it and/or modify
669+# it under the terms of the GNU Affero General Public License as
670+# published by the Free Software Foundation, either version 3 of the
671+# License, or (at your option) any later version.
672+#
673+# This program is distributed in the hope that it will be useful,
674+# but WITHOUT ANY WARRANTY; without even the implied warranty of
675+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
676+# GNU Affero General Public License for more details.
677+#
678+# You should have received a copy of the GNU Affero General Public License
679+# along with this program. If not, see <http://www.gnu.org/licenses/>.
680+#
681+##############################################################################
682+import uuid,re
683+
684+from openerp import tools
685+from openerp import SUPERUSER_ID
686+from openerp.osv import osv, orm, fields
687+from openerp.tools.translate import _
688+
689+class prepare_signers(osv.Model):
690+ _inherit = "mail.compose.message"
691+
692+ def get_mail_values(self, cr, uid, wizard, res_ids, context=None):
693+ attachment_ids = [attach.id for attach in wizard.attachment_ids]
694+ ir_attachment_signature = self.pool.get('ir.attachment.signature')
695+ signer_obj = ir_attachment_signature.search(cr, uid, [('document_id', 'in', attachment_ids)], context=context)
696+ signers = ir_attachment_signature.browse(cr, SUPERUSER_ID, signer_obj)
697+ signer_ids = map(lambda d: d.partner_id.id, signers)
698+
699+ signers_data = {}
700+ for sign_id in signer_ids:
701+ signers_data[sign_id] = []
702+ for doc in signers:
703+ if sign_id == doc.partner_id.id:
704+ signers_data[sign_id].append({'id': doc.document_id.id,'name': doc.document_id.name, 'token': doc.access_token, 'fname': doc.document_id.datas_fname})
705+ if signers_data:
706+ context.update({'signers_data': signers_data})
707+ return super(prepare_signers, self).get_mail_values(cr, uid, wizard, res_ids, context=context)
708+
709+class preprocess_attachment(osv.Model):
710+ _inherit = "mail.message"
711+
712+ def _message_read_dict_postprocess(self, cr, uid, messages, message_tree, context=None):
713+ """ Post-processing on values given by message_read. This method will
714+ handle partners in batch to avoid doing numerous queries.
715+
716+ :param list messages: list of message, as get_dict result
717+ :param dict message_tree: {[msg.id]: msg browse record}
718+ """
719+ res_partner_obj = self.pool.get('res.partner')
720+ ir_attachment_obj = self.pool.get('ir.attachment')
721+ pid = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=context).partner_id.id
722+
723+ # 1. Aggregate partners (author_id and partner_ids) and attachments
724+ partner_ids = set()
725+ attachment_ids = set()
726+ for key, message in message_tree.iteritems():
727+ if message.author_id:
728+ partner_ids |= set([message.author_id.id])
729+ if message.subtype_id and message.notified_partner_ids: # take notified people of message with a subtype
730+ partner_ids |= set([partner.id for partner in message.notified_partner_ids])
731+ elif not message.subtype_id and message.partner_ids: # take specified people of message without a subtype (log)
732+ partner_ids |= set([partner.id for partner in message.partner_ids])
733+ if message.attachment_ids:
734+ attachment_ids |= set([attachment.id for attachment in message.attachment_ids])
735+ # Read partners as SUPERUSER -> display the names like classic m2o even if no access
736+ partners = res_partner_obj.name_get(cr, SUPERUSER_ID, list(partner_ids), context=context)
737+ partner_tree = dict((partner[0], partner) for partner in partners)
738+
739+ # calculate total requested and signed on specific attachment.
740+ sign_obj = self.pool.get('ir.attachment.signature').search(cr, uid, [('document_id', 'in', list(attachment_ids))], context=context)
741+ req_ids = self.pool.get('ir.attachment.signature').browse(cr, SUPERUSER_ID, sign_obj)
742+ req_doc_ids = map(lambda d: d.document_id.id, req_ids)
743+ doc_data = {}
744+ for doc_id in req_doc_ids:
745+ doc_count = []
746+ doc_state = []
747+ for ids in req_ids:
748+ if doc_id == ids.document_id.id:
749+ doc_count.append(doc_id)
750+ if ids.state == 'closed':
751+ doc_state.append(ids.state)
752+ doc_data[doc_id] = {'signed': len(doc_state), 'count': len(doc_count)}
753+
754+ # 2. Attachments as SUPERUSER, because could receive msg and attachments for doc uid cannot see
755+ attachments = ir_attachment_obj.read(cr, SUPERUSER_ID, list(attachment_ids), ['id', 'datas_fname', 'name', 'file_type_icon'], context=context)
756+ attachments_tree = dict((attachment['id'], {
757+ 'id': attachment['id'],
758+ 'filename': attachment['datas_fname'],
759+ 'name': attachment['name'],
760+ 'file_type_icon': attachment['file_type_icon'],
761+ }) for attachment in attachments)
762+
763+ #add attachment data(count and draft state) into main attachment tree
764+ for item in doc_data:
765+ if item in attachments_tree:
766+ for leaf in doc_data[item]:
767+ attachments_tree[item][leaf] = doc_data[item][leaf]
768+ else:
769+ attachments_tree[item] = doc_data[item]
770+
771+ # 3. Update message dictionaries
772+ for message_dict in messages:
773+ message_id = message_dict.get('id')
774+ message = message_tree[message_id]
775+ if message.author_id:
776+ author = partner_tree[message.author_id.id]
777+ else:
778+ author = (0, message.email_from)
779+ partner_ids = []
780+ if message.subtype_id:
781+ partner_ids = [partner_tree[partner.id] for partner in message.notified_partner_ids
782+ if partner.id in partner_tree]
783+ else:
784+ partner_ids = [partner_tree[partner.id] for partner in message.partner_ids
785+ if partner.id in partner_tree]
786+ attachment_ids = []
787+ for attachment in message.attachment_ids:
788+ if attachment.id in attachments_tree:
789+ attachment_ids.append(attachments_tree[attachment.id])
790+ message_dict.update({
791+ 'is_author': pid == author[0],
792+ 'author_id': author,
793+ 'partner_ids': partner_ids,
794+ 'attachment_ids': attachment_ids,
795+ 'user_pid': pid
796+ })
797+ return True
798+
799+class website_sign(osv.Model):
800+ _inherit = 'mail.notification'
801+
802+ def get_partners_to_email(self, cr, uid, ids, message, context=None):
803+ """ Return the list of partners to notify, based on their preferences.
804+
805+ :param browse_record message: mail.message to notify
806+ :param list partners_to_notify: optional list of partner ids restricting
807+ the notifications to process
808+ """
809+ notify_pids = []
810+ for notification in self.browse(cr, uid, ids, context=context):
811+ if notification.read:
812+ continue
813+ partner = notification.partner_id
814+ # Do not send to partners without email address defined
815+ if not partner.email:
816+ continue
817+ # Do not send to partners having same email address than the author (can cause loops or bounce effect due to messy database)
818+ if message.author_id and message.author_id.email == partner.email:
819+ if not context.get('notify_author'):
820+ continue
821+ # Partner does not want to receive any emails or is opt-out
822+ if partner.notify_email == 'none':
823+ continue
824+ notify_pids.append(partner.id)
825+ return notify_pids
826+
827+ def _notify(self, cr, uid, message_id, partners_to_notify=None, context=None, force_send=False, user_signature=True):
828+ if context.get('signers_data'):
829+ for ids in context.get('signers_data'):
830+ super(website_sign, self)._notify(cr, uid, message_id, partners_to_notify=[int(ids)], context=context, force_send=force_send, user_signature=user_signature)
831+ else:
832+ super(website_sign, self)._notify(cr, uid, message_id, partners_to_notify=partners_to_notify, context=context, force_send=force_send, user_signature=user_signature)
833+
834+ def _notify_email(self, cr, uid, ids, message_id, force_send=False, user_signature=True, context=None):
835+ message = self.pool['mail.message'].browse(cr, uid, message_id, context=context)
836+ sign_pool = self.pool['ir.attachment.signature']
837+ attachment_pool = self.pool['ir.attachment']
838+ partner_pool = self.pool['res.partner']
839+
840+ # compute partners
841+ email_pids = self.get_partners_to_email(cr, uid, ids, message, context=context)
842+ if not email_pids:
843+ return True
844+
845+ # compute epartners_to_notify mail body (signature, company data)
846+ if context.get('signers_data'):
847+ local_context = context.copy()
848+ local_context['email_from_usr'] = re.sub('<.*>','',message.email_from)
849+ local_context['email_from'] = message.email_from
850+ docs, links = [], []
851+ signers_data = context.get('signers_data')
852+ template_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'website_sign', 'request_sign_template')[1]
853+ email_to = partner_pool.search_read(cr, uid, [('id', '=', email_pids[0])], ['email'],context=context)[0]
854+ local_context['email_to'] = email_to['email']
855+ for signer_ids in signers_data:
856+ if email_pids[0] == int(signer_ids):
857+ for signers in signers_data[signer_ids]:
858+ docs.append(signers['name'])
859+ res_id = attachment_pool.search_read(cr, uid, [('id', '=', signers['id'])], ['res_id'], context=context)[0]
860+ link = _("sign/document/%s/%s") % (signers['id'], signers['token'])
861+ links.append([link, signers['fname'].split('.')[:-1][0]])
862+ local_context['docnames'] = ", ".join(docs)
863+ local_context['msgbody'] = message.body
864+ local_context['links'] = links
865+ sign_template = self.pool['email.template'].generate_email_batch(cr, uid, template_id, [res_id['res_id']], context=local_context)
866+ body_html = sign_template[res_id['res_id']]['body_html']
867+ else:
868+ body_html = message.body
869+
870+ user_id = message.author_id and message.author_id.user_ids and message.author_id.user_ids[0] and message.author_id.user_ids[0].id or None
871+ if user_signature:
872+ signature_company = self.get_signature_footer(cr, uid, user_id, res_model=message.model, res_id=message.res_id, context=context)
873+ body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
874+
875+ # compute email references
876+ references = message.parent_id.message_id if message.parent_id else False
877+
878+ # create email values
879+ mail_values = {
880+ 'mail_message_id': message.id,
881+ 'auto_delete': True,
882+ 'body_html': body_html,
883+ 'recipient_ids': [(4, id) for id in email_pids],
884+ 'references': references,
885+ }
886+ email_notif_id = self.pool.get('mail.mail').create(cr, uid, mail_values, context=context)
887+ if force_send:
888+ self.pool.get('mail.mail').send(cr, uid, [email_notif_id], context=context)
889+ return True
890+
891+class ir_attachment(osv.Model):
892+ _inherit = 'ir.attachment'
893+ _columns = {
894+ 'signature_ids': fields.one2many('ir.attachment.signature', 'document_id', 'Signatures')
895+ }
896+
897+class ir_attachment_signature(osv.Model):
898+ _name = "ir.attachment.signature"
899+ _description = "Signature For Attachments"
900+ _columns = {
901+ 'partner_id' : fields.many2one('res.partner', 'Partner'),
902+ 'document_id' : fields.many2one('ir.attachment', 'Attachment', ondelete='cascade'),
903+ 'sign': fields.binary('Signature'),
904+ 'date': fields.date('Creation Date', help="Date of requesting Signature."),
905+ 'signing_date': fields.date('Creation Date', help="Date of signing Attachment ."),
906+ 'deadline_date': fields.date('Creation Date', readonly=True, select=True, help="Deadline to sign Attachment."),
907+ 'state': fields.selection([
908+ ('draft', 'To be signed'),
909+ ('closed', 'Signed'),
910+ ('cancelled', 'Cancelled'),
911+ ]),
912+ 'access_token': fields.char('Security Token', size=256, required=True),
913+ 'signer_name': fields.char('Signer Name', size=256),
914+ }
915+ _defaults = {
916+ 'access_token': lambda self, cr, uid, ctx={}: str(uuid.uuid4())
917+ }
918+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
919
920=== added directory 'website_sign/security'
921=== added file 'website_sign/security/ir.model.access.csv'
922--- website_sign/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
923+++ website_sign/security/ir.model.access.csv 2014-05-15 14:05:22 +0000
924@@ -0,0 +1,3 @@
925+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
926+"access_ir_attachment_signature_all","ir_attachment_signature all","model_ir_attachment_signature",,1,0,0,0
927+"access_ir_attachment_signature_group_user","ir_attachment_signature group_user","model_ir_attachment_signature","base.group_user",1,1,1,1
928
929=== added directory 'website_sign/static'
930=== added directory 'website_sign/static/src'
931=== added directory 'website_sign/static/src/css'
932=== added file 'website_sign/static/src/css/website_sign.css'
933--- website_sign/static/src/css/website_sign.css 1970-01-01 00:00:00 +0000
934+++ website_sign/static/src/css/website_sign.css 2014-05-15 14:05:22 +0000
935@@ -0,0 +1,83 @@
936+.openerp .oe_mail .oe_attachment .oe_sign{
937+ display: none;
938+}
939+.openerp .oe_mail .oe_msg_composer .oe_attachment .oe_sign{
940+ display: block;
941+ position: absolute;
942+ top: -15px;
943+ right: 0px;
944+ text-shadow: 1px 0px white, -1px 0px white, 0px 1px white, 0px -1px white;
945+ cursor: pointer;
946+ opacity: 0;
947+ -webkit-transition: opacity 0.2s linear;
948+ -moz-transition: opacity 0.2s linear;
949+ -o-transition: opacity 0.2s linear;
950+ transition: opacity 0.2s linear;
951+}
952+.openerp .oe_mail .oe_msg_composer .oe_attachment:hover .oe_sign{
953+ opacity: 1;
954+ -webkit-transition: opacity 0.2s linear;
955+ -moz-transition: opacity 0.2s linear;
956+ -o-transition: opacity 0.2s linear;
957+ transition: opacity 0.2s linear;
958+}
959+.openerp .oe_mail .oe_msg_composer .oe_attachment .oe_delete{
960+ top: -20px !important;
961+}
962+
963+.bs-sidebar {
964+ position: fixed;
965+ z-index : 1;
966+ background-color: #f7f5fa;
967+ border-radius: 5px;
968+}
969+
970+.media, .media-body {
971+ overflow: hidden;
972+ zoom: 1;
973+ background-color: #f7f5fa;
974+ width: 75%;
975+ padding: 5px 5px;
976+}
977+
978+@media (min-width: 992px) {
979+ .bs-sidebar {
980+ width: 213px;
981+ }
982+}
983+@media (min-width: 1200px) {
984+ .bs-sidebar {
985+ width: 263px;
986+ }
987+}
988+@media print {
989+ body {
990+ padding : 0 !important;
991+ }
992+}
993+.oe_lside_panel{
994+ padding: 10px;
995+ word-wrap:break-word;
996+}
997+
998+.openerp .oe_email{
999+ color: #777777;
1000+}
1001+.openerp .oe_img{
1002+ width:23px;
1003+}
1004+.openerp .table.oe_sel_followers td{
1005+ border-top: 0;
1006+ padding: 3px 3px;
1007+}
1008+.openerp .oe_sign_title{
1009+ padding:10px 15px;
1010+}
1011+.openerp .oe_sign_title span{
1012+ font-weight: bold;
1013+ font-size: 20px;
1014+ color: #7c7bad;
1015+}
1016+.openerp .oe_sign_comment{
1017+ padding:10px 30px;
1018+}
1019
1020=== added directory 'website_sign/static/src/img'
1021=== added file 'website_sign/static/src/img/sign.png'
1022Binary files website_sign/static/src/img/sign.png 1970-01-01 00:00:00 +0000 and website_sign/static/src/img/sign.png 2014-05-15 14:05:22 +0000 differ
1023=== added directory 'website_sign/static/src/js'
1024=== added file 'website_sign/static/src/js/sign.js'
1025--- website_sign/static/src/js/sign.js 1970-01-01 00:00:00 +0000
1026+++ website_sign/static/src/js/sign.js 2014-05-15 14:05:22 +0000
1027@@ -0,0 +1,200 @@
1028+$(document).ready(function () {
1029+ var empty_sign = false;
1030+ $('#modesign').on('shown.bs.modal', function (e) {
1031+ $("#sign").empty().jSignature({'decor-color' : '#D1D0CE'});
1032+ empty_sign = $("#sign").jSignature("getData",'image');
1033+ });
1034+
1035+ $('#sign_clean').on('click', function (e) {
1036+ $("#sign").jSignature('reset');
1037+ });
1038+
1039+
1040+ $('form.js_sign_json').submit(function(ev){
1041+ ev.preventDefault();
1042+ var $link = $(ev.currentTarget);
1043+ var href = $link.attr("action");
1044+ var attach_id = href.match(/signed\/([0-9]+)/);
1045+ var token = href.match(/token=(.*)/);
1046+ if (token)
1047+ token = token[1];
1048+ var signer_name = $("#signer_name").val();
1049+ var sign = $("#sign").jSignature("getData",'image');
1050+ var is_empty = sign?empty_sign[1]==sign[1]:false;
1051+ $('#signer').toggleClass('has-error', ! signer_name);
1052+ $('#drawsign').toggleClass('panel-danger', is_empty).toggleClass('panel-default', ! is_empty);
1053+
1054+ if (is_empty || ! signer_name)
1055+ return false;
1056+ $('#signed_req').prop('disabled',true);
1057+
1058+ openerp.jsonRpc("/website_sign/signed", 'call', {
1059+ 'res_id': parseInt(attach_id[1]),
1060+ 'token': token,
1061+ 'sign': sign?JSON.stringify(sign[1]):false,
1062+ 'signer': signer_name
1063+ }).then(function (data) {
1064+ $('#modesign').modal('hide');
1065+ window.location.href = '/sign/document/'+attach_id[1]+'/'+token+'?message=2';
1066+ });
1067+ return false
1068+ });
1069+});
1070+openerp.website_sign = function (session) {
1071+var QWeb = session.web.qweb,
1072+ _t = session.web._t;
1073+
1074+session.web.SignRequest = session.web.Widget.extend({
1075+ init: function (parent, attachment_id, res_id, model) {
1076+ this._super(parent);
1077+ this.attach_id = attachment_id;
1078+ this.res_id = res_id;
1079+ this.model = model;
1080+ },
1081+ get_followers: function () {
1082+ var self = this;
1083+ $('div.oe_edit_partner_list').remove();
1084+ openerp.jsonRpc("/website_sign/get_followers", 'call', {
1085+ 'thread_id': parseInt(self.res_id),
1086+ 'attachment_id': parseInt(self.attach_id),
1087+ 'model': self.model,
1088+ }).then(function (data) {
1089+ if (data.signer_data.length === 0) {
1090+ var dialog = new session.web.Dialog(this, {
1091+ size: 'small',
1092+ title: _t("Warning"),
1093+ buttons:[{
1094+ text: _t("Ok"),
1095+ click: function() {
1096+ this.parents('.modal').modal('hide');
1097+ }
1098+ }],
1099+ }, $("<div />").text(_t("This document doesn't have any followers, please add them."))).open();
1100+ return false;
1101+ }
1102+ self.$dialog = new session.web.Dialog(this, {
1103+ size: 'medium',
1104+ title: _t('Request Signature From'),
1105+ }, $('<div class="oe_edit_partner_list">' + QWeb.render('select.people', {"result": data, "attach_id": self.attach_id}))).open();
1106+ self.$dialog.$buttons.find('.oe_dialog_custom_buttons').empty();
1107+ self.$dialog.$buttons.find('.oe_dialog_custom_buttons').append('<button class="oe_button oe_form_button oe_highlight" type="button" id="request">Request Signature</button><span> or </span> <button class="oe_button oe_form_button oe_link" type="button" id="cancel_request"><span>Cancel</span></button>');
1108+ $('#request').click(function(event) {
1109+ self.request_followers();
1110+ });
1111+ $('#cancel_request').click(function(event) {
1112+ self.$dialog.$el.parents('.modal').modal('hide');
1113+ });
1114+ return false
1115+ });
1116+ },
1117+ request_followers: function () {
1118+ var self = this;
1119+ var attachment_id = this.$dialog.$el.find("#attach_id").val();
1120+ var title = this.$dialog.$el.find("#title").val();
1121+ var comments = this.$dialog.$el.find("#comments").val() || false;
1122+ var sign_ids = [];
1123+ var oe_action = $('div.oe_edit_partner_list input[type="checkbox"]');
1124+ _(oe_action).each(function (record) {
1125+ if ($(record).is(':checked')) {
1126+ sign_ids.push(parseInt($(record).data('id')));
1127+ }
1128+ });
1129+ $('#doc_title').toggleClass('has-error', !title);
1130+ if (!title) {
1131+ return false;
1132+ };
1133+ openerp.jsonRpc("/website_sign/set_signer", 'call', {
1134+ 'attachment_id': parseInt(attachment_id),
1135+ 'signer_id': sign_ids,
1136+ 'title': title,
1137+ 'comments': comments,
1138+ }).then(function () {
1139+ self.$dialog.$el.parents('.modal').modal('hide');
1140+ });
1141+ return false
1142+ },
1143+});
1144+
1145+session.mail.ThreadComposeMessage.include({
1146+ on_attachment_loaded: function (event, result) {
1147+ var self = this;
1148+ if (result.erorr || !result.id ) {
1149+ this.do_warn( session.web.qweb.render('mail.error_upload'), result.error);
1150+ this.attachment_ids = _.filter(this.attachment_ids, function (val) { return !val.upload; });
1151+ } else {
1152+ for (var i in this.attachment_ids) {
1153+ if (this.attachment_ids[i].filename == result.filename && this.attachment_ids[i].upload) {
1154+ this.attachment_ids[i]={
1155+ 'id': result.id,
1156+ 'name': result.name,
1157+ 'filename': result.filename,
1158+ 'url':session.mail.ChatterUtils.get_attachment_url(this.session, this.id, result.id)
1159+ };
1160+ }
1161+ }
1162+ }
1163+ this.display_attachments();
1164+ if (!self.is_log){
1165+ attach_ids = _.map(this.attachment_ids, function (file) {return file.id;});
1166+ for (var id in attach_ids) {
1167+ self.$el.find("[data-id='" + attach_ids[id] + "']").after(_.str.sprintf("<div id='request_sign' title='Request Signature'><img id='%s' src='/website_sign/static/src/img/sign.png' style='margin-top:32px; margin-left:-17px; height:20px; width:20px'/></div>", attach_ids[id]));
1168+ }
1169+ this.$('div #request_sign').addClass('oe_sign');
1170+ $('div#request_sign img').on('click', function (ev) {
1171+ var attach_id = ev.currentTarget.id;
1172+ var res_id = self.res_id;
1173+ var model = self.model;
1174+ var followers = new session.web.SignRequest(self, attach_id, res_id, model);
1175+ followers.get_followers();
1176+ });
1177+ }
1178+ var $input = this.$('input.oe_form_binary_file');
1179+ $input.after($input.clone(true)).remove();
1180+ this.$(".oe_attachment_file").show();
1181+ },
1182+ do_send_message_post: function (partner_ids, log) {
1183+ var self = this;
1184+ var attach_ids = _.map(this.attachment_ids, function (file) {return file.id;})
1185+ var parent = this._super;
1186+ var args = arguments;
1187+ openerp.jsonRpc("/website_sign/get_signer", 'call', {
1188+ 'attachment_ids': attach_ids,
1189+ }).then(function (res) {
1190+ var values = {
1191+ 'context': _.extend(self.parent_thread.context, {
1192+ 'signers_data': res,
1193+ }),
1194+ };
1195+ return parent.apply(self, args);
1196+ });
1197+ },
1198+});
1199+session.web.form.FieldMany2ManyBinaryMultiFiles.include({
1200+ render_value: function () {
1201+ var self = this;
1202+ this.read_name_values().then(function (ids) {
1203+ var render = $(session.web.qweb.render('FieldBinaryFileUploader.files', {'widget': self, 'values': ids}));
1204+ render.on('click', '.oe_delete', _.bind(self.on_file_delete, self));
1205+ self.$('.oe_placeholder_files, .oe_attachments').replaceWith( render );
1206+ if (!self.field_manager.datarecord.is_log) {
1207+ for (var id in ids) {
1208+ self.$el.find("[data-id='" + ids[id] + "']").after(_.str.sprintf("<div id='request_sign' title='Request Signature'><img id='%s' src='/website_sign/static/src/img/sign.png' style='margin-top:35px; margin-left:32px; height:20px; width:20px'/></div>", ids[id]));
1209+ }
1210+ $('div#request_sign img').on('click', function (ev) {
1211+ var attach_id = ev.currentTarget.id
1212+ var res_id = self.field_manager.datarecord.res_id;
1213+ var model = self.field_manager.datarecord.model;
1214+ var followers = new session.web.SignRequest(self, attach_id, res_id, model);
1215+ followers.get_followers();
1216+ });
1217+ }
1218+
1219+ // reinit input type file
1220+ var $input = self.$('input.oe_form_binary_file');
1221+ $input.after($input.clone(true)).remove();
1222+ self.$(".oe_fileupload").show();
1223+
1224+ });
1225+ },
1226+});
1227+};
1228\ No newline at end of file
1229
1230=== added directory 'website_sign/static/src/xml'
1231=== added file 'website_sign/static/src/xml/sign.xml'
1232--- website_sign/static/src/xml/sign.xml 1970-01-01 00:00:00 +0000
1233+++ website_sign/static/src/xml/sign.xml 2014-05-15 14:05:22 +0000
1234@@ -0,0 +1,58 @@
1235+<?xml version="1.0" encoding="UTF-8"?>
1236+<template>
1237+ <t t-name="select.people">
1238+ <input type="hidden" t-att-value="attach_id" id="attach_id"/>
1239+ <div class="row">
1240+ <div class="col-md-12 oe_sign_title">
1241+ <span>1. Who needs to sign ?</span>
1242+ </div>
1243+ </div>
1244+ <div class="table-responsive col-md-12">
1245+ <table class="table table-hover oe_sel_followers">
1246+ <t t-foreach='result.signer_data' t-as='res'>
1247+ <tr>
1248+ <td>
1249+ <t t-if="res.selected">
1250+ <input type="checkbox" t-att-checked="res.selected" t-att-id="'input_mail_followers_'+res.followers_id" t-att-data-id="res.followers_id" t-att-name="res.name"/>
1251+ </t>
1252+ <t t-if="!res.selected">
1253+ <input type="checkbox" t-att-id="'input_mail_followers_'+res.followers_id" t-att-data-id="res.followers_id" t-att-name="res.name"/>
1254+ </t>
1255+ </td>
1256+ <td>
1257+ <label t-att-for="'input_mail_followers_'+res.followers_id"><t t-raw="res.name"/></label>
1258+ </td>
1259+ <td class="oe_email">
1260+ <label t-att-for="'input_mail_followers_'+res.followers_id"><t t-raw="res.email"/></label>
1261+ </td>
1262+ <td>
1263+ <img class="oe_img" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+res.followers_id"></img>
1264+ </td>
1265+ </tr>
1266+ </t>
1267+ </table>
1268+ </div>
1269+ <div class="row">
1270+ <div class="col-md-12 oe_sign_title">
1271+ <span>2. Add document title and comments.</span>
1272+ </div>
1273+ </div>
1274+ <div class="row">
1275+ <div id="doc_title" class="col-md-12 oe_sign_comment">
1276+ <input id="title" type="text" class="form-control" placeholder="Title" t-att-value="result.doc_data[0].name"/>
1277+ </div>
1278+ </div>
1279+ <div class="row">
1280+ <div class="col-md-12 oe_sign_comment">
1281+ <textarea id="comments" rows="3" class="form-control" placeholder="Comments"><t t-raw="result.doc_data[0].description ? result.doc_data[0].description:'' "/></textarea>
1282+ </div>
1283+ </div>
1284+ </t>
1285+ <t t-extend="mail.thread.message.attachments">
1286+ <t t-jquery="div#attachments" t-operation="append">
1287+ <div style="text-align:center;" t-if="attachment.count">
1288+ <a t-att-href="'/sign/document/'+attachment.id" target="_blank"> <b>Signed(<t t-raw="attachment.signed+'/'+attachment.count"/>)</b> </a>
1289+ </div>
1290+ </t>
1291+ </t>
1292+</template>
1293
1294=== added directory 'website_sign/views'
1295=== added file 'website_sign/views/website_sign.xml'
1296--- website_sign/views/website_sign.xml 1970-01-01 00:00:00 +0000
1297+++ website_sign/views/website_sign.xml 2014-05-15 14:05:22 +0000
1298@@ -0,0 +1,151 @@
1299+<?xml version="1.0" encoding="utf-8"?>
1300+<openerp>
1301+<data>
1302+ <template id="assets_backend" name="website_sign assets" inherit_id="web.assets_backend">
1303+ <xpath expr="." position="inside">
1304+ <link rel="stylesheet" href="/website_sign/static/src/css/website_sign.css"/>
1305+ <script type="text/javascript" src="/website_sign/static/src/js/sign.js"></script>
1306+ </xpath>
1307+ </template>
1308+ <template id="website_sign.doc_sign" name="Document Sign">
1309+ <t t-call="website.layout">
1310+ <t t-set="head">
1311+ <script type="text/javascript" src="/website_sign/static/src/js/sign.js"></script>
1312+ <script type="text/javascript" src="/website/static/lib/jSignature/jSignature.min.js"></script>
1313+ <link rel='stylesheet' href='/website_sign/static/src/css/website_sign.css'/>
1314+ <t t-raw="head or ''"/>
1315+ </t>
1316+ <body data-spy="scroll" data-target=".navspy">
1317+ <div class="container">
1318+ <div class="row mt16">
1319+ <div class="col-md-3">
1320+ <div class="bs-sidebar">
1321+ <div class="text-left hidden-print oe_lside_panel">
1322+ <t t-if="current_sign and current_sign.state == 'draft' ">
1323+ <a class="btn btn-primary btn-block fa fa-check" data-toggle="modal" data-target="#modesign">
1324+ Sign Document
1325+ </a>
1326+ </t>
1327+ <t t-if="current_sign and current_sign.state != 'draft'">
1328+ <h4 class="modal-title">You have Signed the document.</h4>
1329+ </t>
1330+ </div>
1331+ <div class="oe_lside_panel"><h4><t t-raw="attachment.name"/></h4></div>
1332+ <div class="oe_lside_panel" t-if="attachment.description"> <t t-raw="attachment.description"/> </div>
1333+ </div>
1334+ </div>
1335+ <div class="col-md-9">
1336+ <div t-if="message==1" class="alert alert-success alert-dismissable">
1337+ <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
1338+ Your message has been successfully sent!
1339+ </div>
1340+ <div t-if="message==2" class="alert alert-success alert-dismissable">
1341+ <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
1342+ You have successfully signed the document.
1343+ </div>
1344+ <div id="main_panel" class="row mb32" style="margin-left:0">
1345+ <div class="col-md-8">
1346+ <t t-foreach="record.message_ids" t-as="message">
1347+ <t t-foreach='message.attachment_ids' t-as='attachment'>
1348+ <div class="row" t-if="attachment.id == attachment_id">
1349+ <a t-att-href="'/mail/download_attachment?model=mail.message&amp;id='+str(message.id)+'&amp;method=download_attachment&amp;attachment_id='+str(attachment_id)" target="_blank">
1350+ <t t-if="attachment.file_type_icon == 'webimage'">
1351+ <img class="media-object pull-left" t-att-src="'/web/binary/image?model=ir.attachment&amp;field=datas&amp;id=' + str(attachment_id) + '&amp;resize=300,180'" style="width: 400px;"/>
1352+ </t>
1353+ <t t-if="attachment.file_type_icon != 'webimage'">
1354+ <img class="media-object pull-left" t-att-src="'/mail/static/src/img/mimetypes/' + attachment.file_type_icon + '.png'" style="width: 400px;"></img>
1355+ </t>
1356+ </a>
1357+ </div>
1358+ </t>
1359+ </t>
1360+ <t t-if="signatures">
1361+ <t t-foreach="signatures" t-as="sign">
1362+ <div class="row" t-if="sign.state == 'closed'">
1363+ <img class="media-object pull-left" t-att-src="'/web/binary/image?model=ir.attachment.signature&amp;field=sign&amp;id=' + str(sign.id) + '&amp;resize=500,180'" style="width: 150px;"/>
1364+ <div style="line-height:65px; font-weight:bold;">
1365+ by <t t-raw="sign.signer_name"/> on <t t-raw="sign.signing_date"/>.
1366+ </div>
1367+ </div>
1368+ </t>
1369+ </t>
1370+ </div>
1371+ <div class="col-md-4" id="right_panel"> </div>
1372+ </div>
1373+ <div class="modal fade" id="modesign" role="dialog" aria-hidden="true">
1374+ <div class="modal-dialog">
1375+ <form id="sign_doc" method="post" t-attf-action="/website_sign/signed/#{attachment_id}/?token=#{token}" class="js_sign_json modal-content">
1376+ <div class="modal-header">
1377+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&amp;times;</button>
1378+ <h4 class="modal-title">Sign Document</h4>
1379+ </div>
1380+ <div class="modal-body" id="sign-modal-dialog">
1381+ <p>
1382+ I agree, by signing this document.
1383+ </p>
1384+ <div id="signer" class="form-group" t-if="current_sign">
1385+ <label class="control-label" for="name">Your Name:</label>
1386+ <input class="form-control" name="signer" id="signer_name" t-att-value="current_sign.partner_id.name" type="text"/>
1387+ <div class="panel panel-default mt16 mb0" id="drawsign">
1388+ <div class="panel-heading">
1389+ <div class="pull-right">
1390+ <a id="sign_clean" class="btn btn-xs">Clear</a>
1391+ </div>
1392+ <strong>Draw your signature</strong>
1393+ </div>
1394+ <div id="sign" class="panel-body" style="padding: 0"/>
1395+ </div>
1396+ </div>
1397+ </div>
1398+ <div class="modal-footer">
1399+ <button type="submit" id="signed_req" t-attf-class="btn btn-primary">Sign Document</button> or
1400+ <button type="button" class="btn btn-link" data-dismiss="modal" style="padding: 0">Cancel</button>
1401+ </div>
1402+ </form>
1403+ </div>
1404+ </div>
1405+ </div>
1406+ </div>
1407+ </div>
1408+ </body>
1409+ </t>
1410+ </template>
1411+
1412+ <template id="opt_website_sign_right_panel" name="Requested Sign" inherit_option_id="website_sign.doc_sign" inherit_id="website_sign.doc_sign">
1413+ <xpath expr="//div[@id='right_panel']" position="inside">
1414+ <div class="panel panel-default">
1415+ <div class="panel-heading">
1416+ <h4>
1417+ <t t-if="not sign_req">
1418+ Signed(<t t-raw="str(len(signatures)) +'/'+ str(len(signatures))"/>)
1419+ </t>
1420+ <t t-if="len(sign_req) and (len(sign_req) == len(signatures) or len(sign_req) != len(signatures))">
1421+ Waiting Signatures(<t t-raw="str(len(sign_req)) +'/'+ str(len(signatures))"/>)
1422+ </t>
1423+ </h4>
1424+ </div>
1425+ <div class="panel-body">
1426+ <t t-foreach="signatures" t-as="sign">
1427+ <div t-if="sign.state == 'draft'"><b><t t-raw="sign.partner_id.name"/></b> is requested to sign.</div>
1428+ <div t-if="sign.state == 'closed'"><b><t t-raw="sign.partner_id.name"/></b> has signed the Document.</div>
1429+ </t>
1430+ </div>
1431+ </div>
1432+ </xpath>
1433+ </template>
1434+
1435+ <template id="opt_website_sign_chatter_comment" name="Allow Comments" inherit_option_id="website_sign.doc_sign" inherit_id="website_sign.doc_sign">
1436+ <xpath expr="//div[@id='main_panel']" position="after">
1437+ <section class="mt32 mb32 css_editable_mode_hidden hidden-print">
1438+ <form id="comment" t-attf-action="/sign/document/#{attachment_id}/#{token}/note" method="POST">
1439+ <div class="pull-left mb32 mt32" style="width: 75%">
1440+ <textarea rows="4" name="comment" class="form-control" placeholder="Send us a note..."></textarea>
1441+ <button type="submit" class="btn btn-primary mt8">Send</button>
1442+ </div>
1443+ </form>
1444+ </section>
1445+ <div class="clearfix"/>
1446+ </xpath>
1447+ </template>
1448+</data>
1449+</openerp>

Subscribers

People subscribed via source and target branches

to all changes: