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

bzr revid: bth@tinyerp.com-20130822051603-51qaw8rvdd0wjmf0
This commit is contained in:
bth-openerp
2013-08-22 10:46:03 +05:30
27 changed files with 1631 additions and 694 deletions
+29 -28
View File
@@ -289,6 +289,15 @@ class product_template(osv.osv):
_name = "product.template"
_description = "Product Template"
def _get_image(self, cr, uid, ids, name, args, context=None):
result = dict.fromkeys(ids, False)
for obj in self.browse(cr, uid, ids, context=context):
result[obj.id] = tools.image_get_resized_images(obj.image, avoid_resize_medium=True)
return result
def _set_image(self, cr, uid, id, name, value, args, context=None):
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
_columns = {
'name': fields.char('Name', size=128, required=True, translate=True, select=True),
'product_manager': fields.many2one('res.users','Product Manager'),
@@ -328,6 +337,26 @@ class product_template(osv.osv):
'mes_type': fields.selection((('fixed', 'Fixed'), ('variable', 'Variable')), 'Measure Type'),
'seller_ids': fields.one2many('product.supplierinfo', 'product_id', 'Supplier'),
'company_id': fields.many2one('res.company', 'Company', select=1),
# image: all image fields are base64 encoded and PIL-supported
'image': fields.binary("Image",
help="This field holds the image used as image for the product, limited to 1024x1024px."),
'image_medium': fields.function(_get_image, fnct_inv=_set_image,
string="Medium-sized image", type="binary", multi="_get_image",
store={
'product.product': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Medium-sized image of the product. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved, "\
"only when the image exceeds one of those sizes. Use this field in form views or some kanban views."),
'image_small': fields.function(_get_image, fnct_inv=_set_image,
string="Small-sized image", type="binary", multi="_get_image",
store={
'product.product': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Small-sized image of the product. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required."),
'product_variant_ids': fields.one2many('product.product', 'product_tmpl_id', 'Product Variants'),
}
def _get_uom_id(self, cr, uid, *args):
@@ -514,15 +543,6 @@ class product_product(osv.osv):
return result
def _get_image(self, cr, uid, ids, name, args, context=None):
result = dict.fromkeys(ids, False)
for obj in self.browse(cr, uid, ids, context=context):
result[obj.id] = tools.image_get_resized_images(obj.image, avoid_resize_medium=True)
return result
def _set_image(self, cr, uid, id, name, value, args, context=None):
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
def _get_name_template_ids(self, cr, uid, ids, context=None):
result = set()
template_ids = self.pool.get('product.product').search(cr, uid, [('product_tmpl_id', 'in', ids)])
@@ -567,25 +587,6 @@ class product_product(osv.osv):
}, select=True),
'color': fields.integer('Color Index'),
# image: all image fields are base64 encoded and PIL-supported
'image': fields.binary("Image",
help="This field holds the image used as image for the product, limited to 1024x1024px."),
'image_medium': fields.function(_get_image, fnct_inv=_set_image,
string="Medium-sized image", type="binary", multi="_get_image",
store={
'product.product': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Medium-sized image of the product. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved, "\
"only when the image exceeds one of those sizes. Use this field in form views or some kanban views."),
'image_small': fields.function(_get_image, fnct_inv=_set_image,
string="Small-sized image", type="binary", multi="_get_image",
store={
'product.product': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Small-sized image of the product. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required."),
'seller_info_id': fields.function(_calc_seller, type='many2one', relation="product.supplierinfo", string="Supplier Info", multi="seller_info"),
'seller_delay': fields.function(_calc_seller, type='integer', string='Supplier Lead Time', multi="seller_info", help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."),
'seller_qty': fields.function(_calc_seller, type='float', string='Supplier Quantity', multi="seller_info", help="This is minimum quantity to purchase from Main Supplier."),
+22 -8
View File
@@ -27,6 +27,10 @@ def auth_method_public():
http.auth_methods['public'] = auth_method_public
# PIL images have a type flag, but no MIME. Reverse type flag to MIME.
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):
@http.route('/', type='http', auth="admin")
def index(self, **kw):
@@ -45,7 +49,8 @@ class Website(openerp.addons.web.controllers.main.Home):
newview = view.browse(request.cr, request.uid, newview_id, context={})
newview.write({
'arch': newview.arch.replace("website.default_page", path),
'name': "page/%s" % path
'name': "page/%s" % path,
'page': True,
})
if '.' in path:
module, idname = path.split('.')
@@ -145,15 +150,20 @@ class Website(openerp.addons.web.controllers.main.Home):
buf = cStringIO.StringIO(base64.decodestring(attachment.datas))
image = Image.open(buf)
image.thumbnail((1024, 768), Image.ANTIALIAS)
mime = PIL_MIME_MAPPING[image.format]
response = werkzeug.wrappers.Response(status=200, mimetype={
'PNG': 'image/png',
'JPEG': 'image/jpeg',
'GIF': 'image/gif',
}[image.format])
w, h = image.size
resized = w > MAX_IMAGE_WIDTH or h > MAX_IMAGE_HEIGHT
# If saving unnecessary, just send the image buffer, don't go through
# Image.save() (especially as it breaks animated gifs)
if not resized:
buf.seek(0)
return werkzeug.wrappers.Response(buf, status=200, mimetype=mime)
image.thumbnail(IMAGE_LIMITS, Image.ANTIALIAS)
response = werkzeug.wrappers.Response(status=200, mimetype=mime)
image.save(response.stream, image.format)
return response
@http.route('/website/image', type='http', auth="public")
@@ -208,4 +218,8 @@ class Website(openerp.addons.web.controllers.main.Home):
return obj.website_published and "1" or "0"
@http.route(['/website/kanban/'], type='http', auth="public")
def kanban(self, **post):
return request.registry['website'].kanban_col(**post)
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
+91 -1
View File
@@ -1,4 +1,7 @@
@charset "utf-8";
.oe_hidden {
display: none !important;
}
.oe_website_editorbar {
position: fixed;
top: 0;
@@ -54,3 +57,90 @@
z-index: 1;
display: none;
}
.oe_snippets {
position: fixed;
left: 0px;
right: 0px;
bottom: 0px;
max-height: 280px;
min-height: 140px;
background: #282828;
box-shadow: 0px 10px 10px -10px black inset;
overflow-y: auto;
}
.oe_snippet {
display: inline-block;
vertical-align: top;
background: white;
width: 120px;
height: 120px;
border-radius: 3px;
margin: 10px;
margin-right: 0px;
cursor: move;
position: relative;
box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.2);
overflow: hidden;
-webkit-user-select: none;
user-select: none;
}
.oe_snippet .oe_snippet_thumbnail.oe_label {
text-align: center;
background: #747474;
background-image: radial-gradient(rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.4));
height: 100%;
line-height: 120px;
color: white;
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95);
}
.oe_snippet .oe_snippet_body {
display: none;
}
.oe_snippet > * {
pointer-events: none;
}
.oe_snippet > *:first-child:not(.oe_snippet_thumbnail) {
display: block;
-webkit-transform-origin-x: 5px;
-webkit-transform-origin-y: 10px;
-webkit-transform: scale(0.095);
}
.oe_drop_zone {
display: block;
background: rgba(153, 0, 255, 0.17);
border-radius: 3px;
height: 32px;
margin: -16px 0px;
-webkit-transition: margin 250ms linear;
}
.oe_drop_zone:first-child:not(:last-child) {
margin-top: -8px;
margin-bottom: -24px;
}
.oe_drop_zone:last-child:not(:first-child) {
margin-top: -24px;
margin-bottom: -8px;
}
.oe_drop_zone.oe_hover {
background: rgba(0, 255, 133, 0.22);
z-index: 100000;
}
.oe_drop_zone:before {
content: "";
display: block;
border-top: dashed 2px rgba(209, 178, 255, 0.72);
position: relative;
top: 16px;
}
.oe_drop_zone:first-child:not(:last-child):before {
top: 8px;
}
.oe_drop_zone:last-child:not(:first-child):before {
top: 24px;
}
.oe_drop_zone.oe_hover:before {
border-top: dashed 2px rgba(116, 255, 161, 0.72);
}
+82
View File
@@ -1,5 +1,8 @@
@charset "utf-8"
.oe_hidden
display: none !important
.oe_website_editorbar
position: fixed
top: 0
@@ -47,3 +50,82 @@
white-space: nowrap
z-index: 1
display: none
.oe_snippets
position: fixed
left: 0px
right: 0px
bottom: 0px
max-height: 280px
min-height: 140px
background: rgb(40,40,40)
box-shadow: 0px 10px 10px -10px black inset
overflow-y: auto
.oe_snippet
display: inline-block
vertical-align: top
background: white
width: 120px
height: 120px
border-radius: 3px
margin: 10px
margin-right: 0px
cursor: move
position: relative
box-shadow: 0px 3px 10px rgba(0,0,0,0.2)
overflow: hidden
-webkit-user-select: none
user-select: none
.oe_snippet_thumbnail.oe_label
text-align: center
background: rgb(116, 116, 116)
background-image: radial-gradient(rgba(0,0,0,0.25),rgba(0,0,0,0.4))
height: 100%
line-height: 120px
color: white
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.95)
.oe_snippet_body
display: none
& > *
pointer-events: none
& > *:first-child:not(.oe_snippet_thumbnail)
display: block
-webkit-transform-origin-x: 5px
-webkit-transform-origin-y: 10px
-webkit-transform: scale(0.095)
.oe_drop_zone
display: block
background: rgba(153, 0, 255, 0.17)
border-radius: 3px
height: 32px
margin: -16px 0px
-webkit-transition: margin 250ms linear
&:first-child:not(:last-child)
margin-top: -8px
margin-bottom: -24px
&:last-child:not(:first-child)
margin-top: -24px
margin-bottom: -8px
&.oe_hover
background: rgba(0, 255, 133, 0.22)
z-index: 100000
&:before
content: ""
display: block
border-top: dashed 2px rgba(209, 178, 255, 0.72)
position: relative
top: 16px
&:first-child:not(:last-child):before
top: 8px
&:last-child:not(:first-child):before
top: 24px
&.oe_hover:before
border-top: dashed 2px rgba(116, 255, 161, 0.72)
File diff suppressed because it is too large Load Diff
@@ -57,4 +57,10 @@
</div>
</div>
</t>
<t t-name="website.snippets">
<div class='oe_snippets oe_hidden'>
</div>
</t>
</templates>
+233 -122
View File
@@ -6,12 +6,12 @@
<!--
Files used in the generic theme, mostly bootstrap and a few OpenERP tags
-->
<template id="theme" name="Theme">
<template id="website.theme" name="Theme">
<link id="bootstrap_css" rel='stylesheet' href='/website/static/lib/bootstrap/css/bootstrap.css' t-ignore="true"/>
<link id="website_css" rel='stylesheet' href='/website/static/src/css/website.css' t-ignore="true"/>
</template>
<template id="theme_flatly" name="Flat Theme" inherit_option_id="website.theme">
<template id="website.theme_flatly" name="Flat Theme" inherit_option_id="website.theme">
<xpath expr="//link[@id='bootstrap_css']" position="replace">
<link rel='stylesheet' href='/website/static/lib/bootstrap/css/bootstrap-flatly.css' t-ignore="true"/>
<link rel='stylesheet' href='/website/static/src/css/website-flatly.css' t-ignore="true"/>
@@ -27,22 +27,22 @@
<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>
<script type="text/javascript" src="/website/static/lib/bootstrap/js/bootstrap.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/ckeditor/4.2/ckeditor.js"></script>
<script type="text/javascript" src="/website/static/lib/ckeditor.sharedspace/plugin.js"></script>
<script type="text/javascript">
CKEDITOR.disableAutoInline = true;
// EDIT ALL THE THINGS
CKEDITOR.dtd.$editable = $.extend(
{}, CKEDITOR.dtd.$block, CKEDITOR.dtd.$inline);
</script>
<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
<script type="text/javascript" src="/web/static/src/js/openerpframework.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="//cdnjs.cloudflare.com/ajax/libs/ckeditor/4.2/ckeditor.js"></script>
<script type="text/javascript" src="/website/static/lib/ckeditor.sharedspace/plugin.js"></script>
<script type="text/javascript">
CKEDITOR.disableAutoInline = true;
// EDIT ALL THE THINGS
CKEDITOR.dtd.$editable = $.extend( {}, CKEDITOR.dtd.$block, CKEDITOR.dtd.$inline);
</script>
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
<script type="text/javascript" src="/web/static/src/js/openerpframework.js"></script>
</t>
<script type="text/javascript" src="/website/static/src/js/website.js"></script>
@@ -122,107 +122,6 @@
</div>
</footer>
<div class='oe_snippet_editor' style="display: none;">
<div class='oe_snippet_list'>
<div class='oe_snippet' name='Picture' data-selector='[class*="span"]'>
<div class="span6">
<img class="img-rounded" src="/website/static/src/img/volcano.jpg"/>
</div>
</div>
<div class='oe_snippet' name='Introduction' data-selector=".container">
<section class="container">
<div class="row mt32 text-center">
<h2>Wildlife Analysis</h2>
<h3>In a beautiful place out in the country</h3>
</div>
<div class="row mt32">
<div class="span6">
<img class='img-rounded' src="/website/static/src/img/china.jpg"/>
</div>
<div class="span6">
<p class='mt16'>
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum
</p>
</div>
</div>
</section>
</div>
<div class='oe_snippet' name='Three Columns' data-selector=".container">
<section class="container">
<div class="row mt32">
<div class="span4">
<h4>Niagara</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</div>
<div class="span4">
<h4>Whitewater</h4>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum
</p>
</div>
<div class="span4">
<h4>Red Moss</h4>
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
quae ab illo inventore veritatis et quasi architecto beatae vitae
dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas
</p>
</div>
</div>
</section>
</div>
<div class='oe_snippet' name='Introduction' data-selector=".container">
<section class="container">
<div class="row mt32">
<div class="span6">
<h2>Wildlife Analysis</h2>
<h3>In a beautiful place out in the country</h3>
<p class='mt16'>
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum
</p>
</div>
<div class="span6">
<img class='img-rounded' src="/website/static/src/img/greenfields.jpg"/>
</div>
</div>
</section>
</div>
<div class='oe_snippet oe_new' name='New Snippet'>
</div>
</div>
</div>
</body>
</html>
</template>
@@ -233,7 +132,7 @@
</xpath>
</template>
<template id="homepage">
<template id="homepage" page="True">
<t t-call="website.layout">
<section>
@@ -355,7 +254,7 @@
<template id="404">
<t t-call="website.layout">
<div class="container" t-if="editable">
<div class="container" t-if="editable and path">
<div class="well mt32">
<p>
This page does not exists, but you can create
@@ -419,7 +318,7 @@
</template>
<template id="contactus" name="Contact us">
<template id="contactus" name="Contact us" page="True">
<t t-call="website.layout">
<t t-set="title">Contact us - <t t-raw="res_company.name"/></t>
<div class="container">
@@ -454,7 +353,7 @@
</t>
</template>
<template id="aboutus">
<template id="aboutus" page="True">
<t t-call="website.layout">
<t t-set="title">About us - <t t-raw="res_company.name"/></t>
<div class="container">
@@ -481,9 +380,9 @@
<li t-att-class=" 'disabled' if pager['page']['num'] == 1 else '' " >
<a t-att-href=" pager['page_start']['url'] if pager['page']['num'] != 1 else '' ">Prev</a>
</li>
<t t-foreach="pager['pages']">
<li t-att-class=" 'active' if num == pager['page']['num'] else '' ">
<a t-att-href="url" t-raw="num"></a>
<t t-foreach="pager['pages']" t-as="page">
<li t-att-class=" 'active' if page['num'] == pager['page']['num'] else '' ">
<a t-att-href="page['url']" t-raw="page['num']"></a>
</li>
</t>
<li t-att-class=" 'disabled' if pager['page']['num'] == pager['page_count'] else '' " >
@@ -495,10 +394,222 @@
<template id="publish">
<a href="#" t-att-data-id="object.id" t-att-data-object="object._name" class="pull-right" t-if="editable" t-ignore="true">
<span t-att-class="'label label-success js_unpublish %%s' %% (not object.website_published and 'hidden' or '')">Unpublish</span>
<span t-att-class="'label label-important js_publish %%s' %% (object.website_published and 'hidden' or '')">Publish</span>
<span t-attf-class="label label-success js_unpublish #{ '' if object.website_published else 'hidden' }">Unpublish</span>
<span t-attf-class="label label-important js_publish #{ 'hidden' if object.website_published else '' }">Publish</span>
</a>
</template>
<template id="snippets">
<div class='oe_snippet' name='Spacer' data-selector-siblings='#wrap .container'>
<div class='oe_snippet_thumbnail oe_label'>Spacer</div>
<div class="oe_snippet_body container mt32"></div>
</div>
<div class='oe_snippet' name='FooBar' data-selector-siblings='#wrap .container'>
<div class='oe_snippet_thumbnail oe_label'>
Main Title
</div>
<section class="oe_snippet_body container">
<div class="row">
<div class="span12 text-center">
<h1 class='mt64'>Welcome To Your New Homepage,</h1>
<h2 class='mt32 subheader'>where <b><em>everything</em></b> can be customized </h2>
</div>
</div>
</section>
</div>
<div class='oe_snippet' name='BarFoo' data-selector-siblings='#wrap .container'>
<section class="oe_snippet_body container">
<div class="row">
<div class="span12 text-center">
<h2 class='mt48'>Ut enim ad minim veniam</h2>
</div>
</div>
<div class="row mt32">
<div class="span4">
<img class='img-rounded' src="/website/static/src/img/china_thumb.jpg"/>
<h4 class='mt16'>Cillum Dolore</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</div>
<div class="span4">
<img class='img-rounded' src="/website/static/src/img/desert_thumb.jpg"/>
<h4 class='mt16'>Mollit Anim</h4>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum
</p>
</div>
<div class="span4">
<img class='img-rounded' src="/website/static/src/img/deers_thumb.jpg"/>
<h4 class='mt16'>Nemo Enim Ipsam</h4>
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
quae ab illo inventore veritatis et quasi architecto beatae vitae
dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas
</p>
</div>
</div>
</section>
</div>
<div class='oe_snippet' name='Header' data-selector-siblings='#wrap .container'>
<section class="container oe_snippet_body">
<div class="row">
<div class="span5">
<img class='img-rounded' src="/website/static/src/img/island.jpg"/>
</div>
<div class="span6 offset1">
<p class='mt16'>
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum
</p>
<div class="text-center">
<a href="http://www.openerp.com/start" class="btn mt16 btn-large btn-primary">Call To <em>Action</em></a>
</div>
</div>
</div>
</section>
</div>
</template>
<template id="kanban">
<t t-set="step"><t t-esc="step or 0"/></t>
<t t-set="scope"><t t-esc="scope or 0"/></t>
<t t-set="orderby"><t t-esc="orderby or 'name'"/></t>
<t t-raw="snipped['kanban'](model, domain, column, content, step=step, scope=scope, orderby=orderby)"/>
</template>
<template id="kanban_contain">
<table class="table">
<thead>
<tr>
<t t-set="width" t-value="'%%s%%%%' % (100/len(objects))"/>
<t t-foreach="objects">
<th t-att-width="width">
<div t-field="column_id.name" class="text-center"/>
</th>
</t>
</tr>
</thead>
<tbody>
<tr>
<t t-foreach="objects">
<t t-set="pager">
<div t-if="1 != page_end" class="pagination pagination-centered">
<ul>
<li t-attf-class="prev #{'active' if page == 1 else '' }" >
<a t-att-href=" '%s,%s-%s' % (kanban_url, column_id.id, (page &gt; 1 and page-1 or 1)) ">Prev</a>
</li>
<t t-foreach="range(page_start, page_end+1)" t-as="p">
<li t-att-class=" 'active' if page == p else '' ">
<a t-att-href=" '%s,%s-%s' % (kanban_url, column_id.id, p)" t-esc="p"/>
</li>
</t>
<li t-attf-class="next #{'active' if page == page_end else '' }" >
<a t-att-href=" '%s,%s-%s' % (kanban_url, column_id.id, (page &lt; page_end and page+1 or page_end) )">Next</a>
</li>
</ul>
</div>
</t>
<td class="js_kanban_col"
t-att-data-content="content"
t-att-data-domain="domain"
t-att-data-page_count="page_count"
t-att-data-model="model"
t-att-data-step="step"
t-att-data-orderby="orderby">
<t t-raw="pager"/>
<div t-foreach="object_ids" t-as="object_id" class="thumbnail">
<t t-call="website_project.kanban_card"/>
</div>
<t t-raw="pager"/>
</td>
</t>
</tr>
</tbody>
</table>
<script>
$('.js_kanban_col').each(function () {
var $col = $(this);
var $pagination = $('.pagination', $col);
if(!$pagination.size()) {
return;
}
var page_count = $col.data('page_count');
var scope = $pagination.first().find("li").size()-2;
var kanban_url_col = $pagination.find("li a:first").attr("href").replace(/[0-9]+$/, '');
var data = {
'domain': $col.data('domain'),
'model': $col.data('model'),
'content': $col.data('content'),
'step': $col.data('step'),
'orderby': $col.data('orderby')
};
$pagination.on('click', 'a', function (ev) {
ev.preventDefault();
var $a = $(ev.target);
if($a.hasClass('active')) {
return;
}
var page = +$a.attr("href").split(",").pop().split('-')[1];
data['page'] = page;
$.post('/website/kanban/', data, function (col) {
$col.find("&gt; .thumbnail").remove();
$pagination.first().after('<div class="thumbnail">'+col+'</div>');
});
var page_start = page - parseInt(Math.floor((scope-1)/2));
if (page_start &lt; 1 ) page_start = 1;
var page_end = page_start + (scope-1);
if (page_end &gt; page_count ) page_end = page_count;
if (page_end - page_start &lt; scope) {
page_start = page_end - scope &gt; 0 ? page_end - scope : 1;
}
$pagination.find('li.prev a').attr("href", kanban_url_col+(page-1 > 0 ? page-1 : 1));
$pagination.find('li.next a').attr("href", kanban_url_col+(page+1));
for(var i=0; i &lt; scope; i++) {
$pagination.find('li:not(.prev):not(.next):eq('+i+') a').attr("href", kanban_url_col+(page_start+i)).html(page_start+i);
}
$pagination.find('li.active').removeClass('active');
$pagination.find('li:has(a[href="'+kanban_url_col+page+'"])').addClass('active');
});
});
</script>
</template>
</data>
</openerp>
+127 -26
View File
@@ -9,6 +9,7 @@ from openerp.addons.web.http import request
import urllib
import math
import traceback
from openerp.tools.safe_eval import safe_eval
import logging
logger = logging.getLogger(__name__)
@@ -63,47 +64,49 @@ class website(osv.osv):
'host_url': request.httprequest.host_url,
'res_company': request.registry['res.company'].browse(request.cr, openerp.SUPERUSER_ID, 1),
'json': simplejson,
'snipped': {
'kanban': self.kanban
}
}
if additional_values:
values.update(additional_values)
return values
def render(self, template, values={}):
view = request.registry.get("ir.ui.view")
context = {
'inherit_branding': values.get('editable', False),
}
try:
return request.registry.get("ir.ui.view").render(request.cr, request.uid, template, values, context=context)
except (osv.except_osv, orm.except_orm), err:
logger.error(err)
values['error'] = err[1]
return self.render('website.401', values)
except ValueError:
logger.error("Website Rendering Error.\n\n%s" % (traceback.format_exc()))
return self.render('website.404', values)
return view.render(request.cr, request.uid, template, values, context=context)
# except (osv.except_osv, orm.except_orm), err:
# logger.error(err)
# values['error'] = err[1]
# return self.render('website.401', values)
# except ValueError:
# logger.error("Website Rendering Error.\n\n%s" % (traceback.format_exc()))
# return self.render('website.404', values)
except Exception:
logger.error("Website Rendering Error.\n\n%s" % (traceback.format_exc()))
trace = traceback.format_exc()
logger.error("Website Rendering Error.\n\n%s" % trace)
if values['editable']:
values['traceback'] = traceback.format_exc()
return self.render('website.500', values)
values['traceback'] = trace
return view.render(request.cr, request.uid, 'website.500', values, context=context)
else:
return self.render('website.404', values)
return view.render(request.cr, request.uid, 'website.404', values, context=context)
def pager(self, url, total, page=1, step=30, scope=5, url_args=None):
# Compute Pager
d = {}
d["page_count"] = int(math.ceil(float(total) / step))
page_count = int(math.ceil(float(total) / step))
page = max(1, min(int(page), d["page_count"]))
d["offset"] = (page-1) * step
page = max(1, min(int(page), page_count))
scope -= 1
pmin = max(page - int(math.floor(scope/2)), 1)
pmax = min(pmin + scope, d["page_count"])
pmax = min(pmin + scope, page_count)
if pmax - pmin < scope:
pmin = pmax - scope > 0 and pmax - scope or 1
pmin = pmax - scope if pmax - scope > 0 else 1
def get_url(page):
_url = "%spage/%s/" % (url, page)
@@ -111,15 +114,113 @@ class website(osv.osv):
_url = "%s?%s" % (_url, urllib.urlencode(url_args))
return _url
d["page"] = {'url': get_url(page), 'num': page}
d["page_start"] = {'url': get_url(pmin), 'num': pmin}
d["page_end"] = {'url': get_url(min(pmax, page+1)), 'num': min(pmax, page+1)}
d["pages"] = []
for page in range(pmin, pmax+1):
d["pages"].append({'url': get_url(page), 'num': page})
return {
"page_count": page_count,
"offset": (page - 1) * step,
"page": {'url': get_url(page), 'num': page},
"page_start": {'url': get_url(pmin), 'num': pmin},
"page_end": {'url': get_url(min(pmax, page + 1)),
'num': min(pmax, page + 1)},
"pages": [
{'url': get_url(page), 'num': page}
for page in xrange(pmin, pmax+1)
]
}
return d
def list_pages(self, cr, uid, context=None):
""" Available pages in the website/CMS. This is mostly used for links
generation and can be overridden by modules setting up new HTML
controllers for dynamic pages (e.g. blog).
By default, returns template views marked as pages.
:returns: a list of mappings with two keys: ``name`` is the displayable
name of the resource (page), ``url`` is the absolute URL
of the same.
:rtype: list({name: str, url: str})
"""
View = self.pool['ir.ui.view']
views = View.search_read(cr, uid, [['page', '=', True]],
fields=['name'], order='name', context=context)
xids = View.get_external_id(cr, uid, [view['id'] for view in views], context=context)
return [
{'name': view['name'], 'url': '/page/' + xids[view['id']]}
for view in views
if xids[view['id']]
]
def kanban(self, model, domain, column, content, step=None, scope=None, orderby=None):
step = step and int(step) or 10
scope = scope and int(scope) or 5
orderby = orderby or "name"
get_args = dict(request.httprequest.args or {})
model_obj = request.registry[model]
relation = model_obj._columns.get(column)._obj
relation_obj = request.registry[relation]
get_args.setdefault('kanban', "")
kanban = get_args.pop('kanban')
kanban_url = "?%s&kanban=" % urllib.urlencode(get_args)
pages = {}
for col in kanban.split(","):
if col:
col = col.split("-")
pages[int(col[0])] = int(col[1])
objects = []
for group in model_obj.read_group(request.cr, request.uid, domain, ["id", column], groupby=column):
obj = {}
# browse column
relation_id = group[column][0]
obj['column_id'] = relation_obj.browse(request.cr, request.uid, relation_id)
obj['kanban_url'] = kanban_url
for k, v in pages.items():
if k != relation_id:
obj['kanban_url'] += "%s-%s" % (k, v)
# pager
number = model_obj.search(request.cr, request.uid, group['__domain'], count=True)
obj['page'] = pages.get(relation_id) or 1
obj['page_count'] = int(math.ceil(float(number) / step))
offset = (obj['page']-1) * step
obj['page_start'] = max(obj['page'] - int(math.floor((scope-1)/2)), 1)
obj['page_end'] = min(obj['page_start'] + (scope-1), obj['page_count'])
# view data
obj['domain'] = group['__domain']
obj['model'] = model
obj['step'] = step
obj['orderby'] = orderby
# browse objects
object_ids = model_obj.search(request.cr, request.uid, group['__domain'], limit=step, offset=offset, order=orderby)
obj['object_ids'] = model_obj.browse(request.cr, request.uid, object_ids)
objects.append(obj)
values = self.get_rendering_context({
'objects': objects,
'range': range,
'content': content,
})
return self.render("website.kanban_contain", values)
def kanban_col(self, model, domain, page, content, step, orderby):
html = ""
model_obj = request.registry[model]
domain = safe_eval(domain)
step = int(step)
offset = (int(page)-1) * step
object_ids = model_obj.search(request.cr, request.uid, domain, limit=step, offset=offset, order=orderby)
object_ids = model_obj.browse(request.cr, request.uid, object_ids)
for object_id in object_ids:
html += self.render(content, self.get_rendering_context({'object_id': object_id}))
return html
class res_partner(osv.osv):
_inherit = "res.partner"
+2
View File
@@ -0,0 +1,2 @@
import controllers
import website_contract
+18
View File
@@ -0,0 +1,18 @@
{
'name': 'Public References',
'category': 'Website',
'summary': 'Publish Your Public References',
'version': '1.0',
'description': """
OpenERP Blog
============
""",
'author': 'OpenERP SA',
'depends': ['website', 'sale'],
'data': [
'views/website_contract.xml',
],
'qweb': ['static/src/xml/*.xml'],
'installable': True,
}
@@ -0,0 +1 @@
import main
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
import openerp
from openerp.addons.web import http
from openerp.addons.web.http import request
class website_contract(http.Controller):
@http.route(['/references/'], type='http', auth="public")
def blog(self, **post):
website = request.registry['website']
partner_obj = request.registry['res.partner']
account_obj = request.registry['account.analytic.account']
# public partner profile
project_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, [('website_testimonial', "!=", False), ('website_published', '=', True)])
if request.uid != website.get_public_user().id:
contract_ids = account_obj.search(request.cr, openerp.SUPERUSER_ID, [(1, "=", 1)])
contract_project_ids = [contract.partner_id.id
for contract in account_obj.browse(request.cr, openerp.SUPERUSER_ID, contract_ids) if contract.partner_id]
# search for check access rules
project_ids += partner_obj.search(request.cr, request.uid, [('id', "in", contract_project_ids)])
project_ids = list(set(project_ids))
values = website.get_rendering_context({
'partner_ids': partner_obj.browse(request.cr, openerp.SUPERUSER_ID, project_ids)
})
return website.render("website_contract.index", values)
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//body/footer//a[@href='/page/website.aboutus']" position="after">
<li><a href="/references/">References</a></li>
</xpath>
</template>
<!-- Page -->
<template id="index" name="Projects">
<t t-call="website.layout">
<t t-set="title">References</t>
<div class="container">
<div class="thumbnails grid grid-align-top">
<div t-foreach="partner_ids" t-as="partner" class="span6">
<div class="media thumbnail">
<a class="pull-left" href="#">
<img class="media-object" t-att-src="partner.img('image_small')"/>
</a>
<div class="media-body">
<t t-call="website.publish"><t t-set="object" t-value="partner"/></t>
<h5 class="media-heading" t-field="partner.name"/>
<pre t-field="partner.website_testimonial"/>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
</data>
</openerp>
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from openerp.osv import osv, fields
class hr(osv.osv):
_inherit = 'res.partner'
_columns = {
'website_published': fields.boolean('Available in the website'),
'website_testimonial': fields.text('Recommandation'),
}
def img(self, cr, uid, ids, field='image_small', context=None):
return "/website/image?model=%s&field=%s&id=%s" % (self._name, field, ids[0])
+26 -26
View File
@@ -15,7 +15,7 @@
<!-- Page -->
<template id="index" name="Events">
<template id="index" name="Events" page="True">
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_hr/static/src/js/website_hr.js"></script>
@@ -29,7 +29,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-att-href="'/event/%%s&amp;date=%%s' %% (search_path, date[0])"><t t-esc="date[1]"/> <small t-if="date[3]">(<t t-esc="date[3]"/>)</small></a>
<a t-attf-href="/event/#{ search_path }&amp;date=#{ date[0] }"><t t-esc="date[1]"/> <small t-if="date[3]">(<t t-esc="date[3]"/>)</small></a>
</li>
</t>
</ul>
@@ -43,33 +43,33 @@
</div>
<div class="span8">
<ul class="media-list">
<li t-foreach="event_ids" t-as="event_id" class="media thumbnail">
<li t-foreach="event_ids" t-as="event" class="media thumbnail">
<div class="media-body">
<span t-if="not event_id.event_ticket_ids" class="label pull-right">No tickets needed.</span>
<t t-if="event_id.event_ticket_ids">
<span t-if="event_id.register_avail == 9999" class="label pull-right label-info">Tickets availables.</span>
<span t-if="not event_id.register_avail" class="label pull-right">No ticket available.</span>
<span t-if="event_id.register_avail and event_id.register_avail != 9999" t-att-class="'label pull-right label-%%s' %% (event_id.register_avail &lt;= 10 and 'warning' or 'info')">
<t t-if="event_id.register_avail &lt;= 10">Only</t>
<t t-esc="event_id.register_avail"/>
<t t-if="event_id.register_avail &gt; 1">tickets </t>
<t t-if="event_id.register_avail == 1">ticket </t>
<span t-if="not event.event_ticket_ids" class="label pull-right">No tickets needed.</span>
<t t-if="event.event_ticket_ids">
<span t-if="event.register_avail == 9999" class="label pull-right label-info">Tickets availables.</span>
<span t-if="not event.register_avail" class="label pull-right">No ticket available.</span>
<span t-if="event.register_avail and event.register_avail != 9999" t-attf-class="label pull-right label-#{ event.register_avail &lt;= 10 and 'warning' or 'info' }">
<t t-if="event.register_avail &lt;= 10">Only</t>
<t t-esc="event.register_avail"/>
<t t-if="event.register_avail &gt; 1">tickets</t>
<t t-if="event.register_avail == 1">ticket</t>
available.
</span>
</t>
<h4 class="media-heading"><a t-att-href="'/event/%%s/' %% event_id.id"><span t-field="event_id.name"> </span></a></h4>
<t t-call="website.publish"><t t-set="object" t-value="event_id"/></t>
<h4 class="media-heading"><a t-attf-href="/event/#{ event.id }/"><span t-field="event.name"> </span></a></h4>
<t t-call="website.publish"><t t-set="object" t-value="event"/></t>
<div>
<span t-field="event_id.type">: </span>
<t t-if="event_id.user_id">
Organized by: <span t-field="event_id.user_id"> </span>
<span t-field="event.type">: </span>
<t t-if="event.user_id">
Organized by: <span t-field="event.user_id"> </span>
</t>
</div>
<div>
<i class="icon-time"></i> <span t-field="event_id.date_begin"> </span> <i>to</i> <span t-field="event_id.date_end"> </span>
<i class="icon-time"></i> <span t-field="event.date_begin"> </span> <i>to</i> <span t-field="event.date_end"> </span>
</div>
<div t-if="event_id.country_id">
<i class="icon-map-marker"></i> <span t-field="event_id.city"> </span> <span t-field="event_id.zip"> </span>, <span t-field="event_id.country_id"> </span>
<div t-if="event.country_id">
<i class="icon-map-marker"></i> <span t-field="event.city"> </span> <span t-field="event.zip"> </span>, <span t-field="event.country_id"> </span>
</div>
</div>
</li>
@@ -89,7 +89,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-att-href="'/event/%%s&amp;type=%%s' %% (search_path, type[0])"><t t-esc="type[1]"/> <small>(<t t-esc="type_count"/>)</small></a>
<a t-attf-href="/event/#{ search_path }&amp;type=#{ type[0] }"><t t-esc="type[1]"/> <small>(<t t-esc="type_count"/>)</small></a>
</li>
</t>
</ul>
@@ -101,7 +101,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-att-href="'/event/%%s&amp;country=%%s' %% (search_path, country_id[0])"><t t-esc="country_id[1]"/><small>(<t t-esc="country_id_count"/>)</small></a>
<a t-attf-href="/event/#{ search_path }&amp;country=#{ country_id[0] }"><t t-esc="country_id[1]"/><small>(<t t-esc="country_id_count"/>)</small></a>
</li>
</t>
</ul>
@@ -138,7 +138,7 @@
<t t-if="event_id.event_ticket_ids">
<span t-if="event_id.register_avail == 9999" class="label pull-right">Tickets availables.</span>
<span t-if="not event_id.register_avail" class="label pull-right">No ticket available.</span>
<span t-if="event_id.register_avail and event_id.register_avail != 9999" t-att-class="'label pull-right label-%%s' %% (event_id.register_avail &lt;= 10 and 'warning' or 'info')">
<span t-if="event_id.register_avail and event_id.register_avail != 9999" t-attf-class="label pull-right label-#{ event_id.register_avail &lt;= 10 and 'warning' or 'info' }">
<t t-if="event_id.register_avail &lt;= 10">Only</t>
<t t-esc="event_id.register_avail"/>
<t t-if="event_id.register_avail &gt; 1">tickets </t>
@@ -146,7 +146,7 @@
available.
</span>
</t>
<h4 class="media-heading"><a t-att-href="'/event/%%s/' %% event_id.id"><span t-field="event_id.name"> </span></a></h4>
<h4 class="media-heading"><a t-attf-href="/event/#{ event_id.id }/"><span t-field="event_id.name"> </span></a></h4>
<t t-call="website.publish"><t t-set="object" t-value="event_id"/></t>
<h5 class="media-heading">
<span t-field="event_id.address_id"/><br/>
@@ -154,7 +154,7 @@
<i class="icon-time"></i> <span t-field="event_id.date_end"/>
</h5>
<h4 t-if="event_id.event_ticket_ids">Ticket Information</h4>
<form t-att-action="'/event/%%s/add_cart' %% event_id.id" method="post" t-if="event_id.event_ticket_ids">
<form t-attf-action="/event/#{ event_id.id }/add_cart" method="post" t-if="event_id.event_ticket_ids">
<table class="table">
<thead>
<tr>
@@ -172,7 +172,7 @@
<td t-field="ticket.price"/>
<td><span t-if="ticket.register_max"><t t-esc="ticket.register_current"/>/<t t-field="ticket.register_max"/></span></td>
<td>
<select t-if="ticket.register_avail" t-att-name="'ticket-%%s' %% ticket.id">
<select t-if="ticket.register_avail" t-attf-name="ticket- #{ ticket.id }">
<t t-foreach="range(0, ticket.register_avail > 9 and 10 or ticket.register_avail+1 )" t-as="nb"><option t-esc="nb"/></t>
</select>
<span t-if="not ticket.register_avail">Not ticket available</span>
+10 -10
View File
@@ -29,20 +29,20 @@
</t>
<h3>Our team</h3>
<div class="thumbnails">
<div t-foreach="employee_ids" t-as="employee_id" class="span4 mt16">
<div t-foreach="employee_ids" t-as="employee" class="span4 mt16">
<div class="media thumbnail">
<a class="pull-left" href="#">
<img class="media-object" t-att-src="employee_id.img('image_small')"/>
<img class="media-object" t-att-src="employee.img('image_small')"/>
</a>
<div class="media-body">
<t t-call="website.publish"><t t-set="object" t-value="employee_id"/></t>
<h5 class="media-heading" t-field="employee_id.name"/>
<div t-field="employee_id.department_id"/>
<div t-field="employee_id.job_id"/>
<div t-field="employee_id.work_location"/>
<div t-field="employee_id.work_phone"/>
<div t-field="employee_id.work_email"/>
<div t-field="employee_id.public_info"/>
<t t-call="website.publish"><t t-set="object" t-value="employee"/></t>
<h5 class="media-heading" t-field="employee.name"/>
<div t-field="employee.department_id"/>
<div t-field="employee.job_id"/>
<div t-field="employee.work_location"/>
<div t-field="employee.work_phone"/>
<div t-field="employee.work_email"/>
<div t-field="employee.public_info"/>
</div>
</div>
</div>
+10 -12
View File
@@ -16,16 +16,14 @@
<xpath expr="//body/header//ul[@id='top_menu']/li[last()]" position="before">
<li><a href="/blog/%(website_mail.website_mail_blog)d/">News</a></li>
</xpath>
<xpath expr="//body/footer//h5[@name='info_title']" position="after">
<ul class='unstyled'>
<li><a href="/blog/%(website_mail.website_mail_blog)d/">News</a></li>
</ul>
<xpath expr="//body/footer//div[@name='info']/ul" position="inside">
<li><a href="/blog/%(website_mail.website_mail_blog)d/">News</a></li>
</xpath>
</template>
<!-- Page -->
<template id="index" name="Blogs">
<template id="index" name="Blogs" page="True">
<t t-call="website.layout">
<t t-set="head">
<script type="text/javascript" src="/website_mail/static/src/js/blog.js"></script>
@@ -36,7 +34,7 @@
<div class="container mt48 js_website_mail">
<div class="row">
<div class="span4" id="left_column">
<a t-if="mail_group_id and editable" t-att-href="'/blog/%%s/new' %% mail_group_id" class="btn">Add a new Blog</a>
<a t-if="mail_group_id and editable" t-attf-href="/blog/#{ mail_group_id }/new" class="btn">Add a new Blog</a>
<div id="blog_subscribe">
<form action="./subscribe" method="POST" class="form-inline" t-if="not subscribe">
@@ -71,7 +69,7 @@
</small>
</div>
</li>
<form id="post" t-att-action="'/blog/%%s/%%s/post#post' %% (blog_id.res_id, blog_id.id)" method="POST" class="form-horizontal text-center" groups="group_website_mail_reply">
<form id="post" t-attf-action="/blog/#{ blog_id.res_id }/#{ blog_id.id }/post#post" method="POST" class="form-horizontal text-center" groups="group_website_mail_reply">
<textarea rows="4" placeholder="Your comment" class="span7"> </textarea>
<button type="submit" class="btn">Post your comment</button>
</form>
@@ -82,14 +80,14 @@
<div class="span8" t-if="not blog_id and blog_ids">
<ul class="media-list">
<li t-foreach="blog_ids" t-as="blog" class="media well">
<div t-att-class="'media-body %%s' %% (blog.website_published and 'css_published' or '')">
<div t-attf-class="media-body #{ 'css_published' if blog.website_published else '' }">
<t t-call="website.publish"><t t-set="object" t-value="blog"/></t>
<h4 class="media-heading" ><a t-att-href="'/blog/%%s/%%s#comment' %% (blog.res_id, blog.id)" t-field="blog.subject"></a></h4>
<h4 class="media-heading" ><a t-attf-href="/blog/#{ blog.res_id }/#{ blog.id }#comment" t-field="blog.subject"></a></h4>
<div class="media">
<div t-field="blog.body"/>
<small class="pull-left muted text-right" t-if="len(blog.child_ids)">
<a t-if="len(blog.child_ids) &lt;= 1" t-att-href="'/blog/%%s/%%s#comment' %% (blog.res_id, blog.id)"><t t-esc="len(blog.child_ids)"/> Comment</a>
<a t-if="len(blog.child_ids) > 1" t-att-href="'/blog/%%s/%%s#comment' %% (blog.res_id, blog.id)"><t t-esc="len(blog.child_ids)"/> Comments</a>
<a t-if="len(blog.child_ids) &lt;= 1" t-attf-href="/blog/#{ blog.res_id }/#{ blog.id }#comment"><t t-esc="len(blog.child_ids)"/> Comment</a>
<a t-if="len(blog.child_ids) > 1" t-attf-href="/blog/#{ blog.res_id }/#{ blog.id }#comment"><t t-esc="len(blog.child_ids)"/> Comments</a>
</small>
<small class="pull-right muted text-right">
<div t-field="blog.author_id"/>
@@ -111,7 +109,7 @@
<template id="blog_history" inherit_id="website_mail.index" inherit_option_id="website_mail.index" name="History">
<xpath expr="//div[@id='left_column']" position="inside">
<ul class="nav nav-list">
<li class="nav-header"><a t-att-href="'/blog/%%s/' %% mail_group_id">BLOG ARCHIVE</a></li>
<li class="nav-header"><a t-attf-href="/blog/#{ mail_group_id }/">BLOG ARCHIVE</a></li>
<!-- TODO: check qweb iteration -->
<li t-foreach="nav_list" t-as="year" class="js_nav_year">
<t t-set="year" t-value="nav_list[year]"/>
+1
View File
@@ -0,0 +1 @@
import controllers
+18
View File
@@ -0,0 +1,18 @@
{
'name': 'Public Project',
'category': 'Website',
'summary': 'Publish Your Public Projects',
'version': '1.0',
'description': """
OpenERP Blog
============
""",
'author': 'OpenERP SA',
'depends': ['website', 'project'],
'data': [
'views/website_project.xml',
],
'qweb': ['static/src/xml/*.xml'],
'installable': True,
}
@@ -0,0 +1 @@
import main
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
from openerp.osv import osv
from openerp.addons.web import http
from openerp.addons.web.http import request
class website(osv.osv):
_inherit = "website"
def get_rendering_context(self, additional_values=None):
project_obj = request.registry['project.project']
project_ids = project_obj.search(request.cr, request.uid, [('privacy_visibility', "=", "public")])
values = {
'project_ids': project_obj.browse(request.cr, request.uid, project_ids),
}
if additional_values:
values.update(additional_values)
return super(website, self).get_rendering_context(values)
class website_project(http.Controller):
@http.route(['/project/<int:project_id>/'], type='http', auth="public")
def blog(self, project_id=None, **post):
website = request.registry['website']
project_obj = request.registry['project.project']
project = project_obj.browse(request.cr, request.uid, project_id)
values = website.get_rendering_context({
'project_id': project,
})
return website.render("website_project.index", values)
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Layout add nav and footer -->
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
<xpath expr="//body/footer//div[@name='info']/ul" position="inside">
<li t-foreach="project_ids" t-as="project"><a t-attf-href="/project/#{ project.id }/"><t t-esc="project.name"/></a></li>
</xpath>
</template>
<!-- Page -->
<template id="kanban_card" name="Project">
<a t-attf-href="/task/#{ object_id.id }/"><span t-field="object_id.name"/></a>
<div>
Assigned to <span t-field="object_id.user_id"/>
</div>
<div>
<span t-foreach="object_id.categ_ids" t-as="categ_id" class="label">
<t t-esc="categ_id.name"/>
</span>
</div>
<small>
<i class="icon-time"></i> <span t-field="object_id.date_start"/><br/>
<t t-if="object_id.date_end">Ending Date: <span t-field="object_id.date_end"/></t>
</small>
</template>
<template id="index" name="Project">
<t t-call="website.layout">
<t t-set="title">Project</t>
<div class="container">
<h4 t-field="project_id.name"/>
<t t-call="website.kanban">
<t t-set="model">project.task</t>
<t t-set="domain" t-value="[('project_id', '=', project_id.id)]"/>
<t t-set="column">stage_id</t>
<t t-set="content">website_project.kanban_card</t>
</t>
</div>
</t>
</template>
</data>
</openerp>
+26 -13
View File
@@ -4,6 +4,7 @@ from openerp import SUPERUSER_ID
from openerp.osv import osv
from openerp.addons.web import http
from openerp.addons.web.http import request
import random
import werkzeug
def get_order(order_id=None):
@@ -54,7 +55,7 @@ class Ecommerce(http.Controller):
def category(self, cat_id=0, page=0, **post):
website = request.registry['website']
product_obj = request.registry.get('product.product')
product_obj = request.registry.get('product.template')
domain = [("sale_ok", "=", True)]
if SUPERUSER_ID != request.uid:
@@ -65,20 +66,20 @@ class Ecommerce(http.Controller):
('name', 'ilike', "%%%s%%" % post.get("search")),
('description', 'ilike', "%%%s%%" % post.get("search")),
('description_website', 'ilike', "%%%s%%" % post.get("search")),
('pos_categ_id.name', 'ilike', "%%%s%%" % post.get("search"))]
('product_variant_ids.pos_categ_id.name', 'ilike', "%%%s%%" % post.get("search"))]
if cat_id:
cat_id = int(cat_id)
domain += [('pos_categ_id.id', 'child_of', cat_id)] + domain
domain += [('product_variant_ids.pos_categ_id.id', 'child_of', cat_id)] + domain
step = 20
product_count = len(product_obj.search(request.cr, request.uid, domain))
pager = 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'])
product_ids = product_obj.search(request.cr, request.uid, domain, limit=step, offset=pager['offset'], order="website_published,name")
values = website.get_rendering_context({
'categories': self.get_categories(),
'current_category': cat_id,
'category_id': cat_id,
'products': product_obj.browse(request.cr, request.uid, product_ids),
'search': post.get("search"),
'pager': pager,
@@ -86,18 +87,18 @@ class Ecommerce(http.Controller):
return website.render("website_sale.products", values)
@http.route(['/shop/product/<product_id>/'], type='http', auth="public")
def product(self, cat_id=0, product_id=0):
def product(self, cat_id=0, product_id=0, **post):
website = request.registry['website']
order = get_current_order()
product_id = product_id and int(product_id) or 0
product_obj = request.registry.get('product.product')
line = [line for line in order.order_line if line.product_id.id == product_id]
product_obj = request.registry.get('product.template')
product = product_obj.browse(request.cr, request.uid, product_id)
values = website.get_rendering_context({
'category_id': post.get('category_id') and int(post.get('category_id')) or None,
'search': post.get("search"),
'categories': self.get_categories(),
'product': product_obj.browse(request.cr, request.uid, product_id),
'product': product,
})
return website.render("website_sale.product", values)
@@ -105,15 +106,27 @@ class Ecommerce(http.Controller):
def mycart(self, **post):
order = get_current_order()
website = request.registry['website']
prod_obj = request.registry.get('product.product')
if post.get('code'):
pricelist_obj = request.registry.get('product.pricelist')
pricelist_ids = pricelist_obj.search(request.cr, SUPERUSER_ID, [('code', '=', post.get('code'))])
if pricelist_ids:
order.write({'pricelist_id': pricelist_ids[0]})
suggested_ids = []
for line in order.order_line:
suggested_ids += [p.id for p in line.product_id.suggested_product_ids for line in order.order_line]
suggested_ids = prod_obj.search(request.cr, request.uid, [('id', 'in', suggested_ids)])
# select 3 random products
suggested_products = []
while len(suggested_products) < 3 and suggested_ids:
index = random.randrange(0, len(suggested_ids))
suggested_products.append(suggested_ids.pop(index))
values = website.get_rendering_context({
'categories': self.get_categories(),
'suggested_products': prod_obj.browse(request.cr, request.uid, suggested_products),
})
return website.render("website_sale.mycart", values)
@@ -161,7 +174,7 @@ class Ecommerce(http.Controller):
return quantity
@http.route(['/shop/add_cart/<product_id>/', '/shop/<path:path>/add_cart/<product_id>/'], type='http', auth="public")
@http.route(['/shop/<path:path>/add_cart/', '/shop/add_cart/', '/shop/add_cart/<product_id>/', '/shop/<path:path>/add_cart/<product_id>/'], type='http', auth="public")
def add_cart(self, path=None, product_id=0, remove=None):
self.add_product_to_cart(product_id, number=(remove and -1 or 1))
if path:
@@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_product_product_public,product.product.public,product.model_product_product,base.group_public,1,0,0,0
access_product_template_public,product.template.public,product.model_product_template,base.group_public,1,0,0,0
access_point_of_sale_category_public,point_of_sale.category.public,point_of_sale.model_pos_category,base.group_public,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_product_product_public product.product.public product.model_product_product base.group_public 1 0 0 0
3 access_product_template_public product.template.public product.model_product_template base.group_public 1 0 0 0
4 access_point_of_sale_category_public point_of_sale.category.public point_of_sale.model_pos_category base.group_public 1 0 0 0
@@ -1,6 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="product_template_public" model="ir.rule">
<field name="name">product: Public product template</field>
<field name="model_id" ref="product.model_product_template"/>
<field name="domain_force">[('website_published', '=', True), ("sale_ok", "=", True)]</field>
<field name="groups" eval="[(4, ref('base.group_public'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<record id="product_product_public" model="ir.rule">
<field name="name">product: Public product</field>
<field name="model_id" ref="product.model_product_product"/>
+121 -29
View File
@@ -38,6 +38,7 @@
<group name="sale" position="before">
<group name="pos" string="Website">
<field name="website_published"/>
<field name="suggested_product_ids" widget="many2many_tags"/>
</group>
</group>
</field>
@@ -65,7 +66,7 @@
<xpath expr="//div[@id='shop_content']" position="before">
<div class="span4">
<ul class="nav nav-list">
<li t-att-class=" '' if current_category else 'active' " class='active'><a href="/shop/">All Products</a></li>
<li t-att-class=" '' if category_id else 'active' " class='active'><a href="/shop/">All Products</a></li>
<t t-foreach="categories" t-as="category">
<t t-call="website_sale.categories_recursive"/>
</t>
@@ -80,7 +81,7 @@
<!-- List of categories -->
<template id="categories_recursive">
<li t-att-class="category.id == current_category and 'active' or ''">
<li t-att-class="category.id == category_id and 'active' or ''">
<a t-attf-href="/shop/category/#{ category.id }/" t-field="category.name"></a>
<ul t-if="category.child_id" class="nav nav-list">
<t t-foreach="category.child_id" t-as="category">
@@ -92,7 +93,7 @@
<!-- Product list -->
<template id="products">
<template id="products" page="True">
<t t-call="website_sale.layout">
<t t-set="title">Product</t>
<t t-set="shop_content">
@@ -101,17 +102,16 @@
<t t-call="website.pager" >
<t t-set="classname">pull-left</t>
</t>
<form action="/shop/" method="get" class="navbar-search pull-right pagination">
<form t-attf-action="/shop/#{ category_id and ('category/%s/' % category_id) or ''}" method="get" class="navbar-search pull-right pagination">
<input type="text" name="search" class="search-query span2" placeholder="Search" t-att-value="search or '' or ''"/>
</form>
</div>
</div>
<div class='row grid grid-align-top'>
<div t-foreach="products" t-as="product" class="span2 mb16 thumbnail text-center">
<a t-attf-href="/shop/product/#{ product.id }/ ">
<t t-foreach="products" t-as="product">
<div t-attf-class="span2 mb16 thumbnail text-center #{not product.website_published and 'alert' or ''}">
<a t-attf-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&amp;category_id=%s' % category_id) or ''}">
<h5 t-field="product.name"> </h5>
</a>
<a t-attf-href="/shop/product/#{ product.id }/">
<img class="img-rounded" t-att-src="product.img('image_small')"/>
</a>
<div>
@@ -122,6 +122,7 @@
</div>
</div>
</div>
</t>
</div>
<div class="text-center">
<t t-call="website.pager" />
@@ -136,10 +137,24 @@
<t t-call="website_sale.layout">
<t t-set="title">Product</t>
<t t-set="shop_content">
<div class='navbar navbar-inverse'>
<div class='navbar-inner'>
<form t-attf-action="/shop/#{ category_id and ('category/%s/' % category_id) or ''}" method="get" class="navbar-search pull-right pagination">
<input type="text" name="search" class="search-query span2" placeholder="Search" t-att-value="search or '' or ''"/>
</form>
</div>
</div>
<div id="product_detail">
<t t-call="website.publish"><t t-set="object" t-value="product"/></t>
<h2 t-field="product.name"></h2>
<a t-attf-href="./add_cart/#{ product.id }/" class="btn btn-small btn-success pull-right">Add to cart</a>
<a t-if="not product.product_variant_ids" t-attf-href="/shop/add_cart/#{ product.id }/" class="btn btn-small btn-success pull-right">Add to cart</a>
<form t-if="len(product.product_variant_ids) > 1" action="/shop/add_cart/" class="pull-right">
<label class="radio" t-foreach="product.product_variant_ids" t-as="product">
<input type="radio" name="product_id" t-att-value="product.id" t-att-checked="product == product.product_variant_ids[0] or None"/>
<t t-esc="product.variants or ''">Standard</t>
</label><br/>
<button class="btn btn-small btn-success">Add to cart</button>
</form>
<img class="media-object" t-att-src="product.img('image')"/>
<div t-field="product.description_website"></div>
<div><t t-field="product.list_price" />€</div>
@@ -169,19 +184,24 @@
<!-- Page Shop my cart -->
<template id="mycart">
<template id="mycart" name="My cart" page="True">
<t t-call="website_sale.layout">
<t t-set="title">My cart</t>
<t t-set="shop_content">
<div class="span8 oe_mycart">
<h2>My Shopping Cart</h2>
<table class='table table-hover'>
<table class='table table-hover' id="mycart_products">
<colgroup>
<col width="80"/>
<col/>
<col width="80"/>
<col width="160"/>
</colgroup>
<thead>
<tr>
<th width="80">Product</th>
<th></th>
<th width="80">Price</th>
<th width="150">Quantity</th>
<th colspan="2">Product</th>
<th>Price</th>
<th>Quantity</th>
</tr>
</thead>
<tbody t-if="not order.order_line">
@@ -194,14 +214,16 @@
<a t-attf-href="/shop/product/#{ line.product_id.id }/"><img class="img-rounded" t-att-src="line.product_id.img('image_small')"/></a>
</td>
<td>
<a t-attf-href="/shop/product/#{ line.product_id.id }/"><span t-field="line.product_id.name"/></a><br/>
<a t-attf-href="/shop/product/#{ line.product_id.id }/">
<span t-field="line.product_id.name"/> <t t-if="line.product_id.variants">(<small t-field="line.product_id.variants"/>)</t>
</a><br/>
<small t-field="line.product_id.description_sale"/>
</td>
<td>
<span t-field="line.product_id.list_price"></span>€
</td>
<td>
<div class="mb8 mt8">
<div class="mb8 mt8 pull-right">
<input type="text" class="input-mini" t-att-data-id="line.product_id.id" t-att-value="line.product_uom_qty"/>
<a t-attf-href="./remove_cart/#{ line.product_id.id }/" class="btn mb8 btn-small btn-inverse">-</a>
<a t-attf-href="./add_cart/#{ line.product_id.id }/" class="btn mb8 btn-small btn-success">+</a>
@@ -210,21 +232,91 @@
</tr>
</t>
</tbody>
<tfoot>
<tr> <td colspan="3">Subtotal </td> <td><t t-esc="order.amount_untaxed"/> €</td></tr>
<tr> <td colspan="3">Taxes </td> <td><t t-esc="order.amount_tax"/> €</td></tr>
<tr> <td colspan="3"><h4>Total</h4></td> <td><h4><t t-esc="order.amount_total"/> €</h4></td></tr>
</tfoot>
</table>
<form t-if="order.order_line" class="well form-search" action="/shop/mycart/" method="post">
<input name="code" class='input' type="text" placeholder="Reduction Code..."/>
<button class="btn">Apply Code</button>
</form>
<a t-if="order.order_line" href="/shop/checkout/"><button class="btn btn-success">Proceed To Payment</button></a>
<table class='table table-hover table-condensed' id="mycart_total">
<colgroup>
<col width="80"/>
<col/>
<col width="80"/>
<col width="160"/>
</colgroup>
<thead>
<tr> <th colspan="3">Subtotal </th> <th><t t-esc="order.amount_untaxed"/> €</th></tr>
<tr> <th colspan="3">Taxes </th> <th><t t-esc="order.amount_tax"/> €</th></tr>
<tr> <th colspan="3"><h4>Total</h4></th> <th><h4><t t-esc="order.amount_total"/> €</h4></th></tr>
<tr>
<th colspan="4"><a t-if="order.order_line" href="/shop/checkout/" class="pull-right btn btn-success">Proceed To Payment</a></th>
</tr>
</thead>
</table>
</div>
</t>
</t>
</template>
<template id="suggested_products_list" inherit_id="website_sale.mycart" inherit_option_id="website_sale.mycart" name="Suggested Products in list view">
<xpath expr="//table[@id='mycart_products']" position="after">
<table t-if="suggested_products" class='table table-hover table-condensed'>
<colgroup>
<col/>
<col width="80"/>
<col width="160"/>
</colgroup>
<thead>
<tr>
<th>Suggested products</th>
</tr>
</thead>
<tbody>
<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/>
<small t-field="product.description_sale"/>
</td>
<td>
<span t-field="product.list_price"></span>€
</td>
<td>
<div class="pull-right">
<a t-attf-href="./add_cart/#{ product.id }/" class="btn btn-small btn-success">+</a>
</div>
</td>
</tr>
</t>
</tbody>
</table>
</xpath>
</template>
<template id="suggested_products_cell" inherit_option_id="website_sale.mycart" name="Suggested Products in card view">
<xpath expr="//table[@id='mycart_products']" position="after">
<div class="mt16 mb32" t-if="suggested_products">
<h4>Suggested products</h4>
<div class='row mt16'>
<t t-foreach="suggested_products" t-as="product">
<div class='span2 thumbnail'>
<a t-attf-href="/shop/product/#{ product.id }/">
<div class='mt16 text-center'>
<img t-att-src="product.img('image_small')"/>
<h5 t-field='product.name'></h5>
</div>
</a>
</div>
</t>
</div>
</div>
</xpath>
</template>
<template id="reduction_code" inherit_option_id="website_sale.mycart" name="Reduction Code">
<xpath expr="//table[@id='mycart_total']" position="after">
<form t-if="order.order_line" class="well form-search" action="/shop/mycart/" method="post">
<input name="code" class='input' type="text" placeholder="Reduction Code..."/>
<button class="btn">Apply Code</button>
</form>
</xpath>
</template>
<!-- Page confirm my cart -->
@@ -249,7 +341,7 @@
<tbody t-if="order.order_line">
<t t-foreach="order.order_line" t-as="line">
<tr>
<td><t t-esc="line.name"/></td>
<td><t t-esc="line.name"/> <t t-if="line.product_id.variants">(<small t-field="line.product_id.variants"/>)</t></td>
<td><t t-esc="line.product_uom_qty"/></td>
<td><t t-esc="line.product_id.list_price"/>€</td>
</tr>
@@ -264,7 +356,7 @@
</div>
<form class="span8 form-horizontal" action="/shop/confirm_order/" method="post">
<div class=" row">
<a t-if="not partner" t-att-href="'/admin#action=redirect&amp;url=%%s/shop/checkout/' %% host_url" class="btn btn-primary">Log me, I have an account</a>
<a t-if="not partner" t-attf-href="/admin#action=redirect&amp;url=#{ host_url }/shop/checkout/" class="btn btn-primary">Log me, I have an account</a>
<h3 class="span8">Billing Information</h3>
<div t-attf-class="control-group #{error.get('name', '')}">
<label class="control-label" for="contact_name">Name and firstname</label>
+5 -3
View File
@@ -29,10 +29,12 @@ class product_pricelist(osv.osv):
}
class product_product(osv.osv):
_inherit = "product.product"
_inherit = "product.template"
_columns = {
'website_published': fields.boolean('Available in the website'),
'description_website': fields.html('Description for the website'),
'suggested_product_id': fields.many2one('product.product', 'Suggested For Product'),
'suggested_product_ids': fields.one2many('product.product', 'suggested_product_id', 'Suggested Products'),
}
def recommended_products(self, cr, uid, ids, context=None):
@@ -47,14 +49,14 @@ class product_product(osv.osv):
AND sol.product_id not in (%s)
GROUP BY sol.product_id
ORDER BY COUNT(sol.order_id) DESC
LIMIT 8
LIMIT 10
"""
cr.execute(query, (id, id))
for p in cr.fetchall():
product_ids.append(p[0])
# search to apply access rules
product_ids = self.search(cr, uid, [("id", "in", product_ids)])
product_ids = self.search(cr, uid, [("id", "in", product_ids)], limit=3)
return self.browse(cr, uid, product_ids)
def img(self, cr, uid, ids, field='image_small', context=None):