[MERGE] from upstream

bzr revid: fva@openerp.com-20130821163002-y45g9l65xwrcxmsn
This commit is contained in:
Frédéric van der Essen 2013-08-21 18:30:02 +02:00
commit 6ccd087ee0
11 changed files with 489 additions and 133 deletions

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:

View File

@ -250,7 +250,7 @@
]},{
name: 'span', items: [
"Link", "Unlink", "Blockquote", "BulletedList",
"NumberedList", "Indent", "Outdent",
"NumberedList", "Indent", "Outdent"
]},{
name: 'justify', items: [
"JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock"
@ -450,6 +450,146 @@
},
});
function noop() {}
var alter_dialog = {
image: function (definition) {
definition.removeContents('Link');
definition.removeContents('advanced');
var upload = definition.getContents('Upload');
upload.add({
type: 'select',
label: 'Existing attachments',
id: 'ir_attachment',
items: [['']],
/**
* On dialog load, fetch all attachments (on ir.ui.view =>
* previously uploaded images) and add them to the select's
* options (items array & add method)
*/
onLoad: function () {
var field = this;
// FIXME: fuck this garbage, also fuck openerp.Model
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
model: 'ir.attachment',
method: 'search_read',
args: [],
kwargs: {
fields: ['name'],
domain: [['res_model', '=', 'ir.ui.view']],
order: 'name',
}
}).then(function (results) {
_(results).each(function (result) {
field.add(result.name, result.id);
});
});
},
/**
* The image widgets uses "txtUrl" to do most of its stuff.
* Synchronize select & txtUrl by generating the correct URL
* and setting it there
*/
onChange: function () {
var id = this.getValue();
var url = this.getDialog().getContentElement('info', 'txtUrl');
if (!id) {
url.setValue('');
return;
}
url.setValue('/website/attachment/' + id);
},
});
// Override uploadButton to send its information to the select
// created above instead of directly to txtUrl. The select will
// propagate to txtUrl
upload.get('uploadButton').filebrowser = {
onSelect: function (url) {
var id = url.split('/').pop();
var attachments = this.getDialog().getContentElement('Upload', 'ir_attachment');
// TODO: return supplementary info to get image/attachment name?
attachments.add(id, id);
attachments.setValue(id);
}
};
var old_show = definition.onShow;
definition.onShow = function () {
// CKEDITOR does not *override* onShow, is smashes the existing
// one instead, so override "by hand"
if (old_show) {
old_show.call(this);
}
// Assloads of code in the image plugin just go and tear into
// the info tab without a care, so can't just remove the tab or
// its content. Hide the tab instead, the effect is roughly the
// same.
this.hidePage('info');
// Force the dialog to always and only display the Upload tab
this.selectPage('Upload');
this.on('selectPage', function (e) {
setTimeout(function () {
if (e.data.page !== 'Upload') {
this.selectPage('Upload');
}
}.bind(this), 0);
});
}
},
link: function (definition) {
definition.removeContents('target');
definition.removeContents('advanced');
var info = definition.getContents('info');
info.remove('linkType');
info.remove('anchorOptions');
info.remove('emailOptions');
info.get('urlOptions').children[0].widths = [ '0%', '100%' ];
info.get('protocol').style = 'display: none';
// TODO: sync edition of url to website_pages?
info.add({
type: 'select',
label: "Existing page",
id: 'website_pages',
items: [['']],
/**
* onload fetch all the pages existing in the website, then
* display that.
*/
onLoad: function () {
var field = this;
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
model: 'website',
method: 'list_pages',
args: [],
kwargs: {}
}).then(function (results) {
_(results).each(function (result) {
field.add(result.name, result.url);
});
});
},
onChange: function () {
var url = this.getValue();
var url_field = this.getDialog().getContentElement('info', 'url');
if (!url) {
url_field.setValue('');
return;
}
url_field.setValue(url);
}
})
}
};
CKEDITOR.on('dialogDefinition', function (ev) {
(alter_dialog[ev.data.name] || noop)(ev.data.definition);
});
var all_ready = null;
var dom_ready = $.Deferred();
$(dom_ready.resolve);

