[MERGE] forward port of branch saas-4 up to revid bb26dea
This commit is contained in:
commit
a756b82372
|
@ -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'):
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
#------------------------------------------------------
|
#------------------------------------------------------
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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] = [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 ----- */
|
||||||
|
|
|
@ -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\
|
||||||
|
|
|
@ -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"><?xml version="1.0" encoding="UTF-8"?>
|
<template id="sitemap_xml"><?xml version="1.0" encoding="UTF-8"?>
|
||||||
<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>
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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');
|
||||||
},
|
},
|
||||||
|
|
|
@ -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">
|
||||||
&nbsp;
|
&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"/>
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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'],
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
import res_partner
|
import res_partner
|
||||||
import website
|
|
||||||
|
|
|
@ -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'),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -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>
|
||||||
|
|
|
@ -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&' % 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&' % 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&height=240&partner_ids=#{ google_map_partner_ids }&partner_url=/partners"
|
<iframe t-attf-src="/google_map/?width=260&height=240&partner_ids=#{ google_map_partner_ids }&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>
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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'),
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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="{"widget": "duration", "unit": "hour"}"/><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>
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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')"/>
|
||||||
|
|
|
@ -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')" />
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 > 1" class="subtitle">
|
<div t-if="vote_count > 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>=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>=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>=750"
|
<button type="button" t-if="user.partner_id.id == message.author_id.id and user.karma>=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">&times;</button>
|
class="close comment_delete">&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">
|
||||||
|
|
|
@ -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']
|
||||||
|
|
||||||
|
|
|
@ -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)],
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
import main
|
|
|
@ -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)
|
|
|
@ -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>
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
import res_partner
|
import res_partner
|
||||||
import res_company
|
|
||||||
import website
|
|
|
@ -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"),
|
|
||||||
}
|
|
|
@ -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])
|
|
||||||
|
|
|
@ -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"]
|
|
|
@ -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="'<br/>'.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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
Loading…
Reference in New Issue