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

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

View File

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

View File

@ -21,7 +21,7 @@
from openerp.osv import osv, fields
from openerp.addons.edi import EDIMixin
from urllib import urlencode
from werkzeug import url_encode
INVOICE_LINE_EDI_STRUCT = {
'name': True,
@ -274,7 +274,7 @@ class account_invoice(osv.osv, EDIMixin):
"no_note": "1",
"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
_columns = {

View File

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

View File

@ -220,7 +220,7 @@ class account_analytic_account(osv.osv):
res['value']['description'] = template.description
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={}
if partner_id:
partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)

View File

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

View File

@ -170,9 +170,6 @@ class crm_lead(format_address, osv.osv):
"""
:return dict: difference between current date and log date
"""
cal_obj = self.pool.get('resource.calendar')
res_obj = self.pool.get('resource.resource')
res = {}
for lead in self.browse(cr, uid, ids, context=context):
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_open = datetime.strptime(lead.date_open, "%Y-%m-%d %H:%M:%S")
ans = date_open - date_create
date_until = lead.date_open
elif field == 'day_close':
if lead.date_closed:
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_until = lead.date_closed
ans = date_close - date_create
if ans:
resource_id = False
if lead.user_id:
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))
duration = abs(int(ans.days))
res[lead.id][field] = duration
return res
def _meeting_count(self, cr, uid, ids, field_name, arg, context=None):
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', \
multi='day_open', type="float", store=True),
'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),
# Messaging and marketing
@ -972,7 +944,7 @@ class crm_lead(format_address, osv.osv):
if obj.type == 'opportunity':
model, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'crm', 'crm_case_form_view_oppor')
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
def message_get_suggested_recipients(self, cr, uid, ids, context=None):

View File

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

View File

@ -108,7 +108,7 @@
<field name="computation_mode">count</field>
<field name="display_mode">boolean</field>
<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="action_id" eval="ref('base.action_res_company_form')" />
<field name="res_id_field">user.company_id.id</field>

View File

@ -316,19 +316,20 @@ class gamification_challenge(osv.Model):
for challenge in self.browse(cr, uid, ids, context=context):
# goals closed but still opened at the last report date
closed_goals_to_report = goal_obj.search(cr, uid, [
('challenge_id', '=', challenge.id),
('start_date', '>=', challenge.last_report_date),
('end_date', '<=', challenge.last_report_date)
])
if challenge.last_report_date != fields.date.today():
# goals closed but still opened at the last report date
closed_goals_to_report = goal_obj.search(cr, uid, [
('challenge_id', '=', challenge.id),
('start_date', '>=', challenge.last_report_date),
('end_date', '<=', challenge.last_report_date)
])
if len(closed_goals_to_report) > 0:
# some goals need a final report
self.report_progress(cr, uid, challenge, subset_goal_ids=closed_goals_to_report, context=context)
if challenge.next_report_date and fields.date.today() >= challenge.next_report_date:
self.report_progress(cr, uid, challenge, context=context)
if fields.date.today() == challenge.next_report_date:
self.report_progress(cr, uid, challenge, context=context)
elif len(closed_goals_to_report) > 0:
# 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)
return True
@ -446,6 +447,12 @@ class gamification_challenge(osv.Model):
if 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:
values['remind_update_delay'] = challenge.remind_update_delay

View File

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

View File

@ -40,7 +40,7 @@
</record>
<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="domain_force">[('user_id.company_id', 'child_of', [user.company_id.id])]</field>
<field name="global" eval="True"/>

View File

@ -90,6 +90,11 @@ class hr_holidays_status(osv.osv):
}
def name_get(self, cr, uid, ids, context=None):
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 = []
for record in self.browse(cr, uid, ids, context=context):
name = record.name

View File

@ -83,7 +83,7 @@
</div>
</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="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"/>

View File

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

View File

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

View File

@ -152,18 +152,8 @@ class mail_mail(osv.Model):
context = {}
if partner and partner.user_ids:
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
# the parameters to encode for the query and fragment part of url
query = {'db': cr.dbname}
fragment = {
'login': partner.user_ids[0].login,
'action': 'mail.action_mail_redirect',
}
if mail.notification:
fragment['message_id'] = mail.mail_message_id.id
elif mail.model and mail.res_id:
fragment.update(model=mail.model, res_id=mail.res_id)
url = urljoin(base_url, "/web?%s#%s" % (urlencode(query), urlencode(fragment)))
mail_model = mail.model or 'mail.thread'
url = urljoin(base_url, self.pool[mail_model]._get_access_link(cr, uid, mail, partner, context=context))
return _("""<span class='oe_mail_footer_access'><small>about <a style='color:inherit' href="%s">%s %s</a></small></span>""") % (url, context.get('model_name', ''), mail.record_name)
else:
return None

View File

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

View File

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

View File

@ -21,12 +21,11 @@
</group>
<group>
<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"
type="action" target="new"
context="{'template_id':active_id}"/>
<br />
<!-- <field name="website_link" widget='html' radonly='1'
style='margin: 0px; padding: 0px;'/> -->
</div>
</group>
</group>

View File

