diff --git a/addons/account/account.py b/addons/account/account.py index aa5d507b3d2..51700d30731 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -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'): diff --git a/addons/account/edi/invoice.py b/addons/account/edi/invoice.py index 2c2a754de3b..c82d041ef85 100644 --- a/addons/account/edi/invoice.py +++ b/addons/account/edi/invoice.py @@ -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 = { diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index c43e978e590..8d470f45637 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -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 diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py index c1ea1f655d2..61e5c1166ea 100644 --- a/addons/analytic/analytic.py +++ b/addons/analytic/analytic.py @@ -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) diff --git a/addons/auth_oauth/auth_oauth_data.xml b/addons/auth_oauth/auth_oauth_data.xml index 6abdab41b7c..fe64f42b2f6 100644 --- a/addons/auth_oauth/auth_oauth_data.xml +++ b/addons/auth_oauth/auth_oauth_data.xml @@ -2,13 +2,13 @@ - OpenERP.com Accounts - https://accounts.openerp.com/oauth2/auth + Odoo.com Accounts + https://accounts.odoo.com/oauth2/auth userinfo - https://accounts.openerp.com/oauth2/tokeninfo + https://accounts.odoo.com/oauth2/tokeninfo zocial openerp - Log in with OpenERP.com + Log in with Odoo.com diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 7f5ae17dc33..a209bba4890 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -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): diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index 40654f464f4..7964e4a4a3a 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -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: diff --git a/addons/gamification/data/goal_base.xml b/addons/gamification/data/goal_base.xml index f249e566b9f..9f13ac27408 100644 --- a/addons/gamification/data/goal_base.xml +++ b/addons/gamification/data/goal_base.xml @@ -108,7 +108,7 @@ count boolean - [('user_ids', 'in', [user.id]), ('name', '=', 'Your Company')] + [('user_ids', 'in', [user.id]), ('name', '=', 'YourCompany')] lower user.company_id.id diff --git a/addons/gamification/models/challenge.py b/addons/gamification/models/challenge.py index 3f14417ae48..6c79d439c4a 100644 --- a/addons/gamification/models/challenge.py +++ b/addons/gamification/models/challenge.py @@ -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 diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py index b4a47a0d07a..904e6fafa36 100644 --- a/addons/gamification/models/goal.py +++ b/addons/gamification/models/goal.py @@ -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 diff --git a/addons/gamification/security/gamification_security.xml b/addons/gamification/security/gamification_security.xml index 5339b400061..4e73c223a22 100644 --- a/addons/gamification/security/gamification_security.xml +++ b/addons/gamification/security/gamification_security.xml @@ -40,7 +40,7 @@ - User can only see his/her goals or goal from the same challenge in board visibility + Multicompany rule on challenges [('user_id.company_id', 'child_of', [user.company_id.id])] diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 2385983f020..3d467e0b065 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -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 diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index a15a18d20aa..9869218ccb3 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -83,7 +83,7 @@ - + diff --git a/addons/hw_escpos/escpos/supported_devices.py b/addons/hw_escpos/escpos/supported_devices.py index 2c6a6be2d07..bd08433de0e 100644 --- a/addons/hw_escpos/escpos/supported_devices.py +++ b/addons/hw_escpos/escpos/supported_devices.py @@ -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' }, ] diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 33f5f1f8a83..ffc49414b2e 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -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, diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 76a12bc564d..a4814140479 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -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 _("""about %s %s""") % (url, context.get('model_name', ''), mail.record_name) else: return None diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 43cf4cddda4..acb1793b7bd 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -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 #------------------------------------------------------ diff --git a/addons/marketing_campaign/marketing_campaign.py b/addons/marketing_campaign/marketing_campaign.py index 142d059e1f9..a263fbcc197 100644 --- a/addons/marketing_campaign/marketing_campaign.py +++ b/addons/marketing_campaign/marketing_campaign.py @@ -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) diff --git a/addons/mass_mailing/views/email_template.xml b/addons/mass_mailing/views/email_template.xml index cde5685abaf..84ac3925fcc 100644 --- a/addons/mass_mailing/views/email_template.xml +++ b/addons/mass_mailing/views/email_template.xml @@ -21,12 +21,11 @@
+
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 563ef6a991f..b7959832ead 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -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 diff --git a/addons/point_of_sale/static/src/js/db.js b/addons/point_of_sale/static/src/js/db.js index 077a2399c55..0c7ba8f9e1e 100644 --- a/addons/point_of_sale/static/src/js/db.js +++ b/addons/point_of_sale/static/src/js/db.js @@ -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] = []; } diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 16f9f7476b3..c0c376e240e 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -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 diff --git a/addons/product/product.py b/addons/product/product.py index a7a027d0487..28045886464 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -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 ` (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 diff --git a/addons/sale/edi/sale_order.py b/addons/sale/edi/sale_order.py index bf015b3cdfc..cd1b18ef9b5 100644 --- a/addons/sale/edi/sale_order.py +++ b/addons/sale/edi/sale_order.py @@ -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 = { diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml index f71ff2933c7..0549867ddec 100644 --- a/addons/sale/report/sale_report_view.xml +++ b/addons/sale/report/sale_report_view.xml @@ -21,8 +21,8 @@ - - + + diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 0020ebf1d8e..65158582eb0 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -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 diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py index ef4fd05302a..876f444ea57 100644 --- a/addons/survey/controllers/main.py +++ b/addons/survey/controllers/main.py @@ -234,7 +234,7 @@ class WebsiteSurvey(http.Controller): # AJAX submission of a page @http.route(['/survey/submit/'], - 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, diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 2620e4ed40f..1ea14ee8c81 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -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); diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index a4faa9c361f..4b951a8446f 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -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/', type='http', auth="public", website=True) + @http.route('/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-.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 diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 35bae23a7da..433d8644698 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -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" diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index 94de70dc7dc..209d431ae8f 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -121,7 +121,7 @@ header a.navbar-brand img { /* ----- EDITOR ----- */ .css_non_editable_mode_hidden { - display: none; + display: none !important; } /* ----- BOOTSTRAP FIX ----- */ diff --git a/addons/website/tests/test_requests.py b/addons/website/tests/test_requests.py index 2d14cbc668d..2a1eea4c877 100644 --- a/addons/website/tests/test_requests.py +++ b/addons/website/tests/test_requests.py @@ -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\ diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 751a37b3455..8c666dba095 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -709,14 +709,18 @@ User-agent: * Sitemap: sitemap.xml + + diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py index 4e81c3b4437..a36e9fcfafe 100644 --- a/addons/website_blog/controllers/main.py +++ b/addons/website_blog/controllers/main.py @@ -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( diff --git a/addons/website_blog/static/src/js/website_blog.editor.js b/addons/website_blog/static/src/js/website_blog.editor.js index d953b9628a8..b048807111b 100644 --- a/addons/website_blog/static/src/js/website_blog.editor.js +++ b/addons/website_blog/static/src/js/website_blog.editor.js @@ -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'); }, diff --git a/addons/website_blog/views/website_blog_templates.xml b/addons/website_blog/views/website_blog_templates.xml index 9c70282d007..884c638a3ef 100644 --- a/addons/website_blog/views/website_blog_templates.xml +++ b/addons/website_blog/views/website_blog_templates.xml @@ -49,7 +49,7 @@

- + &nbsp; @@ -187,6 +187,7 @@

+
diff --git a/addons/website_crm/controllers/main.py b/addons/website_crm/controllers/main.py index 96c636b093f..fb4535bf3a6 100644 --- a/addons/website_crm/controllers/main.py +++ b/addons/website_crm/controllers/main.py @@ -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: diff --git a/addons/website_crm_partner_assign/__openerp__.py b/addons/website_crm_partner_assign/__openerp__.py index 82b16173666..06b6dc9410e 100644 --- a/addons/website_crm_partner_assign/__openerp__.py +++ b/addons/website_crm_partner_assign/__openerp__.py @@ -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'], diff --git a/addons/website_crm_partner_assign/controllers/main.py b/addons/website_crm_partner_assign/controllers/main.py index b4bf893b368..c23bddc5ea2 100644 --- a/addons/website_crm_partner_assign/controllers/main.py +++ b/addons/website_crm_partner_assign/controllers/main.py @@ -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/', - '/partners/grade/', - '/partners/grade//page/', + '/partners/grade/', + '/partners/grade//page/', - '/partners/country/', - '/partners/country/-', - '/partners/country//page/', - '/partners/country/-/page/', - - '/partners/grade//country/', - '/partners/grade//country/-', - '/partners/grade//country//page/', - '/partners/grade//country/-/page/', - + '/partners/country/', + '/partners/country//page/', + + '/partners/grade//country/', + '/partners/grade//country//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/', '/partners/-'], 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/'], 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) diff --git a/addons/website_crm_partner_assign/models/__init__.py b/addons/website_crm_partner_assign/models/__init__.py index 20036e92f6c..4e5bc1614d1 100644 --- a/addons/website_crm_partner_assign/models/__init__.py +++ b/addons/website_crm_partner_assign/models/__init__.py @@ -1,2 +1 @@ import res_partner -import website diff --git a/addons/website_crm_partner_assign/models/res_partner.py b/addons/website_crm_partner_assign/models/res_partner.py index e295c7ab0b1..cc00f8bd6e1 100644 --- a/addons/website_crm_partner_assign/models/res_partner.py +++ b/addons/website_crm_partner_assign/models/res_partner.py @@ -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'), } diff --git a/addons/website_crm_partner_assign/models/website.py b/addons/website_crm_partner_assign/models/website.py deleted file mode 100644 index f0d428ae5fa..00000000000 --- a/addons/website_crm_partner_assign/models/website.py +++ /dev/null @@ -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 diff --git a/addons/website_crm_partner_assign/views/partner_grade.xml b/addons/website_crm_partner_assign/views/partner_grade.xml new file mode 100644 index 00000000000..2acf8e1eb24 --- /dev/null +++ b/addons/website_crm_partner_assign/views/partner_grade.xml @@ -0,0 +1,18 @@ + + + + + + res.partner.grade.website + res.partner.grade + + + + + + + + + + + diff --git a/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml b/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml index 56ac22d1eee..6d5b93793ad 100644 --- a/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml +++ b/addons/website_crm_partner_assign/views/website_crm_partner_assign.xml @@ -15,7 +15,7 @@ Resellers
-
+
@@ -36,13 +36,13 @@
-
+
-
-
+