[MERGE] upstream

bzr revid: fme@openerp.com-20140122102232-77lvvwpzet58sa7p
This commit is contained in:
Fabien Meghazi 2014-01-22 11:22:32 +01:00
commit df63fcba81
40 changed files with 747 additions and 239 deletions

View File

@ -23,7 +23,7 @@
<button type="image" name="submit" width="100px"
t-att-class="submit_class">
<img t-if="not submit_txt" src="/payment_acquirer_adyen/static/src/img/adyen_icon.png"/>
<span t-if="submit_txt"><t t-esc="submit_txt"/></span>
<span t-if="submit_txt"><t t-esc="submit_txt"/> <span class="fa fa-long-arrow-right"/></span>
</button>
</form>
</template>

View File

@ -44,7 +44,7 @@
<button type="image" name="submit" width="100px"
t-att-class="submit_class">
<img t-if="not submit_txt" src="/payment_acquirer_ogone/static/src/img/ogone_icon.png"/>
<span t-if="submit_txt"><t t-esc="submit_txt"/></span>
<span t-if="submit_txt"><t t-esc="submit_txt"/> <span class="fa fa-long-arrow-right"/></span>
</button>
</form>
</template>

View File

@ -34,7 +34,7 @@
<button type="image" name="submit" width="100px"
t-att-class="submit_class">
<img t-if="not submit_txt" src="/payment_acquirer_paypal/static/src/img/paypal_icon.png"/>
<span t-if="submit_txt"><t t-esc="submit_txt"/></span>
<span t-if="submit_txt"><t t-esc="submit_txt"/> <span class="fa fa-long-arrow-right"/></span>
</button>
</form>
</template>

View File

@ -14,7 +14,7 @@
<button type="image" name="submit" width="100px"
t-att-class="submit_class">
<img t-if="not submit_txt" src="/payment_acquirer_transfer/static/src/img/transfer_icon.png"/>
<span t-if="submit_txt"><t t-esc="submit_txt"/></span>
<span t-if="submit_txt"><t t-esc="submit_txt"/> <span class="fa fa-long-arrow-right"/></span>
</button>
</form>
</template>

View File

@ -404,11 +404,7 @@ class Contact(orm.AbstractModel):
field_browse = self.pool[column._obj].browse(cr, openerp.SUPERUSER_ID, id, context={"show_address": True})
value = werkzeug.utils.escape( field_browse.name_get()[0][1] )
IMD = self.pool["ir.model.data"]
model, id = IMD.get_object_reference(cr, uid, "website", "contact")
view = self.pool["ir.ui.view"].browse(cr, uid, id, context=context)
html = view.render({
val = {
'name': value.split("\n")[0],
'address': werkzeug.utils.escape("\n".join(value.split("\n")[1:])),
'phone': field_browse.phone,
@ -417,7 +413,9 @@ class Contact(orm.AbstractModel):
'email': field_browse.email,
'fields': opf,
'options': options
}, engine='website.qweb', context=context)
}
html = self.pool["ir.ui.view"].render(cr, uid, "website.contact", val, engine='website.qweb', context=context)
return ir_qweb.HTMLSafe(html)

View File

@ -117,6 +117,7 @@ class website(osv.osv):
'social_linkedin': fields.char('LinkedIn Account'),
'social_youtube': fields.char('Youtube Account'),
'social_googleplus': fields.char('Google+ Account'),
'google_analytics_key': fields.char('Google Analytics Key'),
'public_user': fields.function(_get_public_user, relation='res.users', type='many2one', string='Public User'),
'menu_id': fields.function(_get_menu, relation='website.menu', type='many2one', string='Main Menu',
store= {
@ -322,7 +323,7 @@ class website(osv.osv):
:type rule: werkzeug.routing.Rule
:rtype: bool
"""
spec = inspect.getargspec(rule.endpoint)
spec = inspect.getargspec(rule.endpoint.method)
# if *args bail the fuck out, only dragons can live there
if spec.varargs:

View File

@ -207,6 +207,44 @@ ul.oe_menu_editor .disclose {
visibility: visible !important;
}
.modal .font-icons-icons {
font-size: 2em;
max-height: 6em;
overflow: auto;
}
.modal .font-icons-icons .font-icons-icon {
display: inline-block;
width: 2em;
padding: 0.25em;
text-align: center;
cursor: pointer;
}
.modal .font-icons {
position: relative;
display: block;
}
.modal .font-icons:before {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70);
opacity: 0.7;
position: absolute;
top: 2px;
left: 3px;
font-size: 2em;
}
.modal #icon-search {
padding-left: 2.5em;
}
.modal #fa-preview {
text-align: center;
}
.modal #fa-preview span {
cursor: pointer;
padding: 0 15px;
}
.modal #fa-preview .font-icons-selected {
background-color: #dddddd;
}
.existing-attachments .pager .disabled {
display: none;
}
@ -320,6 +358,56 @@ ul.oe_menu_editor .disclose {
-o-border-radius: 0.4em;
border-radius: 0.4em;
}
.oe_seo_configuration li.oe_seo_preview_g {
line-height: 1.2;
list-style: none;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
font-size: small;
font-family: arial, sans-serif;
}
.oe_seo_configuration li.oe_seo_preview_g h3 {
font-size: medium;
}
.oe_seo_configuration li.oe_seo_preview_g .r {
margin: 0;
font-size: 16px;
font-style: normal;
font-weight: normal;
overflow: hidden;
text-overflow: ellipsis;
-webkit-text-overflow: ellipsis;
white-space: nowrap;
}
.oe_seo_configuration li.oe_seo_preview_g .r a {
color: #1e0fbe;
text-decoration: underline;
text-transform: none;
}
.oe_seo_configuration li.oe_seo_preview_g .r a em {
font-style: normal !important;
}
.oe_seo_configuration li.oe_seo_preview_g .s {
color: #444444;
max-width: 42em;
}
.oe_seo_configuration li.oe_seo_preview_g .kv, .oe_seo_configuration li.oe_seo_preview_g .slp {
display: block;
margin-bottom: 1px;
}
.oe_seo_configuration li.oe_seo_preview_g .f {
color: #666666;
margin-bottom: 1px;
}
.oe_seo_configuration li.oe_seo_preview_g .f cite {
color: #006621;
font-style: normal;
font-size: 14px;
}
.oe_seo_configuration li.oe_seo_preview_g .st {
line-height: 1.24;
}
/* ---- ACE EDITOR ---- {{{ */
.oe_ace_view_editor {

View File

@ -172,6 +172,43 @@ ul.oe_menu_editor
display: inline-block !important
visibility: visible !important
// fontawesome modal
.modal
.font-icons-icons
font-size: 2em
max-height: 6em
overflow: auto
.font-icons-icon
display: inline-block
width: 2em
padding: 0.25em
text-align: center
cursor: pointer
.font-icons
position: relative
display: block
&:before
+opacity(0.7)
position: absolute
top: 2px
left: 3px
font-size: 2em
#icon-search
padding-left: 2.5em
#fa-preview
text-align: center
span
cursor: pointer
padding: 0 15px
.font-icons-selected
background-color: #ddd
.existing-attachments .pager .disabled
display: none
@ -276,6 +313,47 @@ $highlighted_text_color: #ffffff
.oe_seo_keyword
padding: .2em .4em .2em .5em
+border-radius(.4em)
li.oe_seo_preview_g
line-height: 1.2
list-style: none
list-style-image: none
list-style-position: outside
list-style-type: none
font-size: small
font-family: arial,sans-serif
h3
font-size: medium
.r
margin: 0
font-size: 16px
font-style: normal
font-weight: normal
overflow: hidden
text-overflow: ellipsis
-webkit-text-overflow: ellipsis
white-space: nowrap
a
color: rgb(30, 15, 190)
text-decoration: underline
text-transform: none
em
font-style: normal !important
.s
color: #444
max-width: 42em
.kv,.slp
display: block
margin-bottom: 1px
.f
color: #666
margin-bottom: 1px
cite
color: #006621
font-style: normal
font-size: 14px
.st
line-height: 1.24
// }}}

View File

@ -463,6 +463,10 @@
$(window).on('resize', _.debounce(this.check_height.bind(this), 50));
this.check_height();
if (website.is_editable_button) {
this.$("button[data-action=edit]").removeClass("hidden");
}
return $.when(
this._super.apply(this, arguments),
this.rte.appendTo(this.$('#website-top-edit .nav.pull-right'))
@ -607,14 +611,19 @@
*/
setup_hover_buttons: function () {
var editor = this.rte.editor;
var $link_button = this.make_hover_button(_t("Edit Link"), function () {
var $link_button = this.make_hover_button(_t("Change"), function () {
var sel = new CKEDITOR.dom.element(previous);
editor.getSelection().selectElement(sel);
link_dialog(editor);
if (previous.tagName.toUpperCase() === 'A') {
link_dialog(editor);
} else if(sel.hasClass('fa')) {
new website.editor.FontIconsDialog(editor, previous)
.appendTo(document.body);
}
$link_button.hide();
previous = null;
}, 'btn-xs');
var $image_button = this.make_hover_button(_t("Edit Image"), function () {
var $image_button = this.make_hover_button(_t("Change"), function () {
image_dialog(editor, new CKEDITOR.dom.element(previous));
$image_button.hide();
previous = null;
@ -625,12 +634,12 @@
// -ish, because when moving to the button itself ``previous`` is
// still set to the element having triggered showing the button.
var previous;
$(editor.element.$).on('mouseover', 'a, img', function (e) {
$(editor.element.$).on('mouseover', 'a, img, .fa', function () {
// Back from edit button -> ignore
if (previous && previous === this) { return; }
var selected = new CKEDITOR.dom.element(this);
if (!is_editable_node(selected)) {
if (!is_editable_node(selected) && !selected.hasClass('fa')) {
return;
}
@ -659,7 +668,7 @@
- $link_button.outerWidth() / 2
})
}
}).on('mouseleave', 'a, img', function (e) {
}).on('mouseleave', 'a, img, .fa', function (e) {
var current = document.elementFromPoint(e.clientX, e.clientY);
if (current === $link_button[0] || current === $image_button[0]) {
return;
@ -817,6 +826,28 @@
document.execCommand("enableInlineTableEditing", false, "false");
} catch (e) {}
// detect & setup any CKEDITOR widget within a newly dropped
// snippet. There does not seem to be a simple way to do it for
// HTML not inserted via ckeditor APIs:
// https://dev.ckeditor.com/ticket/11472
$(document.body)
.off('snippet-dropped')
.on('snippet-dropped', function (e, el) {
// CKEDITOR data processor extended by widgets plugin
// to add wrappers around upcasting elements
el.outerHTML = editor.dataProcessor.toHtml(el.outerHTML, {
fixForBody: false,
dontFilter: true,
});
// then repository.initOnAll() handles the conversion
// from wrapper to actual widget instance (or something
// like that).
setTimeout(function () {
editor.widgets.initOnAll();
}, 0);
});
self.trigger('rte:ready');
def.resolve();
});
@ -1056,15 +1087,20 @@
bind_data: function (text, href, new_window) {
href = href || this.element && (this.element.data( 'cke-saved-href')
|| this.element.getAttribute('href'));
if (!href) { return; }
if (new_window === undefined) {
new_window = this.element.getAttribute('target') === '_blank';
new_window = this.element
? this.element.getAttribute('target') === '_blank'
: false;
}
if (text === undefined) {
text = this.element.getText();
text = this.element ? this.element.getText() : '';
}
this.$('input#link-text').val(text);
this.$('input.window-new').prop('checked', new_window);
if (!href) { return; }
var match, $control;
if ((match = /mailto:(.+)/.exec(href))) {
$control = this.$('input.email-address').val(match[1]);
@ -1074,9 +1110,6 @@
}
this.changed($control);
this.$('input#link-text').val(text);
this.$('input.window-new').prop('checked', new_window);
},
changed: function ($e) {
this.$('.url-source').filter(':input').not($e).val('')
@ -1456,6 +1489,34 @@
template: 'website.editor.dialog.font-icons',
events : _.extend({}, website.editor.Dialog.prototype.events, {
change: 'update_preview',
'click .font-icons-icon': function (e) {
e.preventDefault();
e.stopPropagation();
this.$('#fa-icon').val(e.target.getAttribute('data-id'));
this.update_preview();
},
'click #fa-preview span': function (e) {
e.preventDefault();
e.stopPropagation();
this.$('#fa-size').val(e.target.getAttribute('data-size'));
this.update_preview();
},
'input input#icon-search': function () {
var needle = this.$('#icon-search').val();
var icons = this.icons;
if (needle) {
icons = _(icons).filter(function (icon) {
return icon.id.substring(3).indexOf(needle) !== -1;
});
}
this.$('div.font-icons-icons').html(
openerp.qweb.render(
'website.editor.dialog.font-icons.icons',
{icons: icons}));
},
}),
// List of FontAwesome icons in 4.0.3, extracted from the cheatsheet.
@ -1474,26 +1535,7 @@
* isn't customizable (?) and the fontawesome glyphs fail to appear.
*/
start: function () {
var self = this;
var started = this._super();
this.$('#fa-icon').select2({
data: this.icons,
initSelection: function (element, callback) {
var id = element.val(), match;
if (id) {
match = _.find(self.icons, function (item) {
return item.id === id;
}) || void 0;
}
callback(match);
},
formatSelection: function (object) {
return $('<span class="fa fa-fw">').text(object.text);
},
formatResultCssClass: function () { return 'fa'; },
});
return started.then(this.proxy('load_data'));
return this._super().then(this.proxy('load_data'));
},
/**
* Removes existing FontAwesome classes on the bound element, and sets
@ -1520,7 +1562,7 @@
for (var i = 0; i < classes.length; i++) {
var cls = classes[i];
switch(cls) {
case 'fa-lg':case 'fa-2x':case 'fa-3x':case 'fa-4x':case 'fa-5x':
case 'fa-2x':case 'fa-3x':case 'fa-4x':case 'fa-5x':
// size classes
this.$('#fa-size').val(cls);
continue;
@ -1529,15 +1571,14 @@
case 'fa-flip-horizontal':case 'fa-rotate-vertical':
this.$('#fa-rotation').val(cls);
continue;
case 'fa-fixed':
this.$('#fa-fixed').prop('checked', true);
case 'fa-fw':
continue;
case 'fa-border':
this.$('#fa-border').prop('checked', true);
continue;
default:
if (!/^fa-/.test(cls)) { continue; }
this.$('#fa-icon').select2('val', cls);
this.$('#fa-icon').val(cls);
}
}
this.update_preview();
@ -1549,19 +1590,34 @@
get_fa_classes: function () {
return [
'fa',
this.$('#fa-icon').select2('val'),
this.$('#fa-icon').val(),
this.$('#fa-size').val(),
this.$('#fa-rotation').val(),
this.$('#fa-fixed').prop('checked') ? 'fa-fixed' : '',
this.$('#fa-border').prop('checked') ? 'fa-border' : ''
];
},
update_preview: function () {
this.$('#fa-preview')[0].className = this.get_fa_classes().join(' ');
var $preview = this.$('#fa-preview').empty();
var sizes = ['', 'fa-2x', 'fa-3x', 'fa-4x', 'fa-5x'];
var classes = this.get_fa_classes();
var no_sizes = _.difference(classes, sizes).join(' ');
var selected = false;
for (var i = sizes.length - 1; i >= 0; i--) {
var size = sizes[i];
var $p = $('<span>')
.attr('data-size', size)
.addClass(size)
.addClass(no_sizes);
if ((size && _.contains(classes, size)) || (!size && !selected)) {
$p.addClass('font-icons-selected');
selected = true;
}
$preview.prepend($p);
}
}
});
website.Observer = window.MutationObserver || window.WebkitMutationObserver || window.JsMutationObserver;
var OBSERVER_CONFIG = {
childList: true,

View File

@ -3,11 +3,12 @@
var website = openerp.website;
website.is_editable = true;
website.is_editable_button = true;
website.EditorBar.include({
start: function() {
var res = this._super();
this.$("a[data-action=new_page]").parents("li").removeClass("hidden");
this.$("button[data-action=edit]").removeClass("hidden");
this.$(".oe_content_menu li.divider").removeClass("hidden");
return res;
},

View File

@ -67,6 +67,7 @@
var dom_ready = website.dom_ready = $.Deferred();
$(document).ready(function () {
website.is_editable = website.is_editable || $('html').data('editable');
website.is_editable_button= website.is_editable_button || $('html').data('editable');
dom_ready.resolve();
});

View File

@ -25,7 +25,10 @@
} : htmlPage.isInBody(keyword) ? {
title: 'label label-info',
description: "This keyword is used in the page content."
} : { title: "", description: "" };
} : {
title: 'label label-default',
description: "This keyword is not used anywhere on the page."
};
}
website.seo.Suggestion = openerp.Widget.extend({
@ -234,6 +237,16 @@
},
});
website.seo.Preview = openerp.Widget.extend({
template: 'website.seo_preview',
init: function (parent, options) {
this.title = options.title;
this.url = options.url;
this.description = options.description || "[ The description will be generated by google unless you specify one ]";
this._super(parent);
},
});
website.seo.HtmlPage = openerp.Class.extend(openerp.PropertiesMixin, {
url: function () {
var url = window.location.href;
@ -327,7 +340,7 @@
canEditDescription: false,
canEditKeywords: false,
maxTitleSize: 65,
maxDescriptionSize: 155,
maxDescriptionSize: 150,
start: function () {
var self = this;
var $modal = self.$el;
@ -361,6 +374,7 @@
});
self.keywordList.appendTo($modal.find('.js_seo_keywords_list'));
self.disableUnsavableFields();
self.renderPreview();
$modal.modal();
},
disableUnsavableFields: function () {
@ -376,7 +390,7 @@
if (!self.canEditDescription) {
$modal.find('textarea[name=seo_page_description]').attr('disabled', true);
}
if (!self.canEditTitle && !canEditDescription && !canEditKeywords) {
if (!self.canEditTitle && !self.canEditDescription && !self.canEditKeywords) {
$modal.find('button[data-action=update]').attr('disabled', true);
}
});
@ -484,6 +498,7 @@
setTimeout(function () {
var title = self.$('input[name=seo_page_title]').val();
self.htmlPage.changeTitle(title);
self.renderPreview();
}, 0);
},
descriptionChanged: function () {
@ -491,7 +506,18 @@
setTimeout(function () {
var description = self.$('textarea[name=seo_page_description]').attr('value');
self.htmlPage.changeDescription(description);
}, 1);
self.renderPreview();
}, 0);
},
renderPreview: function () {
var preview = new website.seo.Preview(this, {
title: this.htmlPage.title(),
description: this.htmlPage.description(),
url: this.htmlPage.url(),
});
var $preview = this.$('.js_seo_preview');
$preview.empty();
preview.appendTo($preview);
},
destroy: function () {
this.htmlPage.changeKeywords(this.keywordList.keywords());

View File

@ -76,6 +76,9 @@
hack_to_add_snippet_id();
});
// 'snippet-dropped' is triggered on '#oe_snippets' whith $target as attribute when a snippet is dropped
// 'snippet-activated' is triggered on '#oe_snippets' (and on snippet) when a snippet is activated
website.snippet.styles = {};
website.snippet.selector = [];
website.snippet.BuildingBlock = openerp.Widget.extend({

View File

@ -19,104 +19,108 @@
});
this.registerSteps();
},
registerSteps: function () {
registerStep: function (step) {
var self = this;
this.tour.addSteps(_.map(this.steps, function (step) {
step.title = openerp.qweb.render('website.tour_popover_title', { title: step.title });
if (!step.element) {
step.orphan = true;
}
if (step.snippet) {
step.element = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail';
}
if (step.trigger) {
if (step.trigger === 'click') {
step.title = openerp.qweb.render('website.tour_popover_title', { title: step.title });
if (!step.element) {
step.orphan = true;
}
if (step.snippet) {
step.element = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail';
}
if (step.trigger) {
if (step.trigger === 'click') {
step.triggers = function (callback) {
$(step.element).one('click', function () {
(callback || self.moveToNextStep).apply(self);
});
};
} else if (step.trigger === 'reload') {
step.triggers = function (callback) {
var stack = JSON.parse(localStorage.getItem("website-reloads")) || [];
var index = stack.indexOf(step.stepId);
if (index !== -1) {
stack.splice(index,1);
(callback || self.moveToNextStep).apply(self);
} else {
stack.push(step.stepId);
}
localStorage.setItem("website-reloads", JSON.stringify(stack));
};
} else if (step.trigger.url) {
step.triggers = function (callback) {
var stack = JSON.parse(localStorage.getItem("website-geturls")) || [];
var id = step.trigger.url.toString();
var index = stack.indexOf(id);
if (index !== -1) {
var url = new website.UrlParser(window.location.href);
var test = typeof step.trigger.url === "string" ?
step.trigger.url == url.pathname+url.search :
step.trigger.url.test(url.pathname+url.search);
if (!test) return;
stack.splice(index,1);
(callback || self.moveToNextStep).apply(self);
} else {
stack.push(id);
}
localStorage.setItem("website-geturls", JSON.stringify(stack));
};
} else if (step.trigger === 'drag') {
step.triggers = function (callback) {
self.onSnippetDragged(callback || self.moveToNextStep);
};
} else if (step.trigger.id) {
if (step.trigger.emitter && step.trigger.type === 'openerp') {
step.triggers = function (callback) {
$(step.element).one('click', function () {
(callback || self.moveToNextStep).apply(self);
step.trigger.emitter.on(step.trigger.id, self, function customHandler () {
step.trigger.emitter.off(step.trigger.id, customHandler);
(callback || self.moveToNextStep).apply(self, arguments);
});
};
} else if (step.trigger === 'reload') {
} else {
step.triggers = function (callback) {
var stack = JSON.parse(localStorage.getItem("website-reloads")) || [];
var index = stack.indexOf(step.stepId);
if (index !== -1) {
stack.splice(index,1);
(callback || self.moveToNextStep).apply(self);
} else {
stack.push(step.stepId);
}
localStorage.setItem("website-reloads", JSON.stringify(stack));
};
} else if (step.trigger && step.trigger.url) {
step.triggers = function (callback) {
var stack = JSON.parse(localStorage.getItem("website-geturls")) || [];
var id = step.trigger.url.toString();
var index = stack.indexOf(id);
if (index !== -1) {
var url = new website.UrlParser(window.location.href);
var test = typeof step.trigger.url === "string" ?
step.trigger.url == url.pathname+url.search :
step.trigger.url.test(url.pathname+url.search);
if (!test) return;
stack.splice(index,1);
(callback || self.moveToNextStep).apply(self);
} else {
stack.push(id);
}
localStorage.setItem("website-geturls", JSON.stringify(stack));
};
} else if (step.trigger === 'drag') {
step.triggers = function (callback) {
self.onSnippetDragged(callback || self.moveToNextStep);
};
} else if (step.trigger && step.trigger.id) {
if (step.trigger.emitter && step.trigger.type === 'openerp') {
step.triggers = function (callback) {
step.trigger.emitter.on(step.trigger.id, self, function customHandler () {
step.trigger.emitter.off(step.trigger.id, customHandler);
(callback || self.moveToNextStep).apply(self, arguments);
});
};
} else {
step.triggers = function (callback) {
var emitter = _.isString(step.trigger.emitter) ? $(step.trigger.emitter) : (step.trigger.emitter || $(step.element));
if (!emitter.size()) throw "Emitter is undefined";
emitter.on(step.trigger.id, function () {
(callback || self.moveToNextStep).apply(self, arguments);
});
};
}
} else if (step.trigger.modal) {
step.triggers = function (callback) {
var $doc = $(document);
function onStop () {
if (step.trigger.modal.stopOnClose) {
self.stop();
}
}
$doc.on('hide.bs.modal', onStop);
$doc.one('shown.bs.modal', function () {
$('.modal button.btn-primary').one('click', function () {
$doc.off('hide.bs.modal', onStop);
(callback || self.moveToNextStep).apply(self, [step.trigger.modal.afterSubmit]);
});
(callback || self.moveToNextStep).apply(self);
var emitter = _.isString(step.trigger.emitter) ? $(step.trigger.emitter) : (step.trigger.emitter || $(step.element));
if (!emitter.size()) throw "Emitter is undefined";
emitter.on(step.trigger.id, function () {
(callback || self.moveToNextStep).apply(self, arguments);
});
};
}
}
step.onShow = (function () {
var executed = false;
return function () {
if (!executed) {
_.isFunction(step.onStart) && step.onStart();
_.isFunction(step.triggers) && step.triggers();
executed = true;
} else if (step.trigger.modal) {
step.triggers = function (callback) {
var $doc = $(document);
function onStop () {
if (step.trigger.modal.stopOnClose) {
self.stop();
}
}
$doc.on('hide.bs.modal', onStop);
$doc.one('shown.bs.modal', function () {
$('.modal button.btn-primary').one('click', function () {
$doc.off('hide.bs.modal', onStop);
(callback || self.moveToNextStep).apply(self, [step.trigger.modal.afterSubmit]);
});
(callback || self.moveToNextStep).apply(self);
});
};
}());
return step;
}
}
step.onShow = (function () {
var executed = false;
return function () {
if (!executed) {
_.isFunction(step.onStart) && step.onStart();
_.isFunction(step.triggers) && step.triggers();
executed = true;
}
};
}());
return step;
},
registerSteps: function () {
var self = this;
this.tour.addSteps(_.map(this.steps, function (step) {
return self.registerStep(step);
}));
},
reset: function () {
@ -152,8 +156,8 @@
this.stop();
} else if (index >= 0) {
var self = this;
$('.popover.tour').remove();
setTimeout(function () {
$('.popover.tour').remove();
setTimeout(function () {
self.tour.goto(index);
}, 0);
@ -323,6 +327,7 @@
var testId = 'test_'+tour.id+'_tour';
this.tours.push(tour);
var defaultDelay = 500; //ms
var overlapsCrash;
var test = {
id: tour.id,
run: function (force) {
@ -332,34 +337,63 @@
var actionSteps = _.filter(tour.steps, function (step) {
return step.trigger || step.sampleText;
});
window.onbeforeunload = function () {
clearTimeout(overlapsCrash);
};
function executeStep (step) {
var lastStep = window.localStorage.getItem(testId);
var tryStep = lastStep != step.stepId ? 0 : parseInt(window.localStorage.getItem("last-"+testId) || 0, 10)+1;
window.localStorage.setItem("last-"+testId, tryStep);
if (tryStep > 2) {
window.localStorage.removeItem(testId);
throw "Test: '" + testId + "' cycling step: '" + step.stepId + "'";
}
var _next = false;
window.localStorage.setItem(testId, step.stepId);
function next () {
clearTimeout(overlapsCrash);
_next = true;
var nextStep = actionSteps.shift();
if (nextStep) {
executeStep(nextStep);
setTimeout(function () {
executeStep(nextStep);
}, step.delay || defaultDelay);
} else {
window.localStorage.removeItem(testId);
}
}
setTimeout(function () {
if (step.triggers) step.triggers(next);
var $element = $(step.element);
if (step.snippet && step.trigger === 'drag') {
website.TestConsole.dragAndDropSnippet(step.snippet);
} else if (step.trigger && step.trigger.id === 'change') {
$element.trigger($.Event("change", { srcElement: $element }));
} else if (step.sampleText) {
$element.val(step.sampleText);
$element.trigger($.Event("change", { srcElement: $element }));
} else if ($element.is(":visible")) { // Click by default
$element.trigger($.Event("click", { srcElement: $element }));
overlapsCrash = setTimeout(function () {
window.localStorage.removeItem(testId);
throw "Test: '" + testId + "' can't resolve step: '" + step.stepId + "'";
}, (step.delay || defaultDelay) + 500);
var $element = $(step.element);
if (step.triggers) step.triggers(next);
if ((step.trigger === 'reload' || (step.trigger && step.trigger.url)) && _next) return;
if (step.snippet && step.trigger === 'drag') {
website.TestConsole.dragAndDropSnippet(step.snippet);
} else if (step.trigger && step.trigger.id === 'change') {
$element.trigger($.Event("change", { srcElement: $element }));
} else if (step.sampleText) {
$element.val(step.sampleText);
$element.trigger($.Event("change", { srcElement: $element }));
} else if ($element.is(":visible")) { // Click by default
if (step.trigger.id === 'mousedown') {
$element.trigger($.Event("mousedown", { srcElement: $element }));
}
if (!step.triggers) next();
}, step.delay || defaultDelay);
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
$element[0].dispatchEvent(evt);
if (step.trigger.id === 'mouseup') {
$element.trigger($.Event("mouseup", { srcElement: $element }));
}
}
if (!step.triggers) next();
}
var url = new website.UrlParser(window.location.href);
if (tour.path && url.pathname !== tour.path) {
if (tour.path && url.pathname !== tour.path && !window.localStorage.getItem(testId)) {
window.localStorage.setItem(testId, actionSteps[0].stepId);
window.location.href = tour.path;
} else {

View File

@ -13,9 +13,13 @@
var self = this;
this.initial_content = {};
return this._super.apply(this, arguments).then(function () {
self.$("button[data-action=edit]").removeClass("hidden");
self.$('button[data-action=edit]')
.text("Translate")
.after(openerp.qweb.render('website.TranslatorAdditionalButtons'));
.text("Translate");
if (website.is_editable_button) {
self.$('button[data-action=edit]')
.after(openerp.qweb.render('website.TranslatorAdditionalButtons'));
}
self.$('.js_hide_on_translate').hide();
});
},

View File

@ -207,23 +207,18 @@
<t t-call="website.editor.dialog">
<t t-set="title">Icon:</t>
<form>
<div class="form-group">
Preview: <span id="fa-preview"/>
<div class="form-group" id="fa-preview">
</div>
<div class="form-group">
<label for="fa-icon">icon</label>
<div class="form-group font-icons fa fa-search">
<input type="hidden" id="fa-icon" class="form-control"/>
</div>
<div class="form-group">
<label for="fa-size">size</label>
<select id="fa-size" class="form-control">
<option value="">Default</option>
<option value="fa-lg">Large</option>
<option value="fa-2x">x2</option>
<option value="fa-3x">x3</option>
<option value="fa-4x">x4</option>
<option value="fa-5x">x5</option>
</select>
<input type="hidden" id="fa-size" class="form-control"/>
<input type="search" class="form-control" id="icon-search"/>
<div class="font-icons-icons">
<t t-call="website.editor.dialog.font-icons.icons">
<t t-set="icons" t-value="widget.icons"/>
</t>
</div>
</div>
<div class="form-group">
<label for="fa-rotation">Rotation</label>
@ -239,16 +234,18 @@
</div>
<div class="form-group">
<label>
<input type="checkbox" id="fa-fixed"/> fixed width
</label>
</div>
<div class="form-group">
<label>
<input type="checkbox" id="fa-border"/> bordered
<input type="checkbox" id="fa-border"/> border
</label>
</div>
</form>
</t>
</t>
<t t-name="website.editor.dialog.font-icons.icons">
<span t-foreach="icons" t-as="icon"
class="fa font-icons-icon"
t-att-data-id="icon.id">
<t t-esc="icon.text"/>
</span>
</t>
</templates>

View File

@ -43,19 +43,24 @@
<div class="form-group">
<label for="seo_page_description" class="col-lg-2 control-label">Description</label>
<div class="col-lg-8">
<textarea name="seo_page_description" class="form-control" rows="3" cols="70" size="160"/>
<textarea name="seo_page_description" class="form-control" rows="3" cols="70" t-att-maxlength="widget.maxDescriptionSize"/>
</div>
</div>
</div>
</section>
<section>
<h3 class="page-header">3. Preview <small>how your page will be listed on Google</small></h3>
<div class="js_seo_preview"></div>
</section>
<!--
<section class="js_image_section">
<h3 class="page-header">3. Describe your images</h3>
<h3 class="page-header">4. Describe your images</h3>
<div class="row js_seo_image_list">
</div>
</section>
-->
</div><div class="modal-footer">
</div>
<div class="modal-footer">
<button type="button" data-action="update" class="btn btn-primary">Save</button>
or
<a data-action="discard" data-dismiss="modal" href="#" >Discard</a>
@ -111,4 +116,26 @@
</div>
</t>
<t t-name="website.seo_preview">
<li class="oe_seo_preview_g">
<div class="rc">
<span class="altcts"></span>
<h3 class="r">
<a t-att-href="url">
<em><t t-esc="widget.title"/></em>
</a>
</h3>
<div class="s">
<div>
<div class="f kv" style="white-space:nowrap">
<cite><t t-esc="widget.url"/></cite>
</div>
<div class="f slp"></div>
<span class="st"><t t-esc="widget.description"/></span>
</div>
</div>
</div>
</li>
</t>
</templates>

View File

@ -141,11 +141,11 @@ class WebsiteUiSuite(unittest.TestSuite):
except ValueError:
result.addError(self._test, 'Unexpected message: "%s"' % "\n".join(lines))
def full_path(filename):
return os.path.join(os.path.join(os.path.dirname(__file__), 'ui_suite'), filename)
def full_path(pyfile, filename):
return os.path.join(os.path.join(os.path.dirname(pyfile), 'ui_suite'), filename)
def load_tests(loader, base, _):
base.addTest(WebsiteUiSuite(full_path('dummy_test.js'), {}, 5.0))
base.addTest(WebsiteUiSuite(full_path('simple_dom_test.js'), {'redirect': '/page/website.homepage'}, 60.0))
base.addTest(WebsiteUiSuite(full_path('homepage_test.js'), {'redirect': '/page/website.homepage'}, 60.0))
base.addTest(WebsiteUiSuite(full_path(__file__, 'dummy_test.js'), {}, 5.0))
base.addTest(WebsiteUiSuite(full_path(__file__, 'simple_dom_test.js'), {'redirect': '/page/website.homepage'}, 60.0))
base.addTest(WebsiteUiSuite(full_path(__file__, 'homepage_test.js'), {'redirect': '/page/website.homepage'}, 60.0))
return base

View File

@ -9,7 +9,8 @@ function waitFor (ready, callback, timeout, timeoutMessageCallback) {
if(!condition) {
var message = timeoutMessageCallback ? timeoutMessageCallback() : "Timeout after "+timeout+" ms";
console.log('{ "event": "error", "message": "'+message+'" }');
console.log("Waiting for...\n"+ready);
var json = { "event": "error", "message": "Waiting for...\n"+ready };
console.log(JSON.stringify(json));
phantom.exit(1);
} else {
clearInterval(interval);
@ -17,7 +18,7 @@ function waitFor (ready, callback, timeout, timeoutMessageCallback) {
}
}
}, 100);
};
}
function run (test) {
var options = JSON.parse(phantom.args);

View File

@ -12,12 +12,18 @@
</template>
<template id="website.submenu" name="Submenu">
<li t-if="not submenu.child_id">
<li t-if="not submenu.child_id" t-att-class="
((submenu.url != '/' and request.httprequest.path.startswith(submenu.url)) or
request.httprequest.path == submenu.url) and 'active'
">
<a t-att-href="url_for(submenu.url)" t-ignore="true" t-att-target="'blank' if submenu.new_window else None">
<span t-field="submenu.name"/>
</a>
</li>
<li t-if="submenu.child_id" class="dropdown">
<li t-if="submenu.child_id" t-attf-class="dropdown #{
((submenu.url != '/' and [1 for submenu in submenu.child_id if request.httprequest.path.startswith(submenu.url)]) or
request.httprequest.path == submenu.url) and 'active'
}">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<span t-field="submenu.name"/> <span class="caret" t-ignore="true"></span>
</a>
@ -96,8 +102,8 @@
<t t-foreach="website.menu_id.child_id" t-as="submenu">
<t t-call="website.submenu"/>
</t>
<li class="divider" t-if="user_id.id != website.public_user.id"/>
<li class="dropdown" t-ignore="true" t-if="user_id.id != website.public_user.id">
<li class="divider" groups="base.group_user,base.group_portal"/>
<li class="dropdown" t-ignore="true" groups="base.group_user,base.group_portal">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<b>
<span t-esc="user_id.name"/>
@ -191,6 +197,16 @@
</div>
</footer>
</div>
<t t-if="website.google_analytics_key">
<script>
(function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=
function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;
e=o.createElement(i);r=o.getElementsByTagName(i)[0];
e.src='//www.google-analytics.com/analytics.js';
r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
ga('create','<t t-esc="website.google_analytics_key"/>');ga('send','pageview');
</script>
</t>
</body>
</html>
</template>
@ -247,10 +263,10 @@
</xpath>
</template>
<template id="show_sign_in" inherit_option_id="website.layout" inherit_id="website.layout" name="Show Sign In">
<template id="show_sign_in" inherit_option_id="website.layout" inherit_id="website.layout" name="Show Sign In" groups="base.group_public">
<xpath expr="//ul[@id='top_menu']" position="inside">
<li class="divider" t-if="user_id.id == website.public_user.id"/>
<li t-if="user_id.id == website.public_user.id">
<li class="divider"/>
<li>
<a t-attf-href="/web/login">
<b>Sign in</b>
</a>
@ -643,6 +659,9 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
</div>
</div>
<div class="col-md-4 mb32">
<div groups="base.group_website_publisher" t-ignore="true" class="pull-right css_editable_mode_hidden" t-att-style="style or ''">
<a class="btn btn-primary" t-att-href="'/web#return_label=Website&amp;model=%s&amp;id=%s' % (res_company._name, res_company.id)" title='Edit in backend'>Edit</a>
</div>
<t t-call="website.company_description"/>
</div>
</div>
@ -732,8 +751,8 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
</template>
<template id="contact">
<address t-ignore="true">
<div t-att-class='"" if "name" in fields else "css_non_editable_mode_hidden"'><span t-esc="name"/></div>
<address t-ignore="true" class="mb0">
<div t-attf-class="'name' not in fields and 'css_non_editable_mode_hidden'"><span t-esc="name"/></div>
<div class='css_editable_mode_hidden'>
<div t-if="address and 'address' in fields">
<i class='fa fa-map-marker'/>

View File

@ -57,6 +57,9 @@
<field name="language_ids" widget="many2many_checkboxes"/>
<field name="default_lang_id" widget="selection"/>
</group>
<group string="Website Management">
<field name="google_analytics_key" placeholder="UA-XXXXXXXX-X"/>
</group>
</group>
</sheet>
</form>

View File

@ -62,7 +62,9 @@
placement: 'right',
title: "Create Blog Post",
content: "Click <em>Continue</em> to create the blog post.",
trigger: 'click',
trigger: {
url: /blogpost\/[0-9]+\/.*/,
},
},
{
stepId: 'post-page',
@ -139,7 +141,7 @@
title: "Save Your Blog",
content: "Click the <em>Save</em> button to record changes on the page.",
template: self.popover({ fixed: true }),
trigger: 'click',
trigger: 'reload',
},
{
stepId: 'publish-post',

View File

@ -7,10 +7,8 @@
website.is_editable = true;
website.EditorBar.include({
start: function() {
website.is_editable_button = website.is_editable_button || !!$("#wrap.js_blog").size();
var res = this._super();
if ($("#wrap.js_blog").size()) {
this.$("button[data-action=edit]").removeClass("hidden");
}
this.$(".dropdown:has(.oe_content_menu)").removeClass("hidden");
return res;
},

View File

@ -19,7 +19,7 @@
#
##############################################################################
from openerp.addons.website_blog.tests import test_controllers
import test_controllers
import test_ui

View File

@ -1,10 +1,5 @@
import os
import openerp.addons.website.tests.test_ui as test_ui
def full_path(filename):
return os.path.join(os.path.join(os.path.dirname(__file__), 'ui_suite'), filename)
def load_tests(loader, base, _):
base.addTest(test_ui.WebsiteUiSuite(full_path('post_test.js'), {'redirect': '/page/website.homepage'}, 60.0))
base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'post_test.js'), {'redirect': '/page/website.homepage'}, 60.0))
return base

View File

@ -60,7 +60,9 @@
placement: 'right',
title: "Create Event",
content: "Click <em>Continue</em> to create the event.",
trigger: 'click',
trigger: {
url: /event\/[0-9]+\/register/
},
},
{
stepId: 'event-page',
@ -137,7 +139,7 @@
title: "Save your modifications",
content: "Once you click on save, your event is updated.",
template: self.popover({ fixed: true }),
trigger: 'click',
trigger: 'reload',
},
{
stepId: 'publish-event',

View File

@ -7,10 +7,8 @@
website.is_editable = true;
website.EditorBar.include({
start: function() {
website.is_editable_button = website.is_editable_button || !!$("#wrap.js_event").size();
var res = this._super();
if ($("#wrap.js_event").size()) {
this.$("button[data-action=edit]").removeClass("hidden");
}
this.$(".dropdown:has(.oe_content_menu)").removeClass("hidden");
return res;
},

View File

@ -19,7 +19,7 @@
#
##############################################################################
from openerp.addons.website_blog.tests import test_controllers
import test_controllers
import test_ui

View File

@ -1,10 +1,5 @@
import os
import openerp.addons.website.tests.test_ui as test_ui
def full_path(filename):
return os.path.join(os.path.join(os.path.dirname(__file__), 'ui_suite'), filename)
def load_tests(loader, base, _):
base.addTest(test_ui.WebsiteUiSuite(full_path('event_test.js'), {'redirect': '/page/website.homepage'}, 60.0))
base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'event_test.js'), {'redirect': '/page/website.homepage'}, 60.0))
return base

View File

@ -203,7 +203,7 @@
</t>
</ol>
</div>
<div class="col-sm-5">
<div class="col-sm-5" groups="event.group_event_manager">
<t t-call="website.publish_management">
<t t-set="object" t-value="event"/>
<t t-set="publish_edit" t-value="True"/>

View File

@ -6,10 +6,8 @@
website.is_editable = true;
website.EditorBar.include({
start: function() {
website.is_editable_button = website.is_editable_button || !!$("#wrap.js_hr_recruitment").size();
var res = this._super();
if ($("#wrap.js_hr_recruitment").size()) {
this.$("button[data-action=edit]").removeClass("hidden");
}
this.$(".dropdown:has(.oe_content_menu)").removeClass("hidden");
return res;
},

View File

@ -19,7 +19,7 @@
#
##############################################################################
from openerp.addons.website_mail.tests import test_controllers
import test_controllers
checks = [
test_controllers,

View File

@ -246,7 +246,7 @@ class Ecommerce(http.Controller):
'range': range,
'search': {
'search': search,
'category': category,
'category': category and category.id,
'filters': filters,
},
'pager': pager,

View File

@ -6,6 +6,7 @@
website.EditorBar.include({
start: function () {
this.registerTour(new website.EditorShopTour(this));
this.registerTour(new website.EditorShopTest(this));
return this._super();
},
});
@ -156,4 +157,151 @@
},
});
website.Test = website.Tour.extend({
registerStep: function (step) {
var self = this;
var step = this._super(step);
if (step.beforeTrigger || step.afterTrigger) {
var fn = step.triggers;
step.triggers = function (callback) {
if (step.beforeTrigger) step.beforeTrigger(self);
if (!step.afterTrigger) {
fn.call(step, callback);
} else {
fn.call(step, function () {
(callback || self.moveToNextStep).apply(self);
step.afterTrigger(self);
});
}
};
}
return step;
}
});
website.EditorShopTest = website.Test.extend({
id: 'shoptest',
name: "Try to by 3 products",
path: '/shop',
init: function (editor) {
var self = this;
self.steps = [
{
stepId: 'begin-test',
title: 'begin-test',
template: self.popover({ next: "Start Test"}),
backdrop: true,
},
{
stepId: 'display-ipod',
element: '.oe_product_cart a:contains("iPod")',
trigger: {
url: /shop\/product\/.*/,
},
},
{
stepId: 'choose-ipod',
element: 'input[name="product_id"]:not([checked])',
trigger: {
id: 'mouseup',
},
},
{
stepId: 'add-ipod',
element: 'form[action="/shop/add_cart/"] button',
trigger: {
url: '/shop/mycart/',
},
},
{
stepId: 'add-suggested-product',
element: 'form[action="/shop/add_cart/"] button:contains("Add to Cart")',
trigger: 'reload',
},
{
stepId: 'more-product',
element: '.oe_mycart a.js_add_cart_json:eq(1)',
trigger: 'click',
},
{
stepId: 'less-product',
element: '.oe_mycart a.js_add_cart_json:eq(2)',
trigger: 'reload',
},
{
stepId: 'number-product',
element: '.oe_mycart input.js_quantity',
trigger: 'reload',
beforeTrigger: function (tour) {
if (parseInt($(".oe_mycart input.js_quantity").val(),10) !== 1)
$(".oe_mycart input.js_quantity").val("1").change();
},
afterTrigger: function (tour) {
if ($(".oe_mycart input.js_quantity").size() !== 1)
throw "Can't remove suggested item from my cart";
if (parseInt($(".oe_mycart input.js_quantity").val(),10) !== 1)
throw "Can't defined number of items in my cart";
},
},
{
stepId: 'go-checkout-product',
element: 'a[href="/shop/checkout/"]',
trigger: {
url: '/shop/checkout/',
},
},
{
stepId: 'confirm-false-checkout-product',
element: 'form[action="/shop/confirm_order/"] button',
trigger: {
url: '/shop/confirm_order/',
},
beforeTrigger: function (tour) {
$("input[name='phone']").val("");
},
},
{
stepId: 'confirm-checkout-product',
element: 'form[action="/shop/confirm_order/"] button',
trigger: {
url: '/shop/payment/',
},
beforeTrigger: function (tour) {
if ($("input[name='name']").val() === "")
$("input[name='name']").val("website_sale-test-shoptest");
if ($("input[name='email']").val() === "")
$("input[name='email']").val("website_sale-test-shoptest@website_sale-test-shoptest.optenerp.com");
$("input[name='phone']").val("123");
$("input[name='street']").val("123");
$("input[name='city']").val("123");
$("input[name='zip']").val("123");
$("select[name='country_id']").val("21");
},
},
{
stepId: 'acquirer-checkout-product',
element: 'input[name="acquirer"]',
trigger: 'mouseup',
},
{
stepId: 'pay-checkout-product',
element: 'button:contains("Pay Now")',
trigger: {
url: /shop\/confirmation\//,
},
afterTrigger: function (tour) {
window.localStorage.setItem("test-success", "{}");
throw "afterTrigger test finish, remove this throw";
},
}
];
return this._super();
},
trigger: function () {
return (this.resume() && this.testUrl(/^\/shop\//)) || this._super();
},
});
}());

View File

@ -7,10 +7,8 @@
website.is_editable = true;
website.EditorBar.include({
start: function() {
website.is_editable_button = website.is_editable_button || !!$("#wrap.js_sale").size();
var res = this._super();
if ($("#wrap.js_sale").size()) {
this.$("button[data-action=edit]").removeClass("hidden");
}
this.$(".dropdown:has(.oe_content_menu)").removeClass("hidden");
return res;
},

View File

@ -7,5 +7,6 @@ $(document).ready(function () {
$("div.oe_sale_acquirer_button[data-id]", $payment).addClass("hidden");
$("div.oe_sale_acquirer_button[data-id='"+payment_id+"']", $payment).removeClass("hidden");
});
$payment.find("input[name='acquirer']:checked").click();
});

View File

@ -0,0 +1 @@
import test_ui

View File

@ -0,0 +1,6 @@
import openerp.addons.website.tests.test_ui as test_ui
def load_tests(loader, base, _):
base.addTest(test_ui.WebsiteUiSuite(test_ui.full_path(__file__,'website_sale-sale_process-test.js'),
{ 'action': 'website.action_website_homepage' }, 60.0))
return base

View File

@ -0,0 +1,28 @@
var testRunner = require('../../../website/tests/ui_suite/ui_test_runner.js');
var waitFor = testRunner.waitFor;
testRunner.run(function websiteSaleTest (page, timeout) {
page.evaluate(function () { localStorage.clear(); });
waitFor(function clientReady () {
return page.evaluate(function () {
return window.$ && window.openerp && window.openerp.website
&& window.openerp.website.TestConsole
&& window.openerp.website.TestConsole.test('shoptest');
});
}, function executeTest () {
page.evaluate(function () {
window.openerp.website.TestConsole.test('shoptest').run(true);
});
waitFor(function testExecuted () {
return page.evaluate(function () {
var res = window.localStorage && window.localStorage.getItem
&& window.localStorage.getItem("test-success");
return res;
});
}, function finish () {
console.log('{ "event": "success" }');
phantom.exit();
}, 4*timeout/5);
}, timeout/5);
});

View File

@ -26,7 +26,7 @@
<!-- List of categories -->
<template id="categories_recursive" name="Category list">
<li t-att-class="str(category.id) == search.get('category') and 'active' or ''">
<li t-att-class="category.id == search.get('category') and 'active' or ''">
<a t-href="/shop/category/#{ slug(category) }/" t-field="category.name"></a>
<ul t-if="category.child_id" class="nav nav-pills nav-stacked nav-hierarchy">
<t t-foreach="category.child_id" t-as="category">
@ -282,7 +282,7 @@
<t t-call="website_sale.search" />
</form>
</div>
<div class="col-sm-4">
<div class="col-sm-4" groups="base.group_sale_manager">
<t t-call="website.publish_management">
<t t-set="object" t-value="product"/>
<t t-set="publish_edit" t-value="True"/>
@ -713,7 +713,7 @@
<div class="row">
<div class="col-md-8 oe_mycart">
<h3 class="page-header mt16">Billing Information
<small t-if="user_id.id == website.public_user.id"> or
<small groups="base.group_public"> or
<a t-if="not partner" t-attf-href="/web#action=redirect&amp;url=#{ request.httprequest.url }">sign in</a>
</small>
</h3>
@ -946,6 +946,7 @@
<img class="media-object" style="width: 60px; display: inline-block;"
t-att-title="acquirer.name"
t-att-src="'/payment_acquirer_%s/static/src/img/%s_icon.png' % (acquirer.name, acquirer.name)"/>
<span t-field="acquirer.name"/>
</label>
</t>
</div>