[MERGE] merge bzr saas-4 addons branch
This commit is contained in:
commit
d93f628c72
|
@ -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'):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
#------------------------------------------------------
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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] = [];
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -121,7 +121,7 @@ header a.navbar-brand img {
|
|||
|
||||
/* ----- EDITOR ----- */
|
||||
.css_non_editable_mode_hidden {
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* ----- BOOTSTRAP FIX ----- */
|
||||
|
|
|
@ -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"><?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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&' % 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&' % 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&height=240&partner_ids=#{ google_map_partner_ids }&partner_url=/partners"
|
||||
style="width:320px; height:260px; border:0; padding:0; margin:0;"></iframe>
|
||||
<iframe t-attf-src="/google_map/?width=260&height=240&partner_ids=#{ google_map_partner_ids }&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>
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'"/>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
|
@ -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="{"widget": "duration", "unit": "hour"}"/><br/>
|
||||
<b>Location</b><br/>
|
||||
<span t-field="track.location_id"/><br/>
|
||||
</div>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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')"/>
|
||||
|
|
|
@ -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')" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>=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>=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">&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">
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
import main
|
|
@ -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)
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue