[MERGE] forward port of branch saas-4 up to revid bb26dea

This commit is contained in:
Christophe Simonis 2014-05-20 20:19:55 +02:00
commit a756b82372
84 changed files with 997 additions and 751 deletions

View File

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

View File

@ -21,7 +21,7 @@
from openerp.osv import osv, fields from openerp.osv import osv, fields
from openerp.addons.edi import EDIMixin from openerp.addons.edi import EDIMixin
from urllib import urlencode from werkzeug import url_encode
INVOICE_LINE_EDI_STRUCT = { INVOICE_LINE_EDI_STRUCT = {
'name': True, 'name': True,
@ -274,7 +274,7 @@ class account_invoice(osv.osv, EDIMixin):
"no_note": "1", "no_note": "1",
"bn": "OpenERP_Invoice_PayNow_" + inv.currency_id.name, "bn": "OpenERP_Invoice_PayNow_" + inv.currency_id.name,
} }
res[inv.id] = "https://www.paypal.com/cgi-bin/webscr?" + urlencode(params) res[inv.id] = "https://www.paypal.com/cgi-bin/webscr?" + url_encode(params)
return res return res
_columns = { _columns = {

View File

@ -62,22 +62,28 @@ class account_analytic_invoice_line(osv.osv):
context = context or {} context = context or {}
uom_obj = self.pool.get('product.uom') uom_obj = self.pool.get('product.uom')
company_id = company_id or False 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: if not product:
return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
if partner_id: 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: if part.lang:
context.update({'lang': part.lang}) context.update({'lang': part.lang})
result = {} result = {}
res = self.pool.get('product.product').browse(cr, uid, product, context=context) res = self.pool.get('product.product').browse(cr, uid, product, context=local_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}) 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} res_final = {'value':result}
if result['uom_id'] != res.uom_id.id: 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']) 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 res_final['value']['price_unit'] = new_price
return res_final return res_final

View File

@ -220,7 +220,7 @@ class account_analytic_account(osv.osv):
res['value']['description'] = template.description res['value']['description'] = template.description
return res return res
def on_change_partner_id(self, cr, uid, ids,partner_id, name, context={}): def on_change_partner_id(self, cr, uid, ids,partner_id, name, context=None):
res={} res={}
if partner_id: if partner_id:
partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)

View File

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

View File

@ -170,9 +170,6 @@ class crm_lead(format_address, osv.osv):
""" """
:return dict: difference between current date and log date :return dict: difference between current date and log date
""" """
cal_obj = self.pool.get('resource.calendar')
res_obj = self.pool.get('resource.resource')
res = {} res = {}
for lead in self.browse(cr, uid, ids, context=context): for lead in self.browse(cr, uid, ids, context=context):
for field in fields: for field in fields:
@ -184,39 +181,14 @@ class crm_lead(format_address, osv.osv):
date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S") date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S")
date_open = datetime.strptime(lead.date_open, "%Y-%m-%d %H:%M:%S") date_open = datetime.strptime(lead.date_open, "%Y-%m-%d %H:%M:%S")
ans = date_open - date_create ans = date_open - date_create
date_until = lead.date_open
elif field == 'day_close': elif field == 'day_close':
if lead.date_closed: if lead.date_closed:
date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S") date_create = datetime.strptime(lead.create_date, "%Y-%m-%d %H:%M:%S")
date_close = datetime.strptime(lead.date_closed, "%Y-%m-%d %H:%M:%S") date_close = datetime.strptime(lead.date_closed, "%Y-%m-%d %H:%M:%S")
date_until = lead.date_closed
ans = date_close - date_create ans = date_close - date_create
if ans: if ans:
resource_id = False duration = abs(int(ans.days))
if lead.user_id: res[lead.id][field] = duration
resource_ids = res_obj.search(cr, uid, [('user_id','=',lead.user_id.id)])
if len(resource_ids):
resource_id = resource_ids[0]
duration = float(ans.days)
if lead.section_id and lead.section_id.resource_calendar_id:
duration = float(ans.days) * 24
new_dates = cal_obj.interval_get(cr,
uid,
lead.section_id.resource_calendar_id and lead.section_id.resource_calendar_id.id or False,
datetime.strptime(lead.create_date, '%Y-%m-%d %H:%M:%S'),
duration,
resource=resource_id
)
no_days = []
date_until = datetime.strptime(date_until, '%Y-%m-%d %H:%M:%S')
for in_time, out_time in new_dates:
if in_time.date not in no_days:
no_days.append(in_time.date)
if out_time > date_until:
break
duration = len(no_days)
res[lead.id][field] = abs(int(duration))
return res return res
def _meeting_count(self, cr, uid, ids, field_name, arg, context=None): def _meeting_count(self, cr, uid, ids, field_name, arg, context=None):
Event = self.pool['calendar.event'] Event = self.pool['calendar.event']
@ -261,7 +233,7 @@ class crm_lead(format_address, osv.osv):
'day_open': fields.function(_compute_day, string='Days to Open', \ 'day_open': fields.function(_compute_day, string='Days to Open', \
multi='day_open', type="float", store=True), multi='day_open', type="float", store=True),
'day_close': fields.function(_compute_day, string='Days to Close', \ 'day_close': fields.function(_compute_day, string='Days to Close', \
multi='day_close', type="float", store=True), multi='day_open', type="float", store=True),
'date_last_stage_update': fields.datetime('Last Stage Update', select=True), 'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
# Messaging and marketing # Messaging and marketing
@ -972,7 +944,7 @@ class crm_lead(format_address, osv.osv):
if obj.type == 'opportunity': if obj.type == 'opportunity':
model, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'crm', 'crm_case_form_view_oppor') model, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'crm', 'crm_case_form_view_oppor')
else: else:
view_id = super(crm_lead, self).get_formview_id(cr, uid, id, model='crm.lead', context=context) view_id = super(crm_lead, self).get_formview_id(cr, uid, id, context=context)
return view_id return view_id
def message_get_suggested_recipients(self, cr, uid, ids, context=None): def message_get_suggested_recipients(self, cr, uid, ids, context=None):

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.difference(merged_lead_ids)
active_ids = active_ids.union(remaining_lead_ids) active_ids = active_ids.union(remaining_lead_ids)
ctx['active_ids'] = list(active_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) return self.action_apply(cr, uid, ids, context=ctx)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -108,7 +108,7 @@
<field name="computation_mode">count</field> <field name="computation_mode">count</field>
<field name="display_mode">boolean</field> <field name="display_mode">boolean</field>
<field name="model_id" eval="ref('base.model_res_company')" /> <field name="model_id" eval="ref('base.model_res_company')" />
<field name="domain">[('user_ids', 'in', [user.id]), ('name', '=', 'Your Company')]</field> <field name="domain">[('user_ids', 'in', [user.id]), ('name', '=', 'YourCompany')]</field>
<field name="condition">lower</field> <field name="condition">lower</field>
<field name="action_id" eval="ref('base.action_res_company_form')" /> <field name="action_id" eval="ref('base.action_res_company_form')" />
<field name="res_id_field">user.company_id.id</field> <field name="res_id_field">user.company_id.id</field>

View File

@ -316,19 +316,20 @@ class gamification_challenge(osv.Model):
for challenge in self.browse(cr, uid, ids, context=context): for challenge in self.browse(cr, uid, ids, context=context):
# goals closed but still opened at the last report date if challenge.last_report_date != fields.date.today():
closed_goals_to_report = goal_obj.search(cr, uid, [ # goals closed but still opened at the last report date
('challenge_id', '=', challenge.id), closed_goals_to_report = goal_obj.search(cr, uid, [
('start_date', '>=', challenge.last_report_date), ('challenge_id', '=', challenge.id),
('end_date', '<=', challenge.last_report_date) ('start_date', '>=', challenge.last_report_date),
]) ('end_date', '<=', challenge.last_report_date)
])
if len(closed_goals_to_report) > 0: if challenge.next_report_date and fields.date.today() >= challenge.next_report_date:
# some goals need a final report self.report_progress(cr, uid, challenge, context=context)
self.report_progress(cr, uid, challenge, subset_goal_ids=closed_goals_to_report, context=context)
if fields.date.today() == challenge.next_report_date: elif len(closed_goals_to_report) > 0:
self.report_progress(cr, uid, challenge, context=context) # some goals need a final report
self.report_progress(cr, uid, challenge, subset_goal_ids=closed_goals_to_report, context=context)
self.check_challenge_reward(cr, uid, ids, context=context) self.check_challenge_reward(cr, uid, ids, context=context)
return True return True
@ -446,6 +447,12 @@ class gamification_challenge(osv.Model):
if end_date: if end_date:
values['end_date'] = end_date values['end_date'] = end_date
# the goal is initialised over the limit to make sure we will compute it at least once
if line.condition == 'higher':
values['current'] = line.target_goal - 1
else:
values['current'] = line.target_goal + 1
if challenge.remind_update_delay: if challenge.remind_update_delay:
values['remind_update_delay'] = challenge.remind_update_delay values['remind_update_delay'] = challenge.remind_update_delay

View File

@ -349,8 +349,8 @@ class gamification_goal(osv.Model):
goal = all_goals[goal_id] goal = all_goals[goal_id]
# check goal target reached # check goal target reached
if (goal.definition_condition == 'higher' 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_condition == 'lower' 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' value['state'] = 'reached'
# check goal failure # check goal failure

View File

@ -40,7 +40,7 @@
</record> </record>
<record id="goal_global_multicompany" model="ir.rule"> <record id="goal_global_multicompany" model="ir.rule">
<field name="name">User can only see his/her goals or goal from the same challenge in board visibility</field> <field name="name">Multicompany rule on challenges</field>
<field name="model_id" ref="model_gamification_goal"/> <field name="model_id" ref="model_gamification_goal"/>
<field name="domain_force">[('user_id.company_id', 'child_of', [user.company_id.id])]</field> <field name="domain_force">[('user_id.company_id', 'child_of', [user.company_id.id])]</field>
<field name="global" eval="True"/> <field name="global" eval="True"/>

View File

@ -90,6 +90,11 @@ class hr_holidays_status(osv.osv):
} }
def name_get(self, cr, uid, ids, context=None): def name_get(self, cr, uid, ids, context=None):
if not context.get('employee_id',False):
# leave counts is based on employee_id, would be inaccurate if not based on correct employee
return super(hr_holidays_status, self).name_get(cr, uid, ids, context=context)
res = [] res = []
for record in self.browse(cr, uid, ids, context=context): for record in self.browse(cr, uid, ids, context=context):
name = record.name name = record.name

View File

@ -83,7 +83,7 @@
</div> </div>
</group> </group>
<group> <group>
<field name="holiday_type" on_change="onchange_type(holiday_type)" attrs="{'readonly':[('type', '=', 'remove'),('state','!=','draft')]}" string="Mode" groups="base.group_hr_user"/> <field name="holiday_type" on_change="onchange_type(holiday_type)" attrs="{'readonly':[('type', '=', 'remove'),('state','!=','draft')]}" string="Mode" groups="base.group_hr_user" context="{'employee_id':employee_id}" />
<field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','=','category')]}" on_change="onchange_employee(employee_id)" groups="base.group_hr_user"/> <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','=','category')]}" on_change="onchange_employee(employee_id)" groups="base.group_hr_user"/>
<field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'readonly': [('type', '=', 'remove'),('state','!=','draft'), ('state','!=','confirm')], 'invisible':[('holiday_type','=','employee')]}"/> <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'readonly': [('type', '=', 'remove'),('state','!=','draft'), ('state','!=','confirm')], 'invisible':[('holiday_type','=','employee')]}"/>
<field name="department_id" attrs="{'readonly':['|', ('type','=','add'),('holiday_type','=','category')],'invisible':[('holiday_type','=','category')]}" groups="base.group_hr_user"/> <field name="department_id" attrs="{'readonly':['|', ('type','=','add'),('holiday_type','=','category')],'invisible':[('holiday_type','=','category')]}" groups="base.group_hr_user"/>

View File

@ -6,5 +6,6 @@
device_list = [ device_list = [
{ 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' }, { 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' },
{ 'vendor' : 0x04b8, 'product' : 0x0202, 'name' : 'Epson TM-T70' }, { 'vendor' : 0x04b8, 'product' : 0x0202, 'name' : 'Epson TM-T70' },
{ 'vendor' : 0x04b8, 'product' : 0x0e15, 'name' : 'Epson TM-T20II' },
] ]

View File

