[MERGE] Merge lp:~openerp-dev/openobject-addons/trunk-website-al.

bzr revid: bth@tinyerp.com-20131001051453-h242lpkpdlxqmqig
This commit is contained in:
bth-openerp
2013-10-01 10:44:53 +05:30
28 changed files with 971 additions and 480 deletions
+1 -3
View File
@@ -1,5 +1,3 @@
import controllers
import res_config
import website
import view
import models
+6
View File
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
import ir_fields
import res_config
import view
import website
+87
View File
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
from lxml import etree, html
from openerp.osv import orm, fields
from openerp.tools import ustr
class converter(orm.Model):
_inherit = 'ir.fields.converter'
def _html_to_integer(self, cr, uid, model, column, value, context=None):
return int(value.text_content().strip()), []
def _html_to_float(self, cr, uid, model, column, value, context=None):
return float(value.text_content().strip()), []
def _html_to_passthrough(self, cr, uid, model, column, value, context=None):
return value.text_content().strip(), []
_html_to_char = _html_to_date = _html_to_datetime = _html_to_passthrough
def _html_to_text(self, cr, uid, model, column, value, context=None):
return value.text_content(), []
def _html_to_selection(self, cr, uid, model, column, value, context=None):
text = value.text_content().strip()
selection = column.reify(cr, uid, model, column, context=context)
for k, v in selection:
if isinstance(v, str):
v = ustr(v)
if text == v:
return k, []
warning = u"No value found for label %s in selection %s" % (text, selection)
# FIXME: ?
return False, [Warning(warning.encode('utf-8'))]
def _html_to_many2one(self, cr, uid, model, column, value, context=None):
matches = self.pool[column._obj].name_search(
cr, uid, name=value.text_content().strip(), context=context)
# FIXME: more than one match, error reporting
return matches[0][0], []
def _html_to_html(self, cr, uid, model, column, value, context=None):
content = []
if value.text: content.append(value.text)
content.extend(html.tostring(child)
for child in value.iterchildren(tag=etree.Element))
return '\n'.join(content), []
class test_converter(orm.Model):
_name = 'website.converter.test'
_columns = {
'char': fields.char(),
'integer': fields.integer(),
'float': fields.float(),
'numeric': fields.float(digits=(16, 2)),
'many2one': fields.many2one('website.converter.test.sub'),
'binary': fields.binary(),
'date': fields.date(),
'datetime': fields.datetime(),
'selection': fields.selection([
(1, "réponse A"),
(2, "réponse B"),
(3, "réponse C"),
(4, "réponse D"),
]),
'selection_str': fields.selection([
('A', "Qu'il n'est pas arrivé à Toronto"),
('B', "Qu'il était supposé arriver à Toronto"),
('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
('D', "La réponse D"),
], string="Lorsqu'un pancake prend l'avion à destination de Toronto et "
"qu'il fait une escale technique à St Claude, on dit:"),
'html': fields.html(),
'text': fields.text(),
}
class test_converter_sub(orm.Model):
_name = 'website.converter.test.sub'
_columns = {
'name': fields.char(),
}
@@ -55,57 +55,19 @@ class view(osv.osv):
def extract_embedded_fields(self, cr, uid, arch, context=None):
return arch.xpath('//*[@data-oe-model != "ir.ui.view"]')
def convert_embedded_field(self, cr, uid, el, column,
type_override=None, context=None):
""" Converts the content of an embedded field to a value acceptable
for writing in the column
:param etree._Element el: embedded field being saved
:param fields._column column: column object corresponding to the field
:param type type_override: column type to dispatch on instead of the
column's actual type (for proxy column types
e.g. relateds)
:return: converted value
"""
column_type = type_override or type(column)
if issubclass(column_type, fields.html):
content = []
if el.text: content.append(el.text)
content.extend(html.tostring(child)
for child in el.iterchildren(tag=etree.Element))
return '\n'.join(content)
elif issubclass(column_type, fields.integer):
return int(el.text_content())
elif issubclass(column_type, fields.float):
return float(el.text_content())
elif issubclass(column_type, (fields.char, fields.text,
fields.date, fields.datetime)):
return el.text_content()
# TODO: fields.selection
elif issubclass(column_type, fields.many2one):
matches = self.pool[column._obj].name_search(
cr, uid, name=el.text_content().strip(), context=context)
# FIXME: more than one match, error reporting
return matches[0][0]
elif issubclass(column_type, fields.function):
# FIXME: special-case selection as in get_pg_type?
return self.convert_embedded_field(
cr, uid, el, column,
type_override=getattr(fields, column._type),
context=context)
# TODO?: fields.many2many, fields.one2many
# TODO?: fields.reference
else:
raise TypeError("Un-convertable column type %s" % column_type)
def save_embedded_field(self, cr, uid, el, context=None):
Model = self.pool[el.get('data-oe-model')]
field = el.get('data-oe-field')
column = Model._all_columns[field].column
convert = self.pool['ir.fields.converter'].to_field(
cr, uid, Model, column, fromtype="html", context=context)
value, warnings = convert(el)
# FIXME: report error
if warnings: return
Model.write(cr, uid, [int(el.get('data-oe-id'))], {
field: self.convert_embedded_field(cr, uid, el, column, context=context)
field: value
}, context=context)
def to_field_ref(self, cr, uid, el, context=None):
@@ -137,7 +99,8 @@ class view(osv.osv):
"""
res_id = int(res_id)
arch_section = html.fromstring(value)
arch_section = html.fromstring(
value, parser=html.HTMLParser(encoding='utf-8'))
if xpath is None:
# value is an embedded field on its own, not a view section
+19
View File
@@ -367,6 +367,25 @@ ul.nav-stacked > li > a {
}
/* ---- PUBLISH ---- */
a[data-publish] {
text-decoration: none !important;
}
a[data-publish] .css_unpublish, a[data-publish] .css_publish, a[data-publish] .css_unpublished, a[data-publish] .css_published {
display: none;
}
a[data-publish][data-publish='off'] .css_unpublished, a[data-publish][data-publish='off']:hover .css_publish {
display: inline;
}
a[data-publish][data-publish='off']:hover .css_unpublished {
display: none;
}
a[data-publish][data-publish='on'] .css_published, a[data-publish][data-publish='on']:hover .css_unpublish {
display: inline;
}
a[data-publish][data-publish='on']:hover .css_published {
display: none;
}
.dropdown .css_unpublish, .dropdown .css_publish {
display: none;
}
@@ -283,6 +283,20 @@ ul.nav-stacked > li > a
text-transform: uppercase
/* ---- PUBLISH ---- */
a[data-publish]
text-decoration: none !important
.css_unpublish, .css_publish, .css_unpublished, .css_published
display: none
&[data-publish='off']
.css_unpublished, &:hover .css_publish
display: inline
&:hover .css_unpublished
display: none
&[data-publish='on']
.css_published, &:hover .css_unpublish
display: inline
&:hover .css_published
display: none
.dropdown .css_unpublish, .dropdown .css_publish
display: none
@@ -911,6 +911,7 @@
while (node && !$(node).hasClass('oe_editable')) {
node = node.parentNode;
}
$(m.target).trigger('node_changed');
return node;
})
.compact()
+20
View File
@@ -117,9 +117,29 @@
};
dom_ready.then(function () {
/* ----- PUBLISHING STUFF ---- */
$('[data-publish]:has(.js_publish)').each(function () {
var $pub = $("[data-publish]", this);
if($pub.size())
$(this).attr("data-publish", $pub.attr("data-publish"));
else
$(this).removeAttr("data-publish");
});
$('[data-publish]:has(.js_publish_management)').each(function () {
$(this).attr("data-publish", $(".js_publish_management .btn-success", this).size() ? "on" : 'off');
$(this).attr("data-publish", $(".js_publish_management .btn-success", this).size() ? "on" : 'off');
});
$(document).on('click', '.js_publish', function (e) {
e.preventDefault();
var $data = $(":first", this).parents("[data-publish]");
$data.attr("data-publish", $data.first().attr("data-publish") == 'off' ? 'on' : 'off');
openerp.jsonRpc('/website/publish', 'call', {'id': $(this).data('id'), 'object': $(this).data('object')})
.then(function (result) {
$data.attr("data-publish", +result ? 'on' : 'off');
});
});
$(document).on('click', '.js_publish_management .js_publish_btn', function (e) {
@@ -573,7 +573,6 @@
onBlurEdit : function () {},
});
website.snippet.editorRegistry = {};
website.snippet.Editor = openerp.Class.extend({
init: function (parent, dom) {
@@ -785,6 +784,8 @@
*/
onFocus : function () {
this.$overlay.addClass('oe_active');
if (this.$background_img)
this.$background_img.prependTo(this.$target);
},
/* onFocus
@@ -792,6 +793,75 @@
*/
onBlur : function () {
this.$overlay.removeClass('oe_active');
if (this.$background_img)
this.$background_img.detach();
},
/*
* active the cke rte editor for change image url
*/
active_change_image: function ($image) {
var self = this;
// cke select image with mousedown on image
var $image = $($image);
var image = $image[0];
var event = document.createEvent( 'HTMLEvents' );
event.initEvent( 'mousedown', true, true );
image.dispatchEvent( event );
// hack to active editor with doubleclick on editor
var dblclick = null;
$image.on("dblclick", function (evt) {
evt.preventDefault();
dblclick = new CKEDITOR.dom.event(evt.originalEvent);
return;
});
var event = document.createEvent( 'HTMLEvents' );
event.initEvent( 'dblclick', true, true );
image.dispatchEvent( event );
},
change_background: function (bg, ul_options) {
var self = this;
var $ul = this.$editor.find(ul_options);
var bg_value = $(bg).css("background-image").replace(/url\(|\)/g, "");
// must add an image in the DOM for rte editor
this.$background_img = $("<img src='"+bg_value+"'/>").css({position: "absolute", zIndex: -10, left:-1000000, top:-1000000});
this.$background_img.prependTo(this.$target);
this.$background_img.on("node_changed", function () {
$(bg).css("background-image", "url(" + $(this).attr('src') + ")");
});
// select in ul options
$ul.find("li").removeClass("active");
var selected = $ul.find('[data-value="' + bg_value + '"], [data-value="' + bg_value.replace(/.*:\/\/[^\/]+/, '') + '"]');
selected.addClass('active');
if (!selected.length) {
$ul.find('.oe_custom_bg b').html(bg_value);
}
// bind envent on options
var $li = $ul.find("li");
$li.on('click', function (event) {
if ($(this).data("value")) {
$li.removeClass("active");
$(this).addClass("active");
self.$editor.find('input').val("");
} else {
self.active_change_image(self.$background_img);
}
})
.on('mouseover', function (event) {
if ($(this).data("value")) {
var src = $(this).data("value");
self.$background_img.attr('src', src);
}
})
.on('mouseout', function (event) {
var src = $ul.find('li.active').data("value");
self.$background_img.attr('src', src);
});
},
});
@@ -991,7 +1061,7 @@
this.$editor.find(".js_add").on('click', _.bind(this.on_add, this));
this.$editor.find(".js_remove").on('click', _.bind(this.on_remove, this));
this.change_background();
this.change_background(".item.active", 'ul[name="carousel-background"]');
this.change_style();
this.change_size();
},
@@ -1022,40 +1092,9 @@
}
},
onFocus: function () {
this.set_options_background();
this.set_options_style();
this._super();
},
set_options_background: function () {
var $image = this.$target.find('.carousel-inner .item.active > img');
var bg = $image.attr('src');
var $ul = this.$editor.find('ul[name="carousel-background"]');
$ul.find("li").removeClass("active");
var selected = $ul.find('[data-value="' + bg + '"], [data-value="' + bg.replace(/.*:\/\/[^\/]+/, '') + '"]')
.addClass('active').length;
this.$editor.find('.oe_custom_bg b').html(selected ? "" : bg);
},
change_background: function () {
var self = this;
var $ul = this.$editor.find('ul[name="carousel-background"]');
var $li = $ul.find("li");
$li.on('click', function (event) {
$li.removeClass("active");
$(this).addClass("active");
self.$editor.find('input').val("");
})
.on('mouseover', function (event) {
if ($(this).data("value")) {
self.$target.find('.carousel-inner .item.active > img')
.attr('src', $(this).data("value"));
}
})
.on('mouseout', function (event) {
self.$target.find('.carousel-inner .item.active > img')
.attr('src', $ul.find('li.active').data("value"));
});
},
set_options_style: function () {
var style = false;
var $el = this.$target.find('.carousel-inner .item.active');
@@ -1124,50 +1163,7 @@
website.snippet.editorRegistry.parallax = website.snippet.editorRegistry.resize.extend({
start : function () {
this._super();
this.change_background();
},
onFocus: function () {
this.$imagebg = $("<img/>");
this.$imagebg.prependTo(this.$target);
this.set_options_background();
this._super();
},
onBlur : function () {
this._super();
this.$imagebg.remove();
},
set_options_background: function () {
var bg = this.$target.css("background-image").replace(/url\(|\)/g, "");
this.$imagebg.attr("src", bg);
var $ul = this.$editor.find('ul[name="parallax-background"]');
$ul.find("li").removeClass("active");
var selected = $ul.find('[data-value="' + bg + '"], [data-value="' + bg.replace(/.*:\/\/[^\/]+/, '') + '"]')
.addClass('active').length;
this.$editor.find('.oe_custom_bg b').html(selected ? "" : bg);
},
change_background: function () {
var self = this;
var $ul = this.$editor.find('ul[name="parallax-background"]');
var $li = $ul.find("li");
$li.on('click', function (event) {
$li.removeClass("active");
$(this).addClass("active");
self.$editor.find('input').val("");
})
.on('mouseover', function (event) {
if ($(this).data("value")) {
var src = $(this).data("value");
self.$imagebg.attr('src', src);
self.$target.css("background-image", "url(" + src + ")");
}
})
.on('mouseout', function (event) {
var src = $ul.find('li.active').data("value");
self.$imagebg.attr('src', src);
self.$target.css("background-image", "url(" + src + ")");
});
this.change_background(this.$target, 'ul[name="parallax-background"]');
},
});
@@ -139,7 +139,7 @@
<li data-value="/website/static/src/img/banner/velour.jpg"><a>Velour</a></li>
<li data-value="/website/static/src/img/banner/wood.jpg"><a>Wood</a></li>
<li data-value="/website/static/src/img/banner/yellow_green.jpg"><a>Yellow Green</a></li>
<li class="oe_custom_bg"><b></b></li>
<li class="oe_custom_bg"><a><b>Enter your image</b></a></li>
</ul>
</li>
<li class='oe_snippet_options dropdown-submenu'>
@@ -169,8 +169,7 @@
<div id="myCarousel" class="oe_snippet_body carousel slide oe_medium mb32" data-interval="10000">
<!-- Carousel items -->
<div class="carousel-inner">
<div class="item image_text active">
<img src="/website/static/src/img/banner/color_splash.jpg" width="100%" height="100%"/>
<div class="item image_text active" style="background-image: url(/website/static/src/img/banner/color_splash.jpg)">
<div class="container">
<div class="carousel-caption content">
<h2>Set Your Banner Title</h2>
@@ -810,7 +809,7 @@
<li data-value="/website/static/src/img/banner/landscape.jpg"><a>Landscape</a></li>
<li data-value="/website/static/src/img/banner/mango.jpg"><a>Mango</a></li>
<li data-value="/website/static/src/img/banner/flower.jpg"><a>Purple</a></li>
<li class="oe_custom_bg"><b></b></li>
<li class="oe_custom_bg"><a><b>Enter your image</b></a></li>
</ul>
</li>
+2 -2
View File
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
import test_views
import test_views, test_converter
checks = [ test_views ]
checks = [ test_views, test_converter, ]
+119
View File
@@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
from collections import namedtuple
from functools import partial
from xml.dom.minidom import getDOMImplementation
from lxml import etree, html
from openerp.tests import common
from openerp.tools import qweb
import unittest2
renderer = qweb.QWebXml()
impl = getDOMImplementation()
document = impl.createDocument(None, None, None)
Request = namedtuple('Request', 'cr uid registry')
class RegistryProxy(object):
def __init__(self, func):
self.func = func
def __getitem__(self, name):
return self.func(name)
class TestConvertBack(common.TransactionCase):
def setUp(self):
super(TestConvertBack, self).setUp()
self.Converter = self.registry('ir.fields.converter')
def field_rountrip_result(self, field, value, expected):
model = 'website.converter.test'
Model = self.registry(model)
id = Model.create(
self.cr, self.uid, {
field: value
})
[record] = Model.browse(self.cr, self.uid, [id])
e = document.createElement('span')
field_value = 'record.%s' % field
e.setAttribute('t-field', field_value)
rendered = renderer.render_tag_field(
e, {'field': field_value}, '', {
'record': record,
'request': Request(self.cr, self.uid, RegistryProxy( self.registry)),
})
element = html.fromstring(
rendered, parser=html.HTMLParser(encoding='utf-8'))
column = Model._all_columns[field].column
from_html = self.Converter.to_field(
self.cr, self.uid, model, column, 'html')
value_back, warnings = from_html(element)
self.assertEqual(warnings, [])
if isinstance(expected, str):
expected = expected.decode('utf-8')
self.assertEqual(value_back, expected)
def field_roundtrip(self, field, value):
self.field_rountrip_result(field, value, value)
def test_integer(self):
self.field_roundtrip('integer', 42)
def test_float(self):
self.field_roundtrip('float', 42.567890)
def test_numeric(self):
self.field_roundtrip('numeric', 42.77)
def test_char(self):
self.field_roundtrip('char', "foo bar")
self.field_roundtrip('char', "ⒸⓄⓇⒼⒺ")
def test_m2o(self):
Sub = self.registry('website.converter.test.sub')
sub = partial(Sub.create, self.cr, self.uid)
ids = [
sub({'name': "Foo"}),
sub({'name': "Bar"}),
sub({'name': "Baz"}),
]
self.field_rountrip_result(
'many2one',
ids[2],
ids[2])
def test_selection(self):
self.field_roundtrip('selection', 3)
def test_selection_str(self):
self.field_roundtrip('selection_str', 'B')
def test_text(self):
self.field_roundtrip('text', """
You must obey the dance commander
Givin' out the order for fun
You must obey the dance commander
You know that he's the only one
Who gives the orders here,
Alright
Who gives the orders here,
Alright
It would be awesome
If we could dance-a
It would be awesome, yeah
Let's take the chance-a
It would be awesome, yeah
Let's start the show
Because you never know
You never know
You never know until you go
""")
+22 -7
View File
@@ -361,22 +361,37 @@
<template id="publish_management">
<t t-if="editable" t-ignore="true">
<div t-attf-class="dropdown js_publish_management pull-right #{object.id and object.website_published and 'css_publish' or 'css_unpublish'}" t-att-data-id="object.id" t-att-data-object="object._name">
<a t-attf-class="btn btn-sm btn-#{object.id and object.website_published and 'success' or 'default'}" t-att-id="'dopprod-%s' % object.id" role="button" data-toggle="dropdown">Options <span class="caret"></span></a>
<a t-attf-class="btn btn-xs btn-#{object.id and object.website_published and 'success' or 'default'}" t-att-id="'dopprod-%s' % object.id" role="button" data-toggle="dropdown">Options <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu" t-att-aria-labelledby="'dopprod-%s' % object.id">
<t t-raw="0"/>
<li>
<a href="#" class="js_publish_btn css_unpublish">Unpublish</a>
<a href="#" class="js_publish_btn css_publish">Publish</a>
</li>
<li class="divider"/>
<li>
<a t-att-href="'/admin/#model=%s&amp;id=%s' % (object._name, object.id)">Edit</a>
<a href="#" class="js_publish_btn css_unpublish"><small>Unpublish</small></a>
<a href="#" class="js_publish_btn css_publish"><small>Publish</small></a>
</li>
<t t-if="publish_edit">
<li class="divider"/>
<li>
<a t-att-href="'/admin/#model=%s&amp;id=%s' % (object._name, object.id)"><small>Edit</small></a>
</li>
</t>
</ul>
</div>
</t>
</template>
<template id="publish_short">
<t t-if="editable" t-ignore="true">
<a href="#" t-att-data-id="object.id" t-att-data-object="object._name"
t-att-data-publish="object.id and object.website_published and 'on' or 'off'"
class="pull-right js_publish">
<span t-attf-class="text-success css_publish glyphicon glyphicon-ok"></span>
<span t-attf-class="text-danger css_unpublish glyphicon glyphicon-remove-circle"></span>
<span t-attf-class="text-muted css_published glyphicon glyphicon-ok"></span>
<span t-attf-class="text-muted css_unpublished glyphicon glyphicon-remove-circle"></span>
</a>
</t>
</template>
<template id="kanban">
<t t-set="step"><t t-esc="step or 0"/></t>
<t t-set="scope"><t t-esc="scope or 0"/></t>
+1 -1
View File
@@ -57,8 +57,8 @@
</group>
<group string="Others Info">
<field name="company_id" groups="base.group_multi_company"/>
<field name="default_lang_id" widget="selection"/>
<field name="language_ids" widget="many2many_tags"/>
<field name="default_lang_id" widget="selection"/>
</group>
</group>
</sheet>
+7 -2
View File
@@ -29,7 +29,7 @@ import simplejson
import werkzeug
class website_mail(http.Controller):
class WebsiteBlog(http.Controller):
_category_post_per_page = 6
_post_comment_per_page = 6
@@ -75,7 +75,8 @@ class website_mail(http.Controller):
blog_post_obj = request.registry['blog.post']
tag_obj = request.registry['blog.tag']
category_obj = request.registry['blog.category']
user_obj = request.registry['res.users']
current_user = request.registry['res.users'].browse(cr, uid, uid, context=context)
tag = None
category = None
@@ -103,6 +104,9 @@ class website_mail(http.Controller):
blog_posts = category.blog_post_ids
elif tag:
blog_posts = tag.blog_post_ids
else:
blog_post_ids = blog_post_obj.search(cr, uid, [], context=context)
blog_posts = blog_post_obj.browse(cr, uid, blog_post_ids, context=context)
if blog_posts:
pager = request.website.pager(
@@ -143,6 +147,7 @@ class website_mail(http.Controller):
'blog_posts': blog_posts,
'pager': pager,
'nav_list': nav,
'current_user': current_user,
'unable_editor': post.get('unable_editor')
}
@@ -32,16 +32,27 @@
</div>
</template>
<!-- Options: Blog Post Summary: hide author -->
<template id="view_blog_post_short_author" inherit_option_id="website_blog.view_blog_post_short" name="Hide Authors">
<xpath expr="//span[@class='icon-user']" position="attributes">
<attribute name="class">hidden</attribute>
</xpath>
</template>
<!-- Options: Blog Post Summary: show category -->
<template id="view_blog_post_short_category" inherit_option_id="website_blog.view_blog_post_short" name="Show Blog Name">
<xpath expr="//span[@class='icon-user']" position="after">
<span class="icon-folder-open"> In <span t-field="blog_post.category_id"/> &amp;nbsp;</span>
</xpath>
</template>
<!-- Blog Post Complete -->
<template id="view_blog_post" name="Blog Post">
<div>
<t t-call="website.publish_management"><t t-set="object" t-value="blog_post"/></t>
<t t-call="website.publish_management">
<t t-set="object" t-value="blog_post"/>
<t t-set="publish_edit" t-value="true"/>
</t>
</div><div class="clearfix"/>
<h1 class="text-center" t-field="blog_post.name"/>
@@ -75,9 +86,11 @@
<ul class="media-list">
<li t-foreach="blog_post.website_message_ids" t-as="message" class="media">
<div class="media-body">
<img class="media-object pull-left" t-att-src="'/website/image?model=res.users&amp;field=image_small&amp;id='+str(message.author_id.id)" style="width: 50px; margin-right: 10px;"/>
<img class="media-object pull-left" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(message.author_id.id)" style="width: 50px; margin-right: 10px;"/>
<div class="media-body">
<t t-call="website.publish_management"><t t-set="object" t-value="message"/></t>
<t t-call="website.publish_short">
<t t-set="object" t-value="message"/>
</t>
<h5 class="media-heading">
<span t-field="message.author_id"/> <small>on <span t-field="message.date"/></small>
</h5>
@@ -87,12 +100,11 @@
</li>
</ul>
<section groups="group_website_blog_reply" class="mb32">
<t t-call="website_mail.follow"><t t-set="object" t-value="blog_post"/></t>
<h4>Leave a Comment</h4>
<form id="comment" t-attf-action="/blog/#{blog_post.category_id.id}/#{blog_post.id}/post#post"
method="POST">
<img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&amp;field=image_small&amp;id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
<div class="pull-left" style="width: 75%%">
<div class="pull-left mb32" style="width: 75%%">
<textarea rows="3" class="form-control" placeholder="Write a comment..."></textarea>
<button type="submit" class="btn btn-danger mt8">Post</button>
</div>
@@ -100,12 +112,19 @@
</section>
</template>
<!-- Options: Blog Post: hide author -->
<template id="view_blog_post_author" inherit_option_id="website_blog.view_blog_post" name="Hide Authors">
<xpath expr="//span[@class='icon-user']" position="attributes">
<attribute name="class">hidden</attribute>
</xpath>
</template>
<!-- Options: Blog Post: show category -->
<template id="view_blog_post_show_category" inherit_option_id="website_blog.view_blog_post" name="Show Blog Name">
<xpath expr="//span[@class='icon-user']" position="after">
<span class="icon-folder-open"> In <span t-field="blog_post.category_id"/> &amp;nbsp;</span>
</xpath>
</template>
<!-- Page -->
<template id="index" name="Blogs" page="True">
@@ -135,6 +154,7 @@
</t>
</template>
<!-- Option: right column -->
<template id="blog_right_column" inherit_id="website_blog.index" inherit_option_id="website_blog.index" name="Right Column">
<xpath expr="//div[@class='row']" position="inside">
<div class="col-sm-3 hidden-xs col-lg-offset-1 mb32" id="right_column">
@@ -146,7 +166,7 @@
</xpath>
</template>
<!-- Option: archives -->
<!-- Option: Right Column: archives -->
<template id="blog_history" inherit_id="website_blog.blog_right_column" inherit_option_id="website_blog.blog_right_column" name="Archives">
<xpath expr="//div[@id='right_column']" position="inside">
<section>
@@ -168,6 +188,7 @@
</xpath>
</template>
<!-- Option: Right Column: about us -->
<template id="blog_aboutus" inherit_option_id="website_blog.blog_right_column" inherit_id="website_blog.blog_right_column" name="About" priority="4">
<xpath expr="//div[@id='right_column']" position="inside">
<section class="mb16">
@@ -183,26 +204,36 @@
</xpath>
</template>
<!-- Option: Right Column: follow us -->
<template id="blog_followus" inherit_option_id="website_blog.blog_right_column" inherit_id="website_blog.blog_right_column" name="Follow us" priority="2">
<xpath expr="//div[@id='right_column']" position="inside">
<section class="mb16">
<h4>Follow us</h4>
<p class="text-muted">
Participate on our social stream.
</p>
<t t-call="website_mail.follow"><t t-set="object" t-value="category"/></t>
<h2>
<a t-att-href="website.social_facebook" t-if="website.social_facebook"><i class="icon-facebook-sign"/></a>
<a t-att-href="website.social_twitter" t-if="website.social_twitter"><i class="icon-twitter-sign"/></a>
<a t-att-href="website.social_linkedin" t-if="website.social_linkedin"><i class="icon-linkedin-sign"/></a>
<a t-att-href="website.social_youtube" t-if="website.social_youtube"><i class="icon-youtube-sign"/></a>
<a t-att-href="website.social_googleplus" t-if="website.social_googleplus"><i class="icon-google-plus-sign"/></a>
<a t-att-href="website.social_github" t-if="website.social_github"><i class="icon-github-sign"/></a>
</h2>
<h4>Follow us</h4>
<t t-if="category">
<p class="text-muted">
Be notified of new blog posts in <t t-esc="category.name"/>
</p>
<t t-call="website_mail.follow">
<t t-set="email" t-value="current_user.email"/>
<t t-set="object" t-value="category"/>
</t>
</t>
<p class="text-muted">
Participate on our social stream.
</p>
<h2>
<a t-att-href="website.social_facebook" t-if="website.social_facebook"><i class="icon-facebook-sign"/></a>
<a t-att-href="website.social_twitter" t-if="website.social_twitter"><i class="icon-twitter-sign"/></a>
<a t-att-href="website.social_linkedin" t-if="website.social_linkedin"><i class="icon-linkedin-sign"/></a>
<a t-att-href="website.social_youtube" t-if="website.social_youtube"><i class="icon-youtube-sign"/></a>
<a t-att-href="website.social_googleplus" t-if="website.social_googleplus"><i class="icon-google-plus-sign"/></a>
<a t-att-href="website.social_github" t-if="website.social_github"><i class="icon-github-sign"/></a>
</h2>
</section>
</xpath>
</template>
<!-- Option: Right Column: categories + category in blog post -->
<template id="blog_categories" inherit_option_id="website_blog.blog_right_column" name="Other Blogs" priority="6">
<xpath expr="//div[@id='right_column']" position="inside">
<section class="mb16">
+2 -2
View File
@@ -32,10 +32,10 @@ class WebsiteMail(http.Controller):
partner_obj = request.registry['res.partner']
user_obj = request.registry['res.users']
partner_ids = []
if request.context['is_public_user'] and email:
if request.context['is_public_user'] and email and email != u'false': # post contains stringified booleans
partner_ids = partner_obj.search(request.cr, SUPERUSER_ID, [("email", "=", email)], context=request.context)
if not partner_ids:
partner_ids = [partner_obj.create(request.cr, SUPERUSER_ID, {"email": email, "name": email}, request.context)]
partner_ids = [partner_obj.name_create(request.cr, SUPERUSER_ID, email, request.context)]
else:
partner_ids = [user_obj.browse(request.cr, request.uid, request.uid, request.context).partner_id.id]
return partner_ids
+4
View File
@@ -32,6 +32,10 @@ class MailMessage(osv.Model):
),
}
_defaults = {
'website_published': True,
}
def _search(self, cr, uid, args, offset=0, limit=None, order=None,
context=None, count=False, access_rights_uid=None):
""" Override that adds specific access rights of mail.message, to restrict
@@ -12,12 +12,14 @@ $(document).ready(function () {
$(document).on('click', '.js_follow', function (ev) {
ev.preventDefault();
var $data = $(":first", this).parents("[data-follow]");
var $email = $data.first().siblings(".js_follow_email");
var message_is_follower = $data.first().attr("data-follow");
$data.attr("data-follow", message_is_follower == 'off' ? 'on' : 'off');
$.post('/website_mail/follow', {
'id': $(this).data('id'),
'object': $(this).data('object'),
'message_is_follower': message_is_follower,
'email': $email && $email.val() || false,
}, function (result) {
$data.attr("data-follow", + result ? 'on' : 'off');
});
+1 -1
View File
@@ -19,7 +19,7 @@
#
##############################################################################
from openerp.addons.website_blog.tests import test_controllers
from openerp.addons.website_mail.tests import test_controllers
checks = [
test_controllers,
+14 -1
View File
@@ -20,10 +20,23 @@
##############################################################################
from openerp.addons.mail.tests.test_mail_base import TestMailBase
from openerp.addons.website_mail.controllers.main import WebsiteMail
from openerp.tools import mute_logger, email_split
class TestControllers(TestMailBase):
def test_00(self):
def test_00_subscribe(self):
# from openerp.addons.web.http import request
# print request
cr, uid = self.cr, self.uid
# context = {
# 'is_public_user': True,
# }
# email = 'Marcel Dupuis <marcel.dupuis@example.com>'
# website_mail = WebsiteMail()
# pid = website_mail._find_or_create_partner(email, context)
# partner = self.res_partner.browse(cr, uid, pid)
# print partner.name, partner.email
+5 -2
View File
@@ -6,9 +6,12 @@
<a href="#" t-att-data-id="object.id" t-att-data-object="object._name" t-att-data-follow="object.id and object.message_is_follower and 'on' or 'off'" class="pull-right js_follow" t-if="editable" t-ignore="true">
<span t-attf-class="label label-success css_follow">Follow</span>
<span t-attf-class="label label-danger css_unfollow">Unfollow</span>
<span t-attf-class="label label-success css_followed">Followed</span>
<span t-attf-class="label label-danger css_unfollowed">Unfollowed</span>
<span t-attf-class="label label-success css_followed">Following</span>
<span t-attf-class="label label-danger css_unfollowed">Not Following</span>
</a>
<input type="email" name="email" class="js_follow_email css_unfollowed_email"
t-att-value="email"
t-att-placeholder="email or 'Email Address'"/>
</template>
<template id="website.layout" inherit_id="website.layout">
+2 -3
View File
@@ -395,6 +395,7 @@ class Ecommerce(http.Controller):
suggested_products.append(suggested_ids.pop(index))
values = {
'int': int,
'get_categories': self.get_categories,
'suggested_products': prod_obj.browse(request.cr, request.uid, suggested_products, request.context),
}
@@ -415,7 +416,7 @@ class Ecommerce(http.Controller):
@website.route(['/shop/checkout/'], type='http', auth="public", multilang=True)
def checkout(self, **post):
classic_fields = ["name", "phone", "fax", "email", "street", "city", "state_id", "zip"]
classic_fields = ["name", "phone", "email", "street", "city", "state_id", "zip"]
rel_fields = ['country_id', 'state_id']
order = get_current_order()
@@ -488,7 +489,6 @@ class Ecommerce(http.Controller):
company_id = partner_obj.create(request.cr, SUPERUSER_ID, {'name': post['company'], 'is_company': True}, request.context)
partner_value = {
'fax': post['fax'],
'phone': post['phone'],
'zip': post['zip'],
'email': post['email'],
@@ -508,7 +508,6 @@ class Ecommerce(http.Controller):
shipping_id = None
if post.get('shipping_different'):
shipping_value = {
'fax': post['shipping_fax'],
'phone': post['shipping_phone'],
'zip': post['shipping_zip'],
'street': post['shipping_street'],
@@ -30,6 +30,11 @@
border-top: 1px solid #dddddd;
}
.oe_mycart .input-group-addon {
padding-left: 6px;
padding-right: 6px;
}
.oe_shop_top .oe_product_description {
top: 0;
bottom: auto;
@@ -128,6 +133,7 @@
display: none;
}
}
@media (min-width: 992px) {
.col-md-12 .oe-height-1 {
height: 95px;
@@ -155,6 +161,7 @@
height: 288px;
}
}
@media (min-width: 1200px) {
.col-md-12 .oe-height-1 {
height: 125px;
@@ -182,6 +189,7 @@
height: 380px;
}
}
/* ---- Product list style ---- */
.oe_list_products {
border: none;
@@ -284,3 +292,69 @@
-moz-box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.3);
}
/* Wizard */
ul.wizard {
padding: 0;
margin-top: 20px;
list-style: none outside none;
border: 1px solid #d4d4d4;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065);
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065);
}
ul.wizard li {
position: relative;
float: left;
padding: 0 10px 0 20px;
margin: 0;
line-height: 38px;
background: #fbfbfb;
}
ul.wizard li .chevron {
position: absolute;
top: 0;
right: -10px;
z-index: 1;
display: block;
border: 20px solid transparent;
border-right: 0;
border-left: 10px solid #d4d4d4;
}
ul.wizard li .chevron:before {
position: absolute;
top: -20px;
right: 1px;
display: block;
border: 20px solid transparent;
border-right: 0;
border-left: 10px solid #fbfbfb;
content: "";
}
ul.wizard li.text-success {
background: #f3f4f5;
}
ul.wizard li.text-success .chevron:before {
border-left: 10px solid #f5f5f5;
}
ul.wizard li.text-primary {
background: #f1f6fc;
}
ul.wizard li.text-primary .chevron:before {
border-left: 10px solid #f1f6fc;
}
ul.wizard li:first-child {
padding-left: 15px;
border-radius: 4px 0 0 4px;
}
@@ -29,6 +29,11 @@
right: 15px
border-top: 1px solid #dddddd
.oe_mycart
.input-group-addon
padding-left: 6px
padding-right: 6px
.oe_shop_top /* Option 1 */
.oe_product_description
@@ -250,3 +255,64 @@
-webkit-box-shadow: 0px 0px 3px rgba(0,0,0,0.3)
-moz-box-shadow: 0px 0px 3px rgba(0,0,0,0.3)
box-shadow: 0px 0px 3px rgba(0,0,0,0.3)
/* Wizard */
ul.wizard
padding: 0
margin-top: 20px
list-style: none outside none
border: 1px solid #d4d4d4
-webkit-border-radius: 4px
-moz-border-radius: 4px
border-radius: 4px
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065)
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065)
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065)
ul.wizard li
position: relative
float: left
padding: 0 10px 0 20px
margin: 0
line-height: 38px
background: #fbfbfb
ul.wizard li .chevron
position: absolute
top: 0
right: -10px
z-index: 1
display: block
border: 20px solid transparent
border-right: 0
border-left: 10px solid #d4d4d4
ul.wizard li .chevron:before
position: absolute
top: -20px
right: 1px
display: block
border: 20px solid transparent
border-right: 0
border-left: 10px solid #fbfbfb
content: ""
ul.wizard li.text-success
background: #f3f4f5
ul.wizard li.text-success .chevron:before
border-left: 10px solid #f5f5f5
ul.wizard li.text-primary
background: #f1f6fc
ul.wizard li.text-primary .chevron:before
border-left: 10px solid #f1f6fc
ul.wizard li:first-child
padding-left: 15px
border-radius: 4px 0 0 4px
+367 -310
View File
@@ -379,19 +379,32 @@
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
<t t-raw="head or ''"/>
</t>
<t t-set="title">Your Cart</t>
<div id="wrap">
<div class="container oe_website_sale">
<div class="col-md-12 oe_mycart">
<h1>My Shopping Cart</h1>
<table class='table' id="mycart_products">
<ul class="wizard pull-right">
<li class="text-primary">Review Order<span class="chevron"></span></li>
<li class="text-muted">Shipping &amp; Billing<span class="chevron"></span></li>
<li class="text-muted">Payment<span class="chevron"></span></li>
<li class="text-muted">Confirmation<span class="chevron"></span></li>
</ul>
<h1 class="mb32">Your Cart</h1>
<div class="row">
<div class="col-md-8 oe_mycart">
<div t-if="not website_sale_order or not website_sale_order.order_line" class="well well-lg">
Your cart is empty!
</div>
<table class='table table-striped table-condensed' id="mycart_products" t-if="website_sale_order and website_sale_order.order_line">
<colgroup>
<col width="80"/>
<col/>
<col width="80"/>
<col width="160"/>
<col width="100"/>
<col width="120"/>
</colgroup>
<thead>
<tr>
@@ -400,52 +413,84 @@
<th>Quantity</th>
</tr>
</thead>
<tbody t-if="not website_sale_order or not website_sale_order.order_line">
<tr><td colspan="4"><h3>Your cart is empty</h3></td></tr>
</tbody>
<tbody t-if="website_sale_order and website_sale_order.order_line">
<t t-foreach="website_sale_order.order_line" t-as="line">
<tr>
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
<td t-if="line.product_id.product_tmpl_id">
<a t-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/"><img class="img-rounded" t-att-src="line.product_id.img('image_small')"/></a>
</td>
<td t-if="line.product_id.product_tmpl_id">
<a t-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/"><span t-field="line.name"/></a><br/>
<small t-field="line.product_id.description_sale"/>
</td>
<td>
<t t-if="line.product_id.product_tmpl_id and line.product_id.lst_price != line.price_unit">
<small class="text-error" style="text-decoration: line-through;"><t t-esc="line.product_id.lst_price" /> €</small>
</t>
<span t-field="line.price_unit"></span> €
</td>
<td>
<div class="pull-right">
<input type="text" class="js_quantity input-sm col-md-5" t-att-data-id="line.id" t-att-value="line.product_uom_qty"/>
<a t-href="./remove_cart/?order_line_id=#{ line.id }" t-att-data-id="line.id" class="btn btn-default mb8 btn-sm btn-inverse">-</a>
<a t-href="./add_cart/?order_line_id=#{ line.id }" t-att-data-id="line.id" class="btn btn-default mb8 btn-sm btn-success">+</a>
</div>
</td>
</tr>
</t>
<tbody>
<tr t-foreach="website_sale_order.order_line" t-as="line">
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
<td t-if="line.product_id.product_tmpl_id">
<a t-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/"><img class="img-rounded" t-att-src="line.product_id.img('image_small')"/></a>
</td>
<td t-if="line.product_id.product_tmpl_id">
<div>
<a t-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/">
<strong t-field="line.product_id.name"/>
</a>
</div>
<div class="text-muted" t-field="line.product_id.description_sale"/>
</td>
<td class="text-center">
<span t-field="line.price_unit"></span> €
<div class="text-danger" style="text-decoration: line-through;" t-if="line.product_id.lst_price &gt; line.price_unit">
<t t-esc="line.product_id.lst_price" /> €
</div>
</td>
<td>
<div class="input-group">
<span class="input-group-addon">
<a t-href="./add_cart/?order_line_id=#{ line.id }" t-att-data-id="line.id" class="mb8">
<span class="icon-minus"/>
</a>
</span>
<input type="text" class="js_quantity form-control"
t-att-data-id="line.id" t-att-value="int(line.product_uom_qty)"/>
<span class="input-group-addon">
<a t-href="./remove_cart/?order_line_id=#{ line.id }" t-att-data-id="line.id" class="mb8 float_left">
<span class="icon-plus"/>
</a>
</span>
</div>
</td>
</tr>
</tbody>
</table>
<table class='table table-hover table-condensed' id="mycart_total">
<table class='pull-right mb16' id="mycart_total">
<colgroup>
<col width="80"/>
<col/>
<col width="80"/>
<col width="160"/>
<col width="100"/>
<col width="120"/>
</colgroup>
<thead>
<tr> <th colspan="3">Subtotal </th> <th><t t-esc="website_sale_order and website_sale_order.amount_untaxed or 0"/> €</th></tr>
<tr> <th colspan="3">Taxes </th> <th><t t-esc="website_sale_order and website_sale_order.amount_tax or 0"/> €</th></tr>
<tr> <th colspan="3"><h4>Total</h4></th> <th><h4><t t-esc="website_sale_order and website_sale_order.amount_total or 0"/> €</h4></th></tr>
<tr style="border-top: 1px solid #000">
<th><h3>Total:</h3></th>
<th class="text-right"><h3><t t-esc="website_sale_order and website_sale_order.amount_total or 0"/> €</h3></th>
</tr>
<tr class="text-muted">
<td><abbr title="Taxes may be updated after providing shipping address">Incl. Taxes:</abbr></td>
<td class="text-right"><t t-esc="website_sale_order and website_sale_order.amount_tax or 0"/> €</td>
</tr>
</thead>
</table>
<a t-if="website_sale_order and website_sale_order.order_line" t-href="/shop/checkout/" class="btn btn-success">Proceed To Payment</a>
<div class="clearfix"/>
<a t-href="/shop" class="btn btn-default"><span class="icon-long-arrow-left"/> Continue Shopping</a>
<a t-if="website_sale_order and website_sale_order.order_line" t-href="/shop/checkout/" class="btn btn-primary pull-right mb32">Process Checkout <span class="icon-long-arrow-right"/></a>
<div class="oe_structure"/>
</div>
<div class="col-md-3 col-md-offset-1 text-muted" id="right_column">
<h4>Policies</h4>
<ul class="list-unstyled mb32">
<li><span class="icon-check"/> 30-days money-back guarantee</li>
<li><span class="icon-check"/> Invoice sent by e-Mail</li>
</ul>
<h4>Secure Payment</h4>
<ul class="list-unstyled mb32">
<li><span class="icon-lock"/> Transation 256bit encrypted</li>
<li><span class="icon-check"/> Processed by Ogone</li>
</ul>
</div>
</div>
</div>
<div class="oe_structure"/>
</div>
@@ -474,66 +519,59 @@
<template id="suggested_products_list" inherit_id="website_sale.mycart" inherit_option_id="website_sale.mycart" name="Suggested Products in list view">
<xpath expr="//table[@id='mycart_products']" position="after">
<table t-if="suggested_products" class='table table-hover table-condensed'>
<table t-if="suggested_products" class='table table-striped table-condensed'>
<colgroup>
<col/>
<col width="80"/>
<col width="160"/>
<col/>
<col width="100"/>
<col width="120"/>
</colgroup>
<thead>
<tr>
<th>Suggested products</th>
<th colspan="2">Suggested products</th>
</tr>
</thead>
<tbody>
<t t-foreach="suggested_products" t-as="product">
<tr>
<td>
<a t-href="/shop/product/#{ product.id }/"><span t-field="product.name"/></a><br/>
<small t-field="product.description_sale"/>
</td>
<td>
<span t-field="product.list_price"></span>€
</td>
<td>
<div class="pull-right">
<a t-href="./add_cart/?product_id=#{ product.id }" class="btn btn-sm btn-success">+</a>
</div>
</td>
</tr>
</t>
<tr t-foreach="suggested_products" t-as="product">
<td>
<a t-href="/shop/product/#{ product.product_tmpl_id.id }/">
<img class="img-rounded" t-att-src="product.img('image_small')"/>
</a>
</td>
<td>
<div>
<a t-href="/shop/product/#{ product.product_tmpl_id.id }/">
<strong t-field="product.name"/>
</a>
</div>
<div class="text-muted" t-field="product.description_sale"/>
</td>
<td>
<span t-field="product.lst_price"></span> €
</td>
<td class="text-center">
<a t-href="./add_cart/?product_id=#{ product.id }"><strong>Add to Cart</strong></a>
</td>
</tr>
</tbody>
</table>
</xpath>
</template>
<template id="suggested_products_cell" inherit_option_id="website_sale.mycart" name="Suggested Products in card view">
<xpath expr="//table[@id='mycart_products']" position="after">
<div class="mt16 mb32" t-if="suggested_products">
<h4>Suggested products</h4>
<div class='row mt16'>
<t t-foreach="suggested_products" t-as="product">
<div class='col-md-2 thumbnail'>
<div class='mt16 text-center'>
<a t-href="/shop/product/#{ product.id }/">
<img t-att-src="product.img('image_small')"/>
</a>
<h5>
<a t-href="/shop/product/#{ product.id }/" style="display: block;">
<span t-field="product.name"/>
</a>
</h5>
</div>
</div>
</t>
</div>
</div>
</xpath>
</template>
<template id="reduction_code" inherit_option_id="website_sale.mycart" name="Reduction Code">
<xpath expr="//table[@id='mycart_total']" position="after">
<form t-if="website_sale_order and website_sale_order.order_line" class="well" t-action="/shop/mycart/" method="post">
<input name="promo" class='input' type="text" placeholder="Reduction Code..." t-att-value="website_sale_order.pricelist_id.code or ''"/>
<button class="btn">Apply Code</button>
<xpath expr="//div[@id='right_column']" position="inside">
<h4>Coupon Code</h4>
<p>
Have a coupon code? Fill in this field and apply.
</p>
<form t-if="website_sale_order and website_sale_order.order_line" t-action="/shop/mycart/" method="post" class="mb32">
<div class="input-group">
<input name="promo" class='form-control' type="text" placeholder="code..." t-att-value="website_sale_order.pricelist_id.code or ''"/>
<div class="input-group-btn">
<button class="btn btn-default">Apply</button>
</div>
</div>
</form>
</xpath>
</template>
@@ -545,244 +583,263 @@
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
<t t-raw="head or ''"/>
</t>
<t t-set="title">Shop - Checkout</t>
<div id="wrap">
<div class="container oe_website_sale">
<ul class="wizard pull-right">
<li><a href="/shop/mycart" class="text-success">Review Order<span class="chevron"></span></a></li>
<li class="text-primary">Shipping &amp; Billing<span class="chevron"></span></li>
<li class="text-muted">Payment<span class="chevron"></span></li>
<li class="text-muted">Confirmation<span class="chevron"></span></li>
</ul>
<h1>Your Address</h1>
<form t-action="/shop/confirm_order/" method="post">
<div class="row">
<div class="col-md-4">
<table class="table table-condensed">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Qty.</th>
</tr>
</thead>
<tbody t-if="website_sale_order.order_line">
<t t-foreach="website_sale_order.order_line" t-as="line">
<tr>
<td><t t-esc="line.name"/></td>
<td><t t-esc="line.product_uom_qty"/></td>
<td><t t-esc="line.price_unit"/> €</td>
</tr>
</t>
</tbody>
<tfoot>
<tr><th>Untaxed Amount</th><td></td><td><t t-esc="website_sale_order.amount_untaxed"/> €</td></tr>
<tr><th>Taxes</th><td></td><td><t t-esc="website_sale_order.amount_tax"/> €</td></tr>
<tr><th>Total</th><td></td><td><t t-esc="website_sale_order.amount_total"/> €</td></tr>
</tfoot>
</table>
<div class="col-md-8 oe_mycart">
<h3 class="page-header mt16">Set Billing Information
<small t-if="user_id.id == website.public_user.id"> or
<a t-if="not partner" t-attf-href="/admin#action=redirect&amp;url=#{ request.httprequest.host_url }/shop/checkout/">sign in</a>
</small>
</h3>
<div class="row">
<div t-attf-class="form-group #{error.get('name') and 'has-error' or ''} col-lg-6">
<label for="contact_name">Your Name</label>
<input type="text" name="name" class="form-control" t-att-value="checkout['name']"/>
</div>
<div t-attf-class="form-group #{error.get('company') and 'has-error' or ''} col-lg-6">
<label for="company">Your Company</label>
<input type="text" name="company" class="form-control" t-att-value="checkout['company']"/>
</div>
<div t-attf-class="form-group #{error.get('email') and 'has-error' or ''} col-lg-6">
<label for="contact_name">Email</label>
<input type="email" name="email" class="form-control" t-att-value="checkout['email']"/>
</div>
<div t-attf-class="form-group #{ error.get('phone') and 'has-error' or ''} col-lg-6">
<label for="phone">Telephone</label>
<input type="tel" name="phone" class="form-control" t-att-value="checkout['phone']"/>
</div>
<div t-attf-class="form-group #{error.get('street') and 'has-error' or ''} col-lg-6">
<label for="street">Street</label>
<input type="text" name="street" class="form-control" t-att-value="checkout['street']"/>
</div>
<div class="clearfix"/>
<div t-attf-class="form-group #{error.get('city') and 'has-error' or ''} col-lg-6">
<label for="city">City</label>
<input type="text" name="city" class="form-control" t-att-value="checkout['city']"/>
</div>
<div t-attf-class="form-group #{error.get('zip') and 'has-error' or ''} col-lg-6">
<label for="zip">Zip / Postal Code</label>
<input type="text" name="zip" class="form-control" t-att-value="checkout['zip']"/>
</div>
<div t-attf-class="form-group #{error.get('state_id') and 'has-error' or ''} col-lg-6">
<label for="state_id">State / Province</label>
<select name="state_id" class="form-control">
<option value="">select...</option>
<t t-foreach="states or []" t-as="state">
<option t-att-value="state.id" t-att-selected="state.id == checkout['state_id']"><t t-esc="state.name"/></option>
</t>
</select>
</div>
<div t-attf-class="form-group #{error.get('country_id') and 'has-error' or ''} col-lg-6">
<label for="contact_name">Country</label>
<select name="country_id" class="form-control">
<option value="">Country...</option>
<t t-foreach="countries or []" t-as="country">
<option t-att-value="country.id" t-att-selected="country.id == checkout['country_id']"><t t-esc="country.name"/></option>
</t>
</select>
</div>
<div class="clearfix"/>
<div class="form-group col-lg-6">
<label>
<input t-if="not shipping" type="checkbox" name="shipping_different"/>
<input t-if="shipping" type="checkbox" name="shipping_different" checked="1"/>
Ship to a different address
</label>
</div>
</div>
<div class="js_shipping row mb16" t-att-style="not shipping and 'display:none' or ''">
<h3 class="oe_shipping col-lg-12 mt16">Shipping Information</h3>
<div t-attf-class="form-group #{error.get('shipping_name') and 'has-error' or ''} col-lg-6">
<label for="contact_name">Name (Shipping)</label>
<input type="text" name="shipping_name" class="form-control" t-att-value="checkout.get('shipping_name', '')"/>
</div>
<div t-attf-class="form-group #{error.get('shipping_phone') and 'has-error' or ''} col-lg-6">
<label for="contact_name">Telephone</label>
<input type="tel" name="shipping_phone" class="form-control" t-att-value="checkout.get('shipping_phone', '')"/>
</div>
<div t-attf-class="form-group #{error.get('shipping_street') and 'has-error' or ''} col-lg-6">
<label for="contact_name">Street</label>
<input type="text" name="shipping_street" class="form-control" t-att-value="checkout.get('shipping_street', '')"/>
</div>
<div class="clearfix"/>
<div t-attf-class="form-group #{error.get('shipping_city') and 'has-error' or ''} col-lg-6">
<label for="contact_name">City</label>
<input type="text" name="shipping_city" class="form-control" t-att-value="checkout.get('shipping_city', '')"/>
</div>
<div t-attf-class="form-group #{error.get('shipping_zip') and 'has-error' or ''} col-lg-6">
<label for="contact_name">Zip / Postal Code</label>
<input type="text" name="shipping_zip" class="form-control" t-att-value="checkout.get('shipping_zip', '')"/>
</div>
<div t-attf-class="form-group #{error.get('shipping_state_id') and 'has-error' or ''} col-lg-6">
<label for="contact_name">State / Province</label>
<select name="shipping_state_id" class="form-control">
<option value="">State / Province...</option>
<t t-foreach="states or []" t-as="state">
<option t-att-value="state.id" t-att-selected="state.id == checkout.get('shipping_state_id')"><t t-esc="state.name"/></option>
</t>
</select>
</div>
<div t-attf-class="form-group #{error.get('shipping_country_id') and 'has-error' or ''} col-lg-6">
<label for="contact_name">Country</label>
<select name="shipping_country_id" class="form-control">
<option value="">Country...</option>
<t t-foreach="countries or []" t-as="country">
<option t-att-value="country.id" t-att-selected="country.id == checkout.get('shipping_country_id')"><t t-esc="country.name"/></option>
</t>
</select>
</div>
</div>
<button type="submit" class="btn btn-default btn-primary pull-right mb32">Confirm <span class="icon-long-arrow-right"/></button>
</div>
<div class="col-lg-offset-1 col-lg-3 text-muted">
<h3 class="page-header mt16">Your Order <small><a href="/shop/mycart"><span class="icon-arrow-right"/> change</a></small></h3>
<div class="row">
<div class="col-sm-6 text-right">Subtotal:</div>
<div class="col-sm-6"><t t-esc="website_sale_order.amount_untaxed or 0"/> €</div>
<div class="col-sm-6 text-right">Taxes:</div>
<div class="col-sm-6"><t t-esc="website_sale_order.amount_tax or 0"/> €</div>
<div class="col-sm-6 text-right"><h4>Total To Pay:</h4></div>
<div class="col-sm-6"><h4><t t-esc="website_sale_order.amount_total or 0"/> €</h4></div>
</div>
<form class="col-md-8 form-horizontal" t-action="/shop/confirm_order/" method="post">
<div class=" row">
<a t-if="not partner" t-attf-href="/admin#action=redirect&amp;url=#{ request.httprequest.host_url }/shop/checkout/" class="btn btn-primary">Log me, I have an account</a>
<h3 class="col-md-10">Billing Information</h3>
<div t-attf-class="form-group #{error.get('name') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Name and firstname</label>
<div class="col-lg-4">
<input type="text" name="name" class="form-control" t-att-value="checkout['name']"/>
</div>
</div>
<div t-attf-class="form-group #{ error.get('phone') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Telephone</label>
<div class="col-lg-4">
<input type="tel" name="phone" class="form-control" t-att-value="checkout['phone']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('fax') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Fax</label>
<div class="col-lg-4">
<input type="tel" name="fax" class="form-control" t-att-value="checkout['fax']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('company') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Company</label>
<div class="col-lg-4">
<input type="text" name="company" class="form-control" t-att-value="checkout['company']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('email') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Email address</label>
<div class="col-lg-4">
<input type="email" name="email" class="form-control" t-att-value="checkout['email']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('street') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Street</label>
<div class="col-lg-4">
<input type="text" name="street" class="form-control" t-att-value="checkout['street']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('city') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">City</label>
<div class="col-lg-4">
<input type="text" name="city" class="form-control" t-att-value="checkout['city']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('state_id') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">State / Province</label>
<div class="col-lg-4">
<select name="state_id" class="form-control">
<option value="">State / Province...</option>
<t t-foreach="states or []" t-as="state">
<option t-att-value="state.id" t-att-selected="state.id == checkout['state_id']"><t t-esc="state.name"/></option>
</t>
</select>
</div>
</div>
<div t-attf-class="form-group #{error.get('zip') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Zip / Postal Code</label>
<div class="col-lg-4">
<input type="text" name="zip" class="form-control" t-att-value="checkout['zip']"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('country_id') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Country</label>
<div class="col-lg-4">
<select name="country_id" class="form-control">
<option value="">Country...</option>
<t t-foreach="countries or []" t-as="country">
<option t-att-value="country.id" t-att-selected="country.id == checkout['country_id']"><t t-esc="country.name"/></option>
</t>
</select>
</div>
</div>
<div class="form-group checkbox">
<div class="col-lg-7 col-md-offset-3">
<label>
<input t-if="not shipping" type="checkbox" name="shipping_different"/>
<input t-if="shipping" type="checkbox" name="shipping_different" checked="1"/>
Ship to different address
</label>
</div>
</div>
</div>
<div class="js_shipping row" t-att-style="not shipping and 'display:none' or ''">
<h3 class="col-md-12 oe_shipping">Shipping Information</h3>
<div t-attf-class="form-group #{error.get('shipping_name') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Name and firstname</label>
<div class="col-lg-4">
<input type="text" name="shipping_name" class="form-control" t-att-value="checkout.get('shipping_name', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_phone') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Telephone</label>
<div class="col-lg-4">
<input type="tel" name="shipping_phone" class="form-control" t-att-value="checkout.get('shipping_phone', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_fax') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Fax</label>
<div class="col-lg-4">
<input type="tel" name="shipping_fax" class="form-control" t-att-value="checkout.get('shipping_fax', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_street') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Street</label>
<div class="col-lg-4">
<input type="text" name="shipping_street" class="form-control" t-att-value="checkout.get('shipping_street', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_city') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">City</label>
<div class="col-lg-4">
<input type="text" name="shipping_city" class="form-control" t-att-value="checkout.get('shipping_city', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_state_id') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">State / Province</label>
<div class="col-lg-4">
<select name="shipping_state_id" class="form-control">
<option value="">State / Province...</option>
<t t-foreach="states or []" t-as="state">
<option t-att-value="state.id" t-att-selected="state.id == checkout.get('shipping_state_id')"><t t-esc="state.name"/></option>
</t>
</select>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_zip') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Zip / Postal Code</label>
<div class="col-lg-4">
<input type="text" name="shipping_zip" class="form-control" t-att-value="checkout.get('shipping_zip', '')"/>
</div>
</div>
<div t-attf-class="form-group #{error.get('shipping_country_id') and 'has-error' or ''}">
<label class="col-lg-3 control-label" for="contact_name">Country</label>
<div class="col-lg-4">
<select name="shipping_country_id" class="form-control">
<option value="">Country...</option>
<t t-foreach="countries or []" t-as="country">
<option t-att-value="country.id" t-att-selected="country.id == checkout.get('shipping_country_id')"><t t-esc="country.name"/></option>
</t>
</select>
</div>
</div>
</div>
<button type="submit" class="btn btn-default">Confirm</button>
</form>
<button type="submit" class="btn btn-default btn-primary pull-right mt16" t-if="user_id.id != website.public_user.id">
Confirm <span class="icon-long-arrow-right"/>
</button>
</div>
</div>
</form>
</div>
</div>
</t>
</template>
<template id="payment">
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
<t t-raw="head or ''"/>
</t>
<t t-set="title">Shop - Payment</t>
<div id="wrap">
<div class="container oe_website_sale">
<t t-set="head">
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
<t t-raw="head or ''"/>
</t>
<t t-set="title">Select Payment Mode</t>
<div id="wrap">
<div class="container oe_website_sale">
<ul class="wizard pull-right">
<li><a href="/shop/mycart" class="text-success">Review Order<span class="chevron"></span></a></li>
<li><a href="/shop/checkout" class="text-success">Shipping &amp; Billing<span class="chevron"></span></a></li>
<li class="text-primary">Payment<span class="chevron"></span></li>
<li class="text-muted">Confirmation<span class="chevron"></span></li>
</ul>
<h1 class="mb32">Validate Order</h1>
<div class="row">
<div class="col-md-4">
<table class="table table-condensed" t-if="order">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Qty.</th>
</tr>
</thead>
<tbody t-if="website_sale_order.order_line">
<t t-foreach="website_sale_order.order_line" t-as="line">
<tr>
<td><t t-esc="line.name"/></td>
<td><t t-esc="line.product_uom_qty"/></td>
<td><t t-esc="line.price_unit"/> €</td>
</tr>
</t>
</tbody>
<tfoot>
<tr><th>Untaxed Amount</th><td></td><td><t t-esc="website_sale_order.amount_untaxed"/> €</td></tr>
<tr><th>Taxes</th><td></td><td><t t-esc="website_sale_order.amount_tax"/> €</td></tr>
<tr><th>Total</th><td></td><td><t t-esc="website_sale_order.amount_total"/> €</td></tr>
</tfoot>
</table>
</div>
<div class="col-md-8">
<div class="js_payment row">
<h3 class="col-md-12">Click on your payment method</h3>
<div class="col-md-2">
<t t-foreach="payments or []" t-as="payment">
<label>
<input t-att-value="payment.id" type="radio" name="payment_type"/> <t t-esc="payment.name"/>
</label>
</t>
</div>
<t t-foreach="payments or []" t-as="payment">
<div t-att-data-id="payment.id" t-raw="payment._content" class="hidden col-md-6"/>
</t>
<a t-href="/shop/payment_validate/" class="hidden btn btn-default">I validate my payment</a>
</div>
</div>
<div class="col-md-8 oe_mycart">
<table class='table table-striped table-condensed' id="mycart_products" t-if="website_sale_order and website_sale_order.order_line">
<colgroup>
<col width="80"/>
<col/>
<col width="100"/>
<col width="120"/>
</colgroup>
<thead>
<tr>
<th colspan="2">Product</th>
<th>Price</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
<tr t-foreach="website_sale_order.order_line" t-as="line">
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
<td t-if="line.product_id.product_tmpl_id">
<a t-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/"><img class="img-rounded" t-att-src="line.product_id.img('image_small')"/></a>
</td>
<td t-if="line.product_id.product_tmpl_id">
<strong t-field="line.product_id.name"/>
</td>
<td class="text-center">
<span t-field="line.price_unit"></span> €
</td>
<td>
<div t-esc="line.product_uom_qty"/>
</td>
</tr>
</tbody>
</table>
<table class='pull-right mb16' id="mycart_total">
<colgroup>
<col width="100"/>
<col width="120"/>
</colgroup>
<thead>
<tr style="border-top: 1px solid #000">
<th><h3>Total:</h3></th>
<th class="text-right"><h3><t t-esc="website_sale_order and website_sale_order.amount_total or 0"/> €</h3></th>
</tr>
<tr class="text-muted">
<td><abbr title="Taxes may be updated after providing shipping address">Incl. Taxes:</abbr></td>
<td class="text-right"><t t-esc="website_sale_order and website_sale_order.amount_tax or 0"/> €</td>
</tr>
</thead>
</table>
<div class="clearfix"/>
<div class="oe_structure"/>
</div>
<div class="col-md-3 col-md-offset-1 text-muted" id="right_column">
<h4>Bill To:</h4>
<div t-field="website_sale_order.partner_invoice_id"/>
<div>
<a href="/shop/checkout"><span class="icon-arrow-right"/> Change Address</a>
</div>
<h4 class="mt32">Ship To:</h4>
<div t-field="website_sale_order.partner_shipping_id"/>
<div>
<a href="/shop/checkout"><span class="icon-arrow-right"/> Change Address</a>
</div>
</div>
</div>
<div class="js_payment mb64">
<p>Select your payment method:</p>
<div>
<t t-foreach="payments or []" t-as="payment">
<label>
<input t-att-value="payment.id" type="radio" name="payment_type"/> <t t-esc="payment.name"/>
</label>
</t>
</div>
<t t-foreach="payments or []" t-as="payment">
<div t-att-data-id="payment.id" t-raw="payment._content" class="hidden"/>
</t>
<a t-href="/shop/payment_validate/" class="btn btn-primary mt16">Validate &amp; Pay <span class="icon-long-arrow-right"/></a>
</div>
</div>
<div class="oe_structure"/>
</div>
</div>
</t>
</template>
</data>