[MERGE] merge bzr saas-4 addons branch

This commit is contained in:
Christophe Simonis 2014-05-20 17:12:01 +02:00
commit d93f628c72
43 changed files with 613 additions and 395 deletions

View File

@ -2055,6 +2055,8 @@ class account_tax(osv.osv):
amount = amount2
child_tax = self._unit_compute(cr, uid, tax.child_ids, amount, product, partner, quantity)
res.extend(child_tax)
for child in child_tax:
amount2 += child.get('amount', 0.0)
if tax.child_depend:
for r in res:
for name in ('base','ref_base'):

View File

@ -62,22 +62,28 @@ class account_analytic_invoice_line(osv.osv):
context = context or {}
uom_obj = self.pool.get('product.uom')
company_id = company_id or False
context.update({'company_id': company_id, 'force_company': company_id, 'pricelist_id': pricelist_id})
local_context = dict(context, company_id=company_id, force_company=company_id, pricelist=pricelist_id)
if not product:
return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
if partner_id:
part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=local_context)
if part.lang:
context.update({'lang': part.lang})
result = {}
res = self.pool.get('product.product').browse(cr, uid, product, context=context)
result.update({'name': name or res.description or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': price_unit or res.list_price or 0.0})
res = self.pool.get('product.product').browse(cr, uid, product, context=local_context)
if price_unit is not False:
price = price_unit
elif pricelist_id:
price = res.price
else:
price = res.list_price
result.update({'name': name or res.description or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': price})
res_final = {'value':result}
if result['uom_id'] != res.uom_id.id:
selected_uom = uom_obj.browse(cr, uid, result['uom_id'], context=context)
selected_uom = uom_obj.browse(cr, uid, result['uom_id'], context=local_context)
new_price = uom_obj._compute_price(cr, uid, res.uom_id.id, res_final['value']['price_unit'], result['uom_id'])
res_final['value']['price_unit'] = new_price
return res_final

View File

@ -2,13 +2,13 @@
<openerp>
<data noupdate="1">
<record id="provider_openerp" model="auth.oauth.provider">
<field name="name">OpenERP.com Accounts</field>
<field name="auth_endpoint">https://accounts.openerp.com/oauth2/auth</field>
<field name="name">Odoo.com Accounts</field>
<field name="auth_endpoint">https://accounts.odoo.com/oauth2/auth</field>
<field name="scope">userinfo</field>
<field name="validation_endpoint">https://accounts.openerp.com/oauth2/tokeninfo</field>
<field name="validation_endpoint">https://accounts.odoo.com/oauth2/tokeninfo</field>
<field name="data_endpoint"></field>
<field name="css_class">zocial openerp</field>
<field name="body">Log in with OpenERP.com</field>
<field name="body">Log in with Odoo.com</field>
<field name="enabled" eval="True"/>
</record>
<record id="provider_facebook" model="auth.oauth.provider">

View File

@ -276,7 +276,7 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory):
active_ids = active_ids.difference(merged_lead_ids)
active_ids = active_ids.union(remaining_lead_ids)
ctx['active_ids'] = list(active_ids)
ctx['no_force_assignation'] = not data.force_assignation
ctx['no_force_assignation'] = context.get('no_force_assignation', not data.force_assignation)
return self.action_apply(cr, uid, ids, context=ctx)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -324,7 +324,7 @@ class gamification_challenge(osv.Model):
('end_date', '<=', challenge.last_report_date)
])
if fields.date.today() >= challenge.next_report_date:
if challenge.next_report_date and fields.date.today() >= challenge.next_report_date:
self.report_progress(cr, uid, challenge, context=context)
elif len(closed_goals_to_report) > 0:

View File

@ -349,8 +349,8 @@ class gamification_goal(osv.Model):
goal = all_goals[goal_id]
# check goal target reached
if (goal.definition_condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \
or (goal.definition_condition == 'lower' and value.get('current', goal.current) <= goal.target_goal):
if (goal.definition_id.condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \
or (goal.definition_id.condition == 'lower' and value.get('current', goal.current) <= goal.target_goal):
value['state'] = 'reached'
# check goal failure

View File

@ -183,16 +183,20 @@ class mail_notification(osv.Model):
references = message.parent_id.message_id if message.parent_id else False
# create email values
mail_values = {
'mail_message_id': message.id,
'auto_delete': True,
'body_html': body_html,
'recipient_ids': [(4, id) for id in email_pids],
'references': references,
}
email_notif_id = self.pool.get('mail.mail').create(cr, uid, mail_values, context=context)
if force_send:
self.pool.get('mail.mail').send(cr, uid, [email_notif_id], context=context)
max_recipients = 100
chunks = [email_pids[x:x + max_recipients] for x in xrange(0, len(email_pids), max_recipients)]
email_ids = []
for chunk in chunks:
mail_values = {
'mail_message_id': message.id,
'auto_delete': True,
'body_html': body_html,
'recipient_ids': [(4, id) for id in chunk],
'references': references,
}
email_ids.append(self.pool.get('mail.mail').create(cr, uid, mail_values, context=context))
if force_send and len(chunks) < 6: # for more than 500 followers, use the queue system
self.pool.get('mail.mail').send(cr, uid, email_ids, context=context)
return True
def _notify(self, cr, uid, message_id, partners_to_notify=None, context=None,

View File

@ -149,19 +149,9 @@ class mail_mail(osv.Model):
link to action_mail_redirect action that will redirect to doc or Inbox """
if partner and partner.user_ids:
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
# the parameters to encode for the query and fragment part of url
query = {'db': cr.dbname}
fragment = {
'login': partner.user_ids[0].login,
'action': 'mail.action_mail_redirect',
}
if mail.notification:
fragment['message_id'] = mail.mail_message_id.id
elif mail.model and mail.res_id:
fragment.update(model=mail.model, res_id=mail.res_id)
url = urljoin(base_url, "/web?%s#%s" % (urlencode(query), urlencode(fragment)))
return _("""<span class='oe_mail_footer_access'><small>Access your messages and documents <a style='color:inherit' href="%s">in OpenERP</a></small></span>""") % url
mail_model = mail.model or 'mail.thread'
url = urljoin(base_url, self.pool[mail_model]._get_access_link(cr, uid, mail, partner, context=context))
return _("""<span class='oe_mail_footer_access'><small>Access your messages and documents <a style='color:inherit' href="%s">in Odoo</a></small></span>""") % url
else:
return None

View File

@ -35,6 +35,7 @@ import socket
import time
import xmlrpclib
from email.message import Message
from urllib import urlencode
from openerp import tools
from openerp import SUPERUSER_ID
@ -641,6 +642,20 @@ class mail_thread(osv.AbstractModel):
})
return action
def _get_access_link(self, cr, uid, mail, partner, context=None):
# the parameters to encode for the query and fragment part of url
query = {'db': cr.dbname}
fragment = {
'login': partner.user_ids[0].login,
'action': 'mail.action_mail_redirect',
}
if mail.notification:
fragment['message_id'] = mail.mail_message_id.id
elif mail.model and mail.res_id:
fragment.update(model=mail.model, res_id=mail.res_id)
return "/web?%s#%s" % (urlencode(query), urlencode(fragment))
#------------------------------------------------------
# Email specific
#------------------------------------------------------

View File

@ -491,11 +491,9 @@ class marketing_campaign_activity(osv.osv):
active_ids=[workitem.res_id],
active_model=workitem.object_id.model,
workitem=workitem)
res = server_obj.run(cr, uid, [activity.server_action_id.id],
server_obj.run(cr, uid, [activity.server_action_id.id],
context=action_context)
# server action return False if the action is performed
# except client_action, other and python code
return res == False and True or res
return True
def process(self, cr, uid, act_id, wi_id, context=None):
activity = self.browse(cr, uid, act_id, context=context)

View File

@ -1167,8 +1167,8 @@ class pos_order(osv.osv):
return self.write(cr, uid, ids, {'state': 'payment'}, context=context)
def action_paid(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'paid'}, context=context)
self.create_picking(cr, uid, ids, context=context)
self.write(cr, uid, ids, {'state': 'paid'}, context=context)
return True
def action_cancel(self, cr, uid, ids, context=None):

View File

@ -146,6 +146,9 @@ function openerp_pos_db(instance, module){
var product = products[i];
var search_string = this._product_search_string(product);
var categ_id = product.public_categ_id ? product.public_categ_id[0] : this.root_category_id;
if (product.variants){
product.name = product.name+" ("+product.variants+")";
}
if(!stored_categories[categ_id]){
stored_categories[categ_id] = [];
}

View File

@ -219,7 +219,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
return self.fetch(
'product.product',
['name', 'list_price','price','public_categ_id', 'taxes_id', 'ean13', 'default_code',
['name', 'list_price','price','public_categ_id', 'taxes_id', 'ean13', 'default_code', 'variants',
'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description'],
[['sale_ok','=',true],['available_in_pos','=',true]],
{pricelist: self.pricelist.id} // context for price

View File

@ -234,7 +234,7 @@ class WebsiteSurvey(http.Controller):
# AJAX submission of a page
@http.route(['/survey/submit/<model("survey.survey"):survey>'],
type='http', auth='public', multilang=True, website=True)
type='http', methods=['POST'], auth='public', multilang=True, website=True)
def submit(self, survey, **post):
_logger.debug('Incoming data: %s', post)
page_id = int(post['page_id'])

View File

@ -1,24 +1,18 @@
# -*- coding: utf-8 -*-
import cStringIO
import contextlib
import hashlib
import datetime
from itertools import islice
import json
import logging
import os
import datetime
import re
from sys import maxint
import werkzeug
import werkzeug.exceptions
import werkzeug.utils
import werkzeug.wrappers
from PIL import Image
import openerp
from openerp.osv import fields
from openerp.addons.website.models import website
from openerp.addons.web import http
from openerp.http import request, Response
@ -27,6 +21,7 @@ logger = logging.getLogger(__name__)
# Completely arbitrary limits
MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768)
LOC_PER_SITEMAP = 45000
SITEMAP_CACHE_TIME = datetime.timedelta(hours=12)
class Website(openerp.addons.web.controllers.main.Home):
#------------------------------------------------------
@ -75,33 +70,64 @@ class Website(openerp.addons.web.controllers.main.Home):
@http.route('/sitemap.xml', type='http', auth="public", website=True)
def sitemap_xml_index(self):
pages = list(request.website.enumerate_pages())
if len(pages)<=LOC_PER_SITEMAP:
return self.__sitemap_xml(pages, 0)
# Sitemaps must be split in several smaller files with a sitemap index
values = {
'pages': range(len(pages)/LOC_PER_SITEMAP+1),
'url_root': request.httprequest.url_root
}
headers = {
'Content-Type': 'application/xml;charset=utf-8',
}
return request.render('website.sitemap_index_xml', values, headers=headers)
cr, uid, context = request.cr, openerp.SUPERUSER_ID, request.context
ira = request.registry['ir.attachment']
iuv = request.registry['ir.ui.view']
mimetype ='application/xml;charset=utf-8'
content = None
@http.route('/sitemap-<int:page>.xml', type='http', auth="public", website=True)
def sitemap_xml(self, page):
pages = list(request.website.enumerate_pages())
return self.__sitemap_xml(pages, page)
def create_sitemap(url, content):
ira.create(cr, uid, dict(
datas=content.encode('base64'),
mimetype=mimetype,
type='binary',
name=url,
url=url,
), context=context)
def __sitemap_xml(self, pages, index=0):
values = {
'pages': pages[index*LOC_PER_SITEMAP:(index+1)*LOC_PER_SITEMAP],
'url_root': request.httprequest.url_root.rstrip('/')
}
headers = {
'Content-Type': 'application/xml;charset=utf-8',
}
return request.render('website.sitemap_xml', values, headers=headers)
sitemap = ira.search_read(cr, uid, [('url', '=' , '/sitemap.xml'), ('type', '=', 'binary')], ('datas', 'create_date'), context=context)
if sitemap:
# Check if stored version is still valid
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
create_date = datetime.datetime.strptime(sitemap[0]['create_date'], server_format)
delta = datetime.datetime.now() - create_date
if delta < SITEMAP_CACHE_TIME:
content = sitemap[0]['datas'].decode('base64')
if not content:
# Remove all sitemaps in ir.attachments as we're going to regenerated them
sitemap_ids = ira.search(cr, uid, [('url', '=like' , '/sitemap%.xml'), ('type', '=', 'binary')], context=context)
if sitemap_ids:
ira.unlink(cr, uid, sitemap_ids, context=context)
pages = 0
first_page = None
locs = request.website.enumerate_pages()
while True:
start = pages * LOC_PER_SITEMAP
loc_slice = islice(locs, start, start + LOC_PER_SITEMAP)
urls = iuv.render(cr, uid, 'website.sitemap_locs', dict(locs=loc_slice), context=context)
if urls.strip():
page = iuv.render(cr, uid, 'website.sitemap_xml', dict(content=urls), context=context)
if not first_page:
first_page = page
pages += 1
create_sitemap('/sitemap-%d.xml' % pages, page)
else:
break
if not pages:
return request.not_found()
elif pages == 1:
content = first_page
else:
# Sitemaps must be split in several smaller files with a sitemap index
content = iuv.render(cr, uid, 'website.sitemap_index_xml', dict(
pages=range(1, pages + 1),
url_root=request.httprequest.url_root,
), context=context)
create_sitemap('/sitemap.xml', content)
return request.make_response(content, [('Content-Type', mimetype)])
#------------------------------------------------------
# Edit
@ -329,13 +355,7 @@ class Website(openerp.addons.web.controllers.main.Home):
return request.website.kanban_col(**post)
def placeholder(self, response):
# file_open may return a StringIO. StringIO can be closed but are
# not context managers in Python 2 though that is fixed in 3
with contextlib.closing(openerp.tools.misc.file_open(
os.path.join('web', 'static', 'src', 'img', 'placeholder.png'),
mode='rb')) as f:
response.data = f.read()
return response.make_conditional(request.httprequest)
return request.registry['website']._image_placeholder(response)
@http.route([
'/website/image',
@ -359,74 +379,10 @@ class Website(openerp.addons.web.controllers.main.Home):
The requested field is assumed to be base64-encoded image data in
all cases.
"""
Model = request.registry[model]
response = werkzeug.wrappers.Response()
return request.registry['website']._image(
request.cr, request.uid, model, id, field, response)
id = int(id)
ids = Model.search(request.cr, request.uid,
[('id', '=', id)], context=request.context)
if not ids and 'website_published' in Model._all_columns:
ids = Model.search(request.cr, openerp.SUPERUSER_ID,
[('id', '=', id), ('website_published', '=', True)], context=request.context)
if not ids:
return self.placeholder(response)
presized = '%s_big' % field
concurrency = '__last_update'
[record] = Model.read(request.cr, openerp.SUPERUSER_ID, [id],
[concurrency, field, presized],
context=request.context)
if concurrency in record:
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
try:
response.last_modified = datetime.datetime.strptime(
record[concurrency], server_format + '.%f')
except ValueError:
# just in case we have a timestamp without microseconds
response.last_modified = datetime.datetime.strptime(
record[concurrency], server_format)
# Field does not exist on model or field set to False
if not record.get(field):
# FIXME: maybe a field which does not exist should be a 404?
return self.placeholder(response)
response.set_etag(hashlib.sha1(record[field]).hexdigest())
response.make_conditional(request.httprequest)
# conditional request match
if response.status_code == 304:
return response
data = (record.get(presized) or record[field]).decode('base64')
image = Image.open(cStringIO.StringIO(data))
response.mimetype = Image.MIME[image.format]
# record provides a pre-resized version of the base field, use that
# directly
if record.get(presized):
response.data = data
return response
fit = int(max_width), int(max_height)
w, h = image.size
max_w, max_h = fit
if w < max_w and h < max_h:
response.data = data
else:
image.thumbnail(fit, Image.ANTIALIAS)
image.save(response.stream, image.format)
# invalidate content-length computed by make_conditional as
# writing to response.stream does not do it (as of werkzeug 0.9.3)
del response.headers['Content-Length']
return response
#------------------------------------------------------
# Server actions

View File

@ -1,13 +1,20 @@
# -*- coding: utf-8 -*-
import cStringIO
import contextlib
import datetime
import hashlib
import inspect
import itertools
import logging
import math
import mimetypes
import os
import re
import urlparse
from PIL import Image
from sys import maxint
import werkzeug
import werkzeug.exceptions
import werkzeug.utils
@ -472,6 +479,99 @@ class website(osv.osv):
html += request.website._render(template, {'object_id': object_id})
return html
def _image_placeholder(self, response):
# file_open may return a StringIO. StringIO can be closed but are
# not context managers in Python 2 though that is fixed in 3
with contextlib.closing(openerp.tools.misc.file_open(
os.path.join('web', 'static', 'src', 'img', 'placeholder.png'),
mode='rb')) as f:
response.data = f.read()
return response.make_conditional(request.httprequest)
def _image(self, cr, uid, model, id, field, response, max_width=maxint, max_height=maxint, context=None):
""" Fetches the requested field and ensures it does not go above
(max_width, max_height), resizing it if necessary.
Resizing is bypassed if the object provides a $field_big, which will
be interpreted as a pre-resized version of the base field.
If the record is not found or does not have the requested field,
returns a placeholder image via :meth:`~._image_placeholder`.
Sets and checks conditional response parameters:
* :mailheader:`ETag` is always set (and checked)
* :mailheader:`Last-Modified is set iif the record has a concurrency
field (``__last_update``)
The requested field is assumed to be base64-encoded image data in
all cases.
"""
Model = self.pool[model]
id = int(id)
ids = Model.search(cr, uid,
[('id', '=', id)], context=context)
if not ids and 'website_published' in Model._all_columns:
ids = Model.search(cr, openerp.SUPERUSER_ID,
[('id', '=', id), ('website_published', '=', True)], context=context)
if not ids:
return self._image_placeholder(response)
presized = '%s_big' % field
concurrency = '__last_update'
[record] = Model.read(cr, openerp.SUPERUSER_ID, [id],
[concurrency, field, presized],
context=context)
if concurrency in record:
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
try:
response.last_modified = datetime.datetime.strptime(
record[concurrency], server_format + '.%f')
except ValueError:
# just in case we have a timestamp without microseconds
response.last_modified = datetime.datetime.strptime(
record[concurrency], server_format)
# Field does not exist on model or field set to False
if not record.get(field):
# FIXME: maybe a field which does not exist should be a 404?
return self._image_placeholder(response)
response.set_etag(hashlib.sha1(record[field]).hexdigest())
response.make_conditional(request.httprequest)
# conditional request match
if response.status_code == 304:
return response
data = (record.get(presized) or record[field]).decode('base64')
image = Image.open(cStringIO.StringIO(data))
response.mimetype = Image.MIME[image.format]
# record provides a pre-resized version of the base field, use that
# directly
if record.get(presized):
response.data = data
return response
fit = int(max_width), int(max_height)
w, h = image.size
max_w, max_h = fit
if w < max_w and h < max_h:
response.data = data
else:
image.thumbnail(fit, Image.ANTIALIAS)
image.save(response.stream, image.format)
# invalidate content-length computed by make_conditional as
# writing to response.stream does not do it (as of werkzeug 0.9.3)
del response.headers['Content-Length']
return response
class website_menu(osv.osv):
_name = "website.menu"
_description = "Website Menu"

View File

@ -121,7 +121,7 @@ header a.navbar-brand img {
/* ----- EDITOR ----- */
.css_non_editable_mode_hidden {
display: none;
display: none !important;
}
/* ----- BOOTSTRAP FIX ----- */

View File

@ -697,14 +697,18 @@ User-agent: *
Sitemap: <t t-esc="url_root"/>sitemap.xml
</template>
<template id="sitemap_locs">
<url t-foreach="locs" t-as="page">
<loc><t t-esc="url_root"/><t t-esc="page['loc']"/></loc><t t-if="page.get('lastmod', False)">
<lastmod t-esc="page['lastmod']"/></t><t t-if="page.get('priority', False)">
<priority t-esc="page['priority']"/></t><t t-if="page.get('changefreq', False)">
<changefreq t-esc="page['changefreq']"/></t>
</url>
</template>
<template id="sitemap_xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url t-foreach="pages" t-as="page">
<loc><t t-esc="url_root"/><t t-esc="page['loc']"/></loc><t t-if="page.get('lastmod', False)">
<lastmod t-esc="page['lastmod']"/></t><t t-if="page.get('priority', False)">
<priority t-esc="page['priority']"/></t><t t-if="page.get('changefreq', False)">
<changefreq t-esc="page['changefreq']"/></t>
</url>
<t t-raw="content"/>
</urlset>
</template>

View File

@ -122,7 +122,7 @@ class WebsiteBlog(http.Controller):
blog_url = QueryURL('', ['blog', 'tag'], blog=blog, tag=tag, date_begin=date_begin, date_end=date_end)
post_url = QueryURL('', ['blogpost'], tag_id=tag and tag.id or None, date_begin=date_begin, date_end=date_end)
blog_post_ids = blog_post_obj.search(cr, uid, domain, order="create_date asc", context=context)
blog_post_ids = blog_post_obj.search(cr, uid, domain, order="create_date desc", context=context)
blog_posts = blog_post_obj.browse(cr, uid, blog_post_ids, context=context)
pager = request.website.pager(

View File

@ -1,115 +1,172 @@
# -*- coding: utf-8 -*-
import logging
import re
import werkzeug
import openerp
_logger = logging.getLogger(__name__)
try:
import GeoIP
except ImportError:
GeoIP = None
_logger.warn("Please install GeoIP python module to use events localisation.")
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.tools.translate import _
from openerp.addons.web.http import request
from openerp.addons.website_partner.controllers import main as website_partner
from openerp.addons.website.models.website import slug
from openerp.tools.translate import _
class WebsiteCrmPartnerAssign(http.Controller):
_references_per_page = 20
_references_per_page = 40
def _get_current_country_code(self):
if not GeoIP:
return False
GI = GeoIP.open('/usr/share/GeoIP/GeoIP.dat', 0)
return GI.country_code_by_addr(request.httprequest.remote_addr)
@http.route([
'/partners',
'/partners/page/<int:page>',
'/partners/grade/<int:grade_id>',
'/partners/grade/<int:grade_id>/page/<int:page>',
'/partners/grade/<model("res.partner.grade"):grade>',
'/partners/grade/<model("res.partner.grade"):grade>/page/<int:page>',
'/partners/country/<int:country_id>',
'/partners/country/<country_name>-<int:country_id>',
'/partners/country/<int:country_id>/page/<int:page>',
'/partners/country/<country_name>-<int:country_id>/page/<int:page>',
'/partners/grade/<int:grade_id>/country/<int:country_id>',
'/partners/grade/<int:grade_id>/country/<country_name>-<int:country_id>',
'/partners/grade/<int:grade_id>/country/<int:country_id>/page/<int:page>',
'/partners/grade/<int:grade_id>/country/<country_name>-<int:country_id>/page/<int:page>',
'/partners/country/<model("res.country"):country>',
'/partners/country/<model("res.country"):country>/page/<int:page>',
'/partners/grade/<model("res.partner.grade"):grade>/country/<model("res.country"):country>',
'/partners/grade/<model("res.partner.grade"):grade>/country/<model("res.country"):country>/page/<int:page>',
], type='http', auth="public", website=True, multilang=True)
def partners(self, country_id=0, grade_id=0, page=0, country_name='', **post):
country_obj = request.registry['res.country']
def partners(self, country=None, grade=None, page=0, **post):
country_all = post.pop('country_all', False)
partner_obj = request.registry['res.partner']
post_name = post.get('search', '')
country = None
country_obj = request.registry['res.country']
search = post.get('search', '')
# format displayed membership lines domain
base_partner_domain = [('is_company', '=', True), ('grade_id.website_published', '=', True), ('website_published', '=', True)]
partner_domain = list(base_partner_domain)
if post_name:
partner_domain += ['|', ('name', 'ilike', post_name), ('website_description', 'ilike', post_name)]
if grade_id and grade_id != "all":
partner_domain += [('grade_id', '=', int(grade_id))] # try/catch int
# group by country
countries = partner_obj.read_group(
request.cr, openerp.SUPERUSER_ID, partner_domain, ["id", "country_id"],
groupby="country_id", orderby="country_id", context=request.context)
countries_partners = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, partner_domain,
context=request.context, count=True)
if country_id:
country = country_obj.browse(request.cr, request.uid, country_id, request.context)
partner_domain += [('country_id', '=', country_id)]
if not any(x['country_id'][0] == country_id for x in countries):
countries.append({
'country_id_count': 0,
'country_id': (country_id, country.name)
})
countries.sort(key=lambda d: d['country_id'][1])
countries.insert(0, {
'country_id_count': countries_partners,
'country_id': (0, _("All Countries"))
})
# format pager
partner_count = partner_obj.search_count(
request.cr, openerp.SUPERUSER_ID, partner_domain,
context=request.context)
pager = request.website.pager(url="/partners", total=partner_count, page=page, step=self._references_per_page, scope=7, url_args=post)
partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, partner_domain,
offset=pager['offset'], limit=self._references_per_page,
order="grade_id DESC, partner_weight DESC",
context=request.context)
google_map_partner_ids = ','.join(map(str, partner_ids))
partners = partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids, request.context)
if search:
base_partner_domain += ['|', ('name', 'ilike', search), ('website_description', 'ilike', search)]
# group by grade
grade_domain = list(base_partner_domain)
if not country and not country_all:
country_code = self._get_current_country_code()
if country_code:
country_ids = country_obj.search(request.cr, request.uid, [('code', '=', country_code)], context=request.context)
if country_ids:
country = country_obj.browse(request.cr, request.uid, country_ids[0], context=request.context)
if country:
grade_domain += [('country_id', '=', country.id)]
grades = partner_obj.read_group(
request.cr, openerp.SUPERUSER_ID, base_partner_domain, ["id", "grade_id"],
request.cr, SUPERUSER_ID, grade_domain, ["id", "grade_id"],
groupby="grade_id", orderby="grade_id DESC", context=request.context)
grades_partners = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, base_partner_domain,
request.cr, SUPERUSER_ID, grade_domain,
context=request.context, count=True)
# flag active grade
for grade_dict in grades:
grade_dict['active'] = grade and grade_dict['grade_id'][0] == grade.id
grades.insert(0, {
'grade_id_count': grades_partners,
'grade_id': (0, _("All Categories"))
'grade_id': (0, _("All Categories")),
'active': bool(grade is None),
})
# group by country
country_domain = list(base_partner_domain)
if grade:
country_domain += [('grade_id', '=', grade.id)]
countries = partner_obj.read_group(
request.cr, SUPERUSER_ID, country_domain, ["id", "country_id"],
groupby="country_id", orderby="country_id", context=request.context)
countries_partners = partner_obj.search(
request.cr, SUPERUSER_ID, country_domain,
context=request.context, count=True)
# flag active country
for country_dict in countries:
country_dict['active'] = country and country_dict['country_id'] and country_dict['country_id'][0] == country.id
countries.insert(0, {
'country_id_count': countries_partners,
'country_id': (0, _("All Countries")),
'active': bool(country is None),
})
# current search
if grade:
base_partner_domain += [('grade_id', '=', grade.id)]
if country:
base_partner_domain += [('country_id', '=', country.id)]
# format pager
if grade and not country:
url = '/partners/grade/' + slug(grade)
elif country and not grade:
url = '/partners/country/' + slug(country)
elif country and grade:
url = '/partners/grade/' + slug(grade) + '/country/' + slug(country)
else:
url = '/partners'
url_args = {}
if search:
url_args['search'] = search
partner_count = partner_obj.search_count(
request.cr, SUPERUSER_ID, base_partner_domain,
context=request.context)
pager = request.website.pager(
url=url, total=partner_count, page=page, step=self._references_per_page, scope=7,
url_args=url_args)
# search partners matching current search parameters
partner_ids = partner_obj.search(
request.cr, SUPERUSER_ID, base_partner_domain,
order="grade_id DESC",
context=request.context) # todo in trunk: order="grade_id DESC, implemented_count DESC", offset=pager['offset'], limit=self._references_per_page
partners = partner_obj.browse(request.cr, SUPERUSER_ID, partner_ids, request.context)
# remove me in trunk
partners.sort(key=lambda x: (-1 * (x.grade_id and x.grade_id.id or 0), len(x.implemented_partner_ids)), reverse=True)
partners = partners[pager['offset']:pager['offset'] + self._references_per_page]
google_map_partner_ids = ','.join(map(str, [p.id for p in partners]))
values = {
'countries': countries,
'current_country_id': country_id,
'current_country': country,
'grades': grades,
'grade_id': grade_id,
'current_grade': grade,
'partners': partners,
'google_map_partner_ids': google_map_partner_ids,
'pager': pager,
'searches': post,
'search_path': "?%s" % werkzeug.url_encode(post),
'search_path': "%s" % werkzeug.url_encode(post),
}
return request.website.render("website_crm_partner_assign.index", values)
# Do not use semantic controller due to SUPERUSER_ID
@http.route(['/partners/<int:partner_id>', '/partners/<partner_name>-<int:partner_id>'], type='http', auth="public", website=True, multilang=True)
def partners_ref(self, partner_id, partner_name='', **post):
values = website_partner.get_partner_template_value(partner_id)
if not values:
return self.partners(**post)
values['main_object'] = values['partner']
return request.website.render("website_crm_partner_assign.partner", values)
@http.route(['/partners/<partner_id>'], type='http', auth="public", website=True, multilang=True)
def partners_detail(self, partner_id, partner_name='', **post):
mo = re.search('-([-0-9]+)$', str(partner_id))
current_grade, current_country = None, None
grade_id = post.get('grade_id')
country_id = post.get('country_id')
if grade_id:
grade_ids = request.registry['res.partner.grade'].exists(request.cr, request.uid, int(grade_id), context=request.context)
if grade_ids:
current_grade = request.registry['res.partner.grade'].browse(request.cr, request.uid, grade_ids[0], context=request.context)
if country_id:
country_ids = request.registry['res.country'].exists(request.cr, request.uid, int(country_id), context=request.context)
if country_ids:
current_country = request.registry['res.country'].browse(request.cr, request.uid, country_ids[0], context=request.context)
if mo:
partner_id = int(mo.group(1))
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
if partner.exists() and partner.website_published:
values = {
'main_object': partner,
'partner': partner,
'current_grade': current_grade,
'current_country': current_country
}
return request.website.render("website_crm_partner_assign.partner", values)
return self.partners(**post)

View File

@ -15,7 +15,7 @@
<t t-set="additional_title">Resellers</t>
<div id="wrap">
<div class="oe_structure"/>
<div class="container">
<div class="container mt16">
<div class="row">
<t t-raw="ref_content" />
</div>
@ -36,13 +36,13 @@
</h2>
</div>
<div class="col-md-4 mb32" id="partner_left">
<div class="col-md-3 mb32" id="partner_left">
<ul class="nav nav-pills nav-stacked mt16">
<li class="nav-header"><h3>Categories</h3></li>
<ul id="reseller_grades" class="nav nav-pills nav-stacked mt16">
<li class="nav-header"><h3>Filter by Grade</h3></li>
<t t-foreach="grades" t-as="grade">
<li t-if="grade['grade_id']" t-att-class="grade['grade_id'][0] == grade_id and 'active' or ''">
<a t-attf-href="#{ grade['grade_id'][0] and '/partners/grade/%s' % grade['grade_id'][0] or '/partners' }#{ current_country_id and ('/country/%s' % current_country_id) or '' }#{ search_path }">
<li t-att-class="grade['active'] and 'active' or ''">
<a t-attf-href="/partners#{ grade['grade_id'][0] and '/grade/%s' % grade['grade_id'][0] or '' }#{ current_country and '/country/%s' % slug(current_country) or '' }#{ '?' + (search_path or '') }">
<span class="badge pull-right" t-esc="grade['grade_id_count'] or ''"/>
<t t-esc="grade['grade_id'][1]"/>
</a>
@ -51,32 +51,27 @@
</ul>
<ul id="reseller_countries" class="nav nav-pills nav-stacked mt16">
<li class="nav-header"><h3>Locations</h3></li>
<t t-foreach="countries" t-as="country_dict">
<t t-if="country_dict['country_id']">
<li t-att-class="country_dict['country_id'][0] == current_country_id and 'active' or ''">
<a t-attf-href="#{ country_dict['country_id'][0] and ('/partners/country/%s' % slug(country_dict['country_id'])) or '/partners' }#{ search_path }">
<span class="badge pull-right" t-esc="country_dict['country_id_count'] or ''"/>
<t t-esc="country_dict['country_id'][1]"/>
</a>
</li>
</t>
<li class="nav-header"><h3>Filter by Country</h3></li>
<t t-foreach="countries" t-as="country">
<li t-if="country['country_id']" t-att-class="country['active'] and 'active' or ''">
<a t-attf-href="/partners#{ current_grade and '/grade/%s' % slug(current_grade) or ''}#{country['country_id'][0] and '/country/%s' % country['country_id'][0] or '' }#{ '?' + (search_path or '') + (country['country_id'][0] == 0 and 'country_all=True' or '')}">
<span class="badge pull-right" t-esc="country['country_id_count'] or ''"/>
<t t-esc="country['country_id'][1]"/>
</a>
</li>
</t>
</ul>
</div>
<div class="col-md-8" id="ref_content">
<div class="col-md-8 col-md-offset-1" id="ref_content">
<div class='navbar'>
<div>
<t t-call="website.pager">
<t t-set="classname">pull-left</t>
</t>
<t t-call="website.pager"/>
<form action="" method="get" class="navbar-search pull-right pagination form-inline">
<div class="form-group">
<div class="form-group pull-right">
<input type="text" name="search" class="search-query col-md-2 mt4 form-control" placeholder="Search" t-att-value="searches.get('search', '')"/>
</div>
</form>
</div>
</div>
@ -90,19 +85,28 @@
<t t-set="last_grade" t-value="partner.grade_id.id"/>
</t>
<div class="media">
<a class="pull-left" t-attf-href="/partners/#{slug(partner)}"
<a class="pull-left" t-attf-href="/partners/#{slug(partner)}?#{current_grade and 'grade_id=%s&amp;' % current_grade.id}#{current_country and 'country_id=%s' % current_country.id}"
t-field="partner.image_small"
t-field-options='{"widget": "image", "class": "media-object"}'
></a>
<div class="media-body" style="min-height: 64px;">
<a class="media-heading" t-attf-href="/partners/#{slug(partner)}">
<div class="media-body o_partner_body" style="min-height: 64px;">
<a class="media-heading" t-attf-href="/partners/#{slug(partner)}?#{current_grade and 'grade_id=%s&amp;' % current_grade.id}#{current_country and 'country_id=%s' % current_country.id}">
<span t-field="partner.display_name"/>
</a>
<div t-field="partner.website_short_description"/>
<t t-if="any([p.website_published for p in partner.implemented_partner_ids])">
<small><a t-attf-href="/partners/#{slug(partner)}#right_column">
<t t-raw="len([p for p in partner.implemented_partner_ids if p.website_published])"/> reference(s)
</a></small>
</t>
</div>
</div>
</t>
</div>
<div class='navbar'>
<t t-call="website.pager">
<t t-set="classname">pull-left</t>
</t>
</div>
</div>
</t>
@ -113,8 +117,8 @@
<xpath expr="//ul[@id='reseller_countries']" position="after">
<h3>World Map</h3>
<ul class="nav">
<iframe t-attf-src="/google_map/?width=320&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/partners"
style="width:320px; height:260px; border:0; padding:0; margin:0;"></iframe>
<iframe t-attf-src="/google_map/?width=260&amp;height=240&amp;partner_ids=#{ google_map_partner_ids }&amp;partner_url=/partners"
style="width:260px; height:260px; border:0; padding:0; margin:0;"></iframe>
</ul>
</xpath>
</template>
@ -122,7 +126,17 @@
<template id="partner" name="Partner Detail">
<t t-call="website_crm_partner_assign.layout">
<t t-set="ref_content">
<t t-call="website_partner.partner_detail"/>
<div class="col-md-5">
<ol class="breadcrumb">
<li><a t-attf-href="/partners#{current_grade and '/grade/%s' % slug(current_grade)}#{current_country and '/country/%s' % slug(current_country)}">Our Partners</a></li>
<li class="active"><span t-field="partner.display_name"/></li>
</ol>
</div>
<t t-call="website_partner.partner_detail">
<t t-set="right_column">
<div id="right_column" class="mb16"><t t-call="website_crm_partner_assign.references_block"/></div>
</t>
</t>
</t>
</t>
</template>
@ -134,5 +148,24 @@
</xpath>
</template>
<template id="references_block" name="Partner References Block">
<t t-if="any([p.website_published for p in partner.implemented_partner_ids])">
<h3 id="references">References</h3>
<div t-foreach="partner.implemented_partner_ids" t-as="reference" class="media">
<t t-if="reference.website_published">
<a class="pull-left" t-attf-href="/customers/#{slug(reference)}">
<span t-field="reference.image_small" t-field-options='{"widget": "image", "class": "center-block"}'/>
</a>
<div class="media-body" style="min-height: 64px;">
<a class="media-heading" t-attf-href="/customers/#{slug(reference)}">
<span t-field="reference.self"/>
</a>
<div t-field='reference.website_short_description'/>
</div>
</t>
</div>
</t>
</template>
</data>
</openerp>

View File

@ -30,8 +30,9 @@ OpenERP Customer References
""",
'author': 'OpenERP SA',
'depends': [
'crm_partner_assign',
'website_partner',
'website_google_map'
'website_google_map',
],
'demo': [
'website_customer_demo.xml',

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
import re
import openerp
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.tools.translate import _
from openerp.addons.web.http import request
from openerp.addons.website_partner.controllers import main as website_partner
import werkzeug.urls
class WebsiteCustomer(http.Controller):
@ -80,11 +81,15 @@ class WebsiteCustomer(http.Controller):
}
return request.website.render("website_customer.index", values)
@http.route(['/customers/<int:partner_id>', '/customers/<partner_name>-<int:partner_id>'], type='http', auth="public", website=True, multilang=True)
def customer(self, partner_id, partner_name='', **post):
values = website_partner.get_partner_template_value(partner_id)
if not values:
return self.customers(**post)
values['main_object'] = values['partner']
return request.website.render("website_customer.details", values)
# Do not use semantic controller due to SUPERUSER_ID
@http.route(['/customers/<partner_id>'], type='http', auth="public", website=True, multilang=True)
def partners_detail(self, partner_id, **post):
mo = re.search('-([-0-9]+)$', str(partner_id))
if mo:
partner_id = int(mo.group(1))
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
if partner.exists() and partner.website_published:
values = {}
values['main_object'] = values['partner'] = partner
return request.website.render("website_customer.details", values)
return self.customers(**post)

View File

@ -25,7 +25,7 @@
<div class="row">
<div class="col-md-3 mb32" id="ref_left_column">
</div>
<div class="col-md-9" id="ref_content">
<div class="col-md-8 col-md-offset-1" id="ref_content">
<div class='navbar mb0'>
<t t-call="website.pager">
<t t-set="classname" t-value="'pull-left'"/>

View File

@ -139,7 +139,7 @@ class website_event(http.Controller):
'country_id': ("all", _("All Countries"))
})
step = 5
step = 10 # Number of events per page
event_count = event_obj.search(
request.cr, request.uid, dom_without("none"), count=True,
context=request.context)

View File

@ -1,3 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_event_event_public,event.event.public,event.model_event_event,base.group_public,1,0,0,0
access_event_type_public,event.type.public,event.model_event_type,base.group_public,1,0,0,0
access_event_event_public,event.event.public,event.model_event_event,,1,0,0,0
access_event_type_public,event.type.public,event.model_event_type,,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_event_event_public event.event.public event.model_event_event base.group_public 1 0 0 0
3 access_event_type_public event.type.public event.model_event_type base.group_public 1 0 0 0

View File

@ -224,7 +224,7 @@
<b>Date</b><br/>
<span t-field="track.date" t-field-options='{"hide_seconds":"True"}'/><br/>
<b>Duration</b><br/>
<span t-esc="'%.2f' % (track.duration)"/> hours<br/>
<span t-field="track.duration" t-field-options="{&quot;widget&quot;: &quot;duration&quot;, &quot;unit&quot;: &quot;hour&quot;}"/><br/>
<b>Location</b><br/>
<span t-field="track.location_id"/><br/>
</div>

View File

@ -2,6 +2,7 @@
from datetime import datetime
import werkzeug.urls
import werkzeug.wrappers
import simplejson
from openerp import tools
@ -87,14 +88,15 @@ class WebsiteForum(http.Controller):
@http.route(['/forum/<model("forum.forum"):forum>',
'/forum/<model("forum.forum"):forum>/page/<int:page>',
'''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions'''
'''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions''',
'''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions/page/<int:page>''',
], type='http', auth="public", website=True, multilang=True)
def questions(self, forum, tag=None, page=1, filters='all', sorting='date', search='', **post):
cr, uid, context = request.cr, request.uid, request.context
Post = request.registry['forum.post']
user = request.registry['res.users'].browse(cr, uid, uid, context=context)
domain = [('forum_id', '=', forum.id), ('parent_id', '=', False)]
domain = [('forum_id', '=', forum.id), ('parent_id', '=', False), ('state', '=', 'active')]
if search:
domain += ['|', ('name', 'ilike', search), ('content', 'ilike', search)]
if tag:
@ -110,12 +112,28 @@ class WebsiteForum(http.Controller):
order = 'child_count desc'
elif sorting == 'vote':
order = 'vote_count desc'
else:
sorting = 'date'
elif sorting == 'date':
order = 'write_date desc'
else:
sorting = 'creation'
order = 'create_date desc'
question_count = Post.search(cr, uid, domain, count=True, context=context)
pager = request.website.pager(url="/forum/%s" % slug(forum), total=question_count, page=page, step=self._post_per_page, scope=self._post_per_page)
if tag:
url = "/forum/%s/%s/questions" % (slug(forum), slug(tag))
else:
url = "/forum/%s" % slug(forum)
url_args = {}
if search:
url_args['search'] = search
if filters:
url_args['filters'] = filters
if sorting:
url_args['sorting'] = sorting
pager = request.website.pager(url=url, total=question_count, page=page,
step=self._post_per_page, scope=self._post_per_page,
url_args=url_args)
obj_ids = Post.search(cr, uid, domain, limit=self._post_per_page, offset=pager['offset'], order=order, context=context)
question_ids = Post.browse(cr, uid, obj_ids, context=context)
@ -418,14 +436,16 @@ class WebsiteForum(http.Controller):
# User
# --------------------------------------------------
@http.route('/forum/<model("forum.forum"):forum>/users', type='http', auth="public", website=True, multilang=True)
@http.route(['/forum/<model("forum.forum"):forum>/users',
'/forum/<model("forum.forum"):forum>/users/page/<int:page>'],
type='http', auth="public", website=True, multilang=True)
def users(self, forum, page=1, **searches):
cr, uid, context = request.cr, request.uid, request.context
User = request.registry['res.users']
step = 30
tag_count = User.search(cr, SUPERUSER_ID, [('karma', '>', 1)], count=True, context=context)
pager = request.website.pager(url="/forum/users", total=tag_count, page=page, step=step, scope=30)
pager = request.website.pager(url="/forum/%s/users" % slug(forum), total=tag_count, page=page, step=step, scope=30)
obj_ids = User.search(cr, SUPERUSER_ID, [('karma', '>', 1)], limit=step, offset=pager['offset'], order='karma DESC', context=context)
users = User.browse(cr, SUPERUSER_ID, obj_ids, context=context)
@ -451,6 +471,17 @@ class WebsiteForum(http.Controller):
return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), partner.user_ids[0].id))
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
@http.route(['/forum/user/<int:user_id>/avatar'], type='http', auth="public", website=True, multilang=True)
def user_avatar(self, user_id=0, **post):
cr, uid, context = request.cr, request.uid, request.context
response = werkzeug.wrappers.Response()
User = request.registry['res.users']
Website = request.registry['website']
user = User.browse(cr, SUPERUSER_ID, user_id, context=context)
if not user.exists() or (user_id != request.session.uid and user.karma < 1):
return Website._image_placeholder(response)
return Website._image(cr, SUPERUSER_ID, 'res.users', user.id, 'image', response)
@http.route(['/forum/<model("forum.forum"):forum>/user/<int:user_id>'], type='http', auth="public", website=True, multilang=True)
def open_user(self, forum, user_id=0, **post):
cr, uid, context = request.cr, request.uid, request.context
@ -461,10 +492,9 @@ class WebsiteForum(http.Controller):
Followers = request.registry['mail.followers']
Data = request.registry["ir.model.data"]
user_id = User.search(cr, SUPERUSER_ID, [('id', '=', user_id), ('karma', '>', '1')], context=context)
if not user_id:
user = User.browse(cr, SUPERUSER_ID, user_id, context=context)
if not user.exists() or (user_id != request.session.uid and user.karma < 1):
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
user = User.browse(cr, SUPERUSER_ID, user_id[0], context=context)
# questions and answers by user
user_questions, user_answers = [], []
@ -505,7 +535,7 @@ class WebsiteForum(http.Controller):
#activity by user.
model, comment = Data.get_object_reference(cr, uid, 'mail', 'mt_comment')
activity_ids = Activity.search(cr, uid, [('res_id', 'in', user_post_ids), ('model', '=', 'forum.post'), ('subtype_id', '!=', comment)], context=context)
activity_ids = Activity.search(cr, uid, [('res_id', 'in', user_post_ids), ('model', '=', 'forum.post'), ('subtype_id', '!=', comment)], order='date DESC', limit=100, context=context)
activities = Activity.browse(cr, uid, activity_ids, context=context)
posts = {}
@ -555,7 +585,7 @@ class WebsiteForum(http.Controller):
'website': kwargs.get('website'),
'email': kwargs.get('email'),
'city': kwargs.get('city'),
'country_id': kwargs.get('country'),
'country_id': int(kwargs.get('country')),
'website_description': kwargs.get('description'),
}, context=request.context)
return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), user.id))
@ -569,6 +599,7 @@ class WebsiteForum(http.Controller):
Badge = request.registry['gamification.badge']
badge_ids = Badge.search(cr, SUPERUSER_ID, [('challenge_ids.category', '=', 'forum')], context=context)
badges = Badge.browse(cr, uid, badge_ids, context=context)
badges = sorted(badges, key=lambda b: b.stat_count_distinct, reverse=True)
values = self._prepare_forum_values(forum=forum, searches={'badges': True})
values.update({
'badges': badges,

View File

@ -78,16 +78,16 @@
<field name="target_goal">10</field>
</record>
<!-- Pundit: 10 comments with at least score of 10 -->
<!-- Pundit: 10 answers with at least score of 10 -->
<record id="badge_25" model="gamification.badge">
<field name="name">Pundit</field>
<field name="description">Left comments with score of 10 or more</field>
<field name="description">Left 10 answers with score of 10 or more</field>
<field name="level">silver</field>
<field name="rule_auth">nobody</field>
</record>
<record model="gamification.goal.definition" id="definition_pundit">
<field name="name">Pundit</field>
<field name="description">Post 10 comments with score of 10 or more</field>
<field name="description">Post 10 answers with score of 10 or more</field>
<field name="display_mode">boolean</field>
<field name="condition">higher</field>
<field name="model_id" eval="ref('website_forum.model_forum_post')"/>

View File

@ -372,7 +372,7 @@
<field name="computation_mode">count</field>
<field name="display_mode">boolean</field>
<field name="model_id" eval="ref('website_forum.model_forum_post')" />
<field name="domain">[('parent_id', '=', False), ('is_correct', '=', True)]</field>
<field name="domain">[('parent_id', '=', False), ('has_validated_answer', '=', True)]</field>
<field name="condition">higher</field>
<field name="batch_mode">True</field>
<field name="batch_distinctive_field" eval="ref('website_forum.field_forum_post_create_uid')" />

View File

@ -70,9 +70,6 @@
<record id="reason_4" model="forum.post.reason">
<field name="name">not a real question</field>
</record>
<record id="reason_5" model="forum.post.reason">
<field name="name">already answered and an answer was accepted</field>
</record>
<record id="reason_6" model="forum.post.reason">
<field name="name">not relevant or out dated</field>
</record>

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
import openerp
from urlparse import urljoin
import openerp
from openerp import SUPERUSER_ID
from openerp.addons.website.models.website import slug
from openerp.osv import osv, fields
@ -269,10 +270,14 @@ class Post(osv.Model):
return {'vote_count': self._get_vote_count(cr, uid, ids, None, None, context=context)[ids[0]]}
def set_viewed(self, cr, uid, ids, context=None):
for post in self.browse(cr, uid, ids, context=context):
self.write(cr, uid, [post.id], {'views': post.views + 1}, context=context)
cr.execute("""UPDATE forum_post SET views = views+1 WHERE id IN %s""", (tuple(ids),))
return True
def _get_access_link(self, cr, uid, mail, partner, context=None):
post = self.pool['forum.post'].browse(cr, uid, mail.res_id, context=context)
res_id = post.parent_id and "%s#answer-%s" % (post.parent_id.id, post.id) or post.id
return "/forum/%s/question/%s" % (post.forum_id.id, res_id)
class PostReason(osv.Model):
_name = "forum.post.reason"

View File

@ -2,10 +2,16 @@
from openerp.osv import osv, fields
class Users(osv.Model):
_inherit = 'res.users'
def __init__(self, pool, cr):
init_res = super(Users, self).__init__(pool, cr)
self.SELF_WRITEABLE_FIELDS = list(set(
self.SELF_WRITEABLE_FIELDS + \
['country_id', 'city', 'website', 'website_description']))
return init_res
def _get_user_badge_level(self, cr, uid, ids, name, args, context=None):
"""Return total badge per level of users"""
result = dict.fromkeys(ids, False)

View File

@ -24,16 +24,25 @@
.question .badge-active {
background-color: #428bca;
}
.question img {
max-width: 600px;
height: auto !important;
}
.forum_answer img {
max-width: 600px;
height: auto !important;
}
img.img-avatar {
max-width: 50px;
margin-right: 10px;
}
.oe_grey {
background-color: #eeeeee;
}
.img-avatar {
max-width: 50px;
margin-right: 10px;
}
.badge-gold {
color: #ffcc00;
}

View File

@ -19,14 +19,23 @@
margin-left: 4px
.badge-active
background-color: #428bca
img
max-width: 600px
height: auto !important
.forum_answer
img
max-width: 600px
height: auto !important
img.img-avatar
max-width: 50px
margin-right: 10px
.oe_grey
background-color: #eeeeee
.img-avatar
max-width: 50px
margin-right: 10px
.badge-gold
color: #ffcc00

View File

@ -229,6 +229,7 @@
<t t-if="filters == 'followed'">Followed</t>
<t t-if="tag"><span t-field="tag.name"/></t>
<t t-if="sorting == 'date'"> by activity date</t>
<t t-if="sorting == 'creation'"> by creation date</t>
<t t-if="sorting == 'answered'"> by most answered</t>
<t t-if="sorting == 'vote'"> by most voted</t>
<b class="caret"/>
@ -252,6 +253,9 @@
<li t-att-class="sorting == 'date' and 'active' or '' ">
<a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='date')">Last activity date</a>
</li>
<li t-att-class="sorting == 'creation' and 'active' or '' ">
<a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='creation')">Newest</a>
</li>
<li t-att-class="sorting == 'answered' and 'active' or '' ">
<a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='answered')">Most answered</a>
</li>
@ -430,20 +434,22 @@
<span t-if="not question.active"><b> [Deleted]</b></span>
<span t-if="question.state == 'close'"><b> [Closed]</b></span>
</h1>
<div class="alert alert-info" t-if="question.state == 'close'">
<p class="mt32 mb16 text-center">
<b>The question has been closed for reason: <i t-esc="question.closed_reason_id.name"/>
<br/>
<t t-if="question.closed_uid">
<i>by <a t-attf-href="/forum/#{ slug(forum) }/user/#{ slug(question.closed_uid) }" t-field="question.closed_uid"/> </i>
</t>
on <span t-field="question.closed_date"/></b>
<div class="alert alert-info text-center" t-if="question.state == 'close'">
<p class="mt16">
<b>The question has been closed<t t-if="question.closed_reason_id"> for reason: <i t-esc="question.closed_reason_id.name"/></t></b>
</p>
<div t-if="question.state == 'close' and user.karma&gt;=500" class="mb24 text-center">
<t t-if="question.closed_uid">
<b>by <a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.closed_uid.id }"
t-field="question.closed_uid"
t-field-options='{"widget": "contact", "fields": ["name"]}'
style="display: inline-block;"/></b>
</t>
<b>on <span t-field="question.closed_date"/></b>
<div t-if="question.state == 'close' and user.karma&gt;=500" class="mt16 mb24 text-center">
<t t-call="website_forum.link_button">
<t t-set="url"><t t-escf="/forum/#{ slug(forum) }/question/#{slug(question)}/reopen"/></t>
<t t-set="label"> Reopen</t>
<t t-set="classes">fa-arrow-right</t>
<t t-set="url" t-value="'/forum/' + slug(forum) + '/question/' + slug(question) + '/reopen'"/>
<t t-set="label" t-value="'Reopen'"/>
<t t-set="classes" t-value="'fa-arrow-right'"/>
</t>
</div>
</div>
@ -465,49 +471,50 @@
</li>
<li t-if="question.state != 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)">
<t t-call="website_forum.link_button">
<t t-set="url"><t t-escf="/forum/#{ slug(forum) }/question/#{slug(question)}/ask_for_close"/></t>
<t t-set="label"> Close</t>
<t t-set="classes">text-muted fa-times</t>
<t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/ask_for_close'"/>
<t t-set="label" t-value="'Close'"/>
<t t-set="classes" t-vaoue="'text-muted fa-times'"/>
</t>
</li>
<li t-if="question.state == 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)">
<t t-call="website_forum.link_button">
<t t-set="url"><t t-escf="/forum/#{ slug(forum) }/question/#{slug(question)}/reopen"/></t>
<t t-set="label"> Reopen</t>
<t t-set="classes">text-muted fa-undo</t>
<t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/reopen'"/>
<t t-set="label" t-value="'Reopen'"/>
<t t-set="classes" t-value="'text-muted fa-undo'"/>
</t>
</li>
<li t-if="(user.id == question.create_uid.id and can_edit_own) or can_edit_all">
<t t-call="website_forum.link_button">
<t t-set="url"><t t-escf="/forum/#{ slug(forum) }/post/#{slug(question)}/edit"/></t>
<t t-set="label"> Edit</t>
<t t-set="classes">text-muted fa-edit</t>
<t t-set="url" t-value="'/forum/' + slug(forum) +'/post/' + slug(question) + '/edit'"/>
<t t-set="label" t-value="'Edit'"/>
<t t-set="classes" t-value="'text-muted fa-edit'"/>
</t>
</li>
<li t-if="question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)">
<t t-call="website_forum.link_button">
<t t-set="url"><t t-escf="/forum/#{ slug(forum) }/question/#{slug(question)}/delete"/></t>
<t t-set="label"> Delete</t>
<t t-set="classes">text-muted fa-trash-o</t>
<t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/delete'"/>
<t t-set="label" t-value="'Delete'"/>
<t t-set="classes" t-value="'text-muted fa-trash-o'"/>
</t>
</li>
<li t-if="not question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)">
<t t-call="website_forum.link_button">
<t t-set="url"><t t-escf="/forum/#{ slug(forum) }/question/#{slug(question)}/undelete"/></t>
<t t-set="label"> Undelete</t>
<t t-set="classes">text-muted fa-trash-o</t>
<t t-set="url" t-value="'/forum/' + slug(forum) +'/question/' + slug(question) + '/undelete'"/>
<t t-set="label" t-value="'Undelete'"/>
<t t-set="classes" t-value="'text-muted fa-trash-o'"/>
</t>
</li>
</ul>
</div>
<div>
<span t-field="question.create_uid.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/>
<img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{question.create_uid.id}/avatar"/>
<div>
<a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.create_uid.id }"
t-field="question.create_uid"
t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
style="display: inline-block;"/>
<div t-field="question.create_uid" t-field-options='{"widget": "contact", "badges": true, "fields": ["karma"]}'/>
<span class="text-muted">Asked on <span t-field="question.create_date" t-field-options='{"format":"short"}'/></span>
</div>
</div>
</div>
@ -544,20 +551,20 @@
</li>
<li t-if="(user.id == answer.create_uid.id and can_unlink_own) or can_unlink_all">
<t t-call="website_forum.link_button">
<t t-set="url"><t t-escf="/forum/#{slug(forum)}/post/#{slug(answer)}/delete"/></t>
<t t-set="label"> Delete</t>
<t t-set="classes">text-muted fa-trash-o</t>
<t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/delete'"/>
<t t-set="label" t-value="'Delete'"/>
<t t-set="classes" t-value="'text-muted fa-trash-o'"/>
</t>
</li>
<li t-if="user.id == answer.create_uid.id">
<t t-call="website_forum.link_button">
<t t-set="url"><t t-escf="/forum/#{slug(forum)}/post/#{slug(answer)}/convert_to_comment"/></t>
<t t-set="label">Convert as a comment</t>
<t t-set="classes">text-muted fa-magic</t>
<t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(answer) + '/convert_to_comment'"/>
<t t-set="label" t-value="'Convert as a comment'"/>
<t t-set="classes" t-value="'text-muted fa-magic'"/>
</t>
</li>
</ul>
<span t-field="answer.create_uid.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/>
<img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{answer.create_uid.id}/avatar"/>
<div>
<a t-attf-href="/forum/#{ slug(forum) }/user/#{ answer.create_uid.id }"
t-field="answer.create_uid"
@ -593,16 +600,15 @@
t-attf-data-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/delete"
class="close comment_delete">&amp;times;</button>
<span t-field="message.body"/>
<t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(object) + '/comment/' + slug(message) + '/convert_to_answer'"/>
<t t-set="label" t-value="'Convert as an answer'"/>
<t t-set="classes" t-value="'text-muted fa-magic pull-right'"/>
</t>
<a t-attf-href="/forum/#{slug(forum)}/partner/#{message.author_id.id}"
t-field="message.author_id" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
style="display: inline-block;"/>
on <span t-field="message.date" t-field-options='{"format":"short"}'/>
<t t-call="website_forum.link_button">
<t t-set="url"><t t-escf="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/convert_to_answer"/></t>
<t t-set="label"> Convert as an answer</t>
<t t-set="classes">text-muted fa-magic pull-right</t>
</t>
</small>
</div>
<div class="css_editable_mode_hidden">
@ -692,7 +698,7 @@
</h4>
<div class="row">
<div class="col-sm-3 mt16" t-foreach="users" t-as="user">
<span t-field="user.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/>
<img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{user.id}/avatar"/>
<div>
<a t-attf-href="/forum/#{slug(forum)}/user/#{user.id}" t-field="user.name"/>
</div>
@ -703,9 +709,12 @@
<template id="users">
<t t-call="website_forum.header">
<div class="row">
<div t-foreach="users" t-as="user" class="col-sm-4">
<span t-field="user.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/>
<t t-foreach="users" t-as="user">
<t t-if="user_index % 3 == 0">
<div class="row"></div>
</t>
<div class="col-sm-4">
<img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{user.id}/avatar"/>
<div>
<a t-attf-href="/forum/#{slug(forum)}/user/#{user.id}" t-field="user.name"/>
<t t-if="user.country_id">
@ -725,7 +734,7 @@
<t t-raw="0"/>
</div>
</div>
</div>
</t>
<div class="pull-left">
<t t-call="website.pager"/>
</div>
@ -736,7 +745,7 @@
<t t-call="website_forum.header">
<h3>Edit Profile </h3>
<div class="col-md-2">
<span t-field="user.image" t-field-options='{"widget": "image", "class": "img img-responsive img-circle"}'/>
<img class="img img-responsive img-circle" t-attf-src="/forum/user/#{user.id}/avatar"/>
</div>
<form t-attf-action="/forum/#{slug(forum)}/user/#{slug(user)}/save" method="post" role="form" class="form-horizontal">
<input name="user_id" t-att-value="user.id" type="hidden"/>
@ -786,8 +795,7 @@
</h1>
<div class="row">
<div class="col-sm-2">
<span t-field="user.image"
t-field-options='{"widget": "image", "class": "img img-responsive img-circle"}'/>
<img class="img img-responsive img-circle" t-attf-src="/forum/user/#{user.id}/avatar"/>
</div>
<div class="col-sm-10">
<table class="table table-condensed">

View File

@ -28,7 +28,7 @@ class google_map(http.Controller):
return partner_obj.google_map_json(request.cr, openerp.SUPERUSER_ID,
partner_ids, request.context)
@http.route(['/google_map/set_partner_position'], type='http', auth="public", website=True)
@http.route(['/google_map/set_partner_position'], type='http', methods=['POST'], auth="public", website=True)
def google_map_set_partner_position(self, *arg, **post):
partner_obj = request.registry['res.partner']

View File

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
import re
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp.addons.website_partner.controllers import main as website_partner
from openerp.tools.translate import _
import werkzeug.urls
@ -102,10 +102,15 @@ class WebsiteMembership(http.Controller):
}
return request.website.render("website_membership.index", values)
@http.route(['/members/<int:partner_id>', '/members/<partner_name>-<int:partner_id>'], type='http', auth="public", website=True, multilang=True)
def partners_ref(self, partner_id, **post):
values = website_partner.get_partner_template_value(partner_id)
if not values:
return self.members(**post)
values['main_object'] = values['partner']
return request.website.render("website_membership.partner", values)
# Do not use semantic controller due to SUPERUSER_ID
@http.route(['/members/<partner_id>'], type='http', auth="public", website=True, multilang=True)
def partners_detail(self, partner_id, **post):
mo = re.search('-([-0-9]+)$', str(partner_id))
if mo:
partner_id = int(mo.group(1))
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
if partner.exists() and partner.website_published:
values = {}
values['main_object'] = values['partner'] = partner
return request.website.render("website_membership.partner", values)
return self.customers(**post)

View File

@ -1 +0,0 @@
import main

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
import werkzeug
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.addons.web.http import request
def get_partner_template_value(partner_id):
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
if not partner.exists() or not partner.website_published:
return None
return {
'partner': partner,
}
class WebsitePartner(http.Controller):
@http.route(['/partners/<int:partner_id>', '/partners/<partner_name>-<int:partner_id>'], type='http', auth="public", website=True, multilang=True)
def partner(self, partner_id, partner_name='', **post):
""" Route for displaying a single partner / customer. """
values = get_partner_template_value(partner_id)
if not values:
raise werkzeug.exceptions.NotFound()
return request.website.render("website_partner.partner_page", values)

View File

@ -23,7 +23,7 @@
</t>
<h1 class="col-md-12 text-center" id="partner_name" t-field="partner.display_name"/>
<div class="col-md-4">
<div t-field="partner.image" t-field-options='{"widget": "image", "class": "center-block"}'/>
<div t-field="partner.image" t-field-options='{"widget": "image", "class": "center-block mb16"}'/>
<address class="well">
<div t-field="partner.self" t-field-options='{
"widget": "contact",

View File

@ -64,7 +64,7 @@ class sale_quote(http.Controller):
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id)
if token != order.access_token:
return request.website.render('website.404')
attachments=sign and [('signature.png', sign)] or []
attachments=sign and [('signature.png', sign.decode('base64'))] or []
order_obj.signal_order_confirm(request.cr, SUPERUSER_ID, [order_id], context=request.context)
message = _('Order signed by %s') % (signer,)
self.__message_post(message, order_id, type='comment', subtype='mt_comment', attachments=attachments)

View File

@ -4,7 +4,7 @@
<t t-name="website.Twitter.Tweet">
<div class="tweet" t-attf-data-url="http://twitter.com/#{tweet.user.screen_name}/status/#{tweet.id_str}" t-attf-data-tweet-id="#{tweet.id_str}">
<div class="left">
<img t-att-src="tweet.user.profile_image_url"/>
<img t-att-src="tweet.user.profile_image_url_https"/>
</div>
<div class="right">
<div class="top">