@ -176,16 +176,20 @@ class mail_notification(osv.Model):
references = message.parent_id.message_id if message.parent_id else False references = message.parent_id.message_id if message.parent_id else False
# create email values # create email values
mail_values = { max_recipients = 100
'mail_message_id': message.id, chunks = [email_pids[x:x + max_recipients] for x in xrange(0, len(email_pids), max_recipients)]
'auto_delete': True, email_ids = []
'body_html': body_html, for chunk in chunks:
'recipient_ids': [(4, id) for id in email_pids], mail_values = {
'references': references, 'mail_message_id': message.id,
} 'auto_delete': True,
email_notif_id = self.pool.get('mail.mail').create(cr, uid, mail_values, context=context) 'body_html': body_html,
if force_send: 'recipient_ids': [(4, id) for id in chunk],
self.pool.get('mail.mail').send(cr, uid, [email_notif_id], context=context) '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 return True
def _notify(self, cr, uid, message_id, partners_to_notify=None, context=None, def _notify(self, cr, uid, message_id, partners_to_notify=None, context=None,

View File

@ -152,18 +152,8 @@ class mail_mail(osv.Model):
context = {} context = {}
if partner and partner.user_ids: if partner and partner.user_ids:
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') 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 mail_model = mail.model or 'mail.thread'
query = {'db': cr.dbname} url = urljoin(base_url, self.pool[mail_model]._get_access_link(cr, uid, mail, partner, context=context))
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>about <a style='color:inherit' href="%s">%s %s</a></small></span>""") % (url, context.get('model_name', ''), mail.record_name) return _("""<span class='oe_mail_footer_access'><small>about <a style='color:inherit' href="%s">%s %s</a></small></span>""") % (url, context.get('model_name', ''), mail.record_name)
else: else:
return None return None

View File

@ -35,6 +35,7 @@ import socket
import time import time
import xmlrpclib import xmlrpclib
from email.message import Message from email.message import Message
from urllib import urlencode
from openerp import tools from openerp import tools
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
@ -648,6 +649,20 @@ class mail_thread(osv.AbstractModel):
}) })
return action 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 # Email specific
#------------------------------------------------------ #------------------------------------------------------

View File

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

View File

@ -21,12 +21,11 @@
</group> </group>
<group> <group>
<div class="oe_right oe_button_box" name="buttons"> <div class="oe_right oe_button_box" name="buttons">
<button string="Edit Template" name="action_edit_html" type="object"/>
<button name="%(email_template.wizard_email_template_preview)d" string="Preview" <button name="%(email_template.wizard_email_template_preview)d" string="Preview"
type="action" target="new" type="action" target="new"
context="{'template_id':active_id}"/> context="{'template_id':active_id}"/>
<br /> <br />
<!-- <field name="website_link" widget='html' radonly='1'
style='margin: 0px; padding: 0px;'/> -->
</div> </div>
</group> </group>
</group> </group>

View File

@ -580,7 +580,7 @@ class pos_order(osv.osv):
try: try:
self.signal_paid(cr, uid, [order_id]) self.signal_paid(cr, uid, [order_id])
except Exception as e: except Exception as e:
_logger.error('Could not mark POS Order as Paid: %s', tools.ustr(e)) _logger.error('Could not fully process the POS Order: %s', tools.ustr(e))
if to_invoice: if to_invoice:
self.action_invoice(cr, uid, [order_id], context) self.action_invoice(cr, uid, [order_id], context)
@ -744,8 +744,6 @@ class pos_order(osv.osv):
move_obj = self.pool.get('stock.move') move_obj = self.pool.get('stock.move')
for order in self.browse(cr, uid, ids, context=context): for order in self.browse(cr, uid, ids, context=context):
if not order.state=='draft':
continue
addr = order.partner_id and partner_obj.address_get(cr, uid, [order.partner_id.id], ['delivery']) or {} addr = order.partner_id and partner_obj.address_get(cr, uid, [order.partner_id.id], ['delivery']) or {}
picking_type = order.picking_type_id picking_type = order.picking_type_id
picking_id = False picking_id = False

View File

@ -146,6 +146,9 @@ function openerp_pos_db(instance, module){
var product = products[i]; var product = products[i];
var search_string = this._product_search_string(product); var search_string = this._product_search_string(product);
var categ_id = product.public_categ_id ? product.public_categ_id[0] : this.root_category_id; 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]){ if(!stored_categories[categ_id]){
stored_categories[categ_id] = []; stored_categories[categ_id] = [];
} }

View File

@ -214,7 +214,7 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
return self.fetch( return self.fetch(
'product.product', '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'], 'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description'],
[['sale_ok','=',true],['available_in_pos','=',true]], [['sale_ok','=',true],['available_in_pos','=',true]],
{pricelist: self.pricelist.id} // context for price {pricelist: self.pricelist.id} // context for price

View File

@ -895,14 +895,10 @@ class product_product(osv.osv):
return res return res
def copy(self, cr, uid, id, default=None, context=None): def copy(self, cr, uid, id, default=None, context=None):
context = context or {} if context is None:
default = dict(default or {}) context={}
# Craft our own `<name> (copy)` in en_US (self.copy_translation() product = self.browse(cr, uid, id, context)
# will do the other languages).
context_wo_lang = dict(context or {})
context_wo_lang.pop('lang', None)
product = self.browse(cr, uid, id, context_wo_lang)
if context.get('variant'): if context.get('variant'):
# if we copy a variant or create one, we keep the same template # if we copy a variant or create one, we keep the same template
default['product_tmpl_id'] = product.product_tmpl_id.id default['product_tmpl_id'] = product.product_tmpl_id.id

View File

@ -22,7 +22,7 @@ from openerp.osv import osv, fields
from openerp.addons.edi import EDIMixin from openerp.addons.edi import EDIMixin
from openerp.tools.translate import _ from openerp.tools.translate import _
from urllib import urlencode from werkzeug import url_encode
SALE_ORDER_LINE_EDI_STRUCT = { SALE_ORDER_LINE_EDI_STRUCT = {
'sequence': True, 'sequence': True,
@ -197,7 +197,7 @@ class sale_order(osv.osv, EDIMixin):
"no_note": "1", "no_note": "1",
"bn": "OpenERP_Order_PayNow_" + order.pricelist_id.currency_id.name, "bn": "OpenERP_Order_PayNow_" + order.pricelist_id.currency_id.name,
} }
res[order.id] = "https://www.paypal.com/cgi-bin/webscr?" + urlencode(params) res[order.id] = "https://www.paypal.com/cgi-bin/webscr?" + url_encode(params)
return res return res
_columns = { _columns = {

View File

@ -21,8 +21,8 @@
<search string="Sales Analysis"> <search string="Sales Analysis">
<field name="date"/> <field name="date"/>
<field name="date_confirm"/> <field name="date_confirm"/>
<filter icon="terp-document-new" name="Quotations" domain="[('state','=','draft')]"/> <filter icon="terp-document-new" name="Quotations" domain="[('state','in',('draft','sent'))]"/>
<filter icon="terp-check" name="Sales" string="Sales" domain="[('state','not in',('draft','done','cancel'))]"/> <filter icon="terp-check" name="Sales" string="Sales" domain="[('state','not in',('draft','sent','cancel'))]"/>
<separator/> <separator/>
<filter icon="terp-personal" string="My Sales" help="My Sales" domain="[('user_id','=',uid)]"/> <filter icon="terp-personal" string="My Sales" help="My Sales" domain="[('user_id','=',uid)]"/>
<field name="partner_id"/> <field name="partner_id"/>

View File

@ -358,12 +358,12 @@ class sale_order_line(osv.osv):
uom = False uom = False
if not uom2: if not uom2:
uom2 = product_obj.uom_id uom2 = product_obj.uom_id
compare_qty = float_compare(product_obj.virtual_available * uom2.factor, qty * product_obj.uom_id.factor, precision_rounding=product_obj.uom_id.rounding) compare_qty = float_compare(product_obj.virtual_available, qty, precision_rounding=uom2.rounding)
if (product_obj.type=='product') and int(compare_qty) == -1: if (product_obj.type=='product') and int(compare_qty) == -1:
warn_msg = _('You plan to sell %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') % \ warn_msg = _('You plan to sell %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') % \
(qty, uom2 and uom2.name or product_obj.uom_id.name, (qty, uom2.name,
max(0,product_obj.virtual_available), product_obj.uom_id.name, max(0,product_obj.virtual_available), uom2.name,
max(0,product_obj.qty_available), product_obj.uom_id.name) max(0,product_obj.qty_available), uom2.name)
warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n" warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n"
#update of warning messages #update of warning messages

View File

@ -234,7 +234,7 @@ class WebsiteSurvey(http.Controller):
# AJAX submission of a page # AJAX submission of a page
@http.route(['/survey/submit/<model("survey.survey"):survey>'], @http.route(['/survey/submit/<model("survey.survey"):survey>'],
type='http', auth='public', website=True) type='http', methods=['POST'], auth='public', website=True)
def submit(self, survey, **post): def submit(self, survey, **post):
_logger.debug('Incoming data: %s', post) _logger.debug('Incoming data: %s', post)
page_id = int(post['page_id']) page_id = int(post['page_id'])
@ -310,7 +310,8 @@ class WebsiteSurvey(http.Controller):
current_filters = survey_obj.filter_input_ids(request.cr, request.uid, filter_data, filter_finish, context=request.context) current_filters = survey_obj.filter_input_ids(request.cr, request.uid, filter_data, filter_finish, context=request.context)
filter_display_data = survey_obj.get_filter_display_data(request.cr, request.uid, filter_data, context=request.context) filter_display_data = survey_obj.get_filter_display_data(request.cr, request.uid, filter_data, context=request.context)
return request.website.render(result_template, return request.website.render(result_template,
{'survey_dict': self.prepare_result_dict(survey, current_filters), {'survey': survey,
'survey_dict': self.prepare_result_dict(survey, current_filters),
'page_range': self.page_range, 'page_range': self.page_range,
'current_filters': current_filters, 'current_filters': current_filters,
'filter_display_data': filter_display_data, 'filter_display_data': filter_display_data,

View File

@ -46,6 +46,8 @@ instance.web.Notification = instance.web.Widget.extend({
} }
}); });
var opened_modal = [];
instance.web.action_notify = function(element, action) { instance.web.action_notify = function(element, action) {
element.do_notify(action.params.title, action.params.text, action.params.sticky); element.do_notify(action.params.title, action.params.text, action.params.sticky);
}; };
@ -113,6 +115,8 @@ instance.web.Dialog = instance.web.Widget.extend({
this.init_dialog(); this.init_dialog();
} }
this.$buttons.insertAfter(this.$dialog_box.find(".modal-body")); this.$buttons.insertAfter(this.$dialog_box.find(".modal-body"));
//add to list of currently opened modal
opened_modal.push(this.$dialog_box);
return this; return this;
}, },
_add_buttons: function(buttons) { _add_buttons: function(buttons) {
@ -152,7 +156,7 @@ instance.web.Dialog = instance.web.Widget.extend({
'keyboard': true, 'keyboard': true,
}); });
if (options.size !== 'large'){ if (options.size !== 'large'){
var dialog_class_size = this.$dialog_box.find('.modal-lg').removeClass('modal-lg') var dialog_class_size = this.$dialog_box.find('.modal-lg').removeClass('modal-lg');
if (options.size === 'small'){ if (options.size === 'small'){
dialog_class_size.addClass('modal-sm'); dialog_class_size.addClass('modal-sm');
} }
@ -165,7 +169,7 @@ instance.web.Dialog = instance.web.Widget.extend({
} }
$dialog_content.openerpClass(); $dialog_content.openerpClass();
this.$dialog_box.on('hidden.bs.modal', this, function(){ this.$dialog_box.on('hidden.bs.modal', this, function() {
self.close(); self.close();
}); });
this.$dialog_box.modal('show'); this.$dialog_box.modal('show');
@ -175,13 +179,15 @@ instance.web.Dialog = instance.web.Widget.extend({
return res; return res;
}, },
/** /**
Closes the popup, if destroy_on_close was passed to the constructor, it is also destroyed. Closes (hide) the popup, if destroy_on_close was passed to the constructor, it will be destroyed instead.
*/ */
close: function(reason) { close: function(reason) {
if (this.dialog_inited) { if (this.dialog_inited && !this.__tmp_dialog_hiding) {
this.trigger("closing", reason); this.trigger("closing", reason);
if (this.$el.is(":data(bs.modal)")) { // may have been destroyed by closing signal if (this.$el.is(":data(bs.modal)")) { // may have been destroyed by closing signal
this.$el.parents('.modal').modal('hide'); this.__tmp_dialog_hiding = true;
this.$dialog_box.modal('hide');
this.__tmp_dialog_hiding = undefined;
} }
} }
}, },
@ -212,9 +218,17 @@ instance.web.Dialog = instance.web.Widget.extend({
//we need this to put the instruction to remove modal from DOM at the end //we need this to put the instruction to remove modal from DOM at the end
//of the queue, otherwise it might already have been removed before the modal-backdrop //of the queue, otherwise it might already have been removed before the modal-backdrop
//is removed when pressing escape key //is removed when pressing escape key
var $parent = this.$el.parents('.modal'); var $element = this.$dialog_box;
setTimeout(function () { setTimeout(function () {
$parent.remove(); //remove modal from list of opened modal since we just destroy it
var modal_list_index = $.inArray($element, opened_modal);
if (modal_list_index > -1){
opened_modal.splice(modal_list_index,1)[0].remove();
}
if (opened_modal.length > 0){
//we still have other opened modal so we should focus it
opened_modal[opened_modal.length-1].focus();
}
},0); },0);
} }
this._super(); this._super();
@ -239,8 +253,7 @@ instance.web.CrashManager = instance.web.Class.extend({
this.show_warning({type: "Session Expired", data: { message: _t("Your OpenERP session expired. Please refresh the current web page.") }}); this.show_warning({type: "Session Expired", data: { message: _t("Your OpenERP session expired. Please refresh the current web page.") }});
return; return;
} }
if (error.data.exception_type === "except_osv" || error.data.exception_type === "warning" if (error.data.exception_type === "except_osv" || error.data.exception_type === "warning" || error.data.exception_type === "access_error") {
|| error.data.exception_type === "access_error") {
this.show_warning(error); this.show_warning(error);
} else { } else {
this.show_error(error); this.show_error(error);

View File

@ -1,24 +1,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import cStringIO import cStringIO
import contextlib import datetime
import hashlib from itertools import islice
import json import json
import logging import logging
import os
import datetime
import re import re
from sys import maxint from sys import maxint
import werkzeug
import werkzeug.exceptions
import werkzeug.utils import werkzeug.utils
import werkzeug.wrappers import werkzeug.wrappers
from PIL import Image from PIL import Image
import openerp import openerp
from openerp.osv import fields
from openerp.addons.website.models import website
from openerp.addons.web import http from openerp.addons.web import http
from openerp.http import request, Response from openerp.http import request, Response
@ -27,6 +21,7 @@ logger = logging.getLogger(__name__)
# Completely arbitrary limits # Completely arbitrary limits
MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768) MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768)
LOC_PER_SITEMAP = 45000 LOC_PER_SITEMAP = 45000
SITEMAP_CACHE_TIME = datetime.timedelta(hours=12)
class Website(openerp.addons.web.controllers.main.Home): class Website(openerp.addons.web.controllers.main.Home):
#------------------------------------------------------ #------------------------------------------------------
@ -52,7 +47,7 @@ class Website(openerp.addons.web.controllers.main.Home):
# TODO: can't we just put auth=public, ... in web client ? # TODO: can't we just put auth=public, ... in web client ?
return super(Website, self).web_login(*args, **kw) return super(Website, self).web_login(*args, **kw)
@http.route('/page/<page:page>', type='http', auth="public", website=True) @http.route('/page/<path:page>', type='http', auth="public", website=True)
def page(self, page, **opt): def page(self, page, **opt):
values = { values = {
'path': page, 'path': page,
@ -78,33 +73,64 @@ class Website(openerp.addons.web.controllers.main.Home):
@http.route('/sitemap.xml', type='http', auth="public", website=True) @http.route('/sitemap.xml', type='http', auth="public", website=True)
def sitemap_xml_index(self): def sitemap_xml_index(self):
pages = list(request.website.enumerate_pages()) cr, uid, context = request.cr, openerp.SUPERUSER_ID, request.context
if len(pages)<=LOC_PER_SITEMAP: ira = request.registry['ir.attachment']
return self.__sitemap_xml(pages, 0) iuv = request.registry['ir.ui.view']
# Sitemaps must be split in several smaller files with a sitemap index mimetype ='application/xml;charset=utf-8'
values = { content = None
'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)
@http.route('/sitemap-<int:page>.xml', type='http', auth="public", website=True) def create_sitemap(url, content):
def sitemap_xml(self, page): ira.create(cr, uid, dict(
pages = list(request.website.enumerate_pages()) datas=content.encode('base64'),
return self.__sitemap_xml(pages, page) mimetype=mimetype,
type='binary',
name=url,
url=url,
), context=context)
def __sitemap_xml(self, pages, index=0): sitemap = ira.search_read(cr, uid, [('url', '=' , '/sitemap.xml'), ('type', '=', 'binary')], ('datas', 'create_date'), context=context)
values = { if sitemap:
'pages': pages[index*LOC_PER_SITEMAP:(index+1)*LOC_PER_SITEMAP], # Check if stored version is still valid
'url_root': request.httprequest.url_root.rstrip('/') server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
} create_date = datetime.datetime.strptime(sitemap[0]['create_date'], server_format)
headers = { delta = datetime.datetime.now() - create_date
'Content-Type': 'application/xml;charset=utf-8', if delta < SITEMAP_CACHE_TIME:
} content = sitemap[0]['datas'].decode('base64')
return request.render('website.sitemap_xml', values, headers=headers)
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 # Edit
@ -332,13 +358,7 @@ class Website(openerp.addons.web.controllers.main.Home):
return request.website.kanban_col(**post) return request.website.kanban_col(**post)
def placeholder(self, response): def placeholder(self, response):
# file_open may return a StringIO. StringIO can be closed but are return request.registry['website']._image_placeholder(response)
# 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)
@http.route([ @http.route([
'/website/image', '/website/image',
@ -354,60 +374,15 @@ class Website(openerp.addons.web.controllers.main.Home):
Sets and checks conditional response parameters: Sets and checks conditional response parameters:
* :mailheader:`ETag` is always set (and checked) * :mailheader:`ETag` is always set (and checked)
* :mailheader:`Last-Modified is set iif the record has a concurrency * :mailheader:`Last-Modified is set iif the record has a concurrency
field (``write_date``) field (``__last_update``)
The requested field is assumed to be base64-encoded image data in The requested field is assumed to be base64-encoded image data in
all cases. all cases.
""" """
id = int(id)
response = werkzeug.wrappers.Response() response = werkzeug.wrappers.Response()
concurrency = 'write_date' return request.registry['website']._image(
try: request.cr, request.uid, model, id, field, response)
[record] = request.registry[model].read(request.cr, openerp.SUPERUSER_ID, [id],
[concurrency, field],
context=request.context)
except:
return self.placeholder(response)
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[field].decode('base64')
if (not max_width) and (not max_height):
response.data = data
return response
image = Image.open(cStringIO.StringIO(data))
response.mimetype = Image.MIME[image.format]
w, h = image.size
max_w, max_h = int(max_width), int(max_height)
if w < max_w and h < max_h:
response.data = data
else:
image.thumbnail((max_w, max_h), Image.ANTIALIAS)
image.save(response.stream, image.format)
del response.headers['Content-Length']
return response
#------------------------------------------------------ #------------------------------------------------------
# Server actions # Server actions

View File

@ -1,13 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import cStringIO
import contextlib
import datetime
import hashlib import hashlib
import inspect import inspect
import itertools import itertools
import logging import logging
import math import math
import mimetypes import mimetypes
import os
import re import re
import urlparse import urlparse
from PIL import Image
from sys import maxint
import werkzeug import werkzeug
import werkzeug.exceptions import werkzeug.exceptions
import werkzeug.utils import werkzeug.utils
@ -459,6 +466,95 @@ class website(osv.osv):
html += request.website._render(template, {'object_id': object_id}) html += request.website._render(template, {'object_id': object_id})
return html 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)
concurrency = '__last_update'
[record] = Model.read(cr, openerp.SUPERUSER_ID, [id],
[concurrency, field],
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[field].decode('base64')
if (not max_width) and (not max_height):
response.data = data
return response
image = Image.open(cStringIO.StringIO(data))
response.mimetype = Image.MIME[image.format]
w, h = image.size
max_w, max_h = int(max_width), int(max_height)
if w < max_w and h < max_h:
response.data = data
else:
image.thumbnail((max_w, max_h), 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): class website_menu(osv.osv):
_name = "website.menu" _name = "website.menu"
_description = "Website Menu" _description = "Website Menu"

View File

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

View File

@ -41,6 +41,9 @@ class CrawlSuite(unittest2.TestSuite):
starting the crawl starting the crawl
""" """
at_install = False
post_install = True
def __init__(self, user=None, password=None): def __init__(self, user=None, password=None):
super(CrawlSuite, self).__init__() super(CrawlSuite, self).__init__()
@ -108,14 +111,23 @@ class CrawlSuite(unittest2.TestSuite):
for link in doc.xpath('//a[@href]'): for link in doc.xpath('//a[@href]'):
href = link.get('href') href = link.get('href')
parts = urlparse.urlsplit(href)
# href with any fragment removed
href = urlparse.urlunsplit((
parts.scheme,
parts.netloc,
parts.path,
parts.query,
''
))
# avoid repeats, even for links we won't crawl no need to # avoid repeats, even for links we won't crawl no need to
# bother splitting them if we've already ignored them # bother splitting them if we've already ignored them
# previously # previously
if href in seen: continue if href in seen: continue
seen.add(href) seen.add(href)
parts = urlparse.urlsplit(href) # FIXME: handle relative link (not parts.path.startswith /)
if parts.netloc or \ if parts.netloc or \
not parts.path.startswith('/') or \ not parts.path.startswith('/') or \
parts.path == '/web' or\ parts.path == '/web' or\

View File

@ -709,14 +709,18 @@ User-agent: *
Sitemap: <t t-esc="url_root"/>sitemap.xml Sitemap: <t t-esc="url_root"/>sitemap.xml
</template> </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; <template id="sitemap_xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url t-foreach="pages" t-as="page"> <t t-raw="content"/>
<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>
</urlset> </urlset>
</template> </template>

View File

@ -51,7 +51,7 @@ class WebsiteBlog(http.Controller):
blog_post_obj = request.registry['blog.post'] blog_post_obj = request.registry['blog.post']
groups = blog_post_obj.read_group( groups = blog_post_obj.read_group(
request.cr, request.uid, [], ['name', 'create_date'], request.cr, request.uid, [], ['name', 'create_date'],
groupby="create_date", orderby="create_date asc", context=request.context) groupby="create_date", orderby="create_date desc", context=request.context)
for group in groups: for group in groups:
begin_date = datetime.datetime.strptime(group['__domain'][0][2], tools.DEFAULT_SERVER_DATETIME_FORMAT).date() begin_date = datetime.datetime.strptime(group['__domain'][0][2], tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
end_date = datetime.datetime.strptime(group['__domain'][1][2], tools.DEFAULT_SERVER_DATETIME_FORMAT).date() end_date = datetime.datetime.strptime(group['__domain'][1][2], tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
@ -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) 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) 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) blog_posts = blog_post_obj.browse(cr, uid, blog_post_ids, context=context)
pager = request.website.pager( pager = request.website.pager(

View File

@ -29,11 +29,12 @@
}, },
}), }),
edit: function () { edit: function () {
var self = this;
$('.popover').remove(); $('.popover').remove();
this._super(); this._super();
var vHeight = $(window).height(); var vHeight = $(window).height();
$('body').on('click','#change_cover',_.bind(this.change_bg,{},vHeight)); $('body').on('click','#change_cover',_.bind(this.change_bg, self.rte.editor, vHeight));
$('body').on('click', '#clear_cover',_.bind(this.clean_bg,{},vHeight)); $('body').on('click', '#clear_cover',_.bind(this.clean_bg, self.rte.editor, vHeight));
}, },
save : function() { save : function() {
var res = this._super(); var res = this._super();
@ -50,12 +51,12 @@
}, },
change_bg : function(vHeight) { change_bg : function(vHeight) {
var self = this; var self = this;
var editor = new website.editor.ImageDialog(); var element = new CKEDITOR.dom.element(self.element.find('.cover-storage').$[0]);
editor.on('start', self, function (o) { var editor = new website.editor.MediaDialog(self, element);
o.url = $('.js_fullheight').length ? $('.js_fullheight').css('background-image').replace(/url\(|\)|"|'/g,'') : ''; $(document.body).on('media-saved', self, function (o) {
}); var url = $('.cover-storage').attr('src');
editor.on('save', self, function (o) { $('.js_fullheight').css({"background-image": !_.isUndefined(url) ? 'url(' + url + ')' : "", 'min-height': vHeight});
$('.js_fullheight').css({"background-image": o.url && o.url !== "" ? 'url(' + o.url + ')' : "", 'min-height': vHeight}) $('.cover-storage').remove();
}); });
editor.appendTo('body'); editor.appendTo('body');
}, },

View File

@ -49,7 +49,7 @@
<t t-foreach="posts" t-as="post"> <t t-foreach="posts" t-as="post">
<div class="col-md-4"> <div class="col-md-4">
<h4> <h4>
<a t-attf-href="#{blog_url('', ['blogpost'], blogpost=post)}" t-field="post.name"></a> <a t-attf-href="#{blog_url('', ['blog', 'post'], blog=post.blog_id, post=post)}" t-field="post.name"></a>
<span t-if="not post.website_published" class="text-warning"> <span t-if="not post.website_published" class="text-warning">
&amp;nbsp; &amp;nbsp;
<span class="fa fa-warning" title="Not published"/> <span class="fa fa-warning" title="Not published"/>
@ -187,6 +187,7 @@
<span class="fa fa-times"/> <span class="fa fa-times"/>
</a> </a>
</div> </div>
<div class="cover-storage oe_hidden"></div>
<t t-call="website.publish_management"> <t t-call="website.publish_management">
<t t-set="object" t-value="blog_post"/> <t t-set="object" t-value="blog_post"/>
<t t-set="publish_edit" t-value="True"/> <t t-set="publish_edit" t-value="True"/>

View File

@ -16,45 +16,36 @@ class contactus(http.Controller):
) )
return url return url
@http.route(['/page/website.contactus'], type='http', auth="public", website=True) @http.route(['/page/website.contactus', '/page/contactus'], type='http', auth="public", website=True)
def contact(self, **kwargs): def contact(self, **kwargs):
values = {} values = {}
for field in ['description', 'partner_name', 'phone', 'contact_name', 'email_from', 'name']: for field in ['description', 'partner_name', 'phone', 'contact_name', 'email_from', 'name']:
if kwargs.get(field): if kwargs.get(field):
values[field] = kwargs.pop(field) values[field] = kwargs.pop(field)
values.update(kwargs=kwargs.items()) values.update(kwargs=kwargs.items())
print values
return request.website.render("website.contactus", values) return request.website.render("website.contactus", values)
@http.route(['/crm/contactus'], type='http', auth="public", website=True) @http.route(['/crm/contactus'], type='http', auth="public", website=True)
def contactus(self, description=None, partner_name=None, phone=None, contact_name=None, email_from=None, name=None, **kwargs): def contactus(self, description=None, partner_name=None, phone=None, contact_name=None, email_from=None, name=None, **kwargs):
post = {} post = {
post['description'] = description 'description': description,
post['partner_name'] = partner_name 'partner_name': partner_name,
post['phone'] = phone 'phone': phone,
post['contact_name'] = contact_name 'contact_name': contact_name,
post['email_from'] = email_from 'email_from': email_from,
post['name'] = name 'name': name or contact_name,
'user_id': False,
required_fields = ['contact_name', 'email_from', 'description'] }
error = set()
values = dict((key, post.get(key)) for key in post)
values['error'] = error
# fields validation # fields validation
for field in required_fields: error = set(field for field in ['contact_name', 'email_from', 'description']
if not post.get(field): if not post.get(field))
error.add(field)
values = dict(post, error=error)
if error: if error:
values.update(kwargs=kwargs.items()) values.update(kwargs=kwargs.items())
return request.website.render("website.contactus", values) return request.website.render("website.contactus", values)
# if not given: subject is contact name
if not post.get('name'):
post['name'] = post.get('contact_name')
post['user_id'] = False
try: try:
post['channel_id'] = request.registry['ir.model.data'].get_object_reference(request.cr, SUPERUSER_ID, 'crm', 'crm_case_channel_website')[1] post['channel_id'] = request.registry['ir.model.data'].get_object_reference(request.cr, SUPERUSER_ID, 'crm', 'crm_case_channel_website')[1]
except ValueError: except ValueError:

View File

@ -10,6 +10,7 @@ Publish and Assign Partner
'author': 'OpenERP SA', 'author': 'OpenERP SA',
'depends': ['crm_partner_assign','website_partner', 'website_google_map'], 'depends': ['crm_partner_assign','website_partner', 'website_google_map'],
'data': [ 'data': [
'views/partner_grade.xml',
'views/website_crm_partner_assign.xml', 'views/website_crm_partner_assign.xml',
], ],
'qweb': ['static/src/xml/*.xml'], 'qweb': ['static/src/xml/*.xml'],

View File

@ -1,121 +1,172 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
import re
import werkzeug 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 import SUPERUSER_ID
from openerp.addons.web import http from openerp.addons.web import http
from openerp.tools.translate import _
from openerp.addons.web.http import request 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): 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([ @http.route([
'/partners', '/partners',
'/partners/page/<int:page>', '/partners/page/<int:page>',
'/partners/grade/<int:grade_id>', '/partners/grade/<model("res.partner.grade"):grade>',
'/partners/grade/<int:grade_id>/page/<int:page>', '/partners/grade/<model("res.partner.grade"):grade>/page/<int:page>',
'/partners/country/<int:country_id>', '/partners/country/<model("res.country"):country>',
'/partners/country/<country_name>-<int:country_id>', '/partners/country/<model("res.country"):country>/page/<int:page>',
'/partners/country/<int:country_id>/page/<int:page>',
'/partners/country/<country_name>-<int:country_id>/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>',
'/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>',
], type='http', auth="public", website=True) ], type='http', auth="public", website=True)
def partners(self, country_id=0, grade_id=0, page=0, **post): def partners(self, country=None, grade=None, page=0, **post):
country_obj = request.registry['res.country'] country_all = post.pop('country_all', False)
partner_obj = request.registry['res.partner'] partner_obj = request.registry['res.partner']
post_name = post.get('search', '') country_obj = request.registry['res.country']
country = None search = post.get('search', '')
# format displayed membership lines domain base_partner_domain = [('is_company', '=', True), ('grade_id.website_published', '=', True), ('website_published', '=', True)]
base_partner_domain = [('is_company', '=', True), ('grade_id', '!=', False), ('website_published', '=', True)] if search:
partner_domain = list(base_partner_domain) base_partner_domain += ['|', ('name', 'ilike', search), ('website_description', 'ilike', search)]
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_ids = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, partner_domain,
context=request.context)
pager = request.website.pager(url="/partners", total=len(partner_ids), page=page, step=self._references_per_page, scope=7, url_args=post)
# search for partners to display
partners_data = partner_obj.search_read(request.cr, openerp.SUPERUSER_ID,
domain=partner_domain,
fields=request.website.get_partner_white_list_fields(),
offset=pager['offset'],
limit=self._references_per_page,
order="grade_id DESC,partner_weight DESC",
context=request.context)
google_map_partner_ids = ",".join([str(p['id']) for p in partners_data])
# group by grade # 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( 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", context=request.context) groupby="grade_id", orderby="grade_id DESC", context=request.context)
grades_partners = partner_obj.search( grades_partners = partner_obj.search(
request.cr, openerp.SUPERUSER_ID, base_partner_domain, request.cr, SUPERUSER_ID, grade_domain,
context=request.context, count=True) 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, { grades.insert(0, {
'grade_id_count': grades_partners, '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 = { values = {
'countries': countries, 'countries': countries,
'current_country_id': country_id,
'current_country': country, 'current_country': country,
'grades': grades, 'grades': grades,
'grade_id': grade_id, 'current_grade': grade,
'partners_data': partners_data, 'partners': partners,
'google_map_partner_ids': google_map_partner_ids, 'google_map_partner_ids': google_map_partner_ids,
'pager': pager, 'pager': pager,
'searches': post, '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) return request.website.render("website_crm_partner_assign.index", values)
# Do not use semantic controller due to SUPERUSER_ID # 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) @http.route(['/partners/<partner_id>'], type='http', auth="public", website=True)
def partners_ref(self, partner_id, **post): def partners_detail(self, partner_id, partner_name='', **post):
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context) mo = re.search('-([-0-9]+)$', str(partner_id))
values = website_partner.get_partner_template_value(partner) current_grade, current_country = None, None
if not values: grade_id = post.get('grade_id')
return self.partners(**post) country_id = post.get('country_id')
values['main_object'] = values['partner'] if grade_id:
return request.website.render("website_crm_partner_assign.partner", values) 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

@ -1,2 +1 @@
import res_partner import res_partner
import website

View File

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from openerp.osv import osv, fields from openerp.osv import osv, fields
class res_partner_grade(osv.osv): class res_partner_grade(osv.osv):
_inherit = 'res.partner.grade' _inherit = 'res.partner.grade'
_columns = { _columns = {
'website_description': fields.html('Description for the website'), 'website_published': fields.boolean('Published On Website'),
} }

View File

@ -1,11 +0,0 @@
# -*- coding: utf-8 -*-
from openerp.osv import orm
class Website(orm.Model):
_inherit = 'website'
def get_partner_white_list_fields(self, cr, uid, ids, context=None):
fields = super(Website, self).get_partner_white_list_fields(cr, uid, ids, context=context)
fields += ["grade_id"]
return fields

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record model="ir.ui.view" id="partner_grade_view">
<field name="name">res.partner.grade.website</field>
<field name="model">res.partner.grade</field>
<field name="inherit_id" ref="crm_partner_assign.view_partner_grade_form"/>
<field name="arch" type="xml">
<field name="active" position="after">
<field name="website_published"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@ -15,7 +15,7 @@
<t t-set="additional_title">Resellers</t> <t t-set="additional_title">Resellers</t>
<div id="wrap"> <div id="wrap">
<div class="oe_structure"/> <div class="oe_structure"/>
<div class="container"> <div class="container mt16">
<div class="row"> <div class="row">
<t t-raw="ref_content" /> <t t-raw="ref_content" />
</div> </div>
@ -36,13 +36,13 @@
</h2> </h2>
</div> </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"> <ul id="reseller_grades" class="nav nav-pills nav-stacked mt16">
<li class="nav-header"><h3>Categories</h3></li> <li class="nav-header"><h3>Filter by Grade</h3></li>
<t t-foreach="grades" t-as="grade"> <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 ''"> <li t-att-class="grade['active'] 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 }"> <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 ''"/> <span class="badge pull-right" t-esc="grade['grade_id_count'] or ''"/>
<t t-esc="grade['grade_id'][1]"/> <t t-esc="grade['grade_id'][1]"/>
</a> </a>
@ -51,58 +51,63 @@
</ul> </ul>
<ul id="reseller_countries" class="nav nav-pills nav-stacked mt16"> <ul id="reseller_countries" class="nav nav-pills nav-stacked mt16">
<li class="nav-header"><h3>Locations</h3></li> <li class="nav-header"><h3>Filter by Country</h3></li>
<t t-foreach="countries" t-as="country_dict"> <t t-foreach="countries" t-as="country">
<t t-if="country_dict['country_id']"> <li t-if="country['country_id']" t-att-class="country['active'] and 'active' or ''">
<li t-att-class="country_dict['country_id'][0] == current_country_id 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 '')}">
<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['country_id_count'] or ''"/>
<span class="badge pull-right" t-esc="country_dict['country_id_count'] or ''"/> <t t-esc="country['country_id'][1]"/>
<t t-esc="country_dict['country_id'][1]"/> </a>
</a> </li>
</li>
</t>
</t> </t>
</ul> </ul>
</div> </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 class='navbar'>
<div> <div>
<t t-call="website.pager"> <t t-call="website.pager"/>
<t t-set="classname">pull-left</t>
</t>
<form action="" method="get" class="navbar-search pull-right pagination form-inline"> <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', '')"/> <input type="text" name="search" class="search-query col-md-2 mt4 form-control" placeholder="Search" t-att-value="searches.get('search', '')"/>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<div> <div>
<t t-if="not partners_data"> <p t-if="not partners">No result found</p>
<p>No result found.</p> <t t-foreach="partners" t-as="partner">
</t> <t t-if="last_grade != partner.grade_id.id">
<t t-foreach="partners_data" t-as="partner_data">
<t t-if="internal_gid != partner_data['grade_id'][1]">
<h3 class="text-center"> <h3 class="text-center">
<span t-esc="partner_data['grade_id'][1]"/> Partners <span t-field="partner.grade_id"/> Partners
<t t-if="current_country"> in <t t-esc="current_country.name"/></t>
</h3> </h3>
<t t-set="internal_gid" t-value="partner_data['grade_id'][1]"/> <t t-set="last_grade" t-value="partner.grade_id.id"/>
</t> </t>
<div class="media"> <div class="media">
<a class="pull-left" t-attf-href="/partners/#{ slug([partner_data.get('id'), partner_data.get('name')]) }"> <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}"
<img class="media-object" t-attf-src="data:image/png;base64,#{partner_data['image_small']}"/> t-field="partner.image_small"
</a> t-field-options='{"widget": "image", "class": "media-object"}'
<div class="media-body" style="min-height: 64px;"> ></a>
<a class="media-heading" t-attf-href="/partners/#{ slug([partner_data.get('id'), partner_data.get('name')]) }"><t t-if="partner_data['parent_id']"><span t-esc="partner_data['parent_id'][1]"/></t> <span t-esc="partner_data['name']"/></a> - <span t-esc="partner_data['grade_id'][1]"/> <div class="media-body o_partner_body" style="min-height: 64px;">
<div t-esc="partner_data['website_short_description']"/> <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>
</div> </div>
</t> </t>
</div> </div>
<div class='navbar'>
<t t-call="website.pager">
<t t-set="classname">pull-left</t>
</t>
</div>
</div> </div>
</t> </t>
</t> </t>
@ -112,8 +117,8 @@
<xpath expr="//ul[@id='reseller_countries']" position="after"> <xpath expr="//ul[@id='reseller_countries']" position="after">
<h3>World Map</h3> <h3>World Map</h3>
<ul class="nav"> <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" <iframe t-attf-src="/google_map/?width=260&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> style="width:260px; height:260px; border:0; padding:0; margin:0;"></iframe>
</ul> </ul>
</xpath> </xpath>
</template> </template>
@ -121,10 +126,46 @@
<template id="partner" name="Partner Detail"> <template id="partner" name="Partner Detail">
<t t-call="website_crm_partner_assign.layout"> <t t-call="website_crm_partner_assign.layout">
<t t-set="ref_content"> <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>
</t> </t>
</template> </template>
<template id="grade_in_detail" inherit_id="website_partner.partner_detail">
<xpath expr="//*[@id='partner_name']" position="after">
<h3 class="col-md-12 text-center text-muted" t-if="partner.grade_id and partner.grade_id.website_published">
<span t-field="partner.grade_id"/> Partner</h3>
</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> </data>
</openerp> </openerp>

View File

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

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import re
import openerp import openerp
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
from openerp.addons.web import http from openerp.addons.web import http
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp.addons.web.http import request from openerp.addons.web.http import request
from openerp.addons.website_partner.controllers import main as website_partner
import werkzeug.urls import werkzeug.urls
class WebsiteCustomer(http.Controller): class WebsiteCustomer(http.Controller):
@ -19,14 +19,13 @@ class WebsiteCustomer(http.Controller):
'/customers/country/<int:country_id>/page/<int:page>', '/customers/country/<int:country_id>/page/<int:page>',
'/customers/country/<country_name>-<int:country_id>/page/<int:page>', '/customers/country/<country_name>-<int:country_id>/page/<int:page>',
], type='http', auth="public", website=True) ], type='http', auth="public", website=True)
def customers(self, country_id=0, page=0, **post): def customers(self, country_id=0, page=0, country_name='', **post):
cr, uid, context = request.cr, request.uid, request.context cr, uid, context = request.cr, request.uid, request.context
country_obj = request.registry['res.country'] country_obj = request.registry['res.country']
partner_obj = request.registry['res.partner'] partner_obj = request.registry['res.partner']
partner_name = post.get('search', '') partner_name = post.get('search', '')
base_domain = [('website_published','=',True)] domain = [('website_published', '=', True), ('assigned_partner_id', '!=', False)]
domain = list(base_domain)
if partner_name: if partner_name:
domain += [ domain += [
'|', '|',
@ -57,25 +56,24 @@ class WebsiteCustomer(http.Controller):
}) })
# search customers to display # search customers to display
partner_ids = partner_obj.search(cr, openerp.SUPERUSER_ID, domain, context=request.context) partner_count = partner_obj.search_count(cr, openerp.SUPERUSER_ID, domain, context=request.context)
google_map_partner_ids = ",".join([str(p) for p in partner_ids])
# pager # pager
pager = request.website.pager( pager = request.website.pager(
url="/customers", total=len(partner_ids), page=page, step=self._references_per_page, url="/customers", total=partner_count, page=page, step=self._references_per_page,
scope=7, url_args=post scope=7, url_args=post
) )
# browse page of customers to display partner_ids = partner_obj.search(request.cr, openerp.SUPERUSER_ID, domain,
partner_ids = partner_obj.search( offset=pager['offset'], limit=self._references_per_page,
cr, openerp.SUPERUSER_ID, domain, context=request.context)
limit=self._references_per_page, offset=pager['offset'], context=context) google_map_partner_ids = ','.join(map(str, partner_ids))
partners_data = partner_obj.read( partners = partner_obj.browse(request.cr, openerp.SUPERUSER_ID, partner_ids, request.context)
request.cr, openerp.SUPERUSER_ID, partner_ids, request.website.get_partner_white_list_fields(), context=request.context)
values = { values = {
'countries': countries, 'countries': countries,
'current_country_id': country_id or 0, 'current_country_id': country_id or 0,
'partners_data': partners_data, 'partners': partners,
'google_map_partner_ids': google_map_partner_ids, 'google_map_partner_ids': google_map_partner_ids,
'pager': pager, 'pager': pager,
'post': post, 'post': post,
@ -83,26 +81,15 @@ class WebsiteCustomer(http.Controller):
} }
return request.website.render("website_customer.index", values) 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) # Do not use semantic controller due to SUPERUSER_ID
def customer(self, partner_id, **post): @http.route(['/customers/<partner_id>'], type='http', auth="public", website=True)
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context) def partners_detail(self, partner_id, **post):
values = website_partner.get_partner_template_value(partner) mo = re.search('-([-0-9]+)$', str(partner_id))
if not values: if mo:
return self.customers(**post) partner_id = int(mo.group(1))
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
partner_obj = request.registry['res.partner'] if partner.exists() and partner.website_published:
if values['partner_data'].get('assigned_partner_id', None): values = {}
values['assigned_partner_data'] = partner_obj.read( values['main_object'] = values['partner'] = partner
request.cr, openerp.SUPERUSER_ID, [values['partner_data']['assigned_partner_id'][0]], return request.website.render("website_customer.details", values)
request.website.get_partner_white_list_fields(), context=request.context)[0] return self.customers(**post)
if values['partner_data'].get('implemented_partner_ids', None):
implemented_partners_data = partner_obj.read(
request.cr, openerp.SUPERUSER_ID, values['partner_data']['implemented_partner_ids'],
request.website.get_partner_white_list_fields(), context=request.context)
values['implemented_partners_data'] = []
for data in implemented_partners_data:
if data.get('website_published'):
values['implemented_partners_data'].append(data)
values['main_object'] = values['partner']
return request.website.render("website_customer.details", values)

View File

@ -25,7 +25,7 @@
<div class="row"> <div class="row">
<div class="col-md-3 mb32" id="ref_left_column"> <div class="col-md-3 mb32" id="ref_left_column">
</div> </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'> <div class='navbar mb0'>
<t t-call="website.pager"> <t t-call="website.pager">
<t t-set="classname" t-value="'pull-left'"/> <t t-set="classname" t-value="'pull-left'"/>
@ -39,21 +39,22 @@
</div> </div>
<div class="row"> <div class="row">
<t t-if="not partners_data">
<p>No result found.</p> <p t-if="not partners">No result found</p>
</t> <t t-foreach="partners" t-as="partner">
<t t-foreach="partners_data" t-as="partner_data" class="media"> <div class="media">
<div class="media"> <a class="pull-left" t-attf-href="/customers/#{slug(partner)}"
<a class="pull-left" t-attf-href="/customers/#{ slug([partner_data.get('id'), partner_data.get('name')]) }"> t-field="partner.image_small"
<img class="media-object" t-attf-src="data:image/png;base64,#{partner_data.get('image_small')}"/> t-field-options='{"widget": "image", "class": "media-object"}'
</a> ></a>
<div class="media-body" style="min-height: 64px;"> <div class="media-body" style="min-height: 64px;">
<a t-attf-href="/customers/#{ slug([partner_data.get('id'), partner_data.get('name')]) }" t-esc="partner_data.get('name')"/> <a class="media-heading" t-attf-href="/customers/#{slug(partner)}">
<div t-raw="partner_data.get('website_short_description') or ''"/> <span t-field="partner.display_name"/>
</div> </a>
</div> <div t-field="partner.website_short_description"/>
<div class="clearfix mb8"/> </div>
</t> </div>
</t>
</div> </div>
</div> </div>
@ -101,15 +102,15 @@
<div class="col-md-5"> <div class="col-md-5">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li><a href="/customers">Our References</a></li> <li><a href="/customers">Our References</a></li>
<li class="active"><span t-esc="partner_data.get('name')"/></li> <li class="active"><span t-field="partner.display_name"/></li>
</ol> </ol>
</div> </div>
<t t-call="website_partner.partner_detail"> <t t-call="website_partner.partner_detail">
<t t-set="left_column"> <t t-set="left_column">
<div id="left_column"></div> <div id="left_column"><t t-call="website_customer.implemented_by_block"/></div>
</t> </t>
<t t-set="right_column"> <t t-set="right_column">
<div id="right_column"></div> <div id="right_column"><t t-call="website_customer.references_block"/></div>
</t> </t>
</t> </t>
</div> </div>
@ -119,57 +120,59 @@
</t> </t>
</template> </template>
<template id="partner_assign" inherit_option_id="website_customer.details" inherit_id="website_customer.details" name="Implemented By"> <template id="partner_details" inherit_id="website_partner.partner_page" name="Partner Detail Columns">
<xpath expr="//div[@id='left_column']" position="inside"> <xpath expr="//t[@t-call='website_partner.partner_detail']" position="inside">
<t t-if="assigned_partner_data"> <t t-set="left_column"><div id="left_column"><t t-call="website_customer.implemented_by_block"/></div></t>
<t t-set="right_column"><div id="right_column"><t t-call="website_customer.references_block"/></div></t>
</xpath>
</template>
<template id="implemented_by_block" name="Partner Implemented By Block">
<t t-if="partner.assigned_partner_id and partner.assigned_partner_id.website_published">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h4>Implemented By</h4> <h4>Implemented By</h4>
</div> </div>
<div class="panel-body"> <div class="panel-body text-center">
<div class="text-center"> <h4>
<img class="img img-shadow" t-attf-src="data:image/png;base64,#{assigned_partner_data.get('image_medium')}"/> <a t-attf-href="/partners/#{slug(partner.assigned_partner_id)}">
</div> <span t-field="partner.assigned_partner_id"/>
<address class="mt16 mb8"> <span class="small"> (<t t-esc="len([p for p in partner.assigned_partner_id.implemented_partner_ids if p.website_published])"/> reference(s))</span>
<strong t-esc="assigned_partner_data.get('name')"/> </a>
<div t-if="assigned_partner_data.get('phone')"> </h4>
<span class="fa fa-phone"/> <span t-esc="assigned_partner_data.get('phone')"/> <div><a t-attf-href="/partners/#{slug(partner.assigned_partner_id)}"
</div> t-field="partner.assigned_partner_id.image_medium"
<div t-if="assigned_partner_data.get('email')"> t-field-options='{"widget": "image", "class": "center-block"}'
<span class="fa fa-envelope"/> />
<a t-att-href="'mailto:'+assigned_partner_data.get('email')"> </div>
<span t-esc="assigned_partner_data.get('email')"/> <address class="well text-left">
</a> <div t-field="partner.assigned_partner_id" t-field-options='{
</div> "widget": "contact",
</address> "fields": ["address", "website", "phone", "fax", "email"]
<div> }'/>
<a t-attf-href="/customers/#{ slug([partner_data.get('id'), partner_data.get('name')]) }/#references" t-if="implemented_partner_ids"> </address>
<t t-esc="len(implemented_partner_ids)"/> references
</a>
</div>
</div> </div>
</div> </div>
</t> </t>
</xpath>
</template> </template>
<template id="references" inherit_id="website_customer.details" name="Partner References"> <template id="references_block" name="Partner References Block">
<xpath expr="//div[@id='right_column']" position="inside"> <t t-if="any([p.website_published for p in partner.implemented_partner_ids])">
<t t-if="implemented_partners_data">
<h3 id="references">References</h3> <h3 id="references">References</h3>
<div t-foreach="implemented_partners_data" t-as="partner_data" class="media"> <div t-foreach="partner.implemented_partner_ids" t-as="reference" class="media">
<a class="pull-left" t-attf-href="/customers/#{ slug([partner_data.get('id'), partner_data.get('name')]) }"> <t t-if="reference.website_published">
<img class="media-object" t-attf-src="data:image/png;base64,#{partner_data.get('image_small')}"/> <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> </a>
<div class="media-body" style="min-height: 64px;"> <div class="media-body" style="min-height: 64px;">
<a class="media-heading" t-attf-href="/customers/#{ slug([partner_data.get('id'), partner_data.get('name')]) }"> <a class="media-heading" t-attf-href="/customers/#{slug(reference)}">
<t t-if="partner_data.get('parent_id')"><span t-esc="partner_data.get('parent_id')[1]"/></t> <span t-esc="partner_data.get('name')"/> <span t-field="reference.self"/>
</a> </a>
<div t-if="partner_data.get('website_short_description')" t-raw="partner_data.get('website_short_description')"/> <div t-field='reference.website_short_description'/>
</div> </div>
</t>
</div> </div>
</t> </t>
</xpath>
</template> </template>
</data> </data>

View File

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

View File

@ -50,7 +50,6 @@ class website_event(website_event):
def _add_event(self, event_name="New Event", context={}, **kwargs): def _add_event(self, event_name="New Event", context={}, **kwargs):
try: try:
print kwargs
dummy, res_id = request.registry.get('ir.model.data').get_object_reference(request.cr, request.uid, 'event_sale', 'product_product_event') dummy, res_id = request.registry.get('ir.model.data').get_object_reference(request.cr, request.uid, 'event_sale', 'product_product_event')
context['default_event_ticket_ids'] = [[0,0,{ context['default_event_ticket_ids'] = [[0,0,{
'name': _('Subscription'), 'name': _('Subscription'),

View File

@ -18,19 +18,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import collections
import datetime
import re
import pytz
import werkzeug.utils
import openerp import openerp
from openerp.addons.web import http from openerp.addons.web import http
from openerp.addons.web.http import request from openerp.addons.web.http import request
from openerp.addons.website.controllers.main import Website as controllers
import datetime
import re
import werkzeug.utils
controllers = controllers()
import pytz
from pytz import timezone
class website_event(http.Controller): class website_event(http.Controller):
@http.route(['''/event/<model("event.event"):event>/track/<model("event.track", "[('event_id','=',event[0])]"):track>'''], type='http', auth="public", website=True) @http.route(['''/event/<model("event.event"):event>/track/<model("event.track", "[('event_id','=',event[0])]"):track>'''], type='http', auth="public", website=True)
@ -79,24 +77,21 @@ class website_event(http.Controller):
# TODO: not implemented # TODO: not implemented
@http.route(['''/event/<model("event.event", "[('show_tracks','=',1)]"):event>/agenda'''], type='http', auth="public", website=True) @http.route(['''/event/<model("event.event", "[('show_tracks','=',1)]"):event>/agenda'''], type='http', auth="public", website=True)
def event_agenda(self, event, tag=None, **post): def event_agenda(self, event, tag=None, **post):
comp = lambda x: (x.date, bool(x.location_id)) days_tracks = collections.defaultdict(lambda: [])
event.track_ids.sort(lambda x,y: cmp(comp(x), comp(y))) for track in sorted(event.track_ids, key=lambda x: (x.date, bool(x.location_id))):
if not track.date: continue
days_tracks[track.date[:10]].append(track)
days = {} days = {}
days_nbr = {} days_tracks_count = {}
for track in event.track_ids: for day, tracks in days_tracks.iteritems():
if not track.date: continue days_tracks_count[day] = len(tracks)
days.setdefault(track.date[:10], []) days[day] = self._prepare_calendar(event, tracks)
days[track.date[:10]].append(track)
for d in days:
days_nbr[d] = len(days[d])
days[d] = self._prepare_calendar(event, days[d])
return request.website.render("website_event_track.agenda", { return request.website.render("website_event_track.agenda", {
'event': event, 'event': event,
'days': days, 'days': days,
'days_nbr': days_nbr, 'days_nbr': days_tracks_count,
'tag': tag 'tag': tag
}) })

View File

@ -53,7 +53,9 @@
<input type="text" class="form-control" placeholder="Filter Tracks..." id="event_track_search"/> <input type="text" class="form-control" placeholder="Filter Tracks..." id="event_track_search"/>
</div> </div>
</section> </section>
<section class="container" t-foreach="days.keys()" t-as="day"> <t t-set="dayslist" t-value="days.keys()"/>
<t t-set="dayslist2" t-value="dayslist.sort()"/> <!-- display days in the right order -->
<section class="container" t-foreach="dayslist" t-as="day">
<t t-set="locations" t-value="days[day]['locations']"/> <t t-set="locations" t-value="days[day]['locations']"/>
<t t-set="dates" t-value="days[day]['dates']"/> <t t-set="dates" t-value="days[day]['dates']"/>
<h3 class="page-header mt0"> <h3 class="page-header mt0">
@ -222,7 +224,7 @@
<b>Date</b><br/> <b>Date</b><br/>
<span t-field="track.date" t-field-options='{"hide_seconds":"True"}'/><br/> <span t-field="track.date" t-field-options='{"hide_seconds":"True"}'/><br/>
<b>Duration</b><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/> <b>Location</b><br/>
<span t-field="track.location_id"/><br/> <span t-field="track.location_id"/><br/>
</div> </div>

View File

@ -2,6 +2,7 @@
from datetime import datetime from datetime import datetime
import werkzeug.urls import werkzeug.urls
import werkzeug.wrappers
import simplejson import simplejson
from openerp import tools from openerp import tools
@ -73,7 +74,7 @@ class WebsiteForum(http.Controller):
forums = Forum.browse(cr, uid, obj_ids, context=context) forums = Forum.browse(cr, uid, obj_ids, context=context)
return request.website.render("website_forum.forum_all", {'forums': forums}) return request.website.render("website_forum.forum_all", {'forums': forums})
@http.route('/forum/new', type='http', auth="user", website=True) @http.route('/forum/new', type='http', auth="user", methods=['POST'], website=True)
def forum_create(self, forum_name="New Forum", **kwargs): def forum_create(self, forum_name="New Forum", **kwargs):
forum_id = request.registry['forum.forum'].create(request.cr, request.uid, { forum_id = request.registry['forum.forum'].create(request.cr, request.uid, {
'name': forum_name, 'name': forum_name,
@ -87,14 +88,15 @@ class WebsiteForum(http.Controller):
@http.route(['/forum/<model("forum.forum"):forum>', @http.route(['/forum/<model("forum.forum"):forum>',
'/forum/<model("forum.forum"):forum>/page/<int:page>', '/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) ], type='http', auth="public", website=True)
def questions(self, forum, tag=None, page=1, filters='all', sorting='date', search='', **post): def questions(self, forum, tag=None, page=1, filters='all', sorting='date', search='', **post):
cr, uid, context = request.cr, request.uid, request.context cr, uid, context = request.cr, request.uid, request.context
Post = request.registry['forum.post'] Post = request.registry['forum.post']
user = request.registry['res.users'].browse(cr, uid, uid, context=context) 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: if search:
domain += ['|', ('name', 'ilike', search), ('content', 'ilike', search)] domain += ['|', ('name', 'ilike', search), ('content', 'ilike', search)]
if tag: if tag:
@ -110,12 +112,28 @@ class WebsiteForum(http.Controller):
order = 'child_count desc' order = 'child_count desc'
elif sorting == 'vote': elif sorting == 'vote':
order = 'vote_count desc' order = 'vote_count desc'
else: elif sorting == 'date':
sorting = 'date'
order = 'write_date desc' order = 'write_date desc'
else:
sorting = 'creation'
order = 'create_date desc'
question_count = Post.search(cr, uid, domain, count=True, context=context) 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) 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) question_ids = Post.browse(cr, uid, obj_ids, context=context)
@ -220,7 +238,7 @@ class WebsiteForum(http.Controller):
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'favourite_ids': favourite_ids}, context=request.context) request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'favourite_ids': favourite_ids}, context=request.context)
return favourite return favourite
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/ask_for_close', type='http', auth="user", website=True) @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/ask_for_close', type='http', auth="user", methods=['POST'], website=True)
def question_ask_for_close(self, forum, question, **post): def question_ask_for_close(self, forum, question, **post):
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all') check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all')
if not check_res[0]: if not check_res[0]:
@ -262,7 +280,7 @@ class WebsiteForum(http.Controller):
}, context=request.context) }, context=request.context)
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question))) return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/reopen', type='http', auth="user", website=True) @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/reopen', type='http', auth="user", methods=['POST'], website=True)
def question_reopen(self, forum, question, **kwarg): def question_reopen(self, forum, question, **kwarg):
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all') check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_close_own' or '_karma_modo_close_all')
if not check_res[0]: if not check_res[0]:
@ -271,7 +289,7 @@ class WebsiteForum(http.Controller):
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'state': 'active'}, context=request.context) request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'state': 'active'}, context=request.context)
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question))) return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/delete', type='http', auth="user", website=True) @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/delete', type='http', auth="user", methods=['POST'], website=True)
def question_delete(self, forum, question, **kwarg): def question_delete(self, forum, question, **kwarg):
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all') check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all')
if not check_res[0]: if not check_res[0]:
@ -280,7 +298,7 @@ class WebsiteForum(http.Controller):
request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'active': False}, context=request.context) request.registry['forum.post'].write(request.cr, request.uid, [question.id], {'active': False}, context=request.context)
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question))) return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
@http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/undelete', type='http', auth="user", website=True) @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/undelete', type='http', auth="user", methods=['POST'], website=True)
def question_undelete(self, forum, question, **kwarg): def question_undelete(self, forum, question, **kwarg):
check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all') check_res = self._has_enough_karma(question.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all')
if not check_res[0]: if not check_res[0]:
@ -339,7 +357,7 @@ class WebsiteForum(http.Controller):
request.registry['forum.post'].write(cr, uid, [post.id], {'is_correct': not post.is_correct}, context=context) request.registry['forum.post'].write(cr, uid, [post.id], {'is_correct': not post.is_correct}, context=context)
return not post.is_correct return not post.is_correct
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/delete', type='http', auth="user", website=True) @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/delete', type='http', auth="user", methods=['POST'], website=True)
def post_delete(self, forum, post, **kwargs): def post_delete(self, forum, post, **kwargs):
check_res = self._has_enough_karma(post.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all') check_res = self._has_enough_karma(post.create_uid.id == request.uid and '_karma_modo_unlink_own' or '_karma_modo_unlink_all')
if not check_res[0]: if not check_res[0]:
@ -419,14 +437,16 @@ class WebsiteForum(http.Controller):
# User # User
# -------------------------------------------------- # --------------------------------------------------
@http.route('/forum/<model("forum.forum"):forum>/users', type='http', auth="public", website=True) @http.route(['/forum/<model("forum.forum"):forum>/users',
'/forum/<model("forum.forum"):forum>/users/page/<int:page>'],
type='http', auth="public", website=True)
def users(self, forum, page=1, **searches): def users(self, forum, page=1, **searches):
cr, uid, context = request.cr, request.uid, request.context cr, uid, context = request.cr, request.uid, request.context
User = request.registry['res.users'] User = request.registry['res.users']
step = 30 step = 30
tag_count = User.search(cr, SUPERUSER_ID, [('karma', '>', 1)], count=True, context=context) 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) 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) users = User.browse(cr, SUPERUSER_ID, obj_ids, context=context)
@ -452,6 +472,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/user/%d" % (slug(forum), partner.user_ids[0].id))
return werkzeug.utils.redirect("/forum/%s" % slug(forum)) return werkzeug.utils.redirect("/forum/%s" % slug(forum))
@http.route(['/forum/user/<int:user_id>/avatar'], type='http', auth="public", website=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) @http.route(['/forum/<model("forum.forum"):forum>/user/<int:user_id>'], type='http', auth="public", website=True)
def open_user(self, forum, user_id=0, **post): def open_user(self, forum, user_id=0, **post):
cr, uid, context = request.cr, request.uid, request.context cr, uid, context = request.cr, request.uid, request.context
@ -462,10 +493,9 @@ class WebsiteForum(http.Controller):
Followers = request.registry['mail.followers'] Followers = request.registry['mail.followers']
Data = request.registry["ir.model.data"] Data = request.registry["ir.model.data"]
user_id = User.search(cr, SUPERUSER_ID, [('id', '=', user_id), ('karma', '>', '1')], context=context) user = User.browse(cr, SUPERUSER_ID, user_id, context=context)
if not user_id: if not user.exists() or (user_id != request.session.uid and user.karma < 1):
return werkzeug.utils.redirect("/forum/%s" % slug(forum)) return werkzeug.utils.redirect("/forum/%s" % slug(forum))
user = User.browse(cr, SUPERUSER_ID, user_id[0], context=context)
# questions and answers by user # questions and answers by user
user_questions, user_answers = [], [] user_questions, user_answers = [], []
@ -506,7 +536,7 @@ class WebsiteForum(http.Controller):
#activity by user. #activity by user.
model, comment = Data.get_object_reference(cr, uid, 'mail', 'mt_comment') 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) activities = Activity.browse(cr, uid, activity_ids, context=context)
posts = {} posts = {}
@ -549,14 +579,14 @@ class WebsiteForum(http.Controller):
}) })
return request.website.render("website_forum.edit_profile", values) return request.website.render("website_forum.edit_profile", values)
@http.route('/forum/<model("forum.forum"):forum>/user/<model("res.users"):user>/save', type='http', auth="user", website=True) @http.route('/forum/<model("forum.forum"):forum>/user/<model("res.users"):user>/save', type='http', auth="user", methods=['POST'], website=True)
def save_edited_profile(self, forum, user, **kwargs): def save_edited_profile(self, forum, user, **kwargs):
request.registry['res.users'].write(request.cr, request.uid, [user.id], { request.registry['res.users'].write(request.cr, request.uid, [user.id], {
'name': kwargs.get('name'), 'name': kwargs.get('name'),
'website': kwargs.get('website'), 'website': kwargs.get('website'),
'email': kwargs.get('email'), 'email': kwargs.get('email'),
'city': kwargs.get('city'), 'city': kwargs.get('city'),
'country_id': kwargs.get('country'), 'country_id': int(kwargs.get('country')),
'website_description': kwargs.get('description'), 'website_description': kwargs.get('description'),
}, context=request.context) }, context=request.context)
return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), user.id)) return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), user.id))
@ -570,6 +600,7 @@ class WebsiteForum(http.Controller):
Badge = request.registry['gamification.badge'] Badge = request.registry['gamification.badge']
badge_ids = Badge.search(cr, SUPERUSER_ID, [('challenge_ids.category', '=', 'forum')], context=context) badge_ids = Badge.search(cr, SUPERUSER_ID, [('challenge_ids.category', '=', 'forum')], context=context)
badges = Badge.browse(cr, uid, badge_ids, 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 = self._prepare_forum_values(forum=forum, searches={'badges': True})
values.update({ values.update({
'badges': badges, 'badges': badges,
@ -590,7 +621,7 @@ class WebsiteForum(http.Controller):
# Messaging # Messaging
# -------------------------------------------------- # --------------------------------------------------
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/convert_to_answer', type='http', auth="public", website=True) @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/convert_to_answer', type='http', auth="public", methods=['POST'], website=True)
def convert_comment_to_answer(self, forum, post, comment, **kwarg): def convert_comment_to_answer(self, forum, post, comment, **kwarg):
body = comment.body body = comment.body
request.registry['mail.message'].unlink(request.cr, request.uid, [comment.id], context=request.context) request.registry['mail.message'].unlink(request.cr, request.uid, [comment.id], context=request.context)
@ -600,7 +631,7 @@ class WebsiteForum(http.Controller):
return self.post_comment(forum, answer, comment=html2plaintext(body)) return self.post_comment(forum, answer, comment=html2plaintext(body))
return self.post_new(forum, question, content=body) return self.post_new(forum, question, content=body)
@http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/convert_to_comment', type='http', auth="user", website=True) @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/convert_to_comment', type='http', auth="user", methods=['POST'], website=True)
def convert_answer_to_comment(self, forum, post, **kwarg): def convert_answer_to_comment(self, forum, post, **kwarg):
values = { values = {
'comment': html2plaintext(post.content), 'comment': html2plaintext(post.content),

View File

@ -78,16 +78,16 @@
<field name="target_goal">10</field> <field name="target_goal">10</field>
</record> </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"> <record id="badge_25" model="gamification.badge">
<field name="name">Pundit</field> <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="level">silver</field>
<field name="rule_auth">nobody</field> <field name="rule_auth">nobody</field>
</record> </record>
<record model="gamification.goal.definition" id="definition_pundit"> <record model="gamification.goal.definition" id="definition_pundit">
<field name="name">Pundit</field> <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="display_mode">boolean</field>
<field name="condition">higher</field> <field name="condition">higher</field>
<field name="model_id" eval="ref('website_forum.model_forum_post')"/> <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="computation_mode">count</field>
<field name="display_mode">boolean</field> <field name="display_mode">boolean</field>
<field name="model_id" eval="ref('website_forum.model_forum_post')" /> <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="condition">higher</field>
<field name="batch_mode">True</field> <field name="batch_mode">True</field>
<field name="batch_distinctive_field" eval="ref('website_forum.field_forum_post_create_uid')" /> <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"> <record id="reason_4" model="forum.post.reason">
<field name="name">not a real question</field> <field name="name">not a real question</field>
</record> </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"> <record id="reason_6" model="forum.post.reason">
<field name="name">not relevant or out dated</field> <field name="name">not relevant or out dated</field>
</record> </record>

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import openerp from urlparse import urljoin
import openerp
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
from openerp.addons.website.models.website import slug from openerp.addons.website.models.website import slug
from openerp.osv import osv, fields 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]]} 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): def set_viewed(self, cr, uid, ids, context=None):
for post in self.browse(cr, uid, ids, context=context): cr.execute("""UPDATE forum_post SET views = views+1 WHERE id IN %s""", (tuple(ids),))
self.write(cr, uid, [post.id], {'views': post.views + 1}, context=context)
return True 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): class PostReason(osv.Model):
_name = "forum.post.reason" _name = "forum.post.reason"

View File

@ -2,10 +2,16 @@
from openerp.osv import osv, fields from openerp.osv import osv, fields
class Users(osv.Model): class Users(osv.Model):
_inherit = 'res.users' _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): def _get_user_badge_level(self, cr, uid, ids, name, args, context=None):
"""Return total badge per level of users""" """Return total badge per level of users"""
result = dict.fromkeys(ids, False) result = dict.fromkeys(ids, False)

View File

@ -24,16 +24,25 @@
.question .badge-active { .question .badge-active {
background-color: #428bca; 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 { .oe_grey {
background-color: #eeeeee; background-color: #eeeeee;
} }
.img-avatar {
max-width: 50px;
margin-right: 10px;
}
.badge-gold { .badge-gold {
color: #ffcc00; color: #ffcc00;
} }
@ -94,3 +103,7 @@ a.no-decoration {
font: 1.2em "Helvetica Neue", Helvetica, Arial, sans-serif !important; font: 1.2em "Helvetica Neue", Helvetica, Arial, sans-serif !important;
height: 1.2em !important; height: 1.2em !important;
} }
button.btn-link.text-muted {
color: #999999;
}

View File

@ -19,14 +19,23 @@
margin-left: 4px margin-left: 4px
.badge-active .badge-active
background-color: #428bca 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 .oe_grey
background-color: #eeeeee background-color: #eeeeee
.img-avatar
max-width: 50px
margin-right: 10px
.badge-gold .badge-gold
color: #ffcc00 color: #ffcc00
@ -74,3 +83,6 @@ a.no-decoration
.text-tags .text-tag .text-button .text-tags .text-tag .text-button
font: 1.2em "Helvetica Neue", Helvetica, Arial, sans-serif !important font: 1.2em "Helvetica Neue", Helvetica, Arial, sans-serif !important
height: 1.2em !important height: 1.2em !important
button.btn-link.text-muted
color: #999

View File

@ -3,7 +3,7 @@ $(document).ready(function () {
$('.vote_up ,.vote_down').on('click', function (ev) { $('.vote_up ,.vote_down').on('click', function (ev) {
ev.preventDefault(); ev.preventDefault();
var $link = $(ev.currentTarget); var $link = $(ev.currentTarget);
openerp.jsonRpc($link.attr('href'), 'call', {}) openerp.jsonRpc($link.data('href'), 'call', {})
.then(function (data) { .then(function (data) {
if (data['error']){ if (data['error']){
if (data['error'] == 'own_post'){ if (data['error'] == 'own_post'){
@ -47,7 +47,7 @@ $(document).ready(function () {
$('.accept_answer').on('click', function (ev) { $('.accept_answer').on('click', function (ev) {
ev.preventDefault(); ev.preventDefault();
var $link = $(ev.currentTarget); var $link = $(ev.currentTarget);
openerp.jsonRpc($link.attr('href'), 'call', {}).then(function (data) { openerp.jsonRpc($link.data('href'), 'call', {}).then(function (data) {
if (data['error']) { if (data['error']) {
if (data['error'] == 'anonymous_user'){ if (data['error'] == 'anonymous_user'){
var $warning = $('<div class="alert alert-danger alert-dismissable" id="correct_answer_alert" style="position:absolute; margin-top: -30px; margin-left: 90px;">'+ var $warning = $('<div class="alert alert-danger alert-dismissable" id="correct_answer_alert" style="position:absolute; margin-top: -30px; margin-left: 90px;">'+
@ -83,7 +83,7 @@ $(document).ready(function () {
$('.favourite_question').on('click', function (ev) { $('.favourite_question').on('click', function (ev) {
ev.preventDefault(); ev.preventDefault();
var $link = $(ev.currentTarget); var $link = $(ev.currentTarget);
openerp.jsonRpc($link.attr('href'), 'call', {}).then(function (data) { openerp.jsonRpc($link.data('href'), 'call', {}).then(function (data) {
if (data) { if (data) {
$link.addClass("forum_favourite_question") $link.addClass("forum_favourite_question")
} else { } else {
@ -96,7 +96,7 @@ $(document).ready(function () {
$('.comment_delete').on('click', function (ev) { $('.comment_delete').on('click', function (ev) {
ev.preventDefault(); ev.preventDefault();
var $link = $(ev.currentTarget); var $link = $(ev.currentTarget);
openerp.jsonRpc($link.attr('href'), 'call', {}).then(function (data) { openerp.jsonRpc($link.data('href'), 'call', {}).then(function (data) {
$link.parents('.comment').first().remove(); $link.parents('.comment').first().remove();
}); });
return true; return true;

View File

@ -22,6 +22,14 @@
</xpath> </xpath>
</template> </template>
<!-- helper -->
<template id="link_button">
<form method="POST" t-att-action="url">
<button t-attf-class="fa btn-link #{classes}">
<t t-esc="label"/></button>
</form>
</template>
<!-- Page Index --> <!-- Page Index -->
<template id="header" name="Forum Index"> <template id="header" name="Forum Index">
<t t-call="website.layout"> <t t-call="website.layout">
@ -223,6 +231,7 @@
<t t-if="filters == 'followed'">Followed</t> <t t-if="filters == 'followed'">Followed</t>
<t t-if="tag"><span t-field="tag.name"/></t> <t t-if="tag"><span t-field="tag.name"/></t>
<t t-if="sorting == 'date'"> by activity date</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 == 'answered'"> by most answered</t>
<t t-if="sorting == 'vote'"> by most voted</t> <t t-if="sorting == 'vote'"> by most voted</t>
<b class="caret"/> <b class="caret"/>
@ -246,6 +255,9 @@
<li t-att-class="sorting == 'date' and 'active' or '' "> <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> <a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='date')">Last activity date</a>
</li> </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 '' "> <li t-att-class="sorting == 'answered' and 'active' or '' ">
<a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='answered')">Most answered</a> <a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='answered')">Most answered</a>
</li> </li>
@ -389,10 +401,10 @@
<template id="vote"> <template id="vote">
<div t-attf-class="box oe_grey"> <div t-attf-class="box oe_grey">
<a t-attf-class="vote_up fa fa-thumbs-up no-decoration #{post.user_vote == 1 and 'text-success' or ''}" <a t-attf-class="vote_up fa fa-thumbs-up no-decoration #{post.user_vote == 1 and 'text-success' or ''}"
t-attf-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/upvote"/> t-attf-data-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/upvote"/>
<span id="vote_count" t-esc="post.vote_count"/> <span id="vote_count" t-esc="post.vote_count"/>
<a t-attf-class="vote_down fa fa-thumbs-down no-decoration #{post.user_vote == -1 and 'text-warning' or ''}" <a t-attf-class="vote_down fa fa-thumbs-down no-decoration #{post.user_vote == -1 and 'text-warning' or ''}"
t-attf-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/downvote"/> t-attf-data-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/downvote"/>
<div t-if="vote_count &gt; 1" class="subtitle"> <div t-if="vote_count &gt; 1" class="subtitle">
votes votes
</div> </div>
@ -414,7 +426,7 @@
<span t-field="question.views"/> Views <span t-field="question.views"/> Views
</div> </div>
<div class="mt4"> <div class="mt4">
<a t-attf-href="/forum/#{slug(question.forum_id)}/question/#{slug(question)}/toggle_favourite" <a t-attf-data-href="/forum/#{slug(question.forum_id)}/question/#{slug(question)}/toggle_favourite"
t-attf-class="favourite_question no-decoration fa fa-2x fa-star #{question.user_favourite and 'forum_favourite_question' or ''}"/> t-attf-class="favourite_question no-decoration fa fa-2x fa-star #{question.user_favourite and 'forum_favourite_question' or ''}"/>
</div> </div>
</div> </div>
@ -424,17 +436,23 @@
<span t-if="not question.active"><b> [Deleted]</b></span> <span t-if="not question.active"><b> [Deleted]</b></span>
<span t-if="question.state == 'close'"><b> [Closed]</b></span> <span t-if="question.state == 'close'"><b> [Closed]</b></span>
</h1> </h1>
<div class="alert alert-info" t-if="question.state == 'close'"> <div class="alert alert-info text-center" t-if="question.state == 'close'">
<p class="mt32 mb16 text-center"> <p class="mt16">
<b>The question has been closed for reason: <i t-esc="question.closed_reason_id.name"/> <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>
<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>
</p> </p>
<div t-if="question.state == 'close' and user.karma&gt;=500" class="mb24 text-center"> <t t-if="question.closed_uid">
<a class="fa fa-arrow-right" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/reopen"> Reopen</a> <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-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>
</div> </div>
<t t-raw="question.content"/> <t t-raw="question.content"/>
@ -454,30 +472,51 @@
</a> </a>
</li> </li>
<li t-if="question.state != 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)"> <li t-if="question.state != 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)">
<a class="text-muted fa fa-times" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/ask_for_close"> Close</a> <t t-call="website_forum.link_button">
<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>
<li t-if="question.state == 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)"> <li t-if="question.state == 'close' and ((user.id == question.create_uid.id and can_close_own) or can_close_all)">
<a class="text-muted fa fa-undo" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/reopen"> Reopen</a> <t t-call="website_forum.link_button">
<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>
<li t-if="(user.id == question.create_uid.id and can_edit_own) or can_edit_all"> <li t-if="(user.id == question.create_uid.id and can_edit_own) or can_edit_all">
<a class="text-muted fa fa-edit" t-attf-href="/forum/#{ slug(forum) }/post/#{slug(question)}/edit"> Edit</a> <t t-call="website_forum.link_button">
<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>
<li t-if="question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)"> <li t-if="question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)">
<a class="text-muted fa fa-trash-o" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/delete"> Delete</a> <t t-call="website_forum.link_button">
<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>
<li t-if="not question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)"> <li t-if="not question.active and ((user.id == question.create_uid.id and can_unlink_own) or can_unlink_all)">
<a class="text-muted fa fa-trash-o" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/undelete"> Undelete</a> <t t-call="website_forum.link_button">
<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> </li>
</ul> </ul>
</div> </div>
<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> <div>
<a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.create_uid.id }" <a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.create_uid.id }"
t-field="question.create_uid" t-field="question.create_uid"
t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}' t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
style="display: inline-block;"/> style="display: inline-block;"/>
<div t-field="question.create_uid" t-field-options='{"widget": "contact", "badges": true, "fields": ["karma"]}'/> <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> </div>
</div> </div>
@ -497,7 +536,7 @@
</t> </t>
<div class="text-muted mt8"> <div class="text-muted mt8">
<a t-attf-class="accept_answer fa fa-2x fa-check-circle no-decoration #{answer.is_correct and 'oe_answer_true' or 'oe_answer_false'}" <a t-attf-class="accept_answer fa fa-2x fa-check-circle no-decoration #{answer.is_correct and 'oe_answer_true' or 'oe_answer_false'}"
t-attf-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/> t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/>
</div> </div>
</div> </div>
<div style="margin-left: 95px;" class="clearfix"> <div style="margin-left: 95px;" class="clearfix">
@ -513,13 +552,21 @@
<a class="text-muted fa fa-edit" t-attf-href="/forum/#{slug(forum)}/post/#{slug(answer)}/edit"> Edit</a> <a class="text-muted fa fa-edit" t-attf-href="/forum/#{slug(forum)}/post/#{slug(answer)}/edit"> Edit</a>
</li> </li>
<li t-if="(user.id == answer.create_uid.id and can_unlink_own) or can_unlink_all"> <li t-if="(user.id == answer.create_uid.id and can_unlink_own) or can_unlink_all">
<a class="text-muted fa fa-trash-o" t-attf-href="/forum/#{slug(forum)}/post/#{slug(answer)}/delete"> Delete</a> <t t-call="website_forum.link_button">
<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>
<li t-if="user.id == answer.create_uid.id"> <li t-if="user.id == answer.create_uid.id">
<a class="text-muted fa fa-magic" t-attf-href="/forum/#{slug(forum)}/post/#{slug(answer)}/convert_to_comment"> Convert as a comment</a> <t t-call="website_forum.link_button">
<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> </li>
</ul> </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> <div>
<a t-attf-href="/forum/#{ slug(forum) }/user/#{ answer.create_uid.id }" <a t-attf-href="/forum/#{ slug(forum) }/user/#{ answer.create_uid.id }"
t-field="answer.create_uid" t-field="answer.create_uid"
@ -552,15 +599,18 @@
<div t-foreach="reversed(object.website_message_ids)" t-as="message" class="comment oe_comment_grey"> <div t-foreach="reversed(object.website_message_ids)" t-as="message" class="comment oe_comment_grey">
<small class="text-muted"> <small class="text-muted">
<button type="button" t-if="user.partner_id.id == message.author_id.id and user.karma&gt;=750" <button type="button" t-if="user.partner_id.id == message.author_id.id and user.karma&gt;=750"
t-attf-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/delete" t-attf-data-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/delete"
class="close comment_delete">&amp;times;</button> class="close comment_delete">&amp;times;</button>
<span t-field="message.body"/> <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}" <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"]}' t-field="message.author_id" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
style="display: inline-block;"/> style="display: inline-block;"/>
on <span t-field="message.date" t-field-options='{"format":"short"}'/> on <span t-field="message.date" t-field-options='{"format":"short"}'/>
<a class="fa fa-magic text-muted pull-right"
t-attf-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/convert_to_answer">Convert as an answer</a>
</small> </small>
</div> </div>
<div class="css_editable_mode_hidden"> <div class="css_editable_mode_hidden">
@ -650,7 +700,7 @@
</h4> </h4>
<div class="row"> <div class="row">
<div class="col-sm-3 mt16" t-foreach="users" t-as="user"> <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> <div>
<a t-attf-href="/forum/#{slug(forum)}/user/#{user.id}" t-field="user.name"/> <a t-attf-href="/forum/#{slug(forum)}/user/#{user.id}" t-field="user.name"/>
</div> </div>
@ -661,9 +711,12 @@
<template id="users"> <template id="users">
<t t-call="website_forum.header"> <t t-call="website_forum.header">
<div class="row"> <t t-foreach="users" t-as="user">
<div t-foreach="users" t-as="user" class="col-sm-4"> <t t-if="user_index % 3 == 0">
<span t-field="user.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/> <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> <div>
<a t-attf-href="/forum/#{slug(forum)}/user/#{user.id}" t-field="user.name"/> <a t-attf-href="/forum/#{slug(forum)}/user/#{user.id}" t-field="user.name"/>
<t t-if="user.country_id"> <t t-if="user.country_id">
@ -683,7 +736,7 @@
<t t-raw="0"/> <t t-raw="0"/>
</div> </div>
</div> </div>
</div> </t>
<div class="pull-left"> <div class="pull-left">
<t t-call="website.pager"/> <t t-call="website.pager"/>
</div> </div>
@ -694,7 +747,7 @@
<t t-call="website_forum.header"> <t t-call="website_forum.header">
<h3>Edit Profile </h3> <h3>Edit Profile </h3>
<div class="col-md-2"> <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> </div>
<form t-attf-action="/forum/#{slug(forum)}/user/#{slug(user)}/save" method="post" role="form" class="form-horizontal"> <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"/> <input name="user_id" t-att-value="user.id" type="hidden"/>
@ -719,7 +772,7 @@
<select class="form-control" name="country"> <select class="form-control" name="country">
<option value="">Country...</option> <option value="">Country...</option>
<t t-foreach="countries or []" t-as="country"> <t t-foreach="countries or []" t-as="country">
<option t-att-value="country.id" t-att-selected="country.id == user.partner_id.country.id"><t t-esc="country.name"/></option> <option t-att-value="country.id" t-att-selected="country.id == user.partner_id.country_id.id"><t t-esc="country.name"/></option>
</t> </t>
</select> </select>
</div> </div>
@ -744,8 +797,7 @@
</h1> </h1>
<div class="row"> <div class="row">
<div class="col-sm-2"> <div class="col-sm-2">
<span t-field="user.image" <img class="img img-responsive img-circle" t-attf-src="/forum/user/#{user.id}/avatar"/>
t-field-options='{"widget": "image", "class": "img img-responsive img-circle"}'/>
</div> </div>
<div class="col-sm-10"> <div class="col-sm-10">
<table class="table table-condensed"> <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, return partner_obj.google_map_json(request.cr, openerp.SUPERUSER_ID,
partner_ids, request.context) 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): def google_map_set_partner_position(self, *arg, **post):
partner_obj = request.registry['res.partner'] partner_obj = request.registry['res.partner']

View File

@ -5,7 +5,7 @@ from openerp.addons.web.http import request
class website_hr(http.Controller): class website_hr(http.Controller):
@http.route(['/page/website.aboutus'], type='http', auth="public", website=True) @http.route(['/page/website.aboutus', '/page/aboutus'], type='http', auth="public", website=True)
def blog(self, **post): def blog(self, **post):
hr_obj = request.registry['hr.employee'] hr_obj = request.registry['hr.employee']
employee_ids = hr_obj.search(request.cr, request.uid, [('website_published', '=', True)], employee_ids = hr_obj.search(request.cr, request.uid, [('website_published', '=', True)],

View File

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import re
import openerp
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
from openerp.addons.web import http from openerp.addons.web import http
from openerp.addons.web.http import request from openerp.addons.web.http import request
from openerp.addons.website_partner.controllers import main as website_partner
from openerp.tools.translate import _ from openerp.tools.translate import _
import werkzeug.urls import werkzeug.urls
@ -39,7 +38,7 @@ class WebsiteMembership(http.Controller):
current_country = None current_country = None
# base domain for groupby / searches # base domain for groupby / searches
base_line_domain = [("partner.website_published", "=", True),('state', 'in', ['free', 'paid'])] base_line_domain = [("partner.website_published", "=", True), ('state', 'in', ['free', 'paid'])]
if membership_id: if membership_id:
base_line_domain.append(('membership_id', '=', membership_id)) base_line_domain.append(('membership_id', '=', membership_id))
membership = product_obj.browse(cr, uid, membership_id, context=context) membership = product_obj.browse(cr, uid, membership_id, context=context)
@ -76,12 +75,10 @@ class WebsiteMembership(http.Controller):
membership_line_ids = membership_line_obj.search(cr, uid, line_domain, context=context) membership_line_ids = membership_line_obj.search(cr, uid, line_domain, context=context)
membership_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context) membership_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context)
membership_lines.sort(key=lambda x: x.membership_id.website_sequence) membership_lines.sort(key=lambda x: x.membership_id.website_sequence)
partner_ids = [m.partner and m.partner.id for m in membership_lines] partner_ids = [m.partner.id for m in membership_lines]
google_map_partner_ids = ",".join(map(str, partner_ids)) google_map_partner_ids = ",".join(map(str, partner_ids))
partners_data = {} partners = dict((p.id, p) for p in partner_obj.browse(request.cr, SUPERUSER_ID, partner_ids, request.context))
for partner in partner_obj.read(cr, openerp.SUPERUSER_ID, partner_ids, request.website.get_partner_white_list_fields(), context=context):
partners_data[partner.get("id")] = partner
# format domain for group_by and memberships # format domain for group_by and memberships
membership_ids = product_obj.search(cr, uid, [('membership', '=', True)], order="website_sequence", context=context) membership_ids = product_obj.search(cr, uid, [('membership', '=', True)], order="website_sequence", context=context)
@ -91,7 +88,7 @@ class WebsiteMembership(http.Controller):
pager = request.website.pager(url="/members", total=len(membership_line_ids), page=page, step=self._references_per_page, scope=7, url_args=post) pager = request.website.pager(url="/members", total=len(membership_line_ids), page=page, step=self._references_per_page, scope=7, url_args=post)
values = { values = {
'partners_data': partners_data, 'partners': partners,
'membership_lines': membership_lines, 'membership_lines': membership_lines,
'memberships': memberships, 'memberships': memberships,
'membership': membership, 'membership': membership,
@ -105,11 +102,15 @@ class WebsiteMembership(http.Controller):
} }
return request.website.render("website_membership.index", values) 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) # Do not use semantic controller due to SUPERUSER_ID
def partners_ref(self, partner_id, **post): @http.route(['/members/<partner_id>'], type='http', auth="public", website=True)
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context) def partners_detail(self, partner_id, **post):
values = website_partner.get_partner_template_value(partner) mo = re.search('-([-0-9]+)$', str(partner_id))
if not values: if mo:
return self.members(**post) partner_id = int(mo.group(1))
values['main_object'] = values['partner'] partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
return request.website.render("website_membership.partner", values) 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

@ -59,14 +59,17 @@
<t t-set="current_membership_id" t-value="membership_line_id.membership_id.id"/> <t t-set="current_membership_id" t-value="membership_line_id.membership_id.id"/>
<h3 class="text-center"><span t-field="membership_line_id.membership_id"/></h3> <h3 class="text-center"><span t-field="membership_line_id.membership_id"/></h3>
</t> </t>
<t t-set="partner_data" t-value="partners_data[membership_line_id.partner.id]"/> <t t-set="partner" t-value="partners[membership_line_id.partner.id]"/>
<div class="media"> <div class="media">
<a class="pull-left" t-attf-href="/members/#{ slug([partner_data.get('id'), partner_data.get('name')]) }"> <a class="pull-left" t-attf-href="/members/#{slug(partner)}"
<img class="media-object" t-attf-src="data:image/png;base64,#{partner_data.get('image_small')}"/> t-field="partner.image_small"
</a> t-field-options='{"widget": "image", "class": "media-object"}'
></a>
<div class="media-body" style="min-height: 64px;"> <div class="media-body" style="min-height: 64px;">
<a class="media-heading" t-attf-href="/members/#{ slug([partner_data.get('id'), partner_data.get('name')]) }"><t t-if="partner_data.get('parent_id')"><span t-esc="partner_data.get('parent_id')[1]"/></t> <span t-esc="partner_data.get('name')"/></a> <a class="media-heading" t-attf-href="/members/#{slug(partner)}">
<div t-raw="partner_data.get('website_short_description')"/> <span t-field="partner.display_name"/>
</a>
<div t-field="partner.website_short_description"/>
</div> </div>
</div> </div>
</t> </t>

View File

@ -1 +0,0 @@
import main

View File

@ -1,43 +0,0 @@
# -*- coding: utf-8 -*-
import openerp
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.addons.web.http import request
import werkzeug
def get_partner_template_value(partner):
ctx = dict(request.context, show_address=True)
partner_obj = request.registry['res.partner']
partner_id = partner.id
partner_ids = partner_obj.search(request.cr, request.uid, [('id', '=', partner_id)], context=request.context)
if not partner.exists() or not partner_ids:
partner = None
partner_data = partner_obj.read(
request.cr, openerp.SUPERUSER_ID, [partner_id], request.website.get_partner_white_list_fields(), context=ctx)[0]
if not partner_data["website_published"]:
return None
partner_data['name_get'] = partner_obj.name_get(request.cr, openerp.SUPERUSER_ID, [partner_id],context=request.context)[0]
partner_data['address'] = '<br/>'.join(partner_obj.name_get(
request.cr, openerp.SUPERUSER_ID, [partner_id],context=ctx)[0][1].split('\n')[1:])
values = {
'partner': partner,
'partner_data': partner_data,
}
return values
class WebsitePartner(http.Controller):
@http.route(['/partners/<int:partner_id>', '/partners/<partner_name>-<int:partner_id>'], type='http', auth="public", website=True)
def partner(self, partner_id, **post):
""" Route for displaying a single partner / customer. """
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
values = get_partner_template_value(partner)
if not values:
raise werkzeug.exceptions.NotFound
return request.website.render("website_partner.partner_detail", values)

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data> <data>
<record id="base.main_company" model="res.company"> <record id="base.main_partner" model="res.partner">
<field name="website_published">True</field> <field name="website_published">True</field>
</record> </record>
</data> </data>
</openerp> </openerp>

View File

@ -1,3 +1 @@
import res_partner import res_partner
import res_company
import website

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
from openerp.osv import osv, fields
class WebsiteResCompany(osv.Model):
_inherit = 'res.company'
_columns = {
'website_published': fields.related('partner_id', 'website_published', string='Publish', help="Publish on the website"),
}

View File

@ -5,7 +5,10 @@ from openerp.osv import osv, fields
class WebsiteResPartner(osv.Model): class WebsiteResPartner(osv.Model):
_name = 'res.partner' _name = 'res.partner'
_inherit = ['res.partner','website.seo.metadata'] _inherit = ['res.partner', 'website.seo.metadata']
def _get_ids(self, cr, uid, ids, flds, args, context=None):
return {i: i for i in ids}
_columns = { _columns = {
'website_published': fields.boolean( 'website_published': fields.boolean(
@ -16,10 +19,10 @@ class WebsiteResPartner(osv.Model):
'website_short_description': fields.text( 'website_short_description': fields.text(
'Website artner Short Description' 'Website artner Short Description'
), ),
# hack to allow using plain browse record in qweb views
'self': fields.function(_get_ids, type='many2one', relation=_name),
} }
_defaults = { _defaults = {
'website_published': False 'website_published': False
} }
def img(self, cr, uid, ids, field='image_small', context=None):
return "/website/image?model=%s&field=%s&id=%s" % (self._name, field, ids[0])

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
from openerp.osv import orm
class Website(orm.Model):
_inherit = 'website'
def get_partner_white_list_fields(self, cr, uid, ids, context=None):
return ["name", "parent_id", 'website_short_description', "website_published",
"website_description", "tel", "fax", "image", "image_small", "image_medium"]

View File

@ -2,54 +2,33 @@
<openerp> <openerp>
<data> <data>
<template id="partner_detail" name="Partner Details (Complex Template for Access Right)"> <template id="partner_page" name="Partner Page">
<t t-if="partner" > <t t-call="website.layout">
<t t-call="website.publish_management"> <div id="wrap">
<t t-set="object" t-value="partner"/> <div class="oe_structure"/>
<t t-set="publish_edit" t-value="True"/> <div class="container">
</t> <div class="row">
</t> <t t-call="website_partner.partner_detail"></t>
<t t-if="partner"><h1 class="col-md-12 text-center" t-field="partner.name"/></t>
<t t-if="not partner"><h1 class="col-md-12 text-center" t-esc="partner_data.get('name_get')[1]"/></t>
<div class="col-md-4">
<div class="text-center">
<t t-if="partner"><img t-att-src="partner.img('image')"/></t>
<t t-if="not partner"><img t-attf-src="data:image/png;base64,#{partner_data.get('image')}"/></t>
</div> </div>
<address> </div>
<table style="margin: auto;" class="well"> <div class="oe_structure"/>
<colgroup> </div>
<col width="100"/> </t>
<col/> </template>
</colgroup>
<tbody>
<t t-if="partner">
<t t-set="address" t-value="'&lt;br/&gt;'.join(partner.name_get()[0][1].split('\n')[1:])"/>
<tr t-if="address or editable"><th class="texttop">Address</th><td class="span2" t-raw="address"/></tr>
</t>
<tr t-if="not partner and partner_data.get('address')"><th class="texttop">Address</th><td class="span2" t-raw="partner_data.get('address')"/></tr>
<tr t-if="partner and (partner.website or editable)"><th>Website</th><td class="span2"> <template id="partner_detail" name="Partner Details">
<t t-if="partner.website"><span t-field="partner.website"/></t></td></tr> <t t-call="website.publish_management">
<tr t-if="partner_data.get('website')"><th>Website</th><td class="span2"><span t-esc="partner_data.get('website')"/></td></tr> <t t-set="object" t-value="partner"/>
<t t-set="publish_edit" t-value="True"/>
<tr t-if="partner and (partner.phone or editable)"><th>Phone</th><td class="span2"> </t>
<t t-if="partner.phone"><span t-field="partner.phone"/></t></td></tr> <h1 class="col-md-12 text-center" id="partner_name" t-field="partner.display_name"/>
<tr t-if="partner_data.get('phone')"><th>Phone</th><td class="span2"><span t-esc="partner_data.get('phone')"/></td></tr> <div class="col-md-4">
<div t-field="partner.image" t-field-options='{"widget": "image", "class": "center-block mb16"}'/>
<tr t-if="partner and (partner.mobile or editable)"><th>Tel</th><td class="span2"> <address class="well">
<t t-if="partner.mobile"><span t-field="partner.mobile"/></t></td></tr> <div t-field="partner.self" t-field-options='{
<tr t-if="partner_data.get('mobile')"><th>Tel</th><td class="span2"><span t-esc="partner_data.get('mobile')"/></td></tr> "widget": "contact",
"fields": ["address", "website", "phone", "fax", "email"]
<tr t-if="partner and (partner.fax or editable)"><th>Fax</th><td class="span2"> }'/>
<t t-if="partner.fax"><span t-field="partner.fax"/></t></td></tr>
<tr t-if="partner_data.get('fax')"><th>Fax</th><td class="span2"><span t-esc="partner_data.get('fax')"/></td></tr>
<tr t-if="partner and (partner.email or editable)"><th>Email</th><td class="span2">
<t t-if="partner.email"><span t-field="partner.email"/></t></td></tr>
<tr t-if="partner_data.get('email')"><th>Email</th><td class="span2"><span t-esc="partner_data.get('email')"/></td></tr>
</tbody>
</table>
</address> </address>
<t t-raw="left_column or ''"/> <t t-raw="left_column or ''"/>
</div> </div>
@ -61,9 +40,6 @@
<div class="css_non_editable_mode_hidden" t-field="partner.website_short_description"/> <div class="css_non_editable_mode_hidden" t-field="partner.website_short_description"/>
</t> </t>
</t> </t>
<t t-if="not partner">
<div class="col-md-8 mt32" t-raw="partner_data.get('website_description')"/>
</t>
<t t-raw="right_column or ''"/> <t t-raw="right_column or ''"/>
</div> </div>
</template> </template>

View File

@ -64,7 +64,7 @@ class sale_quote(http.Controller):
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id) order = order_obj.browse(request.cr, SUPERUSER_ID, order_id)
if token != order.access_token: if token != order.access_token:
return request.website.render('website.404') 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) order_obj.signal_order_confirm(request.cr, SUPERUSER_ID, [order_id], context=request.context)
message = _('Order signed by %s') % (signer,) message = _('Order signed by %s') % (signer,)
self.__message_post(message, order_id, type='comment', subtype='mt_comment', attachments=attachments) 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"> <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="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"> <div class="left">
<img t-att-src="tweet.user.profile_image_url"/> <img t-att-src="tweet.user.profile_image_url_https"/>
</div> </div>
<div class="right"> <div class="right">
<div class="top"> <div class="top">

View File

@ -5,8 +5,7 @@ import logging
import re import re
import sys import sys
import werkzeug.exceptions import werkzeug
import werkzeug.routing
import openerp import openerp
from openerp import http from openerp import http
@ -59,6 +58,12 @@ class ir_http(osv.AbstractModel):
def _auth_method_user(self): def _auth_method_user(self):
request.uid = request.session.uid request.uid = request.session.uid
if not request.uid: if not request.uid:
if not request.params.get('noredirect'):
query = werkzeug.url_encode({
'redirect': request.httprequest.url,
})
response = werkzeug.utils.redirect('/web/login?%s' % query)
werkzeug.exceptions.abort(response)
raise http.SessionExpiredException("Session expired") raise http.SessionExpiredException("Session expired")
def _auth_method_none(self): def _auth_method_none(self):

View File

@ -351,6 +351,8 @@ class ir_model_fields(osv.osv):
raise except_orm(_('Error'), _("Model %s does not exist!") % vals['relation']) raise except_orm(_('Error'), _("Model %s does not exist!") % vals['relation'])
if vals['model'] in self.pool: if vals['model'] in self.pool:
if vals['model'].startswith('x_') and vals['name'] == 'x_name':
self.pool[vals['model']]._rec_name = 'x_name'
self.pool[vals['model']].__init__(self.pool, cr) self.pool[vals['model']].__init__(self.pool, cr)
#Added context to _auto_init for special treatment to custom field for select_level #Added context to _auto_init for special treatment to custom field for select_level
ctx = dict(context, ctx = dict(context,

View File

@ -836,7 +836,8 @@ class DurationConverter(osv.AbstractModel):
v*secs_per_unit, threshold=1, locale=locale) v*secs_per_unit, threshold=1, locale=locale)
if section: if section:
sections.append(section) sections.append(section)
return u' '.join(sections) return ' '.join(sections)
class RelativeDatetimeConverter(osv.AbstractModel): class RelativeDatetimeConverter(osv.AbstractModel):
_name = 'ir.qweb.field.relative' _name = 'ir.qweb.field.relative'
@ -880,6 +881,7 @@ class Contact(orm.AbstractModel):
'fax': field_browse.fax, 'fax': field_browse.fax,
'city': field_browse.city, 'city': field_browse.city,
'country_id': field_browse.country_id and field_browse.country_id.name_get()[0][1], 'country_id': field_browse.country_id and field_browse.country_id.name_get()[0][1],
'website': field_browse.website,
'email': field_browse.email, 'email': field_browse.email,
'fields': opf, 'fields': opf,
'object': field_browse, 'object': field_browse,

View File

@ -18,6 +18,10 @@
<div t-if="phone and 'phone' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-phone'/> <span itemprop="telephone" t-esc="phone"/></div> <div t-if="phone and 'phone' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-phone'/> <span itemprop="telephone" t-esc="phone"/></div>
<div t-if="mobile and 'mobile' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-mobile-phone'/> <span itemprop="telephone" t-esc="mobile"/></div> <div t-if="mobile and 'mobile' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-mobile-phone'/> <span itemprop="telephone" t-esc="mobile"/></div>
<div t-if="fax and 'fax' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-file-text-o'/> <span itemprop="faxNumber" t-esc="fax"/></div> <div t-if="fax and 'fax' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-file-text-o'/> <span itemprop="faxNumber" t-esc="fax"/></div>
<div t-if="website and 'website' in fields" class='css_editable_mode_hidden'>
<i t-if="not options.get('no_marker')" class='fa fa-globe'/>
<a t-att-href="website"><span itemprop="website" t-esc="website"/></a>
</div>
<div t-if="email and 'email' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-envelope'/> <span itemprop="email" t-esc="email"/></div> <div t-if="email and 'email' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-envelope'/> <span itemprop="email" t-esc="email"/></div>
</div> </div>
</address> </address>

View File

@ -253,7 +253,7 @@ class view(osv.osv):
['type', '=', view_type], ['type', '=', view_type],
['inherit_id', '=', False], ['inherit_id', '=', False],
] ]
ids = self.search(cr, uid, domain, limit=1, order='priority', context=context) ids = self.search(cr, uid, domain, limit=1, context=context)
if not ids: if not ids:
return False return False
return ids[0] return ids[0]

View File

@ -23,6 +23,7 @@ import datetime
from lxml import etree from lxml import etree
import math import math
import pytz import pytz
import urlparse
import openerp import openerp
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
@ -509,6 +510,14 @@ class res_partner(osv.osv, format_address):
if not parent.is_company: if not parent.is_company:
parent.write({'is_company': True}) parent.write({'is_company': True})
def _clean_website(self, website):
(scheme, netloc, path, params, query, fragment) = urlparse.urlparse(website)
if not scheme:
if not netloc:
netloc, path = path, ''
website = urlparse.urlunparse(('http', netloc, path, params, query, fragment))
return website
def write(self, cr, uid, ids, vals, context=None): def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)): if isinstance(ids, (int, long)):
ids = [ids] ids = [ids]
@ -516,6 +525,8 @@ class res_partner(osv.osv, format_address):
#is the same as the company of all users that inherit from this partner #is the same as the company of all users that inherit from this partner
#(this is to allow the code from res_users to write to the partner!) or #(this is to allow the code from res_users to write to the partner!) or
#if setting the company_id to False (this is compatible with any user company) #if setting the company_id to False (this is compatible with any user company)
if vals.get('website'):
vals['website'] = self._clean_website(vals['website'])
if vals.get('company_id'): if vals.get('company_id'):
for partner in self.browse(cr, uid, ids, context=context): for partner in self.browse(cr, uid, ids, context=context):
if partner.user_ids: if partner.user_ids:
@ -528,6 +539,8 @@ class res_partner(osv.osv, format_address):
return result return result
def create(self, cr, uid, vals, context=None): def create(self, cr, uid, vals, context=None):
if vals.get('website'):
vals['website'] = self._clean_website(vals['website'])
new_id = super(res_partner, self).create(cr, uid, vals, context=context) new_id = super(res_partner, self).create(cr, uid, vals, context=context)
partner = self.browse(cr, uid, new_id, context=context) partner = self.browse(cr, uid, new_id, context=context)
self._fields_sync(cr, uid, partner, vals, context) self._fields_sync(cr, uid, partner, vals, context)

View File

@ -77,7 +77,7 @@
<field name="street">31 Hong Kong street</field> <field name="street">31 Hong Kong street</field>
<field name="email">asusteK@yourcompany.example.com</field> <field name="email">asusteK@yourcompany.example.com</field>
<field name="phone">(+886) (02) 4162 2023</field> <field name="phone">(+886) (02) 4162 2023</field>
<field name="website">www.asustek.com</field> <field name="website">http://www.asustek.com</field>
<field name="image" type="base64" file="base/static/img/res_partner_1-image.jpg"/> <field name="image" type="base64" file="base/static/img/res_partner_1-image.jpg"/>
</record> </record>
<record id="res_partner_2" model="res.partner"> <record id="res_partner_2" model="res.partner">
@ -90,7 +90,7 @@
<field name="street">69 rue de Namur</field> <field name="street">69 rue de Namur</field>
<field name="email">agrolait@yourcompany.example.com</field> <field name="email">agrolait@yourcompany.example.com</field>
<field name="phone">+32 10 588 558</field> <field name="phone">+32 10 588 558</field>
<field name="website">www.agrolait.com</field> <field name="website">http://www.agrolait.com</field>
<field name="image" type="base64" file="base/static/img/res_partner_2-image.jpg"/> <field name="image" type="base64" file="base/static/img/res_partner_2-image.jpg"/>
</record> </record>
<record id="res_partner_3" model="res.partner"> <record id="res_partner_3" model="res.partner">
@ -105,7 +105,7 @@
<field name="street">52 Chop Suey street</field> <field name="street">52 Chop Suey street</field>
<field name="email">chinaexport@yourcompany.example.com</field> <field name="email">chinaexport@yourcompany.example.com</field>
<field name="phone">+86 21 6484 5671</field> <field name="phone">+86 21 6484 5671</field>
<field name="website">www.chinaexport.com/</field> <field name="website">http://www.chinaexport.com/</field>
<field name="image" type="base64" file="base/static/img/res_partner_3-image.png"/> <field name="image" type="base64" file="base/static/img/res_partner_3-image.png"/>
</record> </record>
@ -122,7 +122,7 @@
<field name="street">3661 Station Street</field> <field name="street">3661 Station Street</field>
<field name="email">deltapc@yourcompany.example.com</field> <field name="email">deltapc@yourcompany.example.com</field>
<field name="phone">+1 510 340 2385</field> <field name="phone">+1 510 340 2385</field>
<field name="website">www.distribpc.com/</field> <field name="website">http://www.distribpc.com/</field>
<field name="image" type="base64" file="base/static/img/res_partner_4-image.png"/> <field name="image" type="base64" file="base/static/img/res_partner_4-image.png"/>
</record> </record>
@ -137,7 +137,7 @@
<field name="city">Chicago</field> <field name="city">Chicago</field>
<field name="email">epic@yourcompany.example.com</field> <field name="email">epic@yourcompany.example.com</field>
<field name="phone">+1 312 349 2324</field> <field name="phone">+1 312 349 2324</field>
<field name="website">www.epic-tech.info//</field> <field name="website">http://www.epic-tech.info//</field>
<field name="image" type="base64" file="base/static/img/res_partner_5-image.jpg"/> <field name="image" type="base64" file="base/static/img/res_partner_5-image.jpg"/>
</record> </record>
@ -169,7 +169,7 @@
<field name="country_id" ref="base.uk"/> <field name="country_id" ref="base.uk"/>
<field name="phone">+44 121 690 4596</field> <field name="phone">+44 121 690 4596</field>
<field name="email">wealthyandsons@yourcompany.example.com</field> <field name="email">wealthyandsons@yourcompany.example.com</field>
<field name="website">www.wealthyandsons.com/</field> <field name="website">http://www.wealthyandsons.com/</field>
<field name="image" type="base64" file="base/static/img/res_partner_7-image.jpg"/> <field name="image" type="base64" file="base/static/img/res_partner_7-image.jpg"/>
</record> </record>
@ -198,7 +198,7 @@
<field name="country_id" ref="base.in"/> <field name="country_id" ref="base.in"/>
<field name="email">bestdesigners@yourcompany.example.com</field> <field name="email">bestdesigners@yourcompany.example.com</field>
<field name="phone">+91 22 3445 0349</field> <field name="phone">+91 22 3445 0349</field>
<field name="website">www.bestdesigners.com</field> <field name="website">http://www.bestdesigners.com</field>
<field name="image" type="base64" file="base/static/img/res_partner_9-image.jpg"/> <field name="image" type="base64" file="base/static/img/res_partner_9-image.jpg"/>
</record> </record>
@ -228,7 +228,7 @@
<field name="city">Barcelona</field> <field name="city">Barcelona</field>
<field name="zip">08078</field> <field name="zip">08078</field>
<field name="phone">+34 934 340 230</field> <field name="phone">+34 934 340 230</field>
<field name="website">www.lumitech.com</field> <field name="website">http://www.lumitech.com</field>
<field name="email">luminous@yourcompany.example.com</field> <field name="email">luminous@yourcompany.example.com</field>
<field name="image" type="base64" file="base/static/img/res_partner_11-image.png"/> <field name="image" type="base64" file="base/static/img/res_partner_11-image.png"/>
</record> </record>
@ -245,7 +245,7 @@
<field name="country_id" ref="base.fr"/> <field name="country_id" ref="base.fr"/>
<field name="street">93, Press Avenue</field> <field name="street">93, Press Avenue</field>
<field name="email">camptocamp@yourcompany.example.com</field> <field name="email">camptocamp@yourcompany.example.com</field>
<field name="website">www.camptocamp.com</field> <field name="website">http://www.camptocamp.com</field>
<field name="image" type="base64" file="base/static/img/res_partner_12-image.jpg"/> <field name="image" type="base64" file="base/static/img/res_partner_12-image.jpg"/>
</record> </record>
@ -260,7 +260,7 @@
<field name="email">axelor@yourcompany.example.com</field> <field name="email">axelor@yourcompany.example.com</field>
<field name="phone">+33 1 64 61 04 01</field> <field name="phone">+33 1 64 61 04 01</field>
<field name="street">12 rue Albert Einstein</field> <field name="street">12 rue Albert Einstein</field>
<field name="website">www.axelor.com/</field> <field name="website">http://www.axelor.com/</field>
<field name="image" type="base64" file="base/static/img/res_partner_13-image.jpg"/> <field name="image" type="base64" file="base/static/img/res_partner_13-image.jpg"/>
</record> </record>
@ -336,7 +336,7 @@
<field name="phone">+1 857 349 3049</field> <field name="phone">+1 857 349 3049</field>
<field name="country_id" ref="base.us"/> <field name="country_id" ref="base.us"/>
<field name="street">One Lincoln Street</field> <field name="street">One Lincoln Street</field>
<field name="website">www.think-big.com</field> <field name="website">http://www.think-big.com</field>
<field name="image" type="base64" file="base/static/img/res_partner_18-image.png"/> <field name="image" type="base64" file="base/static/img/res_partner_18-image.png"/>
</record> </record>
@ -394,7 +394,7 @@
<field name="zip">LE4 2BN</field> <field name="zip">LE4 2BN</field>
<field name="phone">+44 20 1294 2193</field> <field name="phone">+44 20 1294 2193</field>
<field name="country_id" ref="base.uk"/> <field name="country_id" ref="base.uk"/>
<field name="website">www.vicking-direct.com</field> <field name="website">http://www.vicking-direct.com</field>
<field name="email">vickingdirect@yourcompany.example.com</field> <field name="email">vickingdirect@yourcompany.example.com</field>
<field name="image" type="base64" file="base/static/img/res_partner_22-image.jpg"/> <field name="image" type="base64" file="base/static/img/res_partner_22-image.jpg"/>
</record> </record>

View File

@ -3758,9 +3758,13 @@ class BaseModel(object):
ir_values_obj.unlink(cr, uid, ir_value_ids, context=context) ir_values_obj.unlink(cr, uid, ir_value_ids, context=context)
for order, obj_name, store_ids, fields in result_store: for order, obj_name, store_ids, fields in result_store:
if obj_name != self._name: if obj_name == self._name:
effective_store_ids = list(set(store_ids) - set(ids))
else:
effective_store_ids = store_ids
if effective_store_ids:
obj = self.pool[obj_name] obj = self.pool[obj_name]
cr.execute('select id from '+obj._table+' where id IN %s', (tuple(store_ids),)) cr.execute('select id from '+obj._table+' where id IN %s', (tuple(effective_store_ids),))
rids = map(lambda x: x[0], cr.fetchall()) rids = map(lambda x: x[0], cr.fetchall())
if rids: if rids:
obj._store_set_values(cr, uid, rids, fields, context) obj._store_set_values(cr, uid, rids, fields, context)
@ -5130,7 +5134,14 @@ class BaseModel(object):
# shortcut read if we only want the ids # shortcut read if we only want the ids
return [{'id': id} for id in record_ids] return [{'id': id} for id in record_ids]
result = self.read(cr, uid, record_ids, fields, context=context) # read() ignores active_test, but it would forward it to any downstream search call
# (e.g. for x2m or function fields), and this is not the desired behavior, the flag
# was presumably only meant for the main search().
# TODO: Move this to read() directly?
read_ctx = dict(context or {})
read_ctx.pop('active_test', None)
result = self.read(cr, uid, record_ids, fields, context=read_ctx)
if len(result) <= 1: if len(result) <= 1:
return result return result

View File

@ -477,6 +477,7 @@ ALL_LANGUAGES = {
'lo_LA': u'Lao / ພາສາລາວ', 'lo_LA': u'Lao / ພາສາລາວ',
'lt_LT': u'Lithuanian / Lietuvių kalba', 'lt_LT': u'Lithuanian / Lietuvių kalba',
'lv_LV': u'Latvian / latviešu valoda', 'lv_LV': u'Latvian / latviešu valoda',
'mk_MK': u'Macedonian / македонски јазик',
'ml_IN': u'Malayalam / മലയാളം', 'ml_IN': u'Malayalam / മലയാളം',
'mn_MN': u'Mongolian / монгол', 'mn_MN': u'Mongolian / монгол',
'nb_NO': u'Norwegian Bokmål / Norsk bokmål', 'nb_NO': u'Norwegian Bokmål / Norsk bokmål',