@ -580,7 +580,7 @@ class pos_order(osv.osv):
try:
self.signal_paid(cr, uid, [order_id])
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:
self.action_invoice(cr, uid, [order_id], context)
@ -744,8 +744,6 @@ class pos_order(osv.osv):
move_obj = self.pool.get('stock.move')
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 {}
picking_type = order.picking_type_id
picking_id = False

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@ from openerp.osv import osv, fields
from openerp.addons.edi import EDIMixin
from openerp.tools.translate import _
from urllib import urlencode
from werkzeug import url_encode
SALE_ORDER_LINE_EDI_STRUCT = {
'sequence': True,
@ -197,7 +197,7 @@ class sale_order(osv.osv, EDIMixin):
"no_note": "1",
"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
_columns = {

View File

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

View File

@ -358,12 +358,12 @@ class sale_order_line(osv.osv):
uom = False
if not uom2:
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:
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,
max(0,product_obj.virtual_available), product_obj.uom_id.name,
max(0,product_obj.qty_available), product_obj.uom_id.name)
(qty, uom2.name,
max(0,product_obj.virtual_available), uom2.name,
max(0,product_obj.qty_available), uom2.name)
warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n"
#update of warning messages

View File

@ -234,7 +234,7 @@ class WebsiteSurvey(http.Controller):
# AJAX submission of a page
@http.route(['/survey/submit/<model("survey.survey"):survey>'],
type='http', auth='public', website=True)
type='http', methods=['POST'], auth='public', website=True)
def submit(self, survey, **post):
_logger.debug('Incoming data: %s', post)
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)
filter_display_data = survey_obj.get_filter_display_data(request.cr, request.uid, filter_data, context=request.context)
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,
'current_filters': current_filters,
'filter_display_data': filter_display_data,

View File