View File

@ -132,7 +132,7 @@
</xpath>
</template>
<template id="homepage">
<template id="homepage" page="True">
<t t-call="website.layout">
<section>
@ -318,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">
@ -353,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">
@ -380,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 '' " >
@ -394,8 +394,8 @@
<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>
@ -491,5 +491,125 @@
</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>

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,6 +64,9 @@ 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)
@ -75,10 +79,10 @@ class website(osv.osv):
}
try:
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 (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)
@ -93,19 +97,16 @@ class website(osv.osv):
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)
@ -113,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"

View File

@ -18,15 +18,15 @@
<t t-set="title">References</t>
<div class="container">
<div class="thumbnails grid grid-align-top">
<div t-foreach="partner_ids" t-as="partner_id" class="span6">
<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_id.img('image_small')"/>
<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_id"/></t>
<h5 class="media-heading" t-field="partner_id.name"/>
<pre t-field="partner_id.website_testimonial"/>
<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>

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>

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>

View File

@ -23,7 +23,7 @@
<!-- 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>
@ -34,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">
@ -69,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>
@ -80,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"/>
@ -109,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]"/>

View File

@ -24,20 +24,10 @@ class website_project(http.Controller):
def blog(self, project_id=None, **post):
website = request.registry['website']
project_obj = request.registry['project.project']
task_obj = request.registry['project.task']
stage_obj = request.registry['project.task.type']
project = project_obj.browse(request.cr, request.uid, project_id)
domain = [('id', 'in', [task.id for task in project.tasks])]
stages = task_obj.read_group(request.cr, request.uid, domain, ["id", "stage_id"], groupby="stage_id")
for stage in stages:
stage['stage_id'] = stage_obj.browse(request.cr, request.uid, stage['stage_id'][0])
task_ids = task_obj.search(request.cr, request.uid, stage['__domain'])
stage['task_ids'] = task_obj.browse(request.cr, request.uid, task_ids)
values = website.get_rendering_context({
'project_id': project,
'stages': stages,
})
return website.render("website_project.index", values)

View File

@ -7,47 +7,40 @@
<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_id"><a t-attf-href="/project/#{ project_id.id }/"><t t-esc="project_id.name"/></a></li>
<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"/>
<table class="table">
<thead>
<tr>
<th t-foreach="stages" t-as="stage">
<span t-field="stage_id.name"/>
</th>
</tr>
</thead>
<tbody>
<tr>
<td t-foreach="stages" t-as="stage">
<div t-foreach="task_ids" t-as="task_id" class="thumbnail">
<a t-attf-href="/task/#{ task_id.id }/"><span t-field="task_id.name"/></a>
<div>
Assigned to <span t-field="task_id.user_id"/>
</div>
<div>
<span t-foreach="task_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="task_id.date_start"/><br/>
<t t-if="task_id.date_end">Ending Date: <span t-field="task_id.date_end"/></t>
</small>
</div>
</td>
</tr>
</tbody>
</table>
<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>

View File

@ -93,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">
@ -184,7 +184,7 @@
<!-- Page Shop my cart -->
<template id="mycart" name="My cart">
<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">
@ -269,18 +269,18 @@
</tr>
</thead>
<tbody>
<t t-foreach="suggested_products" t-as="product_id">
<t t-foreach="suggested_products" t-as="product">
<tr>
<td>
<a t-attf-href="/shop/product/#{ product_id.id }/"><span t-field="product_id.name"/></a><br/>
<small t-field="product_id.description_sale"/>
<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_id.list_price"></span>
<span t-field="product.list_price"></span>
</td>
<td>
<div class="pull-right">
<a t-attf-href="./add_cart/#{ product_id.id }/" class="btn btn-small btn-success">+</a>
<a t-attf-href="./add_cart/#{ product.id }/" class="btn btn-small btn-success">+</a>
</div>
</td>
</tr>
@ -356,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>