[MERGE]Merge lp:~openerp-dev/openobject-addons/trunk-website-al.
bzr revid: bth@tinyerp.com-20130927051709-qltvmcxbte3vjezv
This commit is contained in:
@@ -35,7 +35,7 @@ PIL_MIME_MAPPING = {'PNG': 'image/png', 'JPEG': 'image/jpeg', 'GIF': 'image/gif'
|
||||
# Completely arbitrary limits
|
||||
MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768)
|
||||
class Website(openerp.addons.web.controllers.main.Home):
|
||||
@website.route('/', type='http', auth="admin")
|
||||
@website.route('/', type='http', auth="public", multilang=True)
|
||||
def index(self, **kw):
|
||||
return self.page("website.homepage")
|
||||
|
||||
@@ -107,7 +107,7 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
|
||||
return request.website.render('website.themes', {'theme_changed': True})
|
||||
|
||||
@website.route('/page/<path:path>', type='http', auth="admin")
|
||||
@website.route('/page/<path:path>', type='http', auth="public", multilang=True)
|
||||
def page(self, path, **kwargs):
|
||||
values = {
|
||||
'path': path,
|
||||
@@ -160,6 +160,51 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
})
|
||||
return result
|
||||
|
||||
@website.route('/website/get_view_translations', type='json', auth='admin')
|
||||
def get_view_translations(self, xml_id, lang=None):
|
||||
lang = lang or request.context.get('lang')
|
||||
views = self.customize_template_get(xml_id, optional=False)
|
||||
views_ids = [view.get('id') for view in views if view.get('active')]
|
||||
domain = [('type', '=', 'view'), ('res_id', 'in', views_ids), ('lang', '=', lang)]
|
||||
irt = request.registry.get('ir.translation')
|
||||
return irt.search_read(request.cr, request.uid, domain, ['id', 'res_id', 'value'], context=request.context)
|
||||
|
||||
@website.route('/website/set_translations', type='json', auth='admin')
|
||||
def set_translations(self, data, lang):
|
||||
irt = request.registry.get('ir.translation')
|
||||
for view_id, trans in data.items():
|
||||
view_id = int(view_id)
|
||||
for t in trans:
|
||||
initial_content = t['initial_content'].strip()
|
||||
new_content = t['new_content'].strip()
|
||||
tid = t['translation_id']
|
||||
if not tid:
|
||||
old_trans = irt.search_read(
|
||||
request.cr, request.uid,
|
||||
[
|
||||
('type', '=', 'view'),
|
||||
('res_id', '=', view_id),
|
||||
('lang', '=', lang),
|
||||
('src', '=', initial_content),
|
||||
])
|
||||
if old_trans:
|
||||
tid = old_trans[0]['id']
|
||||
if tid:
|
||||
vals = {'value': new_content}
|
||||
irt.write(request.cr, request.uid, [tid], vals)
|
||||
else:
|
||||
new_trans = {
|
||||
'name': 'website',
|
||||
'res_id': view_id,
|
||||
'lang': lang,
|
||||
'type': 'view',
|
||||
'source': initial_content,
|
||||
'value': new_content,
|
||||
}
|
||||
irt.create(request.cr, request.uid, new_trans)
|
||||
irt._get_source.clear_cache(irt) # FIXME: find why ir.translation does not invalidate
|
||||
return True
|
||||
|
||||
# # FIXME: auth, anybody can upload an attachment if URL known/found
|
||||
@website.route('/website/attach', type='http', auth='admin')
|
||||
def attach(self, func, upload):
|
||||
@@ -258,18 +303,17 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
pass
|
||||
return request.make_response(image_data, headers)
|
||||
|
||||
@website.route(['/website/publish/'], type='http', auth="public")
|
||||
def publish(self, **post):
|
||||
_id = int(post['id'])
|
||||
_object = request.registry[post['object']]
|
||||
@website.route(['/website/publish'], type='json', auth="public")
|
||||
def publish(self, id, object):
|
||||
_id = int(id)
|
||||
_object = request.registry[object]
|
||||
|
||||
obj = _object.browse(request.cr, request.uid, _id)
|
||||
_object.write(request.cr, request.uid, [_id],
|
||||
{'website_published': not obj.website_published},
|
||||
context=request.context)
|
||||
obj = _object.browse(request.cr, request.uid, _id)
|
||||
|
||||
return obj.website_published and "1" or "0"
|
||||
return obj.website_published and True or False
|
||||
|
||||
@website.route(['/website/kanban/'], type='http', auth="public")
|
||||
def kanban(self, **post):
|
||||
|
||||
@@ -107,6 +107,31 @@ table.editorbar-panel td.selected {
|
||||
background-color: #b1c9d9;
|
||||
}
|
||||
|
||||
.oe_translate_examples li {
|
||||
margin: 10px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.oe_translatable_text {
|
||||
outline: 1px solid black;
|
||||
}
|
||||
|
||||
.oe_translatable_field {
|
||||
outline: 1px dashed black;
|
||||
}
|
||||
|
||||
.oe_translatable_text.oe_dirty, .oe_translatable_field.oe_dirty {
|
||||
outline-color: red;
|
||||
}
|
||||
|
||||
.oe_translatable_text.oe_dirty:empty {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.oe_translatable_todo {
|
||||
background: #ffffb6;
|
||||
}
|
||||
|
||||
/* ---- RTE ---- */
|
||||
.oe_editable .btn {
|
||||
-webkit-user-select: auto;
|
||||
|
||||
@@ -93,6 +93,22 @@ table.editorbar-panel
|
||||
td.selected
|
||||
background-color: #b1c9d9
|
||||
|
||||
// ---- TRANSLATIONS ---- {{{
|
||||
.oe_translate_examples li
|
||||
margin: 10px
|
||||
padding: 4px
|
||||
.oe_translatable_text
|
||||
outline: 1px solid black
|
||||
.oe_translatable_field
|
||||
outline: 1px dashed black
|
||||
.oe_translatable_text.oe_dirty, .oe_translatable_field.oe_dirty
|
||||
outline-color: red
|
||||
.oe_translatable_text.oe_dirty:empty
|
||||
padding: 0 10px
|
||||
.oe_translatable_todo
|
||||
background: rgb(255, 255, 182)
|
||||
// }}}
|
||||
|
||||
/* ---- RTE ---- */
|
||||
|
||||
// bootstrap makes .btn elements unselectable -> RTE double-click can't know
|
||||
@@ -485,3 +501,5 @@ $navbar_height: 51px
|
||||
&.oe_ace_closed
|
||||
width: 0
|
||||
opacity: 0
|
||||
|
||||
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
|
||||
|
||||
@@ -337,6 +337,14 @@ footer {
|
||||
padding: 0 0 20px 0;
|
||||
}
|
||||
|
||||
/* Parallax Theme */
|
||||
.parallax_full {
|
||||
background: #eeeeee url(/website/static/src/img/parallax/parallax_bg.jpg) bottom center repeat fixed;
|
||||
min-height: 650px;
|
||||
width: 100%;
|
||||
z-index: -999;
|
||||
}
|
||||
|
||||
/* -- Hack for removing double scrollbar from mobile preview -- */
|
||||
div#mobile-preview.modal {
|
||||
overflow: hidden;
|
||||
@@ -352,27 +360,12 @@ ul.nav-stacked > li > a {
|
||||
}
|
||||
|
||||
/* ---- PUBLISH ---- */
|
||||
a[data-publish] {
|
||||
text-decoration: none !important;
|
||||
z-index: 2;
|
||||
}
|
||||
a[data-publish] .label {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
a[data-publish] .css_unpublish, a[data-publish] .css_publish, a[data-publish] .css_unpublished, a[data-publish] .css_published {
|
||||
.dropdown .css_unpublish, .dropdown .css_publish {
|
||||
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_publish .css_unpublish, .dropdown.css_unpublish .css_publish {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.unpublish {
|
||||
|
||||
@@ -255,6 +255,14 @@ footer
|
||||
#themes-list .well
|
||||
padding: 0 0 20px 0
|
||||
|
||||
/* Parallax Theme */
|
||||
|
||||
.parallax_full
|
||||
background: #eee url(/website/static/src/img/parallax/parallax_bg.jpg) bottom center repeat fixed
|
||||
min-height: 650px
|
||||
width: 100%
|
||||
z-index: -999
|
||||
|
||||
/* -- Hack for removing double scrollbar from mobile preview -- */
|
||||
div#mobile-preview.modal
|
||||
overflow: hidden
|
||||
@@ -267,23 +275,11 @@ ul.nav-stacked > li > a
|
||||
text-transform: uppercase
|
||||
|
||||
/* ---- PUBLISH ---- */
|
||||
a[data-publish]
|
||||
text-decoration: none !important
|
||||
z-index: 2
|
||||
.label
|
||||
padding: 5px 8px
|
||||
.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
|
||||
.dropdown.css_publish .css_unpublish, .dropdown.css_unpublish .css_publish
|
||||
display: block
|
||||
|
||||
.unpublish
|
||||
opacity: 0.5
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -282,7 +282,7 @@
|
||||
});
|
||||
}).get();
|
||||
return $.when.apply(null, defs).then(function () {
|
||||
window.location.href = window.location.href.replace(/unable_editor(=[^&]*)?|#.*/g, '');
|
||||
website.reload();
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,9 @@
|
||||
});
|
||||
return $.when.apply(null, dones);
|
||||
};
|
||||
|
||||
website.reload = function () {
|
||||
window.location.href = window.location.href.replace(/unable_editor(=[^&]*)?|#.*/g, '');
|
||||
};
|
||||
|
||||
var all_ready = null;
|
||||
var dom_ready = website.dom_ready = $.Deferred();
|
||||
@@ -78,7 +80,7 @@
|
||||
$pagination.last().before(col);
|
||||
});
|
||||
|
||||
var page_start = page - parseInt(Math.floor((scope-1)/2));
|
||||
var page_start = page - parseInt(Math.floor((scope-1)/2), 10);
|
||||
if (page_start < 1 ) page_start = 1;
|
||||
var page_end = page_start + (scope-1);
|
||||
if (page_end > page_count ) page_end = page_count;
|
||||
@@ -106,29 +108,34 @@
|
||||
*/
|
||||
website.ready = function() {
|
||||
if (!all_ready) {
|
||||
all_ready = dom_ready.then(function () {
|
||||
// TODO: load translations
|
||||
return website.load_templates(templates);
|
||||
});
|
||||
var tpl = website.load_templates(templates);
|
||||
// var session;
|
||||
// var trads = openerp._t.database.load_translations(session, ['website'], website.get_context().lang);
|
||||
all_ready = $.when(dom_ready, tpl);
|
||||
}
|
||||
return all_ready;
|
||||
};
|
||||
|
||||
dom_ready.then(function () {
|
||||
/* ----- PUBLISHING STUFF ---- */
|
||||
$('[data-publish]:has(.js_publish)').each(function () {
|
||||
$(this).attr("data-publish", $(".js_publish li.active", this).size() ? "on" : 'off');
|
||||
$('[data-publish]:has(.js_publish_management)').each(function () {
|
||||
$(this).attr("data-publish", $(".js_publish_management .btn-success", this).size() ? "on" : 'off');
|
||||
});
|
||||
|
||||
$(document).on('click', '.js_publish a.js_publish_btn', function (e) {
|
||||
var $li = $(this).parent("li");
|
||||
var $data = $li.parents(".js_publish:first");
|
||||
var publish = $li.hasClass("active");
|
||||
$li.toggleClass("active");
|
||||
$.post('/website/publish', {'id': $data.data('id'), 'object': $data.data('object')}, function (result) {
|
||||
$li.toggleClass("active", !!+result);
|
||||
$li.parents("[data-publish]").attr("data-publish", +result ? 'on' : 'off');
|
||||
});
|
||||
$(document).on('click', '.js_publish_management .js_publish_btn', function (e) {
|
||||
var $data = $(this).parents(".js_publish_management:first");
|
||||
var $btn = $data.find('.btn:first');
|
||||
var publish = $btn.hasClass("btn-success");
|
||||
|
||||
$data.toggleClass("css_unpublish css_publish");
|
||||
$btn.removeClass("btn-default btn-success");
|
||||
|
||||
openerp.jsonRpc('/website/publish', 'call', {'id': +$data.data('id'), 'object': $data.data('object')})
|
||||
.then(function (result) {
|
||||
$btn.toggleClass("btn-default", !result).toggleClass("btn-success", result);
|
||||
$data.toggleClass("css_unpublish", !result).toggleClass("css_publish", result);
|
||||
$data.parents("[data-publish]").attr("data-publish", +result ? 'on' : 'off');
|
||||
});
|
||||
});
|
||||
|
||||
/* ----- KANBAN WEBSITE ---- */
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
website.templates.push('/website/static/src/xml/website.translator.xml');
|
||||
var nodialog = 'website_translator_nodialog';
|
||||
|
||||
website.EditorBar.include({
|
||||
start: function () {
|
||||
var self = this;
|
||||
this.initial_content = {};
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
self.$('button[data-action=edit]').text("Translate");
|
||||
self.$('[data-action=snippet]').hide();
|
||||
self.$('#customize-menu-button').hide();
|
||||
});
|
||||
},
|
||||
edit: function () {
|
||||
var self = this;
|
||||
var mysuper = this._super;
|
||||
if (!localStorage[nodialog]) {
|
||||
var dialog = new website.TranslatorDialog();
|
||||
dialog.appendTo($(document.body));
|
||||
dialog.on('activate', this, function () {
|
||||
localStorage[nodialog] = dialog.$('input[name=do_not_show]').prop('checked') || '';
|
||||
dialog.$el.modal('hide');
|
||||
this.translate().then(function () {
|
||||
mysuper.call(self);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.translate().then(function () {
|
||||
mysuper.call(self);
|
||||
});
|
||||
}
|
||||
},
|
||||
translate: function () {
|
||||
var self = this;
|
||||
this.translations = null;
|
||||
return openerp.jsonRpc('/website/get_view_translations', 'call', {
|
||||
'xml_id': $(document.documentElement).data('view-xmlid'),
|
||||
'lang': website.get_context().lang,
|
||||
}).then(function (translations) {
|
||||
self.translations = translations;
|
||||
self.processTranslatableNodes();
|
||||
// Disable non translatable t-fields
|
||||
$('[data-oe-type][data-oe-translate=0]').removeAttr('data-oe-type');
|
||||
});
|
||||
},
|
||||
processTranslatableNodes: function () {
|
||||
var self = this;
|
||||
var $editables = $('[data-oe-model="ir.ui.view"]')
|
||||
.not('link, script')
|
||||
.not('.oe_snippets,.oe_snippet, .oe_snippet *')
|
||||
.not('[data-oe-type]');
|
||||
|
||||
$editables.each(function () {
|
||||
var $node = $(this);
|
||||
var view_id = $node.attr('data-oe-source-id') || $node.attr('data-oe-id');
|
||||
self.transNode(this, view_id|0);
|
||||
});
|
||||
$('.oe_translatable_text').on('paste', function () {
|
||||
var node = $(this);
|
||||
setTimeout(function () {
|
||||
self.sanitizeNode(node);
|
||||
}, 0);
|
||||
});
|
||||
$(document).on('blur keyup paste', '.oe_translatable_text[contenteditable]', function(ev) {
|
||||
var $node = $(this);
|
||||
setTimeout(function () {
|
||||
// Doing stuff next tick because paste and keyup events are
|
||||
// fired before the content is changed
|
||||
if (ev.type == 'paste') {
|
||||
self.sanitizeNode($node[0]);
|
||||
}
|
||||
if (self.getInitialContent($node[0]) !== $node.text()) {
|
||||
$node.addClass('oe_dirty').removeClass('oe_translatable_todo');
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
},
|
||||
getInitialContent: function (node) {
|
||||
return this.initial_content[node.attributes['data-oe-nodeid'].value];
|
||||
},
|
||||
sanitizeNode: function (node) {
|
||||
node.text(node.text());
|
||||
},
|
||||
isTextNode: function (node) {
|
||||
return node.nodeType === 3 || node.nodeType === 4;
|
||||
},
|
||||
isTranslatable: function (text) {
|
||||
return text && _.str.trim(text) !== '';
|
||||
},
|
||||
markTranslatableNode: function (node, view_id) {
|
||||
// TODO: link nodes with same content
|
||||
node.className += ' oe_translatable_text';
|
||||
node.setAttribute('data-oe-translation-view-id', view_id);
|
||||
var content = node.childNodes[0].data.trim();
|
||||
var trans = this.translations.filter(function (t) {
|
||||
return t.res_id === view_id && t.value === content;
|
||||
});
|
||||
if (trans.length) {
|
||||
node.setAttribute('data-oe-translation-id', trans[0].id);
|
||||
} else {
|
||||
node.className += ' oe_translatable_todo';
|
||||
}
|
||||
node.contentEditable = true;
|
||||
var nid = _.uniqueId();
|
||||
$(node).attr('data-oe-nodeid', nid);
|
||||
this.initial_content[nid] = content;
|
||||
},
|
||||
save: function () {
|
||||
var self = this;
|
||||
var mysuper = this._super;
|
||||
var trans = {};
|
||||
// this._super.apply(this, arguments);
|
||||
$('.oe_translatable_text.oe_dirty').each(function () {
|
||||
var $node = $(this);
|
||||
var data = $node.data();
|
||||
if (!trans[data.oeTranslationViewId]) {
|
||||
trans[data.oeTranslationViewId] = [];
|
||||
}
|
||||
trans[data.oeTranslationViewId].push({
|
||||
initial_content: self.getInitialContent(this),
|
||||
new_content: $node.text(),
|
||||
translation_id: data.oeTranslationId || null
|
||||
});
|
||||
});
|
||||
openerp.jsonRpc('/website/set_translations', 'call', {
|
||||
'data': trans,
|
||||
'lang': website.get_context()['lang'],
|
||||
}).then(function () {
|
||||
mysuper.call(self);
|
||||
}).fail(function () {
|
||||
// TODO: bootstrap alert with error message
|
||||
alert("Could not save translation");
|
||||
});
|
||||
},
|
||||
transNode: function (node, view_id) {
|
||||
// Mostly handling text and cdata nodes here
|
||||
// so avoid jquery usage in this function
|
||||
if (node.attributes['data-oe-type']) {
|
||||
if (node.attributes['data-oe-translate'].value == '1') {
|
||||
node.className += ' oe_translatable_field';
|
||||
}
|
||||
return;
|
||||
} else if (node.childNodes.length === 1
|
||||
&& this.isTextNode(node.childNodes[0])
|
||||
&& !node.getAttribute('data-oe-model')) {
|
||||
this.markTranslatableNode(node, view_id);
|
||||
} else {
|
||||
for (var i = 0, l = node.childNodes.length; i < l; i ++) {
|
||||
var n = node.childNodes[i];
|
||||
if (this.isTextNode(n)) {
|
||||
if (this.isTranslatable(n.data)) {
|
||||
var container = document.createElement('span');
|
||||
node.insertBefore(container, n);
|
||||
container.appendChild(n);
|
||||
this.markTranslatableNode(container, view_id);
|
||||
}
|
||||
} else {
|
||||
this.transNode(n, view_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
website.RTE.include({
|
||||
start: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.$el.hide();
|
||||
},
|
||||
fetch_editables: function (root) {
|
||||
$(root).click(function (ev) {
|
||||
ev.preventDefault();
|
||||
});
|
||||
return $('[data-oe-translate=1]');
|
||||
}
|
||||
});
|
||||
|
||||
website.TranslatorDialog = openerp.Widget.extend({
|
||||
events: _.extend({}, website.EditorBar.prototype.events, {
|
||||
'hidden.bs.modal': 'destroy',
|
||||
'click button[data-action=activate]': function (ev) {
|
||||
this.trigger('activate');
|
||||
},
|
||||
}),
|
||||
template: 'website.TranslatorDialog',
|
||||
start: function () {
|
||||
this.$el.modal();
|
||||
},
|
||||
});
|
||||
})();
|
||||
@@ -60,11 +60,13 @@
|
||||
<li><a href="#snippet_structure">Structure</a></li>
|
||||
<li><a href="#snippet_content">Content</a></li>
|
||||
<li><a href="#snippet_business">Business</a></li>
|
||||
<li><a href="#snippet_effect">Effects</a></li>
|
||||
</ul>
|
||||
<div class="pill-content">
|
||||
<div id="snippet_structure"><label><div>Structure</div></label></div>
|
||||
<div id="snippet_content"><label><div>Content</div></label></div>
|
||||
<div id="snippet_business"><label><div>Business</div></label></div>
|
||||
<div id="snippet_effect"><label><div>Effects</div></label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -369,12 +371,16 @@
|
||||
|
||||
<div t-name="website.snippets.title" data-snippet-id='title' data-category='structure' data-selector-children='.oe_structure, [data-oe-type=html]'>
|
||||
<div class="oe_snippet_thumbnail">Title</div>
|
||||
<div class="oe_snippet_body row">
|
||||
<div class="col-md-12">
|
||||
<h2>Your Website Title</h2>
|
||||
<h3 class="text-muted">Great products for great people</h3>
|
||||
<section class="oe_snippet_body">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="text-center">Your Website Title</h2>
|
||||
<h3 class="text-muted text-center">Great products for great people</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div t-name="website.snippets.hr" data-snippet-id='hr' data-category='structure' data-selector-children='.oe_structure, [data-oe-type=html]'>
|
||||
@@ -789,4 +795,22 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div t-name="website.snippets.parallax" data-snippet-id='parallax' data-category='effect' data-selector-children='.oe_structure, [data-oe-type=html]'>
|
||||
<div class='oe_snippet_thumbnail'>Parallax</div>
|
||||
<section class="oe_snippet_body parallax_full oe_structure">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 mt128 mb32">
|
||||
<h1 class="text-center">
|
||||
<b>Our References</b>
|
||||
</h1>
|
||||
<h3 class="text-center">
|
||||
More than 500 happy customers.
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="website.TranslatorDialog">
|
||||
<div class="modal fade oe_website_translator" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button title="Close" type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h2 class="modal-title">Translate this page</h2>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<section>
|
||||
<p>
|
||||
You are about to enter the translation mode.
|
||||
</p>
|
||||
<p>
|
||||
In this mode you can not change the structure of the document, you can only translate text.
|
||||
</p>
|
||||
<p>
|
||||
Here are the visuals used in order to help you in your translation flow:
|
||||
<ul class="oe_translate_examples">
|
||||
<li class="oe_translatable_text">
|
||||
Normal translatable content
|
||||
</li>
|
||||
<li class="oe_translatable_field">
|
||||
ERP translatable object data
|
||||
</li>
|
||||
<li class="oe_translatable_todo">
|
||||
Translation TODO
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</section>
|
||||
<hr/>
|
||||
<section class="row">
|
||||
<div class="col-md-6">
|
||||
<button type="button" data-action="activate" class="btn btn-primary">Ok</button>
|
||||
or
|
||||
<a data-action="discard" data-dismiss="modal" href="#">Cancel</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label>
|
||||
<input type="checkbox" name="do_not_show"/>
|
||||
Do not show this dialog later.
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -22,7 +22,6 @@
|
||||
<li><a data-action="show-mobile-preview" href="#"><span title="Mobile preview" class="icon-mobile-phone"/></a></li>
|
||||
<li class="divider-vertical"></li>
|
||||
<li><a data-action="promote-current-page" href="#"><span title="Promote page on the web">Promote</span></a></li>
|
||||
<li><a href="#">Translate</a></li>
|
||||
<li class="dropdown">
|
||||
<a id="customize-menu-button" class="dropdown-toggle" data-toggle="dropdown" href="#">Customize <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu" id="customize-menu">
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html t-att-lang="lang.replace('_', '-')"
|
||||
t-att-data-editable="'1' if editable else '0'"
|
||||
t-att-data-translatable="'1' if translatable else '0'"
|
||||
t-att-data-view-xmlid="str(__stack__[0])">
|
||||
<head>
|
||||
<title><t t-esc="title or res_company.name"/></title>
|
||||
@@ -30,6 +31,7 @@
|
||||
</t>
|
||||
<t t-call="website.theme"/>
|
||||
|
||||
<script type="text/javascript" src="/web/static/lib/es5-shim/es5-shim.min.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/underscore/underscore.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/underscore.string/lib/underscore.string.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery/jquery.js"></script>
|
||||
@@ -43,7 +45,7 @@
|
||||
<t t-if="editable">
|
||||
<script type="text/javascript" src="/website/static/lib/ckeditor/ckeditor.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/ckeditor.sharedspace/plugin.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/ace/ace.js"></script>
|
||||
<script t-if="not translatable" type="text/javascript" src="/website/static/lib/ace/ace.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/vkbeautify/vkbeautify.0.99.00.beta.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
|
||||
<!-- mutation observers shim backed by mutation events (8 < IE < 11, Safari < 6, FF < 14, Chrome < 17) -->
|
||||
@@ -53,8 +55,9 @@
|
||||
<script type="text/javascript" src="/website/static/src/js/website.editor.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.mobile.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.seo.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.snippets.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.ace.js"></script>
|
||||
<script t-if="not translatable" type="text/javascript" src="/website/static/src/js/website.snippets.js"></script>
|
||||
<script t-if="not translatable" type="text/javascript" src="/website/static/src/js/website.ace.js"></script>
|
||||
<script t-if="translatable" type="text/javascript" src="/website/static/src/js/website.translator.js"></script>
|
||||
</t>
|
||||
|
||||
<t t-raw="head or ''"/>
|
||||
@@ -71,14 +74,14 @@
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/page/website.homepage"><em>Your</em><b>Company</b></a>
|
||||
<a class="navbar-brand" t-href="/page/website.homepage"><em>Your</em><b>Company</b></a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-top-collapse">
|
||||
<ul class="nav navbar-nav navbar-right" id="top_menu">
|
||||
<li name="contactus"><a href="/page/website.contactus">Contact us</a></li>
|
||||
<li name="contactus"><a t-href="/page/website.contactus">Contact us</a></li>
|
||||
<li t-if="user_id.id == website.public_user.id"><a href="/admin">Sign in</a></li>
|
||||
<li t-if="user_id.id != website.public_user.id"><a href="/admin"><t t-field="user_id.name"/></a></li>
|
||||
<li t-if="len(website.language_ids) > 1" class="dropdown">
|
||||
<li t-if="user_id.id != website.public_user.id"><a href="/admin"><span t-field="user_id.name"/></a></li>
|
||||
<li t-if="request.multilang and len(website.language_ids) > 1" class="dropdown">
|
||||
<!-- TODO: use flags for language selection -->
|
||||
<t t-set="lang_selected" t-value="[lg for lg in website.language_ids if lg.code == lang]"/>
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
@@ -86,7 +89,7 @@
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li t-foreach="website.language_ids" t-as="lg">
|
||||
<a href="#" role="menuitem">
|
||||
<a t-att-href="url_for('', lang=lg.code)" role="menuitem">
|
||||
<strong t-att-class="'icon-check' if lg.code == lang
|
||||
else 'icon-check-empty'"></strong>
|
||||
<t t-esc="lg.name"/>
|
||||
@@ -106,13 +109,13 @@
|
||||
<div class="col-md-3" name="product">
|
||||
<h4>Our products & Services</h4>
|
||||
<ul class="list-unstyled" name="products">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a t-href="/">Home</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-3" name="info">
|
||||
<h4 name="info_title">Connect with us</h4>
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="/page/website.contactus">Contact us</a></li>
|
||||
<li><a t-href="/page/website.contactus">Contact us</a></li>
|
||||
</ul>
|
||||
<ul class="list-unstyled">
|
||||
<li><i class="icon-phone"></i> <span t-field="res_company.phone"></span></li>
|
||||
@@ -127,7 +130,7 @@
|
||||
<div class="col-md-5 col-lg-offset-1" name="about_us">
|
||||
<h4>
|
||||
<span t-field="res_company.name">Your Company</span>
|
||||
<small> - <a href="/page/website.aboutus">About us</a></small>
|
||||
<small> - <a t-href="/page/website.aboutus">About us</a></small>
|
||||
</h4>
|
||||
<p>
|
||||
We are a team of passionated people whose goal is to improve everyone's
|
||||
@@ -147,7 +150,7 @@
|
||||
<a class="label label-danger" href="https://openerp.com/apps/website">OpenERP</a>
|
||||
</div>
|
||||
<div class="pull-left text-muted">
|
||||
Copyright &copy; <span t-field="res_company.name">Company name</span> - <a href="/sitemap">Sitemap</a>
|
||||
Copyright &copy; <span t-field="res_company.name">Company name</span> - <a t-href="/sitemap">Sitemap</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -164,7 +167,7 @@
|
||||
<div class="col-md-4">
|
||||
<h4 class="mt16">Subtitle</h4>
|
||||
<p>
|
||||
<a href="/">Homepage</a>
|
||||
<a t-href="/">Homepage</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
@@ -208,7 +211,7 @@
|
||||
<div class="well mt32">
|
||||
<p>This page does not exists, but you can create it as you are administrator of this site.</p>
|
||||
<a class="btn btn-primary" t-att-href="'/pagenew/'+path">Create Page</a>
|
||||
<span class="text-muted">or</span> <a href="/sitemap">Search a Page</a>
|
||||
<span class="text-muted">or</span> <a t-href="/sitemap">Search a Page</a>
|
||||
</div>
|
||||
<div class="text-center text-muted">Edit the content bellow this line to adapt the default "page not found" page.</div>
|
||||
</div>
|
||||
@@ -223,8 +226,8 @@
|
||||
</p>
|
||||
<p>Maybe you were looking for one of these popular pages ?</p>
|
||||
<ul>
|
||||
<li><a href="/">Homepage</a></li>
|
||||
<li><a href="/">Contact Us</a></li>
|
||||
<li><a t-href="/">Homepage</a></li>
|
||||
<li><a t-href="/page/website.contactus/">Contact Us</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -257,8 +260,8 @@
|
||||
</p>
|
||||
<pre t-if="editable" t-esc="error"/>
|
||||
<ul>
|
||||
<li><a href="/">Homepage</a></li>
|
||||
<li><a href="/">Contact Us</a></li>
|
||||
<li><a t-href="/">Homepage</a></li>
|
||||
<li><a t-href="/page/website.contactus/">Contact Us</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -281,7 +284,7 @@
|
||||
<p>We'll do our best to get back to you as soon as possible.</p>
|
||||
</div>
|
||||
<div class="text-center mt64" name="mail_button">
|
||||
<a t-att-href="'mailto:'+res_company.email" class="btn btn-primary">Send us an email</a>
|
||||
<a t-attf-href="mailto:{{ res_company.email }}" class="btn btn-primary">Send us an email</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
@@ -338,13 +341,17 @@
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template id="publish">
|
||||
<template id="publish_management">
|
||||
<t t-if="editable" t-ignore="true">
|
||||
<div class="dropdown js_publish pull-right" t-att-data-id="object.id" t-att-data-object="object._name">
|
||||
<a class="btn btn-default" id="dopprod" role="button" data-toggle="dropdown"> Manage <span class="caret"></span></a>
|
||||
<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-#{object.id and object.website_published and 'success' or 'default'}" id="dopprod" role="button" data-toggle="dropdown"> Publish options <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dopprod">
|
||||
<li t-att-class="object.id and object.website_published and 'active' or ''"><a href="#" class="js_publish_btn">Publish</a></li>
|
||||
<li><a t-att-href="'/admin/#model=%s&id=%s' % (object._name, object.id)">Manage Products</a></li>
|
||||
<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><a t-att-href="'/admin/#model=%s&id=%s' % (object._name, object.id)">Manage</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -7,22 +7,35 @@ from openerp.osv import osv, fields
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
import urllib
|
||||
from urlparse import urljoin
|
||||
import math
|
||||
import traceback
|
||||
from openerp.tools.safe_eval import safe_eval
|
||||
from openerp.exceptions import AccessError, AccessDenied
|
||||
import werkzeug
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def route(*route_args, **route_kwargs):
|
||||
def route(routes, *route_args, **route_kwargs):
|
||||
def decorator(f):
|
||||
@http.route(*route_args, **route_kwargs)
|
||||
new_routes = routes if isinstance(routes, list) else [routes]
|
||||
f.multilang = route_kwargs.get('multilang', False)
|
||||
if f.multilang:
|
||||
route_kwargs.pop('multilang')
|
||||
for r in list(new_routes):
|
||||
new_routes.append('/<string(length=5):lang_code>' + r)
|
||||
@http.route(new_routes, *route_args, **route_kwargs)
|
||||
@functools.wraps(f, assigned=functools.WRAPPER_ASSIGNMENTS + ('func_name',))
|
||||
def wrap(*args, **kwargs):
|
||||
request.route_lang = None # WIP: decorator will support lang argument
|
||||
request.route_lang = kwargs.get('lang_code', None)
|
||||
if not hasattr(request, 'website'):
|
||||
request.multilang = f.multilang
|
||||
request.website = request.registry['website'].get_current()
|
||||
if request.route_lang:
|
||||
lang_ok = [lg.code for lg in request.website.language_ids if lg.code == request.route_lang]
|
||||
if not lang_ok:
|
||||
return request.not_found()
|
||||
request.website.preprocess_request(*args, **kwargs)
|
||||
return f(*args, **kwargs)
|
||||
return wrap
|
||||
@@ -36,6 +49,19 @@ def auth_method_public():
|
||||
request.uid = request.session.uid
|
||||
http.auth_methods['public'] = auth_method_public
|
||||
|
||||
def url_for(path, lang=None):
|
||||
if request:
|
||||
path = urljoin(request.httprequest.path, path)
|
||||
langs = request.context.get('langs')
|
||||
if path[0] == '/' and len(langs) > 1:
|
||||
ps = path.split('/')
|
||||
lang = lang or request.context.get('lang')
|
||||
if ps[1] in langs:
|
||||
ps[1] = lang
|
||||
else:
|
||||
ps.insert(1, lang)
|
||||
path = '/'.join(ps)
|
||||
return path
|
||||
|
||||
def urlplus(url, params):
|
||||
if not params:
|
||||
@@ -77,12 +103,22 @@ class website(osv.osv):
|
||||
return lang
|
||||
|
||||
def preprocess_request(self, cr, uid, ids, *args, **kwargs):
|
||||
def redirect(url):
|
||||
return werkzeug.utils.redirect(url_for(url))
|
||||
request.redirect = redirect
|
||||
|
||||
is_public_user = request.uid == self.get_public_user().id
|
||||
lang = self.get_lang()
|
||||
is_master_lang = lang == request.website.default_lang_id.code
|
||||
request.context.update({
|
||||
'lang': lang,
|
||||
'langs': [lg.code for lg in request.website.language_ids],
|
||||
'multilang': request.multilang,
|
||||
'is_public_user': is_public_user,
|
||||
'editable': not is_public_user, # TODO: check perms
|
||||
'is_master_lang': is_master_lang,
|
||||
'editable': not is_public_user,
|
||||
'translatable': not is_public_user and not is_master_lang and request.multilang,
|
||||
})
|
||||
request.context['lang'] = self.get_lang()
|
||||
|
||||
def get_current(self):
|
||||
# WIP, currently hard coded
|
||||
@@ -103,13 +139,15 @@ class website(osv.osv):
|
||||
registry=request.registry,
|
||||
json=simplejson,
|
||||
website=request.website,
|
||||
url_for=url_for,
|
||||
res_company=request.website.company_id,
|
||||
user_id=user.browse(cr, openerp.SUPERUSER_ID, uid),
|
||||
)
|
||||
|
||||
context = {
|
||||
'inherit_branding': qweb_context.setdefault('editable', False),
|
||||
}
|
||||
context = request.context.copy()
|
||||
context.update(
|
||||
inherit_branding=qweb_context.setdefault('editable', False),
|
||||
)
|
||||
|
||||
# check if xmlid of the template exists
|
||||
try:
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
<group col="4">
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="default_lang_id"/>
|
||||
<field name="default_lang_id" widget="selection"/>
|
||||
</group>
|
||||
<group string="Website languages">
|
||||
<field name="language_ids" nolabel="1" mode="tree,form">
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
<a t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comment" t-field="blog_post.name"></a>
|
||||
</h2>
|
||||
<p class="post-meta text-muted text-center">
|
||||
<span class="icon-calendar"> <t t-field="blog_post.create_date"/></span> &nbsp;
|
||||
<span class="icon-user"> By <t t-field="blog_post.create_uid"/></span> &nbsp;
|
||||
<span class="icon-calendar"> <span t-field="blog_post.create_date"/></span> &nbsp;
|
||||
<span class="icon-user"> By <span t-field="blog_post.create_uid"/></span> &nbsp;
|
||||
<span t-if="len(blog_post.message_ids) > 0" class="icon-comment"> With
|
||||
<a t-if="len(blog_post.message_ids) <= 1" t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comments"><t t-esc="len(blog_post.message_ids)"/> comment</a>
|
||||
<a t-if="len(blog_post.message_ids) > 1" t-attf-href="/blog/#{blog_post.category_id.id}/#{blog_post.id}#comments"><t t-esc="len(blog_post.message_ids)"/> comments</a>
|
||||
@@ -36,13 +36,13 @@
|
||||
<template id="view_blog_post" name="Blog Post">
|
||||
<div>
|
||||
<t t-call="website_mail.follow"><t t-set="object" t-value="blog_post"/></t>
|
||||
<t t-call="website.publish"><t t-set="object" t-value="blog_post"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="blog_post"/></t>
|
||||
</div><div class="clearfix"/>
|
||||
|
||||
<h2 class="text-center" t-field="blog_post.name"/>
|
||||
<p class="post-meta text-muted text-center">
|
||||
<span class="icon-calendar"> <t t-field="blog_post.create_date"/></span> &nbsp;
|
||||
<span class="icon-user"> By <t t-field="blog_post.create_uid"/></span> &nbsp;
|
||||
<span class="icon-calendar"> <span t-field="blog_post.create_date"/></span> &nbsp;
|
||||
<span class="icon-user"> By <span t-field="blog_post.create_uid"/></span> &nbsp;
|
||||
<span t-if="len(blog_post.message_ids) > 0" class="icon-comment"> With
|
||||
<a t-if="len(blog_post.message_ids) <= 1" t-attf-href="#comments"><t t-esc="len(blog_post.message_ids)"/> comment</a>
|
||||
<a t-if="len(blog_post.message_ids) > 1" t-attf-href="#comments"><t t-esc="len(blog_post.message_ids)"/> comments</a>
|
||||
@@ -80,10 +80,10 @@
|
||||
<ul class="media-list" id="comments">
|
||||
<li t-foreach="blog_post.website_message_ids" t-as="message" class="media">
|
||||
<div class="media-body well well-sm">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="message"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="message"/></t>
|
||||
<t t-raw="message.body"/>
|
||||
<small class="pull-left text-muted text-left">
|
||||
<t t-field="message.author_id"/> on <t t-field="message.date"/>
|
||||
<span t-field="message.author_id"/> on <span t-field="message.date"/>
|
||||
</small>
|
||||
</div>
|
||||
</li>
|
||||
@@ -180,7 +180,7 @@
|
||||
<t t-foreach="categories" t-as="nav_category">
|
||||
<li t-att-class="'active' if category and category.id == nav_category.id else ''">
|
||||
<a t-attf-href="/blog/#{nav_category.id}">
|
||||
<t t-field="nav_category.name"/>
|
||||
<span t-field="nav_category.name"/>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class='navbar'>
|
||||
<div>
|
||||
<t t-call="website.pager">
|
||||
<t t-set="classname">pull-left</t>
|
||||
<t t-set="classname" t-value="'pull-left'"/>
|
||||
</t>
|
||||
<form action="/references/" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<div class="form-group">
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
<div>
|
||||
<div t-foreach="partner_ids" t-as="partner" class="media thumbnail" data-publish="">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="partner"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="partner"/></t>
|
||||
<a class="pull-left" t-attf-href="/references/#{ partner.id }/">
|
||||
<img class="media-object" t-att-src="partner.img('image_small')"/>
|
||||
</a>
|
||||
@@ -95,7 +95,7 @@
|
||||
<template id="details" name="Reference Detail">
|
||||
<t t-call="website_contract.layout">
|
||||
<t t-set="ref_content">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="partner_id"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="partner_id"/></t>
|
||||
<h1 class="col-md-12 text-center" t-field="partner_id.name"/>
|
||||
<div class="col-md-4">
|
||||
<div class="text-center">
|
||||
|
||||
@@ -8,7 +8,7 @@ from urllib import quote_plus
|
||||
|
||||
class contactus(http.Controller):
|
||||
|
||||
@website.route(['/crm/contactus'], type='http', auth="admin")
|
||||
@website.route(['/crm/contactus'], type='http', auth="admin", multilang=True)
|
||||
def contactus(self, *arg, **post):
|
||||
post['user_id'] = False
|
||||
request.registry['crm.lead'].create(request.cr, request.uid,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<template id="contactus_form" name="Contact Form" inherit_id="website.contactus" inherit_option_id="website.contactus">
|
||||
<xpath expr="//div[@name='mail_button']" position="replace">
|
||||
<form action="/crm/contactus" method="post" class="form-horizontal mt32" >
|
||||
<form t-action="/crm/contactus" method="post" class="form-horizontal mt32" >
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 col-sm-4 control-label" for="contact_name">Your Name</label>
|
||||
<div class="col-md-7 col-sm-8">
|
||||
|
||||
@@ -9,7 +9,7 @@ import urllib
|
||||
|
||||
class website_crm_partner_assign(http.Controller):
|
||||
|
||||
@website.route(['/partners/', '/partners/page/<int:page>/'], type='http', auth="public")
|
||||
@website.route(['/partners/', '/partners/page/<int:page>/'], type='http', auth="public", multilang=True)
|
||||
def partners(self, page=0, **post):
|
||||
partner_obj = request.registry['res.partner']
|
||||
|
||||
@@ -84,7 +84,7 @@ class website_crm_partner_assign(http.Controller):
|
||||
}
|
||||
return request.website.render("website_crm_partner_assign.index", values)
|
||||
|
||||
@website.route(['/partners/<int:ref_id>/'], type='http', auth="public")
|
||||
@website.route(['/partners/<int:ref_id>/'], type='http', auth="public", multilang=True)
|
||||
def partners_ref(self, ref_id=0, **post):
|
||||
partner_obj = request.registry['res.partner']
|
||||
partner_ids = partner_obj.search(
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
|
||||
<xpath expr="//footer//div[@name='info']/ul" position="inside">
|
||||
<li><a href="/partners/">Partners</a></li>
|
||||
<li><a t-href="/partners/">Partners</a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<t t-call="website.pager">
|
||||
<t t-set="classname">pull-left</t>
|
||||
</t>
|
||||
<form action="/partners/" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<form t-action="/partners/" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<div class="form-group">
|
||||
<input type="text" name="search" class="search-query col-md-2 mt4 form-control" placeholder="Search" t-att-value="searches.get('search') or '' or ''"/>
|
||||
</div>
|
||||
@@ -76,12 +76,12 @@
|
||||
<h3 class="text-center well"><span t-field="partner.grade_id"/> Partners</h3>
|
||||
</t>
|
||||
<div class="media thumbnail" data-publish="">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="partner"/></t>
|
||||
<a class="pull-left" t-attf-href="/partners/#{ partner.id }/">
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="partner"/></t>
|
||||
<a class="pull-left" t-href="/partners/#{ partner.id }/">
|
||||
<img class="media-object" t-att-src="partner.img('image_small')"/>
|
||||
</a>
|
||||
<div class="media-body" style="min-height: 64px;">
|
||||
<a class="media-heading" t-attf-href="/partners/#{ partner.id }/"><span t-field="partner.parent_id"/> <span t-field="partner.name"/></a> - <span t-field="partner.grade_id"/>
|
||||
<a class="media-heading" t-href="/partners/#{ partner.id }/"><span t-field="partner.parent_id"/> <span t-field="partner.name"/></a> - <span t-field="partner.grade_id"/>
|
||||
<div t-field="partner.website_short_description"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,7 +110,7 @@
|
||||
<template id="details" name="Partner Detail">
|
||||
<t t-call="website_crm_partner_assign.layout">
|
||||
<t t-set="ref_content">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="partner_id"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="partner_id"/></t>
|
||||
<h1 class="col-md-12 text-center" t-field="partner_id.name"/>
|
||||
<div class="col-md-4">
|
||||
<div class="text-center">
|
||||
|
||||
@@ -34,8 +34,9 @@ import werkzeug
|
||||
|
||||
|
||||
class website_event(http.Controller):
|
||||
_order = 'website_published desc, date_begin desc'
|
||||
|
||||
@website.route(['/event', '/event/page/<int:page>/'], type='http', auth="public")
|
||||
@website.route(['/event/', '/event/page/<int:page>/'], type='http', auth="public", multilang=True)
|
||||
def events(self, page=1, **searches):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
event_obj = request.registry['event.event']
|
||||
@@ -132,7 +133,7 @@ class website_event(http.Controller):
|
||||
pager = request.website.pager(url="/event/", total=event_count, page=page, step=step, scope=5)
|
||||
obj_ids = event_obj.search(
|
||||
request.cr, request.uid, dom_without("none"), limit=step,
|
||||
offset=pager['offset'], order="date_begin DESC", context=request.context)
|
||||
offset=pager['offset'], order=self._order, context=request.context)
|
||||
events_ids = event_obj.browse(request.cr, request.uid, obj_ids,
|
||||
context=request.context)
|
||||
|
||||
@@ -151,7 +152,7 @@ class website_event(http.Controller):
|
||||
|
||||
return request.website.render("website_event.index", values)
|
||||
|
||||
@website.route(['/event/<int:event_id>'], type='http', auth="public")
|
||||
@website.route(['/event/<int:event_id>'], type='http', auth="public", multilang=True)
|
||||
def event(self, event_id=None, **post):
|
||||
event_obj = request.registry['event.event']
|
||||
values = {
|
||||
@@ -161,7 +162,7 @@ class website_event(http.Controller):
|
||||
}
|
||||
return request.website.render("website_event.event_description_full", values)
|
||||
|
||||
@website.route(['/event/<int:event_id>/add_cart'], type='http', auth="public")
|
||||
@website.route(['/event/<int:event_id>/add_cart'], type='http', auth="public", multilang=True)
|
||||
def add_cart(self, event_id=None, **post):
|
||||
user_obj = request.registry['res.users']
|
||||
order_line_obj = request.registry.get('sale.order.line')
|
||||
@@ -209,5 +210,5 @@ class website_event(http.Controller):
|
||||
order.write({'order_line': [(4, order_line_id)]}, context=request.context)
|
||||
|
||||
if not _values:
|
||||
return werkzeug.utils.redirect("/event/%s/" % event_id)
|
||||
return werkzeug.utils.redirect("/shop/checkout")
|
||||
return request.redirect("/event/%s/" % event_id)
|
||||
return request.redirect("/shop/checkout")
|
||||
|
||||
@@ -45,6 +45,9 @@ class event(osv.osv):
|
||||
help="Website communication history",
|
||||
),
|
||||
}
|
||||
_defaults = {
|
||||
'website_published': False,
|
||||
}
|
||||
|
||||
def google_map_img(self, cr, uid, ids, zoom=8, width=298, height=298, context=None):
|
||||
partner = self.browse(cr, uid, ids[0], context=context)
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
<template id="header_footer" inherit_id="website_sale.header_footer">
|
||||
<xpath expr="//header//ul[@id='top_menu']/li" position="before">
|
||||
<li><a href="/event">Events</a></li>
|
||||
<li><a t-href="/event">Events</a></li>
|
||||
</xpath>
|
||||
<xpath expr="//footer//ul[@name='products']/li" position="after">
|
||||
<li><a href="/event">Events</a></li>
|
||||
<li><a t-href="/event">Events</a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<li class="nav-header">Date</li>
|
||||
<t t-foreach="dates" t-as="date">
|
||||
<li t-att-class="searches.get('date') == date[0] and 'active' or ''">
|
||||
<a t-attf-href="/event/#{ search_path }&date=#{ date[0] }"><t t-esc="date[1]"/> <small t-if="date[3]">(<t t-esc="date[3]"/>)</small></a>
|
||||
<a t-href="/event/#{ search_path }&date=#{ date[0] }"><t t-esc="date[1]"/> <small t-if="date[3]">(<t t-esc="date[3]"/>)</small></a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
<ul class="media-list">
|
||||
<li t-foreach="event_ids" t-as="event" class="media" data-publish="">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="event"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="event"/></t>
|
||||
<div class="media-body">
|
||||
<span t-if="not event.event_ticket_ids" class="label label-default pull-right">No tickets needed.</span>
|
||||
<t t-if="event.event_ticket_ids">
|
||||
@@ -74,7 +74,7 @@
|
||||
available.
|
||||
</span>
|
||||
</t>
|
||||
<h4 class="media-heading"><a t-attf-href="/event/#{ event.id }/"><span t-field="event.name"> </span></a></h4>
|
||||
<h4 class="media-heading"><a t-href="/event/#{ event.id }/"><span t-field="event.name"> </span></a></h4>
|
||||
<div>
|
||||
<span t-field="event.type">: </span>
|
||||
<t t-if="event.organizer_id">
|
||||
@@ -107,7 +107,7 @@
|
||||
<li class="nav-header">Category</li>
|
||||
<t t-foreach="types">
|
||||
<li t-if="type" t-att-class="searches.get('type') == str(type and type[0]) and 'active' or ''">
|
||||
<a t-attf-href="/event/#{ search_path }&type=#{ type[0] }"><t t-esc="type[1]"/> <small>(<t t-esc="type_count"/>)</small></a>
|
||||
<a t-href="/event/#{ search_path }&type=#{ type[0] }"><t t-esc="type[1]"/> <small>(<t t-esc="type_count"/>)</small></a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
@@ -119,7 +119,7 @@
|
||||
<li class="nav-header">Location</li>
|
||||
<t t-foreach="countries">
|
||||
<li t-if="country_id" t-att-class="searches.get('country') == str(country_id and country_id[0]) and 'active' or ''">
|
||||
<a t-attf-href="/event/#{ search_path }&country=#{ country_id[0] }"><t t-esc="country_id[1]"/><small>(<t t-esc="country_id_count"/>)</small></a>
|
||||
<a t-href="/event/#{ search_path }&country=#{ country_id[0] }"><t t-esc="country_id[1]"/><small>(<t t-esc="country_id_count"/>)</small></a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
@@ -137,7 +137,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="event_id"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="event_id"/></t>
|
||||
<t t-call="website_mail.follow"><t t-set="object" t-value="event_id"/></t>
|
||||
<h1 class="text-center" t-field="event_id.name"></h1>
|
||||
<h4 class="text-center">
|
||||
@@ -164,7 +164,7 @@
|
||||
<div class="col-md-8">
|
||||
<t t-if="event_id.event_ticket_ids">
|
||||
<hr/>
|
||||
<form t-attf-action="/event/#{ event_id.id }/add_cart" method="post">
|
||||
<form t-action="/event/#{ event_id.id }/add_cart" method="post">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -180,7 +180,7 @@
|
||||
<td><span t-field="ticket.name"/></td>
|
||||
<td><span t-field="ticket.deadline"/></td>
|
||||
<td><span t-field="ticket.price"/></td>
|
||||
<td><span t-if="ticket.register_max"><t t-esc="ticket.register_current"/>/<t t-field="ticket.register_max"/></span></td>
|
||||
<td><span t-if="ticket.register_max"><t t-esc="ticket.register_current"/>/<span t-field="ticket.register_max"/></span></td>
|
||||
<td>
|
||||
<select t-if="ticket.register_avail" t-attf-name="ticket-#{ ticket.id }" class="form-control">
|
||||
<t t-foreach="range(0, ticket.register_avail > 9 and 10 or ticket.register_avail+1 )" t-as="nb"><option t-esc="nb"/></t>
|
||||
@@ -199,7 +199,7 @@
|
||||
<ul class="media-list" id="comment">
|
||||
<li t-foreach="event_id.website_message_ids" t-as="comment" class="media">
|
||||
<div class="media-body">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="comment"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="comment"/></t>
|
||||
<t t-raw="comment.body"/>
|
||||
<small class="pull-right muted text-right">
|
||||
<div t-field="comment.author_id"/>
|
||||
|
||||
@@ -6,7 +6,7 @@ from openerp.addons.website import website
|
||||
|
||||
class website_hr(http.Controller):
|
||||
|
||||
@website.route(['/page/website.aboutus'], type='http', auth="public")
|
||||
@website.route(['/page/website.aboutus'], type='http', auth="public", multilang=True)
|
||||
def blog(self, **post):
|
||||
hr_obj = request.registry['hr.employee']
|
||||
employee_ids = hr_obj.search(request.cr, request.uid, [(1, "=", 1)],
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="thumbnails">
|
||||
<div t-foreach="employee_ids" t-as="employee" class="col-md-4 mt16">
|
||||
<div class="media img-thumbnail" data-publish="">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="employee"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="employee"/></t>
|
||||
<a class="pull-left" href="#">
|
||||
<img class="media-object" t-att-src="employee.img('image_small')"/>
|
||||
</a>
|
||||
|
||||
@@ -9,7 +9,7 @@ import urllib
|
||||
|
||||
class website_crm_partner_assign(http.Controller):
|
||||
|
||||
@website.route(['/members/', '/members/page/<int:page>/'], type='http', auth="public")
|
||||
@website.route(['/members/', '/members/page/<int:page>/'], type='http', auth="public", multilang=True)
|
||||
def members(self, page=0, **post):
|
||||
membership_obj = request.registry['membership.membership_line']
|
||||
|
||||
@@ -56,7 +56,7 @@ class website_crm_partner_assign(http.Controller):
|
||||
}
|
||||
return request.website.render("website_membership.index", values)
|
||||
|
||||
@website.route(['/members/<int:ref_id>/'], type='http', auth="public")
|
||||
@website.route(['/members/<int:ref_id>/'], type='http', auth="public", multilang=True)
|
||||
def partners_ref(self, ref_id=0, **post):
|
||||
partner_obj = request.registry['res.partner']
|
||||
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, [('website_published', '=', True), ('id', '=', ref_id)], context=request.context)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
|
||||
<xpath expr="//footer//div[@name='info']/ul" position="inside">
|
||||
<li><a href="/members/">Members</a></li>
|
||||
<li><a t-href="/members/">Members</a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<t t-call="website.pager">
|
||||
<t t-set="classname">pull-left</t>
|
||||
</t>
|
||||
<form action="/members/" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<form t-action="/members/" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<div class="form-group">
|
||||
<input type="text" name="search" class="search-query col-md-2 mt4 form-control" placeholder="Search" t-att-value="searches.get('search') or '' or ''"/>
|
||||
</div>
|
||||
@@ -67,12 +67,12 @@
|
||||
</t>
|
||||
<t t-set="partner" t-value="membership_line_id.partner"/>
|
||||
<div class="media thumbnail" data-publish="">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="partner"/></t>
|
||||
<a class="pull-left" t-attf-href="/members/#{ partner.id }/">
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="partner"/></t>
|
||||
<a class="pull-left" t-href="/members/#{ partner.id }/">
|
||||
<img class="media-object" t-att-src="partner.img('image_small')"/>
|
||||
</a>
|
||||
<div class="media-body" style="min-height: 64px;">
|
||||
<a class="media-heading" t-attf-href="/members/#{ partner.id }/"><span t-field="partner.parent_id"/> <span t-field="partner.name"/></a> - <span t-field="membership_line_id.membership_id"/>
|
||||
<a class="media-heading" t-href="/members/#{ partner.id }/"><span t-field="partner.parent_id"/> <span t-field="partner.name"/></a> - <span t-field="membership_line_id.membership_id"/>
|
||||
<div t-field="partner.website_short_description"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,7 +101,7 @@
|
||||
<template id="details" name="Member Detail">
|
||||
<t t-call="website_membership.layout">
|
||||
<t t-set="ref_content">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="partner_id"/></t>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="partner_id"/></t>
|
||||
<h1 class="col-md-12 text-center" t-field="partner_id.name"/>
|
||||
<div class="col-md-4">
|
||||
<div class="text-center">
|
||||
|
||||
@@ -39,7 +39,7 @@ class Website(osv.Model):
|
||||
|
||||
class website_project(http.Controller):
|
||||
|
||||
@website.route(['/project/<int:project_id>/'], type='http', auth="public")
|
||||
@website.route(['/project/<int:project_id>/'], type='http', auth="public", multilang=True)
|
||||
def project(self, project_id=None, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
project_obj = request.registry['project.project']
|
||||
@@ -51,7 +51,7 @@ class website_project(http.Controller):
|
||||
}
|
||||
return request.website.render("website_project.index", render_values)
|
||||
|
||||
@website.route(['/project/task/<int:task_id>'], type='http', auth="public")
|
||||
@website.route(['/project/task/<int:task_id>'], type='http', auth="public", multilang=True)
|
||||
def task(self, task_id=None, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
task_obj = request.registry['project.task']
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<!-- Layout add nav and footer -->
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
|
||||
<xpath expr="//footer//ul[@name='products']" position="inside">
|
||||
<li t-foreach="website_project_ids" t-as="project"><a t-attf-href="/project/#{ project.id }/"><t t-esc="project.name"/></a></li>
|
||||
<li t-foreach="website_project_ids" t-as="project"><a t-href="/project/#{ project.id }/"><t t-esc="project.name"/></a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<!-- Project -->
|
||||
<template id="task_kanban_card" name="TaskKanban">
|
||||
<div class="thumbnail">
|
||||
<a t-attf-href="/project/task/#{object_id.id}/"><span t-field="object_id.name"/></a>
|
||||
<a t-href="/project/task/#{object_id.id}/"><span t-field="object_id.name"/></a>
|
||||
<div>
|
||||
Assigned to <span t-field="object_id.user_id"/>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import controllers
|
||||
import website_styles
|
||||
import product
|
||||
import website_sale
|
||||
|
||||
@@ -49,6 +49,8 @@ class Website(osv.osv):
|
||||
|
||||
class Ecommerce(http.Controller):
|
||||
|
||||
_order = 'website_sequence desc, website_published desc'
|
||||
|
||||
def get_categories(self):
|
||||
domain = [('parent_id', '=', False)]
|
||||
|
||||
@@ -68,7 +70,146 @@ class Ecommerce(http.Controller):
|
||||
|
||||
return (categories, full_category_ids)
|
||||
|
||||
@website.route(['/shop/', '/shop/category/<cat_id>/', '/shop/category/<cat_id>/page/<int:page>/', '/shop/page/<int:page>/'], type='http', auth="public")
|
||||
def get_bin_packing_products(self, product_ids, fill_hole, col_number=4):
|
||||
"""
|
||||
Packing all products of the search into a table of #col_number columns in function of the product sizes
|
||||
The size datas of website_style_ids is use for fill table (default 1x1)
|
||||
The other website_style_ids datas are concatenate in a html class
|
||||
|
||||
@values:
|
||||
|
||||
product_ids: list of product template
|
||||
fill_hole: list of extra product template use to fill the holes
|
||||
col_number: number of columns
|
||||
|
||||
@return:
|
||||
|
||||
table (list of list of #col_number items)
|
||||
items: {
|
||||
'product': browse of product template,
|
||||
'x': size x,
|
||||
'y': size y,
|
||||
'class': html class
|
||||
}
|
||||
"""
|
||||
product_obj = request.registry.get('product.template')
|
||||
style_obj = request.registry.get('website.product.style')
|
||||
|
||||
# search for checking of access rules and keep order
|
||||
product_ids = [id for id in product_ids if id in product_obj.search(request.cr, request.uid, [("id", 'in', product_ids)], context=request.context)]
|
||||
|
||||
size_ids = {}
|
||||
style_ids = style_obj.search(request.cr, SUPERUSER_ID, [('html_class', 'like', 'size_%')], context=request.context)
|
||||
for style in style_obj.browse(request.cr, SUPERUSER_ID, style_ids, context=request.context):
|
||||
size_ids[style.id] = [int(style.html_class[-3]), int(style.html_class[-1])]
|
||||
|
||||
product_list = []
|
||||
bin_packing = {}
|
||||
bin_packing[0] = {}
|
||||
|
||||
for product in product_obj.browse(request.cr, SUPERUSER_ID, product_ids, context=request.context):
|
||||
index = len(product_list)
|
||||
|
||||
# get size and all html classes
|
||||
_class = ""
|
||||
x = 1
|
||||
y = 1
|
||||
for style_id in product.website_style_ids:
|
||||
if style_id.id in size_ids:
|
||||
size = size_ids[style_id.id]
|
||||
x = size[0]
|
||||
y = size[1]
|
||||
elif style_id.html_class:
|
||||
_class += " " + style_id.html_class
|
||||
product_list.append({'product': product, 'x': x, 'y': y, 'class': _class })
|
||||
|
||||
# bin packing products
|
||||
insert = False
|
||||
line = 0
|
||||
while not insert:
|
||||
# if not full column get next line
|
||||
if len(bin_packing.setdefault(line, {})) >= col_number:
|
||||
line += 1
|
||||
continue
|
||||
|
||||
col = 0
|
||||
while col < col_number:
|
||||
if bin_packing[line].get(col, None) != None:
|
||||
col += 1
|
||||
continue
|
||||
|
||||
insert = True
|
||||
|
||||
# check if the box can be inserted
|
||||
copy_line = line
|
||||
copy_y = y
|
||||
while copy_y > 0:
|
||||
copy_col = col
|
||||
copy_x = x
|
||||
while copy_x > 0:
|
||||
if copy_col >= col_number or bin_packing.setdefault(copy_line, {}).get(copy_col, None) != None:
|
||||
insert = False
|
||||
break
|
||||
copy_col += 1
|
||||
copy_x -= 1
|
||||
if not insert:
|
||||
break
|
||||
copy_line += 1
|
||||
copy_y -= 1
|
||||
|
||||
if not insert:
|
||||
col += 1
|
||||
continue
|
||||
|
||||
# insert the box
|
||||
copy_y = y
|
||||
while copy_y > 0:
|
||||
copy_y -= 1
|
||||
copy_x = x
|
||||
while copy_x > 0:
|
||||
copy_x -= 1
|
||||
bin_packing[line + copy_y][col + copy_x] = False
|
||||
bin_packing[line + copy_y][col + copy_x] = product_list[index]
|
||||
break
|
||||
|
||||
if not insert:
|
||||
line += 1
|
||||
else:
|
||||
break
|
||||
|
||||
length = len(bin_packing)
|
||||
|
||||
# browse product to fill the holes
|
||||
if fill_hole:
|
||||
fill_hole_products = []
|
||||
# search for checking of access rules and keep order
|
||||
fill_hole = [id for id in fill_hole if id in product_obj.search(request.cr, request.uid, [("id", 'in', fill_hole)], context=request.context)]
|
||||
for product in product_obj.browse(request.cr, SUPERUSER_ID, fill_hole, context=request.context):
|
||||
fill_hole_products.append(product)
|
||||
fill_hole_products.reverse()
|
||||
|
||||
# packaging in list (from dict)
|
||||
bin_packing_list = []
|
||||
line = 0
|
||||
while line < length:
|
||||
bin_packing_list.append([])
|
||||
col = 0
|
||||
while col < col_number:
|
||||
if fill_hole and fill_hole_products and bin_packing[line].get(col) == None:
|
||||
bin_packing[line][col] = {'product': fill_hole_products.pop(), 'x': 1, 'y': 1, 'class': _class }
|
||||
bin_packing_list[line].append(bin_packing[line].get(col))
|
||||
col += 1
|
||||
line += 1
|
||||
|
||||
return bin_packing_list
|
||||
|
||||
def get_products(self, product_ids):
|
||||
product_obj = request.registry.get('product.template')
|
||||
# search for checking of access rules and keep order
|
||||
product_ids = [id for id in product_ids if id in product_obj.search(request.cr, request.uid, [("id", 'in', product_ids)], context=request.context)]
|
||||
return product_obj.browse(request.cr, SUPERUSER_ID, product_ids, context=request.context)
|
||||
|
||||
@website.route(['/shop/', '/shop/category/<cat_id>/', '/shop/category/<cat_id>/page/<int:page>/', '/shop/page/<int:page>/'], type='http', auth="public", multilang=True)
|
||||
def category(self, cat_id=0, page=0, **post):
|
||||
|
||||
if 'promo' in post:
|
||||
@@ -92,20 +233,24 @@ class Ecommerce(http.Controller):
|
||||
product_count = len(product_obj.search(request.cr, request.uid, domain, context=request.context))
|
||||
pager = request.website.pager(url="/shop/category/%s/" % cat_id, total=product_count, page=page, step=step, scope=7, url_args=post)
|
||||
|
||||
product_ids = product_obj.search(request.cr, request.uid, domain, limit=step, offset=pager['offset'], context=request.context)
|
||||
|
||||
request.context['pricelist'] = self.get_pricelist()
|
||||
|
||||
product_ids = product_obj.search(request.cr, request.uid, domain, limit=step, offset=pager['offset'], order=self._order, context=request.context)
|
||||
fill_hole = product_obj.search(request.cr, request.uid, domain, limit=step, offset=pager['offset']+step, order=self._order, context=request.context)
|
||||
|
||||
values = {
|
||||
'get_categories': self.get_categories,
|
||||
'category_id': cat_id,
|
||||
'products': product_obj.browse(request.cr, SUPERUSER_ID, product_ids, context=request.context),
|
||||
'product_ids': product_ids,
|
||||
'product_ids_for_holes': fill_hole,
|
||||
'get_bin_packing_products': self.get_bin_packing_products,
|
||||
'get_products': self.get_products,
|
||||
'search': post.get("search"),
|
||||
'pager': pager,
|
||||
}
|
||||
return request.website.render("website_sale.products", values)
|
||||
|
||||
@website.route(['/shop/product/<product_id>/'], type='http', auth="public")
|
||||
@website.route(['/shop/product/<product_id>/'], type='http', auth="public", multilang=True)
|
||||
def product(self, cat_id=0, product_id=0, **post):
|
||||
|
||||
if 'promo' in post:
|
||||
@@ -126,6 +271,16 @@ class Ecommerce(http.Controller):
|
||||
category = category_obj.browse(request.cr, request.uid, int(post.get('category_id')), context=request.context)
|
||||
|
||||
product = product_obj.browse(request.cr, request.uid, product_id, context=request.context)
|
||||
|
||||
styles = []
|
||||
styles_used = []
|
||||
if not request.context['is_public_user']:
|
||||
style_obj = request.registry.get('website.product.style')
|
||||
style_ids = style_obj.search(request.cr, request.uid, [(1, '=', 1)], context=request.context)
|
||||
styles = style_obj.browse(request.cr, request.uid, style_ids, context=request.context)
|
||||
for style in product.website_style_ids:
|
||||
styles_used.append(style.id)
|
||||
|
||||
values = {
|
||||
'category_id': post.get('category_id') and int(post.get('category_id')) or None,
|
||||
'category': category,
|
||||
@@ -133,21 +288,16 @@ class Ecommerce(http.Controller):
|
||||
'get_categories': self.get_categories,
|
||||
'category_list': category_list,
|
||||
'product': product,
|
||||
'styles': styles,
|
||||
'styles_used': styles_used,
|
||||
}
|
||||
return request.website.render("website_sale.product", values)
|
||||
|
||||
@website.route(['/shop/add_product/', '/shop/category/<cat_id>/add_product/'], type='http', auth="public")
|
||||
@website.route(['/shop/add_product/', '/shop/category/<cat_id>/add_product/'], type='http', auth="public", multilang=True)
|
||||
def add_product(self, cat_id=0, **post):
|
||||
product_id = request.registry.get('product.product').create(request.cr, request.uid,
|
||||
{'name': 'New Product', 'public_categ_id': cat_id}, request.context)
|
||||
return werkzeug.utils.redirect("/shop/product/%s/?unable_editor=1" % product_id)
|
||||
|
||||
@website.route(['/shop/change_category/<product_id>/'], type='http', auth="public")
|
||||
def edit_product(self, product_id=0, **post):
|
||||
request.registry.get('product.template').write(
|
||||
request.cr, request.uid, [int(product_id)],
|
||||
{'public_categ_id': int(post.get('public_categ_id', 0))}, request.context)
|
||||
return "1"
|
||||
return request.redirect("/shop/product/%s/?unable_editor=1" % product_id)
|
||||
|
||||
def get_pricelist(self):
|
||||
if not request.httprequest.session.get('ecommerce_pricelist'):
|
||||
@@ -229,12 +379,13 @@ class Ecommerce(http.Controller):
|
||||
if not quantity:
|
||||
order_line_obj.unlink(request.cr, SUPERUSER_ID, order_line_ids, context=context)
|
||||
else:
|
||||
#values['name'] = "website order"
|
||||
order_line_id = order_line_obj.create(request.cr, SUPERUSER_ID, values, context=context)
|
||||
order.write({'order_line': [(4, order_line_id)]}, context=context)
|
||||
|
||||
return [quantity, order.get_total_quantity()]
|
||||
|
||||
@website.route(['/shop/mycart/'], type='http', auth="public")
|
||||
@website.route(['/shop/mycart/'], type='http', auth="public", multilang=True)
|
||||
def mycart(self, **post):
|
||||
order = get_current_order()
|
||||
prod_obj = request.registry.get('product.product')
|
||||
@@ -260,31 +411,20 @@ class Ecommerce(http.Controller):
|
||||
}
|
||||
return request.website.render("website_sale.mycart", values)
|
||||
|
||||
@website.route(['/shop/<path:path>/add_cart/', '/shop/add_cart/'], type='http', auth="public")
|
||||
def add_cart(self, path=None, product_id=None, order_line_id=None, remove=None, json=None):
|
||||
quantity = self.add_product_to_cart(product_id=product_id, order_line_id=order_line_id, number=(remove and -1 or 1))
|
||||
if json:
|
||||
return simplejson.dumps(quantity)
|
||||
if path:
|
||||
return werkzeug.utils.redirect("/shop/%s/" % path)
|
||||
else:
|
||||
return werkzeug.utils.redirect("/shop/")
|
||||
@website.route(['/shop/<path:path>/add_cart/', '/shop/add_cart/'], type='http', auth="public", multilang=True)
|
||||
def add_cart(self, path=None, product_id=None, order_line_id=None, remove=None, **kw):
|
||||
self.add_product_to_cart(product_id=product_id, order_line_id=order_line_id, number=(remove and -1 or 1))
|
||||
return request.redirect("/shop/mycart/")
|
||||
|
||||
@website.route(['/shop/remove_cart/', '/shop/<path:path>/remove_cart/'], type='http', auth="public")
|
||||
def remove_cart(self, path=None, product_id=None, order_line_id=None, json=None):
|
||||
return self.add_cart(product_id=product_id, order_line_id=order_line_id, path=path, remove=True, json=json)
|
||||
@website.route(['/shop/add_cart_json/'], type='json', auth="public")
|
||||
def add_cart_json(self, product_id=None, order_line_id=None, remove=None):
|
||||
return self.add_product_to_cart(product_id=product_id, order_line_id=order_line_id, number=(remove and -1 or 1))
|
||||
|
||||
@website.route(['/shop/set_cart/', '/shop/<path:path>/set_cart/'], type='http', auth="public")
|
||||
def set_cart(self, path=None, product_id=None, order_line_id=None, set_number=0, json=None):
|
||||
quantity = self.add_product_to_cart(product_id=product_id, order_line_id=order_line_id, set_number=set_number)
|
||||
if json:
|
||||
return simplejson.dumps(quantity)
|
||||
if path:
|
||||
return werkzeug.utils.redirect("/shop/%s/" % path)
|
||||
else:
|
||||
return werkzeug.utils.redirect("/shop/")
|
||||
@website.route(['/shop/set_cart_json/'], type='json', auth="public")
|
||||
def set_cart_json(self, path=None, product_id=None, order_line_id=None, set_number=0, json=None):
|
||||
return self.add_product_to_cart(product_id=product_id, order_line_id=order_line_id, set_number=set_number)
|
||||
|
||||
@website.route(['/shop/checkout/'], type='http', auth="public")
|
||||
@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"]
|
||||
rel_fields = ['country_id', 'state_id']
|
||||
@@ -325,7 +465,7 @@ class Ecommerce(http.Controller):
|
||||
|
||||
return request.website.render("website_sale.checkout", values)
|
||||
|
||||
@website.route(['/shop/confirm_order/'], type='http', auth="public")
|
||||
@website.route(['/shop/confirm_order/'], type='http', auth="public", multilang=True)
|
||||
def confirm_order(self, **post):
|
||||
order = get_current_order()
|
||||
|
||||
@@ -334,10 +474,10 @@ class Ecommerce(http.Controller):
|
||||
user_obj = request.registry.get('res.users')
|
||||
|
||||
if order.state != 'draft':
|
||||
return werkzeug.utils.redirect("/shop/checkout/")
|
||||
return request.redirect("/shop/checkout/")
|
||||
if not order.order_line:
|
||||
error.append("empty_cart")
|
||||
return werkzeug.utils.redirect("/shop/checkout/")
|
||||
return request.redirect("/shop/checkout/")
|
||||
|
||||
# check values
|
||||
request.session['checkout'] = post
|
||||
@@ -348,7 +488,7 @@ class Ecommerce(http.Controller):
|
||||
if post.get('shipping_different') and key != 'email' and not post.get("shipping_%s" % key):
|
||||
error.append("shipping_%s" % key)
|
||||
if error:
|
||||
return werkzeug.utils.redirect("/shop/checkout/?error=%s&shipping=%s" % (",".join(error), post.get('shipping_different') and 'on' or ''))
|
||||
return request.redirect("/shop/checkout/?error=%s&shipping=%s" % (",".join(error), post.get('shipping_different') and 'on' or ''))
|
||||
|
||||
# search or create company
|
||||
company_id = None
|
||||
@@ -408,9 +548,9 @@ class Ecommerce(http.Controller):
|
||||
order_value.update(request.registry.get('sale.order').onchange_partner_id(request.cr, SUPERUSER_ID, [], order.partner_id.id, context=request.context)['value'])
|
||||
order.write(order_value)
|
||||
|
||||
return werkzeug.utils.redirect("/shop/payment/")
|
||||
return request.redirect("/shop/payment/")
|
||||
|
||||
@website.route(['/shop/payment/'], type='http', auth="public")
|
||||
@website.route(['/shop/payment/'], type='http', auth="public", multilang=True)
|
||||
def payment(self, **post):
|
||||
order = get_current_order()
|
||||
|
||||
@@ -431,10 +571,49 @@ class Ecommerce(http.Controller):
|
||||
|
||||
return request.website.render("website_sale.payment", values)
|
||||
|
||||
@website.route(['/shop/payment_validate/'], type='http', auth="public")
|
||||
@website.route(['/shop/payment_validate/'], type='http', auth="public", multilang=True)
|
||||
def payment_validate(self, **post):
|
||||
request.httprequest.session['ecommerce_order_id'] = False
|
||||
request.httprequest.session['ecommerce_pricelist'] = False
|
||||
return werkzeug.utils.redirect("/shop/")
|
||||
return request.redirect("/shop/")
|
||||
|
||||
@website.route(['/shop/change_sequence/'], type='json', auth="public")
|
||||
def change_sequence(self, id, top):
|
||||
product_obj = request.registry.get('product.template')
|
||||
if top:
|
||||
product_obj.set_sequence_top(request.cr, request.uid, [id], request.context)
|
||||
else:
|
||||
product_obj.set_sequence_bottom(request.cr, request.uid, [id], request.context)
|
||||
|
||||
@website.route(['/shop/change_styles/'], type='json', auth="public")
|
||||
def change_styles(self, id, style_id):
|
||||
product = request.registry.get('product.template').browse(request.cr, request.uid, id, request.context)
|
||||
|
||||
remove = []
|
||||
active = False
|
||||
for style in product.website_style_ids:
|
||||
if style.id == style_id:
|
||||
remove.append(style.id)
|
||||
active = True
|
||||
break
|
||||
|
||||
style = request.registry.get('website.product.style').browse(request.cr, request.uid, style_id, request.context)
|
||||
|
||||
if 'size_' in style.html_class and style.html_class.index('size_') == 0:
|
||||
remove = []
|
||||
for pstyle in product.website_style_ids:
|
||||
if 'size_' in pstyle.html_class and pstyle.html_class.index('size_') == 0:
|
||||
remove.append(pstyle.id)
|
||||
|
||||
if remove:
|
||||
print "remove", remove
|
||||
product.write({'website_style_ids': [(3, rid) for rid in remove]})
|
||||
|
||||
if not active:
|
||||
print "add", style.id
|
||||
product.write({'website_style_ids': [(4, style.id)]})
|
||||
|
||||
return not active
|
||||
|
||||
|
||||
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -31,22 +31,28 @@ class product_pricelist(osv.Model):
|
||||
|
||||
class product_template(osv.Model):
|
||||
_inherit = "product.template"
|
||||
_order = 'website_published,name'
|
||||
_order = 'website_sequence desc, website_published, name'
|
||||
_columns = {
|
||||
'website_published': fields.boolean('Available in the website'),
|
||||
'website_description': fields.html('Description for the website'),
|
||||
'suggested_product_id': fields.many2one('product.template', 'Suggested For Product'),
|
||||
'suggested_product_ids': fields.one2many('product.template', 'suggested_product_id', 'Suggested Products'),
|
||||
'website_sizex': fields.selection(map(lambda x: (str(x+1),str(x+1)), range(12)), 'Size X'),
|
||||
'website_sizey': fields.selection(map(lambda x: (str(x+1),str(x+1)), range(6)), 'Size Y'),
|
||||
'website_product_class': fields.selection([('','Default'), ('oe_image_full','Image Full')], 'Size Y'),
|
||||
'website_style_ids' : fields.many2many('website.product.style','product_website_style_rel', 'product_id', 'style_id', 'Styles'),
|
||||
'website_sequence': fields.integer('Sequence', help="Determine the display order in the Website E-commerce"),
|
||||
}
|
||||
_defaults = {
|
||||
'website_sizex': '3',
|
||||
'website_sizey': '2',
|
||||
'website_product_class': '',
|
||||
'website_sequence': 1,
|
||||
'website_published': False,
|
||||
}
|
||||
|
||||
def set_sequence_top(self, cr, uid, ids, context=None):
|
||||
cr.execute('SELECT MAX(website_sequence) FROM product_template')
|
||||
max_sequence = cr.fetchone()[0] or 0
|
||||
return self.write(cr, uid, ids, {'website_sequence': max_sequence + 1}, context=context)
|
||||
|
||||
def set_sequence_bottom(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'website_sequence': 0}, context=context)
|
||||
|
||||
def recommended_products(self, cr, uid, ids, context=None):
|
||||
id = ids[0]
|
||||
product_ids = []
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
border: 1px solid rgba(100, 100, 100, 0.2);
|
||||
max-width: 100%;
|
||||
max-height: 140px;
|
||||
position: relative;
|
||||
}
|
||||
.oe_list_products .oe_product_image {
|
||||
position: absolute;
|
||||
@@ -142,3 +143,7 @@
|
||||
right: 0;
|
||||
border-top: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
.oe_website_sale .row .row .col-md-12 {
|
||||
float: none;
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
border: 1px solid rgba(100, 100, 100, 0.2)
|
||||
max-width: 100%
|
||||
max-height: 140px
|
||||
position: relative
|
||||
.oe_product_image
|
||||
position: absolute
|
||||
left: 15px
|
||||
@@ -132,3 +133,6 @@
|
||||
left: 180px
|
||||
right: 0
|
||||
border-top: 1px solid #dddddd
|
||||
|
||||
.oe_website_sale .row .row .col-md-12
|
||||
float: none
|
||||
@@ -23,45 +23,52 @@ $(document).ready(function () {
|
||||
var $input = $(this);
|
||||
var value = parseInt($input.val());
|
||||
if (isNaN(value)) value = 0;
|
||||
$.get("./set_cart/", {'order_line_id': $input.data('id'), 'set_number': value, 'json': true}, function (data) {
|
||||
var data = JSON.parse(data);
|
||||
set_my_cart_quantity(data[1]);
|
||||
$input.val(data[0]);
|
||||
if (!+data[0]) {
|
||||
$.get('/shop/remove_cart/', {'order_line_id': $input.data('id'), 'json': true,}, function (data) {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
});
|
||||
openerp.jsonRpc("/shop/set_cart_json/", 'call', {'order_line_id': $input.data('id'), 'set_number': value})
|
||||
.then(function (data) {
|
||||
set_my_cart_quantity(data[1]);
|
||||
$input.val(data[0]);
|
||||
if (!data[0]) {
|
||||
openerp.jsonRpc('/shop/add_cart_json/', 'call', {'order_line_id': $input.data('id')}).then(function (data) {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".oe_website_sale #product_detail select[name='public_categ_id']").change(function () {
|
||||
var $select = $(this);
|
||||
$.get("/shop/change_category/"+$select.data('id')+"/", {'public_categ_id': $select.val()});
|
||||
});
|
||||
|
||||
|
||||
// hack to add and rome from cart with json
|
||||
$('.oe_website_sale a[href*="/add_cart/"], a[href*="/remove_cart/"]').on('click', function (ev) {
|
||||
ev.preventDefault();
|
||||
$link = $(ev.currentTarget);
|
||||
$.get($link.attr("href"), {'json': true}, function (data) {
|
||||
var data = JSON.parse(data);
|
||||
if (!+data[0]) {
|
||||
location.reload();
|
||||
}
|
||||
set_my_cart_quantity(data[1]);
|
||||
$link.parent().find(".js_quantity").val(data[0]);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
$('.oe_website_sale form[action*="/add_cart/"]').on('submit', function (ev) {
|
||||
ev.preventDefault();
|
||||
$form = $(ev.currentTarget);
|
||||
$.get($form.attr("action"), {'product_id': $form.find('input[name="product_id"]:checked').val(), 'json': true}, function (data) {
|
||||
set_my_cart_quantity(JSON.parse(data)[1]);
|
||||
});
|
||||
var $link = $(ev.currentTarget);
|
||||
openerp.jsonRpc("/shop/add_cart_json/", 'call', {'order_line_id': $link.data('id'), 'remove': $link.is('[href*="/remove_cart/"]')})
|
||||
.then(function (data) {
|
||||
if (!data[0]) {
|
||||
location.reload();
|
||||
}
|
||||
set_my_cart_quantity(data[1]);
|
||||
$link.parent().find(".js_quantity").val(data[0]);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.js_publish_management .js_go_to_top,.js_publish_management .js_go_to_bottom').on('click', function () {
|
||||
var $data = $(this).parents(".js_publish_management:first");
|
||||
openerp.jsonRpc('/shop/change_sequence/', 'call', {'id': $data.data('id'), 'top': $(this).hasClass('js_go_to_top')});
|
||||
});
|
||||
|
||||
$('.js_publish_management ul[name="style"] a').on('click', function () {
|
||||
var $a = $(this);
|
||||
var $li = $a.parent();
|
||||
var $data = $(this).parents(".js_publish_management:first");
|
||||
|
||||
var data = $a.data();
|
||||
if (data.class.toLowerCase().indexOf('size_') === 0) {
|
||||
$('.js_publish_management ul[name="style"] li:has(a[data-class^="size_"])').removeClass("active");
|
||||
}
|
||||
$li.parent().removeClass("active");
|
||||
openerp.jsonRpc('/shop/change_styles/', 'call', {'id': $data.data('id'), 'style_id': data.value})
|
||||
.then(function (result) {
|
||||
$li.toggleClass("active", result);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<group name="website" string="Website">
|
||||
<field name="website_published"/>
|
||||
<field name="suggested_product_ids" widget="many2many_tags"/>
|
||||
<field name="website_style_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
@@ -44,9 +45,9 @@
|
||||
|
||||
<template id="header_footer" inherit_id="website.layout" name="Custom Footer">
|
||||
<xpath expr="//header//ul[@id='top_menu']/li" position="before">
|
||||
<li><a href="/shop/">Shop</a></li>
|
||||
<li><a t-href="/shop/">Shop</a></li>
|
||||
<li>
|
||||
<a href="/shop/mycart/">
|
||||
<a t-href="/shop/mycart/">
|
||||
<i class="icon-shopping-cart"></i>
|
||||
My cart <span t-attf-class="my_cart_quantity badge #{(not website_sale_order or not website_sale_order.get_total_quantity()) and 'hidden' or ''}"
|
||||
t-esc="website_sale_order and website_sale_order.get_total_quantity() or ''"/>
|
||||
@@ -54,9 +55,9 @@
|
||||
</li>
|
||||
</xpath>
|
||||
<xpath expr="//footer//ul[@name='products']" position="inside">
|
||||
<li><a href="/shop/">Shop</a></li>
|
||||
<li><a t-href="/shop/">Shop</a></li>
|
||||
<li>
|
||||
<a href="/shop/mycart/">
|
||||
<a t-href="/shop/mycart/">
|
||||
<i class="icon-shopping-cart"></i>
|
||||
My cart <span t-attf-class="my_cart_quantity badge #{(not website_sale_order or not website_sale_order.get_total_quantity()) and 'hidden' or ''}"
|
||||
t-esc="website_sale_order and website_sale_order.get_total_quantity() or ''"/>
|
||||
@@ -68,7 +69,7 @@
|
||||
|
||||
<template id="categories_recursive">
|
||||
<li t-att-class="category.id == category_id and 'active' or ''">
|
||||
<a t-att-class="category.id not in categ[1] and 'unpublish' or ''" t-attf-href="/shop/category/#{ category.id }/" t-field="category.name"></a>
|
||||
<a t-att-class="category.id not in categ[1] and 'unpublish' or ''" t-href="/shop/category/#{ category.id }/" 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">
|
||||
<t t-if="category.id in categ[1] or editable">
|
||||
@@ -81,6 +82,34 @@
|
||||
|
||||
<!-- Product list -->
|
||||
|
||||
<template id="products_cart">
|
||||
<div class="oe_product_description">
|
||||
<a t-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<b t-field="product.name"/>
|
||||
</a>
|
||||
<!-- This should be an option -->
|
||||
<div t-if="product.description_sale" class="text-muted oe_subdescription">
|
||||
<div id="product_description" t-att-data-name="product.id"/>
|
||||
</div>
|
||||
<div class="product_price">
|
||||
<b>
|
||||
<t t-if="product.product_variant_ids[0].lst_price != product.product_variant_ids[0].price">
|
||||
<span class="text-danger" style="text-decoration: line-through;">
|
||||
<t t-esc="product.product_variant_ids[0].lst_price" /> €
|
||||
</span>&nbsp;
|
||||
</t>
|
||||
<t t-esc="product.product_variant_ids[0].price" /> €
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oe_product_image text-center">
|
||||
<a t-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<img class="img" t-att-src="product.img('image')"/>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="products" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
@@ -96,9 +125,9 @@
|
||||
<div class="col-sm-4">
|
||||
<h1>Our Products</h1>
|
||||
</div><div class="col-sm-4 pagination text-center">
|
||||
<a t-if="editable" t-attf-href="/shop/#{ category_id and ('category/%s/' % category_id) or ''}add_product/" class="btn btn-default">New Product</a>
|
||||
<a t-if="editable" t-href="/shop/#{ category_id and ('category/%s/' % category_id) or ''}add_product/" class="btn btn-default">New Product</a>
|
||||
</div><div class="col-sm-4">
|
||||
<form t-attf-action="/shop/#{ category_id and ('category/%s/' % category_id) or ''}" method="get" class="pull-right pagination">
|
||||
<form t-action="/shop/#{ category_id and ('category/%s/' % category_id) or ''}" method="get" class="pull-right pagination">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-search"/></span>
|
||||
<input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search or ''"/>
|
||||
@@ -110,40 +139,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='row style_default'>
|
||||
<div class='style_default'>
|
||||
<div class="col-md-12" id="products_grid">
|
||||
<t t-foreach="products" t-as="product">
|
||||
<div t-attf-class="col-md-#{ search and 3 or product.website_sizex } oe_product oe-height-#{ search and 2 or product.website_sizey } #{ product.website_product_class}" t-att-data-publish="product.website_published and 'on' or 'off'" data-name="products_layout">
|
||||
|
||||
<div class="oe_product_description">
|
||||
<a t-attf-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<b t-field="product.name"/>
|
||||
</a>
|
||||
<!-- This should be an option -->
|
||||
<div t-if="product.description_sale" class="text-muted oe_subdescription">
|
||||
<div id="product_description" t-att-data-name="product.id"/>
|
||||
</div>
|
||||
<div>
|
||||
<b>
|
||||
<t t-if="product.product_variant_ids[0].lst_price != product.product_variant_ids[0].price">
|
||||
<span class="text-danger" style="text-decoration: line-through;">
|
||||
<t t-esc="product.product_variant_ids[0].lst_price" /> €
|
||||
</span>&nbsp;
|
||||
</t>
|
||||
<t t-esc="product.product_variant_ids[0].price" /> €
|
||||
</b>
|
||||
<span id="add_to_cart" t-att-data-name="product.id"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oe_product_image text-center">
|
||||
<a t-attf-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<img class="img" t-att-src="product.img('image')"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
<table width="100%">
|
||||
<colgroup>
|
||||
<col style="width: 1px; margin-right: -1px;" />
|
||||
<col class="col-md-3" />
|
||||
<col class="col-md-3" />
|
||||
<col class="col-md-3" />
|
||||
<col class="col-md-3" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<t t-set="table_products" t-value="get_bin_packing_products(product_ids, product_ids_for_holes, 4)"/>
|
||||
<tr t-foreach="table_products" t-as="tr_product">
|
||||
<td class="oe-height-2"></td>
|
||||
<t t-foreach="tr_product" t-as="td_product">
|
||||
<t t-if="td_product">
|
||||
<t t-set="product" t-value="td_product['product']"/>
|
||||
<td t-att-colspan="td_product['x']"
|
||||
t-att-rowspan="td_product['y']"
|
||||
t-attf-class="oe_product #{ td_product['class'] }"
|
||||
t-att-data-publish="product.website_published and 'on' or 'off'">
|
||||
<t t-call="website_sale.products_cart"/>
|
||||
</td>
|
||||
</t>
|
||||
<td t-if="td_product == None"/>
|
||||
</t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
@@ -160,16 +184,16 @@
|
||||
<template id="product_description" inherit_option_id="website_sale.products" name="Product Description">
|
||||
<xpath expr="//div[@id='product_description']" position="replace">
|
||||
<small>
|
||||
<t t-field="product.description_sale"/>
|
||||
<span t-field="product.description_sale"/>
|
||||
</small>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Add to cart button-->
|
||||
|
||||
<template id="add_to_basket" inherit_option_id="website_sale.products" name="Add to Cart">
|
||||
<xpath expr="//span[@id='add_to_cart']" position="replace">
|
||||
<a t-attf-href="./add_cart/?product_id=#{ product.id }">
|
||||
<template id="add_to_basket" inherit_option_id="website_sale.products_cart" name="Add to Cart">
|
||||
<xpath expr="//div[@class='product_price']" position="inside">
|
||||
<a t-href="./add_cart/?product_id=#{ product.id }">
|
||||
<span class="icon-shopping-cart"/>
|
||||
</a>
|
||||
</xpath>
|
||||
@@ -178,8 +202,15 @@
|
||||
<!-- List view of products -->
|
||||
|
||||
<template id="list_view" inherit_option_id="website_sale.products" name="List View">
|
||||
<xpath expr="//div[@data-name='products_layout']" position="attributes">
|
||||
<attribute name="class">col-md-12 oe_list_products oe-height-1</attribute>
|
||||
<xpath expr="//div[@id='products_grid']/table" position="replace">
|
||||
<div class="row">
|
||||
<t t-set="products" t-value="get_products(product_ids)"/>
|
||||
<t t-foreach="products" t-as="product">
|
||||
<div class="col-md-12 oe_list_products oe-height-1">
|
||||
<t t-call="website_sale.products_cart"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -198,14 +229,31 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="/shop">Products</a></li>
|
||||
<li t-if="category"><a t-att-href="'/shop/category/%s' % (category_id,)"><t t-field="category.name"/></a></li>
|
||||
<li><a t-href="/shop">Products</a></li>
|
||||
<li t-if="category"><a t-href="'/shop/category/%s' % (category_id,)"><span t-field="category.name"/></a></li>
|
||||
<li class="active" t-field="product.name">Product Name</li>
|
||||
</ol>
|
||||
</div><div class="col-sm-3">
|
||||
<t t-call="website.publish"><t t-set="object" t-value="product"/></t>
|
||||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="product"/>
|
||||
<li class='dropdown-submenu'>
|
||||
<a tabindex="-1" href="#">Sequence</a>
|
||||
<ul class="dropdown-menu" name="sequence">
|
||||
<li><a href="#" class="js_go_to_top">Push on top</a></li>
|
||||
<li><a href="#" class="js_go_to_bottom">Push on bottom</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class='dropdown-submenu'>
|
||||
<a tabindex="-1" href="#">Styles</a>
|
||||
<ul class="dropdown-menu" name="style">
|
||||
<t t-foreach="styles" t-as="style">
|
||||
<li t-att-class="style.id in styles_used and 'active' or ''"><a href="#" t-att-data-value="style.id" t-att-data-class="style.html_class"><t t-esc="style.name"/></a></li>
|
||||
</t>
|
||||
</ul>
|
||||
</li>
|
||||
</t>
|
||||
</div><div class="col-sm-3 col-sm-offset-1">
|
||||
<form t-attf-action="/shop/#{ category_id and ('category/%s/' % category_id) or ''}" method="get" class="pull-right">
|
||||
<form t-action="/shop/#{ category_id and ('category/%s/' % category_id) or ''}" method="get" class="pull-right">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-search"/></span>
|
||||
<input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search or ''"/>
|
||||
@@ -222,7 +270,7 @@
|
||||
</div><div class="col-sm-5 col-md-5 col-lg-4 col-lg-offset-1">
|
||||
<h1 t-field="product.name">Product Name</h1>
|
||||
|
||||
<form action="./add_cart/">
|
||||
<form t-action="./add_cart/">
|
||||
<input type="hidden" t-if="len(product.product_variant_ids) <= 1" name="product_id" t-att-value="product.id"/>
|
||||
<t t-if="len(product.product_variant_ids) > 1">
|
||||
<label label-default="label-default" class="radio" t-foreach="product.product_variant_ids" t-as="product">
|
||||
@@ -236,11 +284,11 @@
|
||||
</t>
|
||||
<div>
|
||||
<t t-if="product.product_variant_ids[0].lst_price != product.product_variant_ids[0].price">
|
||||
<h5 class="text-error" style="text-decoration: line-through;" title="Price without reduction"><t t-field="product.product_variant_ids[0].lst_price" /> €</h5>
|
||||
<h5 class="text-error" style="text-decoration: line-through;" title="Price without reduction"><span t-field="product.product_variant_ids[0].lst_price" /> €</h5>
|
||||
<h4><t t-esc="product.product_variant_ids[0].price" /> €</h4>
|
||||
</t>
|
||||
<h4 t-if="product.product_variant_ids[0].lst_price == product.product_variant_ids[0].price">
|
||||
<b><t t-field="product.product_variant_ids[0].lst_price" /> €</b>
|
||||
<b><span t-field="product.product_variant_ids[0].lst_price" /> €</b>
|
||||
</h4>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-lg mt8">Add to Cart</button>
|
||||
@@ -271,7 +319,7 @@
|
||||
<div class='mt16 text-center'>
|
||||
<img t-att-src="product.img('image_small')"/>
|
||||
<h5>
|
||||
<a t-attf-href="/shop/product/#{ product.id }/"
|
||||
<a t-href="/shop/product/#{ product.id }/"
|
||||
style="display: block">
|
||||
<span t-field='product.name'
|
||||
style="display: block"/>
|
||||
@@ -320,10 +368,10 @@
|
||||
<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-attf-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/"><img class="img-rounded" t-att-src="line.product_id.img('image_small')"/></a>
|
||||
<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-attf-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/"><span t-field="line.name"/></a><br/>
|
||||
<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>
|
||||
@@ -335,8 +383,8 @@
|
||||
<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-attf-href="./remove_cart/?order_line_id=#{ line.id }" class="btn btn-default mb8 btn-sm btn-inverse">-</a>
|
||||
<a t-attf-href="./add_cart/?order_line_id=#{ line.id }" class="btn btn-default mb8 btn-sm btn-success">+</a>
|
||||
<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>
|
||||
@@ -356,7 +404,7 @@
|
||||
<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>
|
||||
</thead>
|
||||
</table>
|
||||
<a t-if="website_sale_order and website_sale_order.order_line" href="/shop/checkout/" class="btn btn-success">Proceed To Payment</a>
|
||||
<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>
|
||||
</div>
|
||||
<div class="oe_structure"/>
|
||||
@@ -370,7 +418,7 @@
|
||||
<xpath expr="//div[@id='products_grid']" position="before">
|
||||
<div class="col-md-3">
|
||||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<li t-att-class=" '' if category_id else 'active' "><a href="/shop/">All Products</a></li>
|
||||
<li t-att-class=" '' if category_id else 'active' "><a t-href="/shop/">All Products</a></li>
|
||||
<t t-set="categ" t-value="get_categories()"/>
|
||||
<t t-foreach="categ[0]" t-as="category">
|
||||
<t t-call="website_sale.categories_recursive"/>
|
||||
@@ -401,7 +449,7 @@
|
||||
<t t-foreach="suggested_products" t-as="product">
|
||||
<tr>
|
||||
<td>
|
||||
<a t-attf-href="/shop/product/#{ product.id }/"><span t-field="product.name"/></a><br/>
|
||||
<a t-href="/shop/product/#{ product.id }/"><span t-field="product.name"/></a><br/>
|
||||
<small t-field="product.description_sale"/>
|
||||
</td>
|
||||
<td>
|
||||
@@ -409,7 +457,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="pull-right">
|
||||
<a t-attf-href="./add_cart/?product_id=#{ product.id }" class="btn btn-sm btn-success">+</a>
|
||||
<a t-href="./add_cart/?product_id=#{ product.id }" class="btn btn-sm btn-success">+</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -426,11 +474,11 @@
|
||||
<t t-foreach="suggested_products" t-as="product">
|
||||
<div class='col-md-2 thumbnail'>
|
||||
<div class='mt16 text-center'>
|
||||
<a t-attf-href="/shop/product/#{ product.id }/">
|
||||
<a t-href="/shop/product/#{ product.id }/">
|
||||
<img t-att-src="product.img('image_small')"/>
|
||||
</a>
|
||||
<h5>
|
||||
<a t-attf-href="/shop/product/#{ product.id }/" style="display: block;">
|
||||
<a t-href="/shop/product/#{ product.id }/" style="display: block;">
|
||||
<span t-field="product.name"/>
|
||||
</a>
|
||||
</h5>
|
||||
@@ -443,7 +491,7 @@
|
||||
</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" action="/shop/mycart/" method="post">
|
||||
<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>
|
||||
</form>
|
||||
@@ -488,7 +536,7 @@
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<form class="col-md-8 form-horizontal" action="/shop/confirm_order/" method="post">
|
||||
<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&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>
|
||||
@@ -689,7 +737,7 @@
|
||||
<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 href="/shop/payment_validate/" class="hidden btn btn-default">I validate my payment</a>
|
||||
<a t-href="/shop/payment_validate/" class="hidden btn btn-default">I validate my payment</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,5 +12,55 @@
|
||||
<field name="state">open</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale.image_full" model="website.product.style">
|
||||
<field name="name">Image Full</field>
|
||||
<field name="html_class">oe_image_full</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale.size2x1" model="website.product.style">
|
||||
<field name="name">Size 2x1</field>
|
||||
<field name="html_class">size_2x1</field>
|
||||
</record>
|
||||
<record id="website_sale.size3x1" model="website.product.style">
|
||||
<field name="name">Size 3x1</field>
|
||||
<field name="html_class">size_3x1</field>
|
||||
</record>
|
||||
<record id="website_sale.size1x2" model="website.product.style">
|
||||
<field name="name">Size 1x2</field>
|
||||
<field name="html_class">size_1x2</field>
|
||||
</record>
|
||||
<record id="website_sale.size1x3" model="website.product.style">
|
||||
<field name="name">Size 1x3</field>
|
||||
<field name="html_class">size_1x3</field>
|
||||
</record>
|
||||
<record id="website_sale.size2x2" model="website.product.style">
|
||||
<field name="name">Size 2x2</field>
|
||||
<field name="html_class">size_2x2</field>
|
||||
</record>
|
||||
<record id="website_sale.size2x3" model="website.product.style">
|
||||
<field name="name">Size 2x3</field>
|
||||
<field name="html_class">size_2x3</field>
|
||||
</record>
|
||||
<record id="website_sale.size3x2" model="website.product.style">
|
||||
<field name="name">Size 3x2</field>
|
||||
<field name="html_class">size_3x2</field>
|
||||
</record>
|
||||
<record id="website_sale.size3x3" model="website.product.style">
|
||||
<field name="name">Size 3x3</field>
|
||||
<field name="html_class">size_3x3</field>
|
||||
</record>
|
||||
<record id="website_sale.size4x1" model="website.product.style">
|
||||
<field name="name">Size 4x1</field>
|
||||
<field name="html_class">size_4x1</field>
|
||||
</record>
|
||||
<record id="website_sale.size4x2" model="website.product.style">
|
||||
<field name="name">Size 4x2</field>
|
||||
<field name="html_class">size_4x2</field>
|
||||
</record>
|
||||
<record id="website_sale.size4x3" model="website.product.style">
|
||||
<field name="name">Size 4x3</field>
|
||||
<field name="html_class">size_4x3</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="0">
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="product.product_product_consultant" model="product.product">
|
||||
<field name="image" file="website/static/description/website_edit.png"/>
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
<record id="product.product_product_4" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_product_class">oe_image_full</field>
|
||||
<field name="website_sizex">6</field>
|
||||
<field name="website_sizey">4</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.size2x2')])]"/>
|
||||
<field name="website_description" type="html">
|
||||
<section snippet-id="text-image" class="mt16 mb16 dark">
|
||||
<div class="container">
|
||||
@@ -59,11 +57,11 @@
|
||||
</record>
|
||||
<record id="product.product_product_5" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_sizex">6</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.size2x1')])]"/>
|
||||
</record>
|
||||
<record id="product.product_product_6" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_product_class">oe_image_full</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full')])]"/>
|
||||
</record>
|
||||
<record id="product.product_product_7" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
@@ -76,7 +74,7 @@
|
||||
</record>
|
||||
<record id="product.product_product_11" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.size1x2')])]"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
|
||||
|
||||
class website_product_style(osv.Model):
|
||||
_name = "website.product.style"
|
||||
_columns = {
|
||||
'name' : fields.char('Style Name', required=True, translate=True),
|
||||
'html_class': fields.char('HTML Classes', size=64),
|
||||
}
|
||||
Reference in New Issue
Block a user