@ -46,6 +46,8 @@ instance.web.Notification = instance.web.Widget.extend({
}
});
var opened_modal = [];
instance.web.action_notify = function(element, action) {
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.$buttons.insertAfter(this.$dialog_box.find(".modal-body"));
//add to list of currently opened modal
opened_modal.push(this.$dialog_box);
return this;
},
_add_buttons: function(buttons) {
@ -152,7 +156,7 @@ instance.web.Dialog = instance.web.Widget.extend({
'keyboard': true,
});
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'){
dialog_class_size.addClass('modal-sm');
}
@ -165,7 +169,7 @@ instance.web.Dialog = instance.web.Widget.extend({
}
$dialog_content.openerpClass();
this.$dialog_box.on('hidden.bs.modal', this, function(){
this.$dialog_box.on('hidden.bs.modal', this, function() {
self.close();
});
this.$dialog_box.modal('show');
@ -175,13 +179,15 @@ instance.web.Dialog = instance.web.Widget.extend({
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) {
if (this.dialog_inited) {
if (this.dialog_inited && !this.__tmp_dialog_hiding) {
this.trigger("closing", reason);
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
//of the queue, otherwise it might already have been removed before the modal-backdrop
//is removed when pressing escape key
var $parent = this.$el.parents('.modal');
var $element = this.$dialog_box;
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);
}
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.") }});
return;
}
if (error.data.exception_type === "except_osv" || error.data.exception_type === "warning"
|| error.data.exception_type === "access_error") {
if (error.data.exception_type === "except_osv" || error.data.exception_type === "warning" || error.data.exception_type === "access_error") {
this.show_warning(error);
} else {
this.show_error(error);

View File

@ -1,24 +1,18 @@
# -*- coding: utf-8 -*-
import cStringIO
import contextlib
import hashlib
import datetime
from itertools import islice
import json
import logging
import os
import datetime
import re
from sys import maxint
import werkzeug
import werkzeug.exceptions
import werkzeug.utils
import werkzeug.wrappers
from PIL import Image
import openerp
from openerp.osv import fields
from openerp.addons.website.models import website
from openerp.addons.web import http
from openerp.http import request, Response
@ -27,6 +21,7 @@ logger = logging.getLogger(__name__)
# Completely arbitrary limits
MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768)
LOC_PER_SITEMAP = 45000
SITEMAP_CACHE_TIME = datetime.timedelta(hours=12)
class Website(openerp.addons.web.controllers.main.Home):
#------------------------------------------------------
@ -52,7 +47,7 @@ class Website(openerp.addons.web.controllers.main.Home):
# TODO: can't we just put auth=public, ... in web client ?
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):
values = {
'path': page,
@ -78,33 +73,64 @@ class Website(openerp.addons.web.controllers.main.Home):
@http.route('/sitemap.xml', type='http', auth="public", website=True)
def sitemap_xml_index(self):
pages = list(request.website.enumerate_pages())
if len(pages)<=LOC_PER_SITEMAP:
return self.__sitemap_xml(pages, 0)
# Sitemaps must be split in several smaller files with a sitemap index
values = {
'pages': range(len(pages)/LOC_PER_SITEMAP+1),
'url_root': request.httprequest.url_root
}
headers = {
'Content-Type': 'application/xml;charset=utf-8',
}
return request.render('website.sitemap_index_xml', values, headers=headers)
cr, uid, context = request.cr, openerp.SUPERUSER_ID, request.context
ira = request.registry['ir.attachment']
iuv = request.registry['ir.ui.view']
mimetype ='application/xml;charset=utf-8'
content = None
@http.route('/sitemap-<int:page>.xml', type='http', auth="public", website=True)
def sitemap_xml(self, page):
pages = list(request.website.enumerate_pages())
return self.__sitemap_xml(pages, page)
def create_sitemap(url, content):
ira.create(cr, uid, dict(
datas=content.encode('base64'),
mimetype=mimetype,
type='binary',
name=url,
url=url,
), context=context)
def __sitemap_xml(self, pages, index=0):
values = {
'pages': pages[index*LOC_PER_SITEMAP:(index+1)*LOC_PER_SITEMAP],
'url_root': request.httprequest.url_root.rstrip('/')
}
headers = {
'Content-Type': 'application/xml;charset=utf-8',
}
return request.render('website.sitemap_xml', values, headers=headers)
sitemap = ira.search_read(cr, uid, [('url', '=' , '/sitemap.xml'), ('type', '=', 'binary')], ('datas', 'create_date'), context=context)
if sitemap:
# Check if stored version is still valid
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
create_date = datetime.datetime.strptime(sitemap[0]['create_date'], server_format)
delta = datetime.datetime.now() - create_date
if delta < SITEMAP_CACHE_TIME:
content = sitemap[0]['datas'].decode('base64')
if not content:
# Remove all sitemaps in ir.attachments as we're going to regenerated them
sitemap_ids = ira.search(cr, uid, [('url', '=like' , '/sitemap%.xml'), ('type', '=', 'binary')], context=context)
if sitemap_ids:
ira.unlink(cr, uid, sitemap_ids, context=context)
pages = 0
first_page = None
locs = request.website.enumerate_pages()
while True:
start = pages * LOC_PER_SITEMAP
loc_slice = islice(locs, start, start + LOC_PER_SITEMAP)
urls = iuv.render(cr, uid, 'website.sitemap_locs', dict(locs=loc_slice), context=context)
if urls.strip():
page = iuv.render(cr, uid, 'website.sitemap_xml', dict(content=urls), context=context)
if not first_page:
first_page = page
pages += 1
create_sitemap('/sitemap-%d.xml' % pages, page)
else:
break
if not pages:
return request.not_found()
elif pages == 1:
content = first_page
else:
# Sitemaps must be split in several smaller files with a sitemap index
content = iuv.render(cr, uid, 'website.sitemap_index_xml', dict(
pages=range(1, pages + 1),
url_root=request.httprequest.url_root,
), context=context)
create_sitemap('/sitemap.xml', content)
return request.make_response(content, [('Content-Type', mimetype)])
#------------------------------------------------------
# Edit
@ -332,13 +358,7 @@ class Website(openerp.addons.web.controllers.main.Home):
return request.website.kanban_col(**post)
def placeholder(self, response):
# file_open may return a StringIO. StringIO can be closed but are
# not context managers in Python 2 though that is fixed in 3
with contextlib.closing(openerp.tools.misc.file_open(
os.path.join('web', 'static', 'src', 'img', 'placeholder.png'),
mode='rb')) as f:
response.data = f.read()
return response.make_conditional(request.httprequest)
return request.registry['website']._image_placeholder(response)
@http.route([
'/website/image',
@ -354,60 +374,15 @@ class Website(openerp.addons.web.controllers.main.Home):
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 (``write_date``)
field (``__last_update``)
The requested field is assumed to be base64-encoded image data in
all cases.
"""
id = int(id)
response = werkzeug.wrappers.Response()
concurrency = 'write_date'
try:
[record] = request.registry[model].read(request.cr, openerp.SUPERUSER_ID, [id],
[concurrency, field],
context=request.context)
except:
return self.placeholder(response)
return request.registry['website']._image(
request.cr, request.uid, model, id, field, 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

View File

@ -1,13 +1,20 @@
# -*- coding: utf-8 -*-
import cStringIO
import contextlib
import datetime
import hashlib
import inspect
import itertools
import logging
import math
import mimetypes
import os
import re
import urlparse
from PIL import Image
from sys import maxint
import werkzeug
import werkzeug.exceptions
import werkzeug.utils
@ -459,6 +466,95 @@ class website(osv.osv):
html += request.website._render(template, {'object_id': object_id})
return html
def _image_placeholder(self, response):
# file_open may return a StringIO. StringIO can be closed but are
# not context managers in Python 2 though that is fixed in 3
with contextlib.closing(openerp.tools.misc.file_open(
os.path.join('web', 'static', 'src', 'img', 'placeholder.png'),
mode='rb')) as f:
response.data = f.read()
return response.make_conditional(request.httprequest)
def _image(self, cr, uid, model, id, field, response, max_width=maxint, max_height=maxint, context=None):
""" Fetches the requested field and ensures it does not go above
(max_width, max_height), resizing it if necessary.
Resizing is bypassed if the object provides a $field_big, which will
be interpreted as a pre-resized version of the base field.
If the record is not found or does not have the requested field,
returns a placeholder image via :meth:`~._image_placeholder`.
Sets and checks conditional response parameters:
* :mailheader:`ETag` is always set (and checked)
* :mailheader:`Last-Modified is set iif the record has a concurrency
field (``__last_update``)
The requested field is assumed to be base64-encoded image data in
all cases.
"""
Model = self.pool[model]
id = int(id)
ids = Model.search(cr, uid,
[('id', '=', id)], context=context)
if not ids and 'website_published' in Model._all_columns:
ids = Model.search(cr, openerp.SUPERUSER_ID,
[('id', '=', id), ('website_published', '=', True)], context=context)
if not ids:
return self._image_placeholder(response)
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):
_name = "website.menu"
_description = "Website Menu"

View File

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

View File

@ -41,6 +41,9 @@ class CrawlSuite(unittest2.TestSuite):
starting the crawl
"""
at_install = False
post_install = True
def __init__(self, user=None, password=None):
super(CrawlSuite, self).__init__()
@ -108,14 +111,23 @@ class CrawlSuite(unittest2.TestSuite):
for link in doc.xpath('//a[@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
# bother splitting them if we've already ignored them
# previously
if href in seen: continue
seen.add(href)
parts = urlparse.urlsplit(href)
# FIXME: handle relative link (not parts.path.startswith /)
if parts.netloc or \
not parts.path.startswith('/') or \
parts.path == '/web' or\

View File

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

View File

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

View File

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

View File

@ -49,7 +49,7 @@
<t t-foreach="posts" t-as="post">
<div class="col-md-4">
<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">
&amp;nbsp;
<span class="fa fa-warning" title="Not published"/>
@ -187,6 +187,7 @@
<span class="fa fa-times"/>
</a>
</div>
<div class="cover-storage oe_hidden"></div>
<t t-call="website.publish_management">
<t t-set="object" t-value="blog_post"/>
<t t-set="publish_edit" t-value="True"/>

View File

@ -16,45 +16,36 @@ class contactus(http.Controller):
)
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):
values = {}
for field in ['description', 'partner_name', 'phone', 'contact_name', 'email_from', 'name']:
if kwargs.get(field):
values[field] = kwargs.pop(field)
values.update(kwargs=kwargs.items())
print values
return request.website.render("website.contactus", values)
@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):
post = {}
post['description'] = description
post['partner_name'] = partner_name
post['phone'] = phone
post['contact_name'] = contact_name
post['email_from'] = email_from
post['name'] = name
required_fields = ['contact_name', 'email_from', 'description']
error = set()
values = dict((key, post.get(key)) for key in post)
values['error'] = error
post = {
'description': description,
'partner_name': partner_name,
'phone': phone,
'contact_name': contact_name,
'email_from': email_from,
'name': name or contact_name,
'user_id': False,
}
# fields validation
for field in required_fields:
if not post.get(field):
error.add(field)
error = set(field for field in ['contact_name', 'email_from', 'description']
if not post.get(field))
values = dict(post, error=error)
if error:
values.update(kwargs=kwargs.items())
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:
post['channel_id'] = request.registry['ir.model.data'].get_object_reference(request.cr, SUPERUSER_ID, 'crm', 'crm_case_channel_website')[1]
except ValueError:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -50,7 +50,6 @@ class website_event(website_event):
def _add_event(self, event_name="New Event", context={}, **kwargs):
try:
print kwargs
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,{
'name': _('Subscription'),

View File

@ -18,19 +18,17 @@
# 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
from openerp.addons.web import http
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):
@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
@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):
comp = lambda x: (x.date, bool(x.location_id))
event.track_ids.sort(lambda x,y: cmp(comp(x), comp(y)))
days_tracks = collections.defaultdict(lambda: [])
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_nbr = {}
for track in event.track_ids:
if not track.date: continue
days.setdefault(track.date[:10], [])
days[track.date[:10]].append(track)
for d in days:
days_nbr[d] = len(days[d])
days[d] = self._prepare_calendar(event, days[d])
days_tracks_count = {}
for day, tracks in days_tracks.iteritems():
days_tracks_count[day] = len(tracks)
days[day] = self._prepare_calendar(event, tracks)
return request.website.render("website_event_track.agenda", {
'event': event,
'days': days,
'days_nbr': days_nbr,
'days_nbr': days_tracks_count,
'tag': tag
})

View File

@ -53,7 +53,9 @@
<input type="text" class="form-control" placeholder="Filter Tracks..." id="event_track_search"/>
</div>
</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="dates" t-value="days[day]['dates']"/>
<h3 class="page-header mt0">
@ -222,7 +224,7 @@
<b>Date</b><br/>
<span t-field="track.date" t-field-options='{"hide_seconds":"True"}'/><br/>
<b>Duration</b><br/>
<span t-esc="'%.2f' % (track.duration)"/> hours<br/>
<span t-field="track.duration" t-field-options="{&quot;widget&quot;: &quot;duration&quot;, &quot;unit&quot;: &quot;hour&quot;}"/><br/>
<b>Location</b><br/>
<span t-field="track.location_id"/><br/>
</div>

View File

@ -2,6 +2,7 @@
from datetime import datetime
import werkzeug.urls
import werkzeug.wrappers
import simplejson
from openerp import tools
@ -73,7 +74,7 @@ class WebsiteForum(http.Controller):
forums = Forum.browse(cr, uid, obj_ids, context=context)
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):
forum_id = request.registry['forum.forum'].create(request.cr, request.uid, {
'name': forum_name,
@ -87,14 +88,15 @@ class WebsiteForum(http.Controller):
@http.route(['/forum/<model("forum.forum"):forum>',
'/forum/<model("forum.forum"):forum>/page/<int:page>',
'''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions'''
'''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions''',
'''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions/page/<int:page>''',
], type='http', auth="public", website=True)
def questions(self, forum, tag=None, page=1, filters='all', sorting='date', search='', **post):
cr, uid, context = request.cr, request.uid, request.context
Post = request.registry['forum.post']
user = request.registry['res.users'].browse(cr, uid, uid, context=context)
domain = [('forum_id', '=', forum.id), ('parent_id', '=', False)]
domain = [('forum_id', '=', forum.id), ('parent_id', '=', False), ('state', '=', 'active')]
if search:
domain += ['|', ('name', 'ilike', search), ('content', 'ilike', search)]
if tag:
@ -110,12 +112,28 @@ class WebsiteForum(http.Controller):
order = 'child_count desc'
elif sorting == 'vote':
order = 'vote_count desc'
else:
sorting = 'date'
elif sorting == 'date':
order = 'write_date desc'
else:
sorting = 'creation'
order = 'create_date desc'
question_count = Post.search(cr, uid, domain, count=True, context=context)
pager = request.website.pager(url="/forum/%s" % slug(forum), total=question_count, page=page, step=self._post_per_page, scope=self._post_per_page)
if tag:
url = "/forum/%s/%s/questions" % (slug(forum), slug(tag))
else:
url = "/forum/%s" % slug(forum)
url_args = {}
if search:
url_args['search'] = search
if filters:
url_args['filters'] = filters
if sorting:
url_args['sorting'] = sorting
pager = request.website.pager(url=url, total=question_count, page=page,
step=self._post_per_page, scope=self._post_per_page,
url_args=url_args)
obj_ids = Post.search(cr, uid, domain, limit=self._post_per_page, offset=pager['offset'], order=order, context=context)
question_ids = Post.browse(cr, uid, obj_ids, context=context)
@ -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)
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):
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]:
@ -262,7 +280,7 @@ class WebsiteForum(http.Controller):
}, context=request.context)
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):
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]:
@ -271,7 +289,7 @@ class WebsiteForum(http.Controller):
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)))
@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):
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]:
@ -280,7 +298,7 @@ class WebsiteForum(http.Controller):
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)))
@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):
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]:
@ -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)
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):
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]:
@ -419,14 +437,16 @@ class WebsiteForum(http.Controller):
# 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):
cr, uid, context = request.cr, request.uid, request.context
User = request.registry['res.users']
step = 30
tag_count = User.search(cr, SUPERUSER_ID, [('karma', '>', 1)], count=True, context=context)
pager = request.website.pager(url="/forum/users", total=tag_count, page=page, step=step, scope=30)
pager = request.website.pager(url="/forum/%s/users" % slug(forum), total=tag_count, page=page, step=step, scope=30)
obj_ids = User.search(cr, SUPERUSER_ID, [('karma', '>', 1)], limit=step, offset=pager['offset'], order='karma DESC', context=context)
users = User.browse(cr, SUPERUSER_ID, obj_ids, context=context)
@ -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" % 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)
def open_user(self, forum, user_id=0, **post):
cr, uid, context = request.cr, request.uid, request.context
@ -462,10 +493,9 @@ class WebsiteForum(http.Controller):
Followers = request.registry['mail.followers']
Data = request.registry["ir.model.data"]
user_id = User.search(cr, SUPERUSER_ID, [('id', '=', user_id), ('karma', '>', '1')], context=context)
if not user_id:
user = User.browse(cr, SUPERUSER_ID, user_id, context=context)
if not user.exists() or (user_id != request.session.uid and user.karma < 1):
return werkzeug.utils.redirect("/forum/%s" % slug(forum))
user = User.browse(cr, SUPERUSER_ID, user_id[0], context=context)
# questions and answers by user
user_questions, user_answers = [], []
@ -506,7 +536,7 @@ class WebsiteForum(http.Controller):
#activity by user.
model, comment = Data.get_object_reference(cr, uid, 'mail', 'mt_comment')
activity_ids = Activity.search(cr, uid, [('res_id', 'in', user_post_ids), ('model', '=', 'forum.post'), ('subtype_id', '!=', comment)], context=context)
activity_ids = Activity.search(cr, uid, [('res_id', 'in', user_post_ids), ('model', '=', 'forum.post'), ('subtype_id', '!=', comment)], order='date DESC', limit=100, context=context)
activities = Activity.browse(cr, uid, activity_ids, context=context)
posts = {}
@ -549,14 +579,14 @@ class WebsiteForum(http.Controller):
})
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):
request.registry['res.users'].write(request.cr, request.uid, [user.id], {
'name': kwargs.get('name'),
'website': kwargs.get('website'),
'email': kwargs.get('email'),
'city': kwargs.get('city'),
'country_id': kwargs.get('country'),
'country_id': int(kwargs.get('country')),
'website_description': kwargs.get('description'),
}, context=request.context)
return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), user.id))
@ -570,6 +600,7 @@ class WebsiteForum(http.Controller):
Badge = request.registry['gamification.badge']
badge_ids = Badge.search(cr, SUPERUSER_ID, [('challenge_ids.category', '=', 'forum')], context=context)
badges = Badge.browse(cr, uid, badge_ids, context=context)
badges = sorted(badges, key=lambda b: b.stat_count_distinct, reverse=True)
values = self._prepare_forum_values(forum=forum, searches={'badges': True})
values.update({
'badges': badges,
@ -590,7 +621,7 @@ class WebsiteForum(http.Controller):
# 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):
body = comment.body
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_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):
values = {
'comment': html2plaintext(post.content),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ $(document).ready(function () {
$('.vote_up ,.vote_down').on('click', function (ev) {
ev.preventDefault();
var $link = $(ev.currentTarget);
openerp.jsonRpc($link.attr('href'), 'call', {})
openerp.jsonRpc($link.data('href'), 'call', {})
.then(function (data) {
if (data['error']){
if (data['error'] == 'own_post'){
@ -47,7 +47,7 @@ $(document).ready(function () {
$('.accept_answer').on('click', function (ev) {
ev.preventDefault();
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'] == 'anonymous_user'){
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) {
ev.preventDefault();
var $link = $(ev.currentTarget);
openerp.jsonRpc($link.attr('href'), 'call', {}).then(function (data) {
openerp.jsonRpc($link.data('href'), 'call', {}).then(function (data) {
if (data) {
$link.addClass("forum_favourite_question")
} else {
@ -96,7 +96,7 @@ $(document).ready(function () {
$('.comment_delete').on('click', function (ev) {
ev.preventDefault();
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();
});
return true;

View File

@ -22,6 +22,14 @@
</xpath>
</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 -->
<template id="header" name="Forum Index">
<t t-call="website.layout">
@ -223,6 +231,7 @@
<t t-if="filters == 'followed'">Followed</t>
<t t-if="tag"><span t-field="tag.name"/></t>
<t t-if="sorting == 'date'"> by activity date</t>
<t t-if="sorting == 'creation'"> by creation date</t>
<t t-if="sorting == 'answered'"> by most answered</t>
<t t-if="sorting == 'vote'"> by most voted</t>
<b class="caret"/>
@ -246,6 +255,9 @@
<li t-att-class="sorting == 'date' and 'active' or '' ">
<a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='date')">Last activity date</a>
</li>
<li t-att-class="sorting == 'creation' and 'active' or '' ">
<a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='creation')">Newest</a>
</li>
<li t-att-class="sorting == 'answered' and 'active' or '' ">
<a t-att-href="url_for('') + '?' + keep_query( 'search', 'filters', sorting='answered')">Most answered</a>
</li>
@ -389,10 +401,10 @@
<template id="vote">
<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 ''}"
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"/>
<a t-attf-class="vote_down fa fa-thumbs-down no-decoration #{post.user_vote == -1 and 'text-warning' or ''}"
t-attf-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/downvote"/>
t-attf-data-href="/forum/#{slug(post.forum_id)}/post/#{slug(post)}/downvote"/>
<div t-if="vote_count &gt; 1" class="subtitle">
votes
</div>
@ -414,7 +426,7 @@
<span t-field="question.views"/> Views
</div>
<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 ''}"/>
</div>
</div>
@ -424,17 +436,23 @@
<span t-if="not question.active"><b> [Deleted]</b></span>
<span t-if="question.state == 'close'"><b> [Closed]</b></span>
</h1>
<div class="alert alert-info" t-if="question.state == 'close'">
<p class="mt32 mb16 text-center">
<b>The question has been closed for reason: <i t-esc="question.closed_reason_id.name"/>
<br/>
<t t-if="question.closed_uid">
<i>by <a t-attf-href="/forum/#{ slug(forum) }/user/#{ slug(question.closed_uid) }" t-field="question.closed_uid"/> </i>
</t>
on <span t-field="question.closed_date"/></b>
<div class="alert alert-info text-center" t-if="question.state == 'close'">
<p class="mt16">
<b>The question has been closed<t t-if="question.closed_reason_id"> for reason: <i t-esc="question.closed_reason_id.name"/></t></b>
</p>
<div t-if="question.state == 'close' and user.karma&gt;=500" class="mb24 text-center">
<a class="fa fa-arrow-right" t-attf-href="/forum/#{ slug(forum) }/question/#{slug(question)}/reopen"> Reopen</a>
<t t-if="question.closed_uid">
<b>by <a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.closed_uid.id }"
t-field="question.closed_uid"
t-field-options='{"widget": "contact", "fields": ["name"]}'
style="display: inline-block;"/></b>
</t>
<b>on <span t-field="question.closed_date"/></b>
<div t-if="question.state == 'close' and user.karma&gt;=500" class="mt16 mb24 text-center">
<t t-call="website_forum.link_button">
<t t-set="url" t-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>
<t t-raw="question.content"/>
@ -454,30 +472,51 @@
</a>
</li>
<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 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 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 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 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>
</ul>
</div>
<div>
<span t-field="question.create_uid.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/>
<img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{question.create_uid.id}/avatar"/>
<div>
<a t-attf-href="/forum/#{ slug(forum) }/user/#{ question.create_uid.id }"
t-field="question.create_uid"
t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
style="display: inline-block;"/>
<div t-field="question.create_uid" t-field-options='{"widget": "contact", "badges": true, "fields": ["karma"]}'/>
<span class="text-muted">Asked on <span t-field="question.create_date" t-field-options='{"format":"short"}'/></span>
</div>
</div>
</div>
@ -497,7 +536,7 @@
</t>
<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'}"
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 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>
</li>
<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 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>
</ul>
<span t-field="answer.create_uid.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/>
<img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{answer.create_uid.id}/avatar"/>
<div>
<a t-attf-href="/forum/#{ slug(forum) }/user/#{ answer.create_uid.id }"
t-field="answer.create_uid"
@ -552,15 +599,18 @@
<div t-foreach="reversed(object.website_message_ids)" t-as="message" class="comment oe_comment_grey">
<small class="text-muted">
<button type="button" t-if="user.partner_id.id == message.author_id.id and user.karma&gt;=750"
t-attf-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/delete"
t-attf-data-href="/forum/#{slug(forum)}/post/#{slug(object)}/comment/#{slug(message)}/delete"
class="close comment_delete">&amp;times;</button>
<span t-field="message.body"/>
<t t-call="website_forum.link_button">
<t t-set="url" t-value="'/forum/' + slug(forum) + '/post/' + slug(object) + '/comment/' + slug(message) + '/convert_to_answer'"/>
<t t-set="label" t-value="'Convert as an answer'"/>
<t t-set="classes" t-value="'text-muted fa-magic pull-right'"/>
</t>
<a t-attf-href="/forum/#{slug(forum)}/partner/#{message.author_id.id}"
t-field="message.author_id" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
style="display: inline-block;"/>
on <span t-field="message.date" t-field-options='{"format":"short"}'/>
<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>
</div>
<div class="css_editable_mode_hidden">
@ -650,7 +700,7 @@
</h4>
<div class="row">
<div class="col-sm-3 mt16" t-foreach="users" t-as="user">
<span t-field="user.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/>
<img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{user.id}/avatar"/>
<div>
<a t-attf-href="/forum/#{slug(forum)}/user/#{user.id}" t-field="user.name"/>
</div>
@ -661,9 +711,12 @@
<template id="users">
<t t-call="website_forum.header">
<div class="row">
<div t-foreach="users" t-as="user" class="col-sm-4">
<span t-field="user.image" t-field-options='{"widget": "image", "class":"pull-left img img-circle img-avatar"}'/>
<t t-foreach="users" t-as="user">
<t t-if="user_index % 3 == 0">
<div class="row"></div>
</t>
<div class="col-sm-4">
<img class="pull-left img img-circle img-avatar" t-attf-src="/forum/user/#{user.id}/avatar"/>
<div>
<a t-attf-href="/forum/#{slug(forum)}/user/#{user.id}" t-field="user.name"/>
<t t-if="user.country_id">
@ -683,7 +736,7 @@
<t t-raw="0"/>
</div>
</div>
</div>
</t>
<div class="pull-left">
<t t-call="website.pager"/>
</div>
@ -694,7 +747,7 @@
<t t-call="website_forum.header">
<h3>Edit Profile </h3>
<div class="col-md-2">
<span t-field="user.image" t-field-options='{"widget": "image", "class": "img img-responsive img-circle"}'/>
<img class="img img-responsive img-circle" t-attf-src="/forum/user/#{user.id}/avatar"/>
</div>
<form t-attf-action="/forum/#{slug(forum)}/user/#{slug(user)}/save" method="post" role="form" class="form-horizontal">
<input name="user_id" t-att-value="user.id" type="hidden"/>
@ -719,7 +772,7 @@
<select class="form-control" name="country">
<option value="">Country...</option>
<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>
</select>
</div>
@ -744,8 +797,7 @@
</h1>
<div class="row">
<div class="col-sm-2">
<span t-field="user.image"
t-field-options='{"widget": "image", "class": "img img-responsive img-circle"}'/>
<img class="img img-responsive img-circle" t-attf-src="/forum/user/#{user.id}/avatar"/>
</div>
<div class="col-sm-10">
<table class="table table-condensed">

View File

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

View File

@ -5,7 +5,7 @@ from openerp.addons.web.http import request
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):
hr_obj = request.registry['hr.employee']
employee_ids = hr_obj.search(request.cr, request.uid, [('website_published', '=', True)],

View File

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
import re
import openerp
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp.addons.website_partner.controllers import main as website_partner
from openerp.tools.translate import _
import werkzeug.urls
@ -39,7 +38,7 @@ class WebsiteMembership(http.Controller):
current_country = None
# 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:
base_line_domain.append(('membership_id', '=', membership_id))
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_lines = membership_line_obj.browse(cr, uid, membership_line_ids, context=context)
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))
partners_data = {}
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
partners = dict((p.id, p) for p in partner_obj.browse(request.cr, SUPERUSER_ID, partner_ids, request.context))
# format domain for group_by and memberships
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)
values = {
'partners_data': partners_data,
'partners': partners,
'membership_lines': membership_lines,
'memberships': memberships,
'membership': membership,
@ -105,11 +102,15 @@ class WebsiteMembership(http.Controller):
}
return request.website.render("website_membership.index", values)
@http.route(['/members/<int:partner_id>', '/members/<partner_name>-<int:partner_id>'], type='http', auth="public", website=True)
def partners_ref(self, partner_id, **post):
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
values = website_partner.get_partner_template_value(partner)
if not values:
return self.members(**post)
values['main_object'] = values['partner']
return request.website.render("website_membership.partner", values)
# Do not use semantic controller due to SUPERUSER_ID
@http.route(['/members/<partner_id>'], type='http', auth="public", website=True)
def partners_detail(self, partner_id, **post):
mo = re.search('-([-0-9]+)$', str(partner_id))
if mo:
partner_id = int(mo.group(1))
partner = request.registry['res.partner'].browse(request.cr, SUPERUSER_ID, partner_id, context=request.context)
if partner.exists() and partner.website_published:
values = {}
values['main_object'] = values['partner'] = partner
return request.website.render("website_membership.partner", values)
return self.customers(**post)

View File

@ -59,14 +59,17 @@
<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>
</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">
<a class="pull-left" t-attf-href="/members/#{ slug([partner_data.get('id'), partner_data.get('name')]) }">
<img class="media-object" t-attf-src="data:image/png;base64,#{partner_data.get('image_small')}"/>
</a>
<a class="pull-left" t-attf-href="/members/#{slug(partner)}"
t-field="partner.image_small"
t-field-options='{"widget": "image", "class": "media-object"}'
></a>
<div class="media-body" style="min-height: 64px;">
<a class="media-heading" t-attf-href="/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>
<div t-raw="partner_data.get('website_short_description')"/>
<a class="media-heading" t-attf-href="/members/#{slug(partner)}">
<span t-field="partner.display_name"/>
</a>
<div t-field="partner.website_short_description"/>
</div>
</div>
</t>

View File

@ -1 +0,0 @@
import main

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,8 +5,7 @@ import logging
import re
import sys
import werkzeug.exceptions
import werkzeug.routing
import werkzeug
import openerp
from openerp import http
@ -59,6 +58,12 @@ class ir_http(osv.AbstractModel):
def _auth_method_user(self):
request.uid = request.session.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")
def _auth_method_none(self):

View File

@ -351,6 +351,8 @@ class ir_model_fields(osv.osv):
raise except_orm(_('Error'), _("Model %s does not exist!") % vals['relation'])
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)
#Added context to _auto_init for special treatment to custom field for select_level
ctx = dict(context,

View File

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

View File

@ -18,6 +18,10 @@
<div t-if="phone and 'phone' in fields" class='css_editable_mode_hidden'><i t-if="not options.get('no_marker')" class='fa fa-phone'/> <span itemprop="telephone" t-esc="phone"/></div>
<div t-if="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="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>
</address>

View File

@ -253,7 +253,7 @@ class view(osv.osv):
['type', '=', view_type],
['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:
return False
return ids[0]

View File

@ -23,6 +23,7 @@ import datetime
from lxml import etree
import math
import pytz
import urlparse
import openerp
from openerp import SUPERUSER_ID
@ -509,6 +510,14 @@ class res_partner(osv.osv, format_address):
if not parent.is_company:
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):
if isinstance(ids, (int, long)):
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
#(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 vals.get('website'):
vals['website'] = self._clean_website(vals['website'])
if vals.get('company_id'):
for partner in self.browse(cr, uid, ids, context=context):
if partner.user_ids:
@ -528,6 +539,8 @@ class res_partner(osv.osv, format_address):
return result
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)
partner = self.browse(cr, uid, new_id, context=context)
self._fields_sync(cr, uid, partner, vals, context)

View File

@ -77,7 +77,7 @@
<field name="street">31 Hong Kong street</field>
<field name="email">asusteK@yourcompany.example.com</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"/>
</record>
<record id="res_partner_2" model="res.partner">
@ -90,7 +90,7 @@
<field name="street">69 rue de Namur</field>
<field name="email">agrolait@yourcompany.example.com</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"/>
</record>
<record id="res_partner_3" model="res.partner">
@ -105,7 +105,7 @@
<field name="street">52 Chop Suey street</field>
<field name="email">chinaexport@yourcompany.example.com</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"/>
</record>
@ -122,7 +122,7 @@
<field name="street">3661 Station Street</field>
<field name="email">deltapc@yourcompany.example.com</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"/>
</record>
@ -137,7 +137,7 @@
<field name="city">Chicago</field>
<field name="email">epic@yourcompany.example.com</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"/>
</record>
@ -169,7 +169,7 @@
<field name="country_id" ref="base.uk"/>
<field name="phone">+44 121 690 4596</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"/>
</record>
@ -198,7 +198,7 @@
<field name="country_id" ref="base.in"/>
<field name="email">bestdesigners@yourcompany.example.com</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"/>
</record>
@ -228,7 +228,7 @@
<field name="city">Barcelona</field>
<field name="zip">08078</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="image" type="base64" file="base/static/img/res_partner_11-image.png"/>
</record>
@ -245,7 +245,7 @@
<field name="country_id" ref="base.fr"/>
<field name="street">93, Press Avenue</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"/>
</record>
@ -260,7 +260,7 @@
<field name="email">axelor@yourcompany.example.com</field>
<field name="phone">+33 1 64 61 04 01</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"/>
</record>
@ -336,7 +336,7 @@
<field name="phone">+1 857 349 3049</field>
<field name="country_id" ref="base.us"/>
<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"/>
</record>
@ -394,7 +394,7 @@
<field name="zip">LE4 2BN</field>
<field name="phone">+44 20 1294 2193</field>
<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="image" type="base64" file="base/static/img/res_partner_22-image.jpg"/>
</record>

View File

@ -3758,9 +3758,13 @@ class BaseModel(object):
ir_values_obj.unlink(cr, uid, ir_value_ids, context=context)
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]
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())
if rids:
obj._store_set_values(cr, uid, rids, fields, context)
@ -5130,7 +5134,14 @@ class BaseModel(object):
# shortcut read if we only want the 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:
return result

View File

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