[MERGE] upstream
bzr revid: fme@openerp.com-20130926133155-y1fz603wa4oxw6g3
This commit is contained in:
commit
b6e1e1ec71
|
@ -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", multilang=True)
|
||||
@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", multilang=True)
|
||||
@website.route('/page/<path:path>', type='http', auth="public", multilang=True)
|
||||
def page(self, path, **kwargs):
|
||||
values = {
|
||||
'path': path,
|
||||
|
@ -303,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):
|
||||
|
|
|
@ -352,27 +352,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 {
|
||||
|
|
|
@ -267,23 +267,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
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
if (globalEditor) {
|
||||
globalEditor.open();
|
||||
} else {
|
||||
globalEditor = new website.ace.ViewEditor(this);
|
||||
globalEditor = new website.ace.ViewEditor();
|
||||
globalEditor.appendTo($(document.body));
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@
|
|||
method: 'read',
|
||||
args: [[viewId], ['arch'], website.get_context()],
|
||||
}).then(function(result) {
|
||||
var editingSession = self.buffers[viewId] = new ace.EditSession(result[0].arch);;
|
||||
var editingSession = self.buffers[viewId] = new ace.EditSession(result[0].arch);
|
||||
editingSession.setMode("ace/mode/xml");
|
||||
editingSession.setUndoManager(new ace.UndoManager());
|
||||
editingSession.on("change", function () {
|
||||
|
|
|
@ -118,19 +118,24 @@
|
|||
|
||||
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 ---- */
|
||||
|
|
|
@ -327,7 +327,7 @@
|
|||
self.suggestImprovements();
|
||||
self.imageList = new website.seo.ImageList(self, { page: htmlPage });
|
||||
if (htmlPage.images().length === 0) {
|
||||
$modal.find('.js_image_section').remove()
|
||||
$modal.find('.js_image_section').remove();
|
||||
} else {
|
||||
self.imageList.appendTo($modal.find('.js_seo_image_list'));
|
||||
}
|
||||
|
|
|
@ -22,6 +22,15 @@
|
|||
<meta name="openerp.company" t-att-value="res_company.name" />
|
||||
<meta name="description" value="" />
|
||||
<meta name="keywords" value="" />
|
||||
|
||||
<!-- Load stylesheets before scripts to avoid blocking -->
|
||||
<link rel='stylesheet' href='/web/static/lib/fontawesome/css/font-awesome.css'/>
|
||||
<t t-if="editable">
|
||||
<link rel='stylesheet' href='/website/static/src/css/snippets.css'/>
|
||||
<link rel='stylesheet' href='/website/static/src/css/editor.css'/>
|
||||
</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>
|
||||
|
@ -34,9 +43,6 @@
|
|||
<script type="text/javascript" src="/website/static/src/js/website.js"></script>
|
||||
|
||||
<t t-if="editable">
|
||||
<link rel='stylesheet' href='/website/static/src/css/snippets.css'/>
|
||||
<link rel='stylesheet' href='/website/static/src/css/editor.css'/>
|
||||
|
||||
<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 t-if="not translatable" type="text/javascript" src="/website/static/lib/ace/ace.js"></script>
|
||||
|
@ -55,9 +61,6 @@
|
|||
</t>
|
||||
|
||||
<t t-raw="head or ''"/>
|
||||
<t t-call="website.theme"/>
|
||||
|
||||
<link rel='stylesheet' href='/web/static/lib/fontawesome/css/font-awesome.css'/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapwrap">
|
||||
|
@ -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>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<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"/>
|
||||
|
@ -80,7 +80,7 @@
|
|||
<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"/>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
<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>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="partner"/></t>
|
||||
<a class="pull-left" t-attf-href="/partners/#{ partner.id }/">
|
||||
<img class="media-object" t-att-src="partner.img('image_small')"/>
|
||||
</a>
|
||||
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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"/>
|
||||
|
|
|
@ -30,7 +30,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>
|
||||
|
|
|
@ -12,8 +12,11 @@
|
|||
</template>
|
||||
|
||||
<template id="website.layout" inherit_id="website.layout" inherit_option_id="website.layout">
|
||||
<xpath expr="//head" position="inside">
|
||||
<!-- Load stylesheets before scripts to avoid blocking -->
|
||||
<xpath expr="//head/link" position="after">
|
||||
<link rel='stylesheet' href='/website_mail/static/src/css/website_mail.css'/>
|
||||
</xpath>
|
||||
<xpath expr="//head" position="inside">
|
||||
<script type="text/javascript" src="/website_mail/static/src/js/website_mail.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
</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>
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="partner"/></t>
|
||||
<a class="pull-left" t-attf-href="/members/#{ partner.id }/">
|
||||
<img class="media-object" t-att-src="partner.img('image_small')"/>
|
||||
</a>
|
||||
|
@ -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">
|
||||
|
|
|
@ -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'
|
||||
|
||||
def get_categories(self):
|
||||
domain = [('parent_id', '=', False)]
|
||||
|
||||
|
@ -68,6 +70,145 @@ class Ecommerce(http.Controller):
|
|||
|
||||
return (categories, full_category_ids)
|
||||
|
||||
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")
|
||||
def category(self, cat_id=0, page=0, **post):
|
||||
|
||||
|
@ -92,14 +233,18 @@ 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,
|
||||
}
|
||||
|
@ -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,6 +288,8 @@ 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)
|
||||
|
||||
|
@ -437,4 +594,41 @@ class Ecommerce(http.Controller):
|
|||
request.httprequest.session['ecommerce_pricelist'] = False
|
||||
return werkzeug.utils.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:
|
||||
product.write({'website_style_ids': [(3, rid) for rid in remove]})
|
||||
|
||||
if not active:
|
||||
product.write({'website_style_ids': [(4, style.id)]})
|
||||
|
||||
return not active
|
||||
|
||||
|
||||
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -37,16 +37,21 @@ class product_template(osv.Model):
|
|||
'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,
|
||||
}
|
||||
|
||||
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
|
|
@ -64,4 +64,25 @@ $(document).ready(function () {
|
|||
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>
|
||||
|
@ -81,6 +82,34 @@
|
|||
|
||||
<!-- Product list -->
|
||||
|
||||
<template id="products_cart">
|
||||
<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 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-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>
|
||||
</template>
|
||||
|
||||
<template id="products" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
|
@ -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">
|
||||
|
@ -167,8 +191,8 @@
|
|||
|
||||
<!-- 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">
|
||||
<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-attf-href="./add_cart/?product_id=#{ product.id }">
|
||||
<span class="icon-shopping-cart"/>
|
||||
</a>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -203,7 +234,24 @@
|
|||
<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">
|
||||
<div class="input-group">
|
||||
|
|
|
@ -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),
|
||||
}
|
Loading…
Reference in New Issue