diff --git a/.gitignore b/.gitignore index 6148a29a95d0bcaa062cdf89beabb1a55386a24d..4b38dc02ac7d81f071a41dbecf00701eb5a555e1 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ install/win32/meta.py /lib/ /man/ /share/ -/src/ +/src/ \ No newline at end of file diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 61c397364b9e753e406a8c876fa2b2b5d471aa5c..fe701a85661243d37715fa6bdf2188e7f12a0c19 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -60,7 +60,7 @@ </group> <group> <field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '<>', 'view')]" name="account_id" on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)" groups="account.group_account_user"/> - <field name="invoice_line_tax_id" context="{'type':parent.type}" domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]" widget="many2many_tags"/> + <field name="invoice_line_tax_id" context="{'type':parent.get('type')}" domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]" widget="many2many_tags"/> <field domain="[('type','<>','view'), ('company_id', '=', parent.company_id)]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/> <field name="company_id" groups="base.group_multi_company" readonly="1"/> </group> diff --git a/addons/account/report/account_aged_partner_balance.py b/addons/account/report/account_aged_partner_balance.py index 3fd83c19207090a3ef968b030a852b55999fbeb1..2e3c9b7c3748d2604c586cd4868e0b64133058b0 100644 --- a/addons/account/report/account_aged_partner_balance.py +++ b/addons/account/report/account_aged_partner_balance.py @@ -161,7 +161,7 @@ class aged_trial_report(report_sxw.rml_parse, common_report_header): dates_query += ' < %s)' args_list += (form[str(i)]['stop'],) args_list += (self.date_from,) - self.cr.execute('''SELECT l.partner_id, SUM(l.debit-l.credit) + self.cr.execute('''SELECT l.partner_id, SUM(l.debit-l.credit), l.reconcile_partial_id FROM account_move_line AS l, account_account, account_move am WHERE (l.account_id = account_account.id) AND (l.move_id=am.id) AND (am.state IN %s) @@ -173,12 +173,24 @@ class aged_trial_report(report_sxw.rml_parse, common_report_header): AND account_account.active AND ''' + dates_query + ''' AND (l.date <= %s) - GROUP BY l.partner_id''', args_list) - t = self.cr.fetchall() - d = {} - for i in t: - d[i[0]] = i[1] - history.append(d) + GROUP BY l.partner_id, l.reconcile_partial_id''', args_list) + partners_partial = self.cr.fetchall() + partners_amount = dict((i[0],0) for i in partners_partial) + for partner_info in partners_partial: + if partner_info[2]: + # in case of partial reconciliation, we want to keep the left amount in the oldest period + self.cr.execute('''SELECT MIN(COALESCE(date_maturity,date)) FROM account_move_line WHERE reconcile_partial_id = %s''', (partner_info[2],)) + date = self.cr.fetchall() + if date and args_list[-3] <= date[0][0] <= args_list[-2]: + # partial reconcilation + self.cr.execute('''SELECT SUM(l.debit-l.credit) + FROM account_move_line AS l + WHERE l.reconcile_partial_id = %s''', (partner_info[2],)) + unreconciled_amount = self.cr.fetchall() + partners_amount[partner_info[0]] += unreconciled_amount[0][0] + else: + partners_amount[partner_info[0]] += partner_info[1] + history.append(partners_amount) for partner in partners: values = {} diff --git a/addons/account/views/report_invoice.xml b/addons/account/views/report_invoice.xml index 6c4b701c6cf375848e38705512e184464d56e86d..a250eb9ebf7d6ac75e661970d873e04b6fad177a 100644 --- a/addons/account/views/report_invoice.xml +++ b/addons/account/views/report_invoice.xml @@ -5,7 +5,7 @@ <t t-call="report.external_layout"> <div class="page"> <div class="row"> - <div class="col-xs-4 col-xs-offset-8"> + <div class="col-xs-5 col-xs-offset-7"> <address t-field="o.partner_id" t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' /> <span t-field="o.partner_id.vat"/> diff --git a/addons/account/views/report_overdue.xml b/addons/account/views/report_overdue.xml index bc522aa8b9d985a8c84a9445b7ec1a99851b2541..df3934ff0deb20687d3db2a82e655aae5ab2a4e7 100644 --- a/addons/account/views/report_overdue.xml +++ b/addons/account/views/report_overdue.xml @@ -5,7 +5,7 @@ <t t-call="report.external_layout"> <div class="page"> <div class="row"> - <div class="col-xs-4 col-xs-offset-6"> + <div class="col-xs-5 col-xs-offset-7"> <span t-field="o.name"/><br/> <span t-raw="addresses[o.id].replace('\n\n', '\n').replace('\n', '<br>')"/> <span t-field="o.vat"/> diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 8d470f45637c4a5153082fb03defd3f1e2783fd0..4b2352bc880d6e78dae42902e73df28cca8dfa42 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -69,7 +69,7 @@ class account_analytic_invoice_line(osv.osv): if partner_id: part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=local_context) if part.lang: - context.update({'lang': part.lang}) + local_context.update({'lang': part.lang}) result = {} res = self.pool.get('product.product').browse(cr, uid, product, context=local_context) @@ -79,7 +79,12 @@ class account_analytic_invoice_line(osv.osv): 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}) + if not name: + name = self.pool.get('product.product').name_get(cr, uid, [res.id], context=local_context)[0][1] + if res.description_sale: + name += '\n'+res.description_sale + + result.update({'name': name 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: diff --git a/addons/account_analytic_analysis/account_analytic_analysis_view.xml b/addons/account_analytic_analysis/account_analytic_analysis_view.xml index 97fb15e929097466b36723d147b15cc4ef792f57..af6eb47ffa40f0e2df3c048c2837f540e35757f4 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_view.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_view.xml @@ -169,7 +169,7 @@ <div attrs="{'invisible': [('recurring_invoices','=',False)]}"> <field name="recurring_invoice_line_ids"> <tree string="Account Analytic Lines" editable="bottom"> - <field name="product_id" on_change="product_id_change(product_id, uom_id, quantity, name, parent.partner_id, price_unit, parent.pricelist_id, parent.company_id)"/> + <field name="product_id" on_change="product_id_change(product_id, uom_id, quantity, False, parent.partner_id, False, parent.pricelist_id, parent.company_id)"/> <field name="name"/> <field name="quantity"/> <field name="uom_id"/> diff --git a/addons/account_budget/account_budget.py b/addons/account_budget/account_budget.py index 753a5df79f4642308c0f71388090edd51ed4e993..78e85716bbdc3069a210092fc153ce80d617a403 100644 --- a/addons/account_budget/account_budget.py +++ b/addons/account_budget/account_budget.py @@ -22,6 +22,7 @@ import datetime from openerp.osv import fields, osv +from openerp.tools import ustr from openerp.tools.translate import _ import openerp.addons.decimal_precision as dp @@ -114,7 +115,7 @@ class crossovered_budget_lines(osv.osv): for line in self.browse(cr, uid, ids, context=context): acc_ids = [x.id for x in line.general_budget_id.account_ids] if not acc_ids: - raise osv.except_osv(_('Error!'),_("The Budget '%s' has no accounts!") % str(line.general_budget_id.name)) + raise osv.except_osv(_('Error!'),_("The Budget '%s' has no accounts!") % ustr(line.general_budget_id.name)) date_to = line.date_to date_from = line.date_from if context.has_key('wizard_date_from'): diff --git a/addons/account_followup/views/report_followup.xml b/addons/account_followup/views/report_followup.xml index 6c79fe4cc554987db045d987c516202c5ca2bc08..c0d98e7bb28c6f043784bf96a8a412f1e4adda3c 100644 --- a/addons/account_followup/views/report_followup.xml +++ b/addons/account_followup/views/report_followup.xml @@ -7,7 +7,7 @@ <t t-call="report.external_layout"> <div class="page"> <div class="row"> - <div class="col-xs-4 col-xs-offset-6"> + <div class="col-xs-5 col-xs-offset-7"> <div t-field="o.partner_id" t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/> <span t-field="o.partner_id.vat"/> diff --git a/addons/base_gengo/doc/changelog.rst b/addons/base_gengo/doc/changelog.rst index fe343069a381e2af1d03512652ceccd9d5899f4b..19f93bfa5a87b144fcdcb5c346bb1690551fa05f 100644 --- a/addons/base_gengo/doc/changelog.rst +++ b/addons/base_gengo/doc/changelog.rst @@ -3,7 +3,7 @@ ======================== ****** -saas-5 +saas-4 ****** - - Library update: ``mygengo`` (https://pypi.python.org/pypi/mygengo/1.3.3) was outdated and has been replaced by ``gengo`` (https://pypi.python.org/pypi/gengo). +- Library update: ``mygengo`` (https://pypi.python.org/pypi/mygengo/1.3.3) was outdated and has been replaced by ``gengo`` (https://pypi.python.org/pypi/gengo). diff --git a/addons/crm_partner_assign/security/ir.model.access.csv b/addons/crm_partner_assign/security/ir.model.access.csv index 6a26c574a1c7b5d8d69fc2222549f44f31d985d1..d5324ba6c53c62477c1acea9d0e60f42e9cf7d88 100644 --- a/addons/crm_partner_assign/security/ir.model.access.csv +++ b/addons/crm_partner_assign/security/ir.model.access.csv @@ -3,6 +3,7 @@ access_ crm_lead_report_assign,crm.lead.report.assign,model_crm_lead_report_assi access_ crm_lead_report_assign_all,crm.lead.report.assign.all,model_crm_lead_report_assign,base.group_user,1,0,0,0 access_crm_partner_report,crm.partner.report.assign.all,model_crm_partner_report_assign,base.group_sale_salesman,1,0,0,0 access_res_partner_grade,res.partner.grade,model_res_partner_grade,base.group_sale_salesman,1,1,1,0 +access_res_partner_grade_public,res.partner.grade,model_res_partner_grade,base.group_public,1,0,0,0 access_res_partner_grade_manager,res.partner.grade.manager,model_res_partner_grade,base.group_sale_manager,1,1,1,1 "access_partner_activation_manager","res.partner.activation.manager","model_res_partner_activation","base.group_partner_manager",1,1,1,1 -partner_access_crm_lead,crm.lead,model_crm_lead,base.group_portal,1,1,0,0 \ No newline at end of file +partner_access_crm_lead,crm.lead,model_crm_lead,base.group_portal,1,1,0,0 diff --git a/addons/delivery/delivery.py b/addons/delivery/delivery.py index 46d76180d08d37e492c5214c925a06e7814b1c13..873b7e60662e4b0d2915b83db60718a048f8cf64 100644 --- a/addons/delivery/delivery.py +++ b/addons/delivery/delivery.py @@ -192,15 +192,16 @@ class delivery_grid(osv.osv): weight = 0 volume = 0 quantity = 0 + product_uom_obj = self.pool.get('product.uom') for line in order.order_line: if not line.product_id or line.is_delivery: continue - weight += (line.product_id.weight or 0.0) * line.product_uom_qty - volume += (line.product_id.volume or 0.0) * line.product_uom_qty - quantity += line.product_uom_qty + q = product_uom_obj._compute_qty(cr, uid, line.product_uom.id, line.product_uos_qty, line.product_id.uom_id.id) + weight += (line.product_id.weight or 0.0) * q + volume += (line.product_id.volume or 0.0) * q + quantity += q total = order.amount_total or 0.0 - return self.get_price_from_picking(cr, uid, id, total,weight, volume, quantity, context=context) def get_price_from_picking(self, cr, uid, id, total, weight, volume, quantity, context=None): diff --git a/addons/hr_payroll/views/report_payslip.xml b/addons/hr_payroll/views/report_payslip.xml index 5ac9dfa557eb65a46585c8305aeadd4ff0e27bc4..b30cb089d1a165cf064b7a16ad0e57da1168ab92 100644 --- a/addons/hr_payroll/views/report_payslip.xml +++ b/addons/hr_payroll/views/report_payslip.xml @@ -19,7 +19,7 @@ <tr> <td><strong>Address</strong></td> <td colspan="3"> - <div t-filed="o.employee_id.address_home_id" + <div t-field="o.employee_id.address_home_id" t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/> </td> </tr> diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 9c69f5db94bcdfb2234e323a7f6cc92b491e10cd..775b84756ce63b82a3944b81fd77b8d613924f07 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -114,6 +114,15 @@ class hr_applicant(osv.Model): return int(department_ids[0][0]) return None + def _get_default_company_id(self, cr, uid, department_id=None, context=None): + company_id = False + if department_id: + department = self.pool['hr.department'].browse(cr, uid, department_id, context=context) + company_id = department.company_id.id if department and department.company_id else False + if not company_id: + company_id = self.pool['res.company']._company_default_get(cr, uid, 'hr.applicant', context=context) + return company_id + def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None): access_rights_uid = access_rights_uid or uid stage_obj = self.pool.get('hr.recruitment.stage') @@ -231,7 +240,7 @@ class hr_applicant(osv.Model): 'user_id': lambda s, cr, uid, c: uid, 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), 'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c), - 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'hr.applicant', context=c), + 'company_id': lambda s, cr, uid, c: s._get_default_company_id(cr, uid, s._get_default_department_id(cr, uid, c), c), 'color': 0, 'date_last_stage_update': fields.datetime.now, } diff --git a/addons/lunch/views/report_lunchorder.xml b/addons/lunch/views/report_lunchorder.xml index 93d43f6521db58ccff3887e7a9a9de261657edfa..30401bc142be26703515f4f3fb1be603d9cea219 100644 --- a/addons/lunch/views/report_lunchorder.xml +++ b/addons/lunch/views/report_lunchorder.xml @@ -8,7 +8,7 @@ <div class="oe_structure"/> <div class="row"> - <div class="col-xs-4 col-xs-offset-7"> + <div class="col-xs-5 col-xs-offset-7"> <div t-field="user.partner_id" t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' /> </div> diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index acb1793b7bdb4982f818d82b59d0e7af20647cc3..11f93e25632ac9f013155a761431702ccdd2fd96 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -669,9 +669,10 @@ class mail_thread(osv.AbstractModel): def message_get_default_recipients(self, cr, uid, ids, context=None): if context and context.get('thread_model') and context['thread_model'] in self.pool and context['thread_model'] != self._name: - sub_ctx = dict(context) - sub_ctx.pop('thread_model') - return self.pool[context['thread_model']].message_get_default_recipients(cr, uid, ids, context=sub_ctx) + if hasattr(self.pool[context['thread_model']], 'message_get_default_recipients'): + sub_ctx = dict(context) + sub_ctx.pop('thread_model') + return self.pool[context['thread_model']].message_get_default_recipients(cr, uid, ids, context=sub_ctx) res = {} for record in self.browse(cr, SUPERUSER_ID, ids, context=context): recipient_ids, email_to, email_cc = set(), False, False diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 99a7751e15e9a5633406a421070d54a7f2186842..e5eca7246595c8776cf42bbea37d945353f51773 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -919,15 +919,45 @@ openerp.mail = function (session) { this.$('.oe_mail_expand').on('click', this.on_expand); this.$('.oe_mail_reduce').on('click', this.on_expand); this.$('.oe_mail_action_model').on('click', this.on_record_clicked); + this.$('.oe_mail_action_author').on('click', this.on_record_author_clicked); }, on_record_clicked: function (event) { + event.preventDefault(); + var self = this; var state = { 'model': this.model, 'id': this.res_id, 'title': this.record_name }; session.webclient.action_manager.do_push_state(state); + this.context.params = { + model: this.model, + res_id: this.res_id, + }; + this.thread.ds_thread.call("message_redirect_action", {context: this.context}).then(function(action){ + self.do_action(action); + }); + }, + + on_record_author_clicked: function (event) { + event.preventDefault(); + var partner_id = $(event.target).data('partner'); + var state = { + 'model': 'res.partner', + 'id': partner_id, + 'title': this.record_name + }; + session.webclient.action_manager.do_push_state(state); + var action = { + type:'ir.actions.act_window', + view_type: 'form', + view_mode: 'form', + res_model: 'res.partner', + views: [[false, 'form']], + res_id: partner_id, + } + this.do_action(action); }, /* Call the on_compose_message on the thread of this message. */ diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index ec7b0fa2911d01df7eba280eb9952469f11e0726..3aea4a19555638987bce512d368c145313536b88 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -273,7 +273,7 @@ <t t-if="widget.attachment_ids.length > 0"> <div class="oe_msg_attachment_list"></div> </t> - <a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}"><t t-esc="widget.author_id[2]"/></a> + <a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}" t-att-data-partner="widget.author_id[0]" class="oe_mail_action_author"><t t-esc="widget.author_id[2]"/></a> <span t-if="widget.author_id and (!widget.options.show_link or !widget.author_id[0])"><t t-esc="widget.author_id[2]"/></span> <t t-if="widget.type == 'notification'"> updated document @@ -293,7 +293,7 @@ <t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and (widget.subtype or widget.partner_ids.length > 0))" t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner"> <span t-attf-class="oe_partner_follower"> - <a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-esc="partner[1]"/></a> + <a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}" t-att-data-partner="partner[0]" class="oe_mail_action_author"><t t-esc="partner[1]"/></a> <t t-if="!widget.options.show_link" t-esc="partner[1]"/> </span> <t t-if="!partner_last">,</t> diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py index 2e8a82a8f4d5dddb0f7e6df76a35ff556321d40e..f721ed2f3858af1dd476daa518eb3aad58055f90 100644 --- a/addons/mass_mailing/models/mass_mailing.py +++ b/addons/mass_mailing/models/mass_mailing.py @@ -4,10 +4,9 @@ from datetime import datetime from dateutil import relativedelta import json import random -import urllib -import urlparse from openerp import tools +from openerp.exceptions import Warning from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ from openerp.osv import osv, fields @@ -62,6 +61,12 @@ class MassMailingContact(osv.Model): rec_id = self.create(cr, uid, {'name': name, 'email': email}, context=context) return self.name_get(cr, uid, [rec_id], context)[0] + def message_get_default_recipients(self, cr, uid, ids, context=None): + res = {} + for record in self.browse(cr, uid, ids, context=context): + res[record.id] = {'partner_ids': [], 'email_to': record.email, 'email_cc': False} + return res + class MassMailingList(osv.Model): """Model of a contact list. """ @@ -549,6 +554,8 @@ class MassMailing(osv.Model): for mailing in self.browse(cr, uid, ids, context=context): # instantiate an email composer + send emails res_ids = self.get_recipients(cr, uid, mailing, context=context) + if not res_ids: + raise Warning('Please select recipients.') comp_ctx = dict(context, active_ids=res_ids) composer_values = { 'author_id': author_id, diff --git a/addons/mrp_repair/views/report_mrprepairorder.xml b/addons/mrp_repair/views/report_mrprepairorder.xml index 0ea3f22167c4cc1baf95a1218c96883117645493..2897fee1fd92fed5ddf6903c12bdc13f8524612b 100644 --- a/addons/mrp_repair/views/report_mrprepairorder.xml +++ b/addons/mrp_repair/views/report_mrprepairorder.xml @@ -22,7 +22,7 @@ <p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p> </div> </div> - <div class="col-xs-4 col-xs-offset-2"> + <div class="col-xs-5 col-xs-offset-1"> <div t-field="o.partner_id" t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' /> </div> diff --git a/addons/pad/static/src/img/pad_link_companies.jpeg b/addons/pad/static/src/img/pad_link_companies.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1bce3a5618652970429d1febfd7c613c1c330755 Binary files /dev/null and b/addons/pad/static/src/img/pad_link_companies.jpeg differ diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml index fc195f86ee41436baa2fe93152c530a93ccfcfd7..391c61a8e948bc57f13fcd46b12036d82c1e7cc9 100644 --- a/addons/purchase/edi/purchase_order_action_data.xml +++ b/addons/purchase/edi/purchase_order_action_data.xml @@ -111,7 +111,7 @@ <p style="border-left: 1px solid #8e0000; margin-left: 30px;"> <strong>REFERENCES</strong><br /> Order number: <strong>${object.name}</strong><br /> - Order total: <strong>${object.amount_total} ${object.pricelist_id.currency_id.name}</strong><br /> + Order total: <strong>${object.amount_total} ${object.currency_id.name}</strong><br /> Order date: ${object.date_order}<br /> % if object.origin: Order reference: ${object.origin}<br /> diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 6ff6feae376ac751345033f24d92639330bc0ed7..67b07644dc4b2d06103d1a496c859c55eadf7fb1 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -220,7 +220,7 @@ class purchase_order(osv.osv): 'picking_ids': fields.function(_get_picking_ids, method=True, type='one2many', relation='stock.picking', string='Picking List', help="This is the list of reception operations that have been generated for this purchase order."), 'shipped':fields.boolean('Received', readonly=True, select=True, help="It indicates that a picking has been done"), 'shipped_rate': fields.function(_shipped_rate, string='Received Ratio', type='float'), - 'invoiced': fields.function(_invoiced, string='Invoice Received', type='boolean', help="It indicates that an invoice has been paid"), + 'invoiced': fields.function(_invoiced, string='Invoice Received', type='boolean', help="It indicates that an invoice has been validated"), 'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced', type='float'), 'invoice_method': fields.selection([('manual','Based on Purchase Order lines'),('order','Based on generated draft invoice'),('picking','Based on incoming shipments')], 'Invoicing Control', required=True, readonly=True, states={'draft':[('readonly',False)], 'sent':[('readonly',False)]}, diff --git a/addons/purchase/views/report_purchaseorder.xml b/addons/purchase/views/report_purchaseorder.xml index 300149e4f1d71e6fc158a7294d671c3e3b116531..91486c035aa67b8499347c0bebbd382204d436c3 100644 --- a/addons/purchase/views/report_purchaseorder.xml +++ b/addons/purchase/views/report_purchaseorder.xml @@ -23,7 +23,7 @@ <p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p> </div> </div> - <div class="col-xs-4 col-xs-offset-2"> + <div class="col-xs-5 col-xs-offset-1"> <div t-field="o.partner_id" t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/> </div> diff --git a/addons/purchase/views/report_purchasequotation.xml b/addons/purchase/views/report_purchasequotation.xml index a18619f99f9397a7f7333d440f5768b74864dc0a..6abd64b2f334dafb292015f17f76d4bff5d51cfb 100644 --- a/addons/purchase/views/report_purchasequotation.xml +++ b/addons/purchase/views/report_purchasequotation.xml @@ -23,7 +23,7 @@ <p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p> </div> </div> - <div class="col-xs-4 col-xs-offset-2"> + <div class="col-xs-5 col-xs-offset-1"> <div t-field="o.partner_id" t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/> </div> diff --git a/addons/report/controllers/main.py b/addons/report/controllers/main.py index de3bbd63db69ca0d553c1014ee6912a6c939aab1..c6bc13d38f8e64e4df7e9105de5f6f75c6b35417 100644 --- a/addons/report/controllers/main.py +++ b/addons/report/controllers/main.py @@ -66,7 +66,7 @@ class ReportController(Controller): # Misc. route utils #------------------------------------------------------ @route(['/report/barcode', '/report/barcode/<type>/<path:value>'], type='http', auth="user") - def report_barcode(self, type, value, width=300, height=50): + def report_barcode(self, type, value, width=600, height=100): """Contoller able to render barcode images thanks to reportlab. Samples: <img t-att-src="'/report/barcode/QR/%s' % o.name"/> diff --git a/addons/report/data/report_paperformat.xml b/addons/report/data/report_paperformat.xml index 275d65edd13f265b9624fa3766e9357efe6691da..f05e0afb6f077aca3837e989465caadc71dede43 100644 --- a/addons/report/data/report_paperformat.xml +++ b/addons/report/data/report_paperformat.xml @@ -9,7 +9,7 @@ <field name="page_width">0</field> <field name="orientation">Portrait</field> <field name="margin_top">40</field> - <field name="margin_bottom">20</field> + <field name="margin_bottom">23</field> <field name="margin_left">7</field> <field name="margin_right">7</field> <field name="header_line" eval="False" /> diff --git a/addons/report/models/report.py b/addons/report/models/report.py index d8a51387cb7055c7accc0623006b478c3989d843..cbbee7fcebda842974721739b314f6440250ff0b 100644 --- a/addons/report/models/report.py +++ b/addons/report/models/report.py @@ -25,10 +25,8 @@ from openerp.tools.translate import _ from openerp.addons.web.http import request from openerp.tools.safe_eval import safe_eval as eval -import os +import re import time -import psutil -import signal import base64 import logging import tempfile @@ -52,15 +50,19 @@ try: ['wkhtmltopdf', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) except OSError: - _logger.error('You need wkhtmltopdf to print a pdf version of the reports.') + _logger.info('You need wkhtmltopdf to print a pdf version of the reports.') else: out, err = process.communicate() - version = out.splitlines()[1].strip() - version = version.split(' ')[1] + version = re.search('([0-9.]+)', out).group(0) if LooseVersion(version) < LooseVersion('0.12.0'): - _logger.warning('Upgrade wkhtmltopdf to (at least) 0.12.0') + _logger.info('Upgrade wkhtmltopdf to (at least) 0.12.0') wkhtmltopdf_state = 'upgrade' - wkhtmltopdf_state = 'ok' + else: + wkhtmltopdf_state = 'ok' + + if config['workers'] == 1: + _logger.info('You need to start OpenERP with at least two workers to print a pdf version of the reports.') + wkhtmltopdf_state = 'workers' class Report(osv.Model): @@ -343,19 +345,20 @@ class Report(osv.Model): command_args = [] tmp_dir = tempfile.gettempdir() - # Passing the cookie to wkhtmltopdf in order to resolve URL. + # Passing the cookie to wkhtmltopdf in order to resolve internal links. try: if request: command_args.extend(['--cookie', 'session_id', request.session.sid]) except AttributeError: pass - # Display arguments + # Wkhtmltopdf arguments + command_args.extend(['--quiet']) # Less verbose error messages if paperformat: + # Convert the paperformat record into arguments command_args.extend(self._build_wkhtmltopdf_args(paperformat, spec_paperformat_args)) - command_args.extend(['--load-error-handling', 'ignore']) - + # Force the landscape orientation if necessary if landscape and '--orientation' in command_args: command_args_copy = list(command_args) for index, elem in enumerate(command_args_copy): @@ -366,61 +369,46 @@ class Report(osv.Model): elif landscape and not '--orientation' in command_args: command_args.extend(['--orientation', 'landscape']) + # Execute WKhtmltopdf pdfdocuments = [] - # HTML to PDF thanks to WKhtmltopdf for index, reporthtml in enumerate(bodies): - command_arg_local = [] - pdfreport = tempfile.NamedTemporaryFile(suffix='.pdf', prefix='report.tmp.', - mode='w+b') - # Directly load the document if we have it + local_command_args = [] + pdfreport = tempfile.NamedTemporaryFile(suffix='.pdf', prefix='report.tmp.', mode='w+b') + + # Directly load the document if we already have it if save_in_attachment and save_in_attachment['loaded_documents'].get(reporthtml[0]): pdfreport.write(save_in_attachment['loaded_documents'].get(reporthtml[0])) pdfreport.seek(0) pdfdocuments.append(pdfreport) continue - # Header stuff + # Wkhtmltopdf handles header/footer as separate pages. Create them if necessary. if headers: - head_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.header.tmp.', - dir=tmp_dir, mode='w+') + head_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.header.tmp.', dir=tmp_dir, mode='w+') head_file.write(headers[index]) head_file.seek(0) - command_arg_local.extend(['--header-html', head_file.name]) - - # Footer stuff + local_command_args.extend(['--header-html', head_file.name]) if footers: - foot_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.footer.tmp.', - dir=tmp_dir, mode='w+') + foot_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.footer.tmp.', dir=tmp_dir, mode='w+') foot_file.write(footers[index]) foot_file.seek(0) - command_arg_local.extend(['--footer-html', foot_file.name]) + local_command_args.extend(['--footer-html', foot_file.name]) # Body stuff - content_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.body.tmp.', - dir=tmp_dir, mode='w+') + content_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.body.tmp.', dir=tmp_dir, mode='w+') content_file.write(reporthtml[1]) content_file.seek(0) try: - # If the server is running with only one worker, ask to create a secund to be able - # to serve the http request of wkhtmltopdf subprocess. - if config['workers'] == 1: - ppid = psutil.Process(os.getpid()).ppid - os.kill(ppid, signal.SIGTTIN) - - wkhtmltopdf = command + command_args + command_arg_local + wkhtmltopdf = command + command_args + local_command_args wkhtmltopdf += [content_file.name] + [pdfreport.name] - process = subprocess.Popen(wkhtmltopdf, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + process = subprocess.Popen(wkhtmltopdf, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = process.communicate() - if config['workers'] == 1: - os.kill(ppid, signal.SIGTTOU) - - if process.returncode != 0: + if process.returncode not in [0, 1]: raise osv.except_osv(_('Report (PDF)'), - _('wkhtmltopdf failed with error code = %s. ' + _('Wkhtmltopdf failed (error code: %s). ' 'Message: %s') % (str(process.returncode), err)) # Save the pdf in attachment if marked @@ -446,7 +434,7 @@ class Report(osv.Model): except: raise - # Get and return the full pdf + # Return the entire document if len(pdfdocuments) == 1: content = pdfdocuments[0].read() pdfdocuments[0].close() diff --git a/addons/report/static/src/js/qwebactionmanager.js b/addons/report/static/src/js/qwebactionmanager.js index 37a0fae6ea6938e1ef14e6e9b3ff61bc6f005d2c..cb0f3921482191e757b6f74acde31ecd9076574c 100644 --- a/addons/report/static/src/js/qwebactionmanager.js +++ b/addons/report/static/src/js/qwebactionmanager.js @@ -54,33 +54,35 @@ openerp.report = function(instance) { if (action.report_type == 'qweb-html') { window.open(report_url, '_blank', 'height=900,width=1280'); instance.web.unblockUI(); - } else { + } else if (action.report_type === 'qweb-pdf') { // Trigger the download of the pdf/controller report - - if (action.report_type == 'qweb-pdf') { - (wkhtmltopdf_state = wkhtmltopdf_state || openerp.session.rpc('/report/check_wkhtmltopdf')).then(function (presence) { - // Fallback of qweb-pdf if wkhtmltopdf is not installed - if (presence == 'install' && action.report_type == 'qweb-pdf') { - self.do_notify(_t('Report'), _t('Unable to find Wkhtmltopdf on this \ - system. The report will be shown in html.<br><br><a href="http://wkhtmltopdf.org/" target="_blank">\ - wkhtmltopdf.org</a>'), true); - report_url = report_url.substring(12) - window.open('/report/html/' + report_url, '_blank', 'height=768,width=1024'); - instance.web.unblockUI(); - return; - } else { - if (presence == 'upgrade') { - self.do_notify(_t('Report'), _t('You should upgrade your version of\ - Wkhtmltopdf to at least 0.12.0 in order to get a correct display of headers and footers as well as\ - support for table-breaking between pages.<br><br><a href="http://wkhtmltopdf.org/" \ - target="_blank">wkhtmltopdf.org</a>'), true); - } - } - return trigger_download(self.session, response, c); - }); - } else if (action.report_type == 'controller') { + (wkhtmltopdf_state = wkhtmltopdf_state || openerp.session.rpc('/report/check_wkhtmltopdf')).then(function (presence) { + // Fallback on html if wkhtmltopdf is not installed or if OpenERP is started with one worker + if (presence === 'install') { + self.do_notify(_t('Report'), _t('Unable to find Wkhtmltopdf on this \ +system. The report will be shown in html.<br><br><a href="http://wkhtmltopdf.org/" target="_blank">\ +wkhtmltopdf.org</a>'), true); + report_url = report_url.substring(12) + window.open('/report/html/' + report_url, '_blank', 'height=768,width=1024'); + instance.web.unblockUI(); + return; + } else if (presence === 'workers') { + self.do_notify(_t('Report'), _t('You need to start OpenERP with at least two \ +workers to print a pdf version of the reports.'), true); + report_url = report_url.substring(12) + window.open('/report/html/' + report_url, '_blank', 'height=768,width=1024'); + instance.web.unblockUI(); + return; + } else if (presence === 'upgrade') { + self.do_notify(_t('Report'), _t('You should upgrade your version of\ + Wkhtmltopdf to at least 0.12.0 in order to get a correct display of headers and footers as well as\ + support for table-breaking between pages.<br><br><a href="http://wkhtmltopdf.org/" \ + target="_blank">wkhtmltopdf.org</a>'), true); + } return trigger_download(self.session, response, c); - } + }); + } else if (action.report_type === 'controller') { + return trigger_download(self.session, response, c); } } else { return self._super(action, options); diff --git a/addons/report/views/layouts.xml b/addons/report/views/layouts.xml index dd7a404196ec8d70766762fb0814c7de453246fc..f9c0ea7ef73017dbf0cf6f1dfd1e3dab4703283e 100644 --- a/addons/report/views/layouts.xml +++ b/addons/report/views/layouts.xml @@ -9,7 +9,8 @@ t-att-data-main-object="repr(main_object) if editable else None" t-att-data-report-margin-top="data_report_margin_top if data_report_margin_top else None" t-att-data-report-header-spacing="data_report_header_spacing if data_report_header_spacing else None" - t-att-data-report-dpi="data_report_dpi if data_report_dpi else None"> + t-att-data-report-dpi="data_report_dpi if data_report_dpi else None" + t-att-data-oe-company-name="res_company.name"> <head> <meta name="viewport" content="width=device-width, initial-scale=1"/> @@ -19,7 +20,6 @@ <t t-if="not title and main_object and 'name' in main_object"> <t t-set="additional_title" t-value="main_object.name"/> </t> - <meta name="openerp.company" t-att-value="res_company.name"/> <meta name="description" t-att-value="main_object and 'website_meta_description' in main_object and main_object.website_meta_description or website_meta_description"/> <meta name="keywords" t-att-value="main_object and 'website_meta_keywords' in main_object diff --git a/addons/report_intrastat/views/report_intrastatinvoice.xml b/addons/report_intrastat/views/report_intrastatinvoice.xml index c534f808c95ba26232a467f3ba9d534702470390..abf4bdd56baf1b5a2fb952727223031d3a324427 100644 --- a/addons/report_intrastat/views/report_intrastatinvoice.xml +++ b/addons/report_intrastat/views/report_intrastatinvoice.xml @@ -5,7 +5,7 @@ <t t-call="report.external_layout"> <div class="page"> <div class="row"> - <div class="col-xs-4 col-xs-offset-8"> + <div class="col-xs-5 col-xs-offset-7"> <address t-field="o.partner_id" t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' /> <span t-field="o.partner_id.vat"/> diff --git a/addons/report_webkit/webkit_report.py b/addons/report_webkit/webkit_report.py index 60fb536ca961c3275bc9062fe5f06838d7a3c0ad..c442ea32549ae210a0b06ef5167a161e027e7862 100644 --- a/addons/report_webkit/webkit_report.py +++ b/addons/report_webkit/webkit_report.py @@ -156,8 +156,8 @@ class WebKitParser(report_sxw): """Call webkit in order to generate pdf""" if not webkit_header: webkit_header = report_xml.webkit_header - tmp_dir = tempfile.gettempdir() - out_filename = tempfile.mktemp(suffix=".pdf", prefix="webkit.tmp.") + fd, out_filename = tempfile.mkstemp(suffix=".pdf", + prefix="webkit.tmp.") file_to_del = [out_filename] if comm_path: command = [comm_path] @@ -168,25 +168,15 @@ class WebKitParser(report_sxw): # default to UTF-8 encoding. Use <meta charset="latin-1"> to override. command.extend(['--encoding', 'utf-8']) if header : - head_file = file( os.path.join( - tmp_dir, - str(time.time()) + '.head.html' - ), - 'w' - ) - head_file.write(self._sanitize_html(header.encode('utf-8'))) - head_file.close() + with tempfile.NamedTemporaryFile(suffix=".head.html", + delete=False) as head_file: + head_file.write(self._sanitize_html(header.encode('utf-8'))) file_to_del.append(head_file.name) command.extend(['--header-html', head_file.name]) if footer : - foot_file = file( os.path.join( - tmp_dir, - str(time.time()) + '.foot.html' - ), - 'w' - ) - foot_file.write(self._sanitize_html(footer.encode('utf-8'))) - foot_file.close() + with tempfile.NamedTemporaryFile(suffix=".foot.html", + delete=False) as foot_file: + foot_file.write(self._sanitize_html(footer.encode('utf-8'))) file_to_del.append(foot_file.name) command.extend(['--footer-html', foot_file.name]) @@ -204,10 +194,10 @@ class WebKitParser(report_sxw): command.extend(['--page-size', str(webkit_header.format).replace(',', '.')]) count = 0 for html in html_list : - html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w') - count += 1 - html_file.write(self._sanitize_html(html.encode('utf-8'))) - html_file.close() + with tempfile.NamedTemporaryFile(suffix="%d.body.html" %count, + delete=False) as html_file: + count += 1 + html_file.write(self._sanitize_html(html.encode('utf-8'))) file_to_del.append(html_file.name) command.append(html_file.name) command.append(out_filename) @@ -227,9 +217,9 @@ class WebKitParser(report_sxw): if status : raise except_osv(_('Webkit error' ), _("The command 'wkhtmltopdf' failed with error code = %s. Message: %s") % (status, error_message)) - pdf_file = open(out_filename, 'rb') - pdf = pdf_file.read() - pdf_file.close() + with open(out_filename, 'rb') as pdf_file: + pdf = pdf_file.read() + os.close(fd) finally: if stderr_fd is not None: os.close(stderr_fd) diff --git a/addons/sale/views/report_saleorder.xml b/addons/sale/views/report_saleorder.xml index c23e680e664a66b8d944de954cd9fe3af79ad86c..5c198c635a3bbd23b87f93aae96af5ad743318b8 100644 --- a/addons/sale/views/report_saleorder.xml +++ b/addons/sale/views/report_saleorder.xml @@ -19,7 +19,7 @@ <p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p> </div> </div> - <div class="col-xs-4 col-xs-offset-2"> + <div class="col-xs-5 col-xs-offset-1"> <div t-field="o.partner_id" t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' /> </div> diff --git a/addons/survey/security/ir.model.access.csv b/addons/survey/security/ir.model.access.csv index 89c001ce44ecba98ac54212b3a945ead822b77a3..e49c976ea1f4c65806ab67784a54a04113c32bb1 100644 --- a/addons/survey/security/ir.model.access.csv +++ b/addons/survey/security/ir.model.access.csv @@ -1,11 +1,11 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_survey_public,survey.survey public,model_survey_survey,base.group_public,1,0,0,0 -access_survey_page_public,survey.page public,model_survey_page,base.group_public,1,0,0,0 -access_survey_question_public,survey.question public,model_survey_question,base.group_public,1,0,0,0 -access_survey_label_public,survey.label public,model_survey_label,base.group_public,1,0,0,0 -access_survey_user_input_public,survey.user_input public,model_survey_user_input,base.group_public,1,1,1,0 -access_survey_user_input_line_public,survey.user_input_line public,model_survey_user_input_line,base.group_public,1,1,1,0 -access_survey_stage_public,survey.stage public,model_survey_stage,base.group_public,1,0,0,0 +access_survey_public,survey.survey public,model_survey_survey,,1,0,0,0 +access_survey_page_public,survey.page public,model_survey_page,,1,0,0,0 +access_survey_question_public,survey.question public,model_survey_question,,1,0,0,0 +access_survey_label_public,survey.label public,model_survey_label,,1,0,0,0 +access_survey_user_input_public,survey.user_input public,model_survey_user_input,,1,1,1,0 +access_survey_user_input_line_public,survey.user_input_line public,model_survey_user_input_line,,1,1,1,0 +access_survey_stage_public,survey.stage public,model_survey_stage,,1,0,0,0 access_survey_user,survey.survey user,model_survey_survey,base.group_survey_user,1,0,0,0 access_survey_page_user,survey.page user,model_survey_page,base.group_survey_user,1,0,0,0 access_survey_question_user,survey.question user,model_survey_question,base.group_survey_user,1,0,0,0 diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index ffe9d26824019d6cb37face884efa7c2cd5b3216..a4ecc4e17d903416291e6b8e553e826648966415 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2596,6 +2596,7 @@ instance.web.form.FieldCharDomain = instance.web.form.AbstractField.extend(insta this.$('.select_records').on('click', self.on_click); }, on_click: function(ev) { + event.preventDefault(); var self = this; var model = this.options.model || this.field_manager.get_field_value(this.options.model_field); this.pop = new instance.web.form.SelectCreatePopup(this); @@ -2614,15 +2615,14 @@ instance.web.form.FieldCharDomain = instance.web.form.AbstractField.extend(insta }); } else { - var domain = ["id", "in", element_ids]; + var domain = [["id", "in", element_ids]]; var domain_done = $.Deferred().resolve(domain); } $.when(domain_done).then(function (domain) { var domain = self.pop.dataset.domain.concat(domain || []); - self.set_value(JSON.stringify(domain)) + self.set_value(domain); }); }); - event.preventDefault(); }, }); @@ -4463,7 +4463,11 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ else return $.when(); }).done(function () { - if (!self.o2m.options.reload_on_button) { + var ds = self.o2m.dataset; + var cached_records = _.any([ds.to_create, ds.to_delete, ds.to_write], function(value) { + return value.length; + }); + if (!self.o2m.options.reload_on_button && !cached_records) { self.handle_button(name, id, callback); }else { self.handle_button(name, id, function(){ diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 753d52d497a473ed4b71765268ee1bff1442737a..c6e28301c42b5d9e4022c2ccb204b999609f912f 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -436,6 +436,16 @@ instance.web.ActionManager = instance.web.Widget.extend({ ir_actions_act_window: function (action, options) { var self = this; + if (action.target === 'current'){ + action.context['active_model'] = action.res_model; + if (action.res_id){ + action.context['active_id'] = action.res_id; + action.context['active_ids'] = [action.res_id]; + } else{ + delete action.context['active_id']; + delete action.context['active_ids']; + } + } return this.ir_actions_common({ widget: function () { return new instance.web.ViewManagerAction(self, action); }, action: action, diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 39f3067720f5dcdc6125a29cb6f27f4874c56a82..446080875a6b8c0439f8c4825433eab83afdf18d 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -378,7 +378,7 @@ class Website(openerp.addons.web.controllers.main.Home): """ response = werkzeug.wrappers.Response() return request.registry['website']._image( - request.cr, request.uid, model, id, field, response) + request.cr, request.uid, model, id, field, response, max_width, max_height) #------------------------------------------------------ diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py index 360b34c877c5ff74a2548d64783dc995a51b1f36..44b3f771f62f20000ba265199dd24972c6841125 100644 --- a/addons/website/models/ir_http.py +++ b/addons/website/models/ir_http.py @@ -141,49 +141,50 @@ class ir_http(orm.AbstractModel): return response def _handle_exception(self, exception=None, code=500): - if isinstance(exception, werkzeug.exceptions.HTTPException) and hasattr(exception, 'response') and exception.response: - return exception.response - - attach = self._serve_attachment() - if attach: - return attach - - if getattr(request, 'website_enabled', False) and request.website: - values = dict( - exception=exception, - traceback=traceback.format_exc(exception), - ) - if exception: - code = getattr(exception, 'code', code) - if isinstance(exception, ir_qweb.QWebException): - values.update(qweb_exception=exception) - if isinstance(exception.qweb.get('cause'), openerp.exceptions.AccessError): - code = 403 - if code == 500: - logger.error("500 Internal Server Error:\n\n%s", values['traceback']) - if 'qweb_exception' in values: - view = request.registry.get("ir.ui.view") - views = view._views_get(request.cr, request.uid, exception.qweb['template'], request.context) - to_reset = [v for v in views if v.model_data_id.noupdate is True] - values['views'] = to_reset - elif code == 403: - logger.warn("403 Forbidden:\n\n%s", values['traceback']) - - values.update( - status_message=werkzeug.http.HTTP_STATUS_CODES[code], - status_code=code, - ) - - if not request.uid: - self._auth_method_public() - - try: - html = request.website._render('website.%s' % code, values) - except Exception: - html = request.website._render('website.http_error', values) - return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8') + try: + return super(ir_http, self)._handle_exception(exception) + except Exception: - return super(ir_http, self)._handle_exception(exception) + attach = self._serve_attachment() + if attach: + return attach + + if getattr(request, 'website_enabled', False) and request.website: + values = dict( + exception=exception, + traceback=traceback.format_exc(exception), + ) + if exception: + code = getattr(exception, 'code', code) + if isinstance(exception, ir_qweb.QWebException): + values.update(qweb_exception=exception) + if isinstance(exception.qweb.get('cause'), openerp.exceptions.AccessError): + code = 403 + if code == 500: + logger.error("500 Internal Server Error:\n\n%s", values['traceback']) + if 'qweb_exception' in values: + view = request.registry.get("ir.ui.view") + views = view._views_get(request.cr, request.uid, exception.qweb['template'], request.context) + to_reset = [v for v in views if v.model_data_id.noupdate is True] + values['views'] = to_reset + elif code == 403: + logger.warn("403 Forbidden:\n\n%s", values['traceback']) + + values.update( + status_message=werkzeug.http.HTTP_STATUS_CODES[code], + status_code=code, + ) + + if not request.uid: + self._auth_method_public() + + try: + html = request.website._render('website.%s' % code, values) + except Exception: + html = request.website._render('website.http_error', values) + return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8') + + raise class ModelConverter(ir.ir_http.ModelConverter): def __init__(self, url_map, model=False, domain='[]'): diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index ad2b63a6dd9868d58b71dc393f71a25154f5c024..0e1fe2beb89c74127a0db505ae41fcab46ccc4cd 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -15,6 +15,7 @@ import urllib2 import urlparse import re +import werkzeug.urls import werkzeug.utils from dateutil import parser from lxml import etree, html @@ -265,10 +266,19 @@ class Image(orm.AbstractModel): if options is None: options = {} classes = ['img', 'img-responsive'] + options.get('class', '').split() - return ir_qweb.HTMLSafe('<img class="%s" src="/website/image?model=%s&field=%s&id=%s"/>' % ( + url_params = { + 'model': record._model._name, + 'field': field_name, + 'id': record.id, + } + for options_key in ['max_width', 'max_height']: + if options.get(options_key): + url_params[options_key] = options[options_key] + + return ir_qweb.HTMLSafe('<img class="%s" src="/website/image?%s"/>' % ( ' '.join(itertools.imap(werkzeug.utils.escape, classes)), - record._model._name, - field_name, record.id)) + werkzeug.urls.url_encode(url_params) + )) local_url_re = re.compile(r'^/(?P<module>[^]]+)/static/(?P<rest>.+)$') def from_html(self, cr, uid, model, column, element, context=None): diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 433d8644698cee022a9f1326a50b16368646fc89..db02dcce51e75ae9396dd244af08e25cf97f5b2b 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -541,7 +541,10 @@ class website(osv.osv): response.mimetype = Image.MIME[image.format] w, h = image.size - max_w, max_h = int(max_width), int(max_height) + try: + max_w, max_h = int(max_width), int(max_height) + except: + max_w, max_h = (maxint, maxint) if w < max_w and h < max_h: response.data = data diff --git a/addons/website/static/src/js/website.seo.js b/addons/website/static/src/js/website.seo.js index 8588b7a37655fe63959710a20325175ecf5471b7..1c4bad90ffc37a5eef24105347792891f09a98cb 100644 --- a/addons/website/static/src/js/website.seo.js +++ b/addons/website/static/src/js/website.seo.js @@ -264,21 +264,21 @@ }, description: function () { var $description = $('meta[name=description]'); - return ($description.length > 0) && ($description.attr('value') && $description.attr('value').trim()); + return ($description.length > 0) && ($description.attr('content') && $description.attr('content').trim()); }, changeDescription: function (description) { // TODO create tag if missing - $('meta[name=description]').attr('value', description); + $('meta[name=description]').attr('content', description); this.trigger('description-changed', description); }, keywords: function () { var $keywords = $('meta[name=keywords]'); - var parsed = ($keywords.length > 0) && $keywords.attr('value') && $keywords.attr('value').split(","); + var parsed = ($keywords.length > 0) && $keywords.attr('content') && $keywords.attr('content').split(","); return (parsed && parsed[0]) ? parsed: []; }, changeKeywords: function (keywords) { // TODO create tag if missing - $('meta[name=keywords]').attr('value', keywords.join(",")); + $('meta[name=keywords]').attr('content', keywords.join(",")); this.trigger('keywords-changed', keywords); }, headers: function (tag) { @@ -296,7 +296,7 @@ }); }, company: function () { - return $('meta[name="openerp.company"]').attr('value'); + return $('html').attr('data-oe-company-name'); }, bodyText: function () { return $('body').children().not('.js_seo_configuration').text(); diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 6226e3a0aa7d091b9c8ed965a27fb82e2ba70ebd..56d954e8a353be38e42caadc50ff8755dfc58999 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -61,8 +61,10 @@ t-att-data-editable="'1' if editable else None" t-att-data-translatable="'1' if translatable else None" t-att-data-view-xmlid="xmlid if editable else None" - t-att-data-main-object="repr(main_object) if editable else None"> + t-att-data-main-object="repr(main_object) if editable else None" + t-att-data-oe-company-name="res_company.name"> <head> + <meta charset="utf-8" /> <t t-if="main_object and 'website_meta_title' in main_object"> <t t-set="title" t-value="main_object.website_meta_title"/> </t> @@ -73,10 +75,9 @@ <t t-set="title"><t t-raw="res_company.name"/><t t-if="additional_title"> - <t t-raw="additional_title"/></t></t> </t> <meta name="viewport" content="initial-scale=1"/> - <meta name="openerp.company" t-att-value="res_company.name"/> - <meta name="description" t-att-value="main_object and 'website_meta_description' in main_object + <meta name="description" t-att-content="main_object and 'website_meta_description' in main_object and main_object.website_meta_description or website_meta_description"/> - <meta name="keywords" t-att-value="main_object and 'website_meta_keywords' in main_object + <meta name="keywords" t-att-content="main_object and 'website_meta_keywords' in main_object and main_object.website_meta_keywords or website_meta_keywords"/> <title><t t-esc="title"/></title> @@ -137,14 +138,14 @@ <footer> <div class="container hidden-print" id="footer_container"> <div class="row"> - <div class="col-md-3" name="product"> + <div class="col-md-3"> <h4>Our products & Services</h4> <ul class="list-unstyled" name="products"> <li><a href="/">Home</a></li> </ul> </div> <div class="col-md-3" name="info"> - <h4 name="info_title">Connect with us</h4> + <h4>Connect with us</h4> <ul class="list-unstyled"> <li><a href="/page/website.contactus">Contact us</a></li> </ul> @@ -161,7 +162,7 @@ <a t-att-href="website.social_github" t-if="website.social_github"><i class="fa fa-github"/></a> </h2> </div> - <div class="col-md-5 col-lg-offset-1" name="about_us"> + <div class="col-md-5 col-lg-offset-1"> <div> <h4> <span t-field="res_company.name">Your Company</span> @@ -306,7 +307,7 @@ <template id="debugger" inherit_id="website.layout" optional="disabled" name="Debugger & Tests"> <xpath expr='//t[@name="layout_head"]' position="after"> - <script type="text/javascript" src="/website/static/src/js/website.tour.js"></script> + <t t-set="debugger_hook" t-value="1" /> </xpath> </template> 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 f960511edfc35df730721aa187b9342e0881d18e..06b5317c4eecd570c56c94a52f2884b5005d59cc 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 @@ -86,8 +86,8 @@ </t> <div class="media"> <a class="pull-left" t-attf-href="/partners/#{slug(partner)}?#{current_grade and 'grade_id=%s&' % current_grade.id}#{current_country and 'country_id=%s' % current_country.id}" - t-field="partner.image_small" - t-field-options='{"widget": "image", "class": "media-object"}' + t-field="partner.image" + t-field-options='{"widget": "image", "class": "media-object", "max_width": 128}' ></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&' % current_grade.id}#{current_country and 'country_id=%s' % current_country.id}"> diff --git a/addons/website_event_sale/views/website_event_sale.xml b/addons/website_event_sale/views/website_event_sale.xml index 967d9c029ccfda9edae807187f6eb0917ecba133..cf46797b34d6dd5e4b4e50f1ab923e36d388c6cf 100644 --- a/addons/website_event_sale/views/website_event_sale.xml +++ b/addons/website_event_sale/views/website_event_sale.xml @@ -3,7 +3,7 @@ <data> <template id="debugger" inherit_id="website.debugger" name="Event Debugger"> - <xpath expr="//script[last()]" position="after"> + <xpath expr='//t[@t-set="debugger_hook"]' position="after"> <script type="text/javascript" src="/website_event_sale/static/src/js/website.tour.event_sale.js"></script> </xpath> </template> diff --git a/addons/website_event_track/controllers/event.py b/addons/website_event_track/controllers/event.py index f0a9efa8dfcc06af0a352cef1815bee7de4b9672..b2e689e5cb68118d6974fc396aec8af5480ccd2f 100644 --- a/addons/website_event_track/controllers/event.py +++ b/addons/website_event_track/controllers/event.py @@ -88,10 +88,21 @@ class website_event(http.Controller): days_tracks_count[day] = len(tracks) days[day] = self._prepare_calendar(event, tracks) + cr, uid, context = request.cr, request.uid, request.context + track_obj = request.registry['event.track'] + tracks_ids = track_obj.search(cr, openerp.SUPERUSER_ID, [('event_id', '=', event.id)], context=context) + speakers = dict() + for t in track_obj.browse(cr, openerp.SUPERUSER_ID, tracks_ids, context=context): + acc = "" + for speaker in t.speaker_ids: + acc = speaker.name + u" – " + acc if acc else speaker.name + speakers[t.id] = acc + return request.website.render("website_event_track.agenda", { 'event': event, 'days': days, 'days_nbr': days_tracks_count, + 'speakers': speakers, 'tag': tag }) diff --git a/addons/website_event_track/views/website_event.xml b/addons/website_event_track/views/website_event.xml index cb5140282664bfc83a435d45740dbd9b51f27a05..9aece0f5c11435633dccdd5e6122fe21b5450170 100644 --- a/addons/website_event_track/views/website_event.xml +++ b/addons/website_event_track/views/website_event.xml @@ -85,8 +85,8 @@ <a t-attf-href="/event/#{ slug(event) }/track/#{ slug(track) }"> <span t-esc="track and track.name"/> </a> - <div class="text-muted" t-foreach="track.speaker_ids" t-as="speaker"> - <small t-esc="speaker.display_name"/> + <div class="text-muted"> + <small t-esc="speakers[track.id]"/> </div> </t> </td> @@ -98,11 +98,11 @@ <t t-set="track" t-value="dt[1][False][0]"/> <td t-att-colspan="len(locations)-1" t-attf-class="text-center event_color_#{track.color} #{track and 'event_track' or ''}"> <a t-attf-href="/event/#{ slug(event) }/track/#{ slug(track) }"> - <span t-esc="track.name"/><br/> - <div class="text-muted" t-foreach="track.speaker_ids" t-as="speaker"> - <small t-esc="speaker.display_name"/> - </div> + <span t-esc="track.name"/> </a> + <div class="text-muted"> + <small t-esc="speakers[track.id]"/> + </div> </td> </t> </tr> diff --git a/addons/website_forum/controllers/main.py b/addons/website_forum/controllers/main.py index 53397f38b63d4d4d3ba2197bee13de6b25627235..81cbfdcadc89d1c18f6cb82107780a990e4da0d9 100644 --- a/addons/website_forum/controllers/main.py +++ b/addons/website_forum/controllers/main.py @@ -330,13 +330,12 @@ class WebsiteForum(http.Controller): cr, uid, context = request.cr, request.uid, request.context if kwargs.get('comment') and post.forum_id.id == forum.id: # TDE FIXME: check that post_id is the question or one of its answers - if request.registry['res.users'].has_group(cr, uid, 'website_mail.group_comment'): - request.registry['forum.post'].message_post( - cr, uid, post.id, - body=kwargs.get('comment'), - type='comment', - subtype='mt_comment', - context=dict(context, mail_create_nosubcribe=True)) + request.registry['forum.post'].message_post( + cr, uid, post.id, + body=kwargs.get('comment'), + type='comment', + subtype='mt_comment', + context=dict(context, mail_create_nosubcribe=True)) return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question))) @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/toggle_correct', type='json', auth="public", website=True) diff --git a/addons/website_hr_recruitment/security/website_hr_recruitment_security.xml b/addons/website_hr_recruitment/security/website_hr_recruitment_security.xml index 792995ca606671ea122ac1ae895a3fcda48a72d0..5d47e69feadfc597481f8da28fe09a431972e625 100644 --- a/addons/website_hr_recruitment/security/website_hr_recruitment_security.xml +++ b/addons/website_hr_recruitment/security/website_hr_recruitment_security.xml @@ -11,6 +11,26 @@ <field name="perm_create" eval="False"/> <field name="perm_unlink" eval="False"/> </record> + <record id="hr_job_portal" model="ir.rule"> + <field name="name">Job Positions: Portal</field> + <field name="model_id" ref="hr.model_hr_job"/> + <field name="domain_force">[('website_published', '=', True)]</field> + <field name="groups" eval="[(4, ref('base.group_portal'))]"/> + <field name="perm_read" eval="True"/> + <field name="perm_write" eval="False"/> + <field name="perm_create" eval="False"/> + <field name="perm_unlink" eval="False"/> + </record> + <record id="hr_job_officer" model="ir.rule"> + <field name="name">Job Positions: HR Officer</field> + <field name="model_id" ref="hr.model_hr_job"/> + <field name="domain_force">[(1, '=', 1)]</field> + <field name="groups" eval="[(4, ref('base.group_hr_user'))]"/> + <field name="perm_read" eval="True"/> + <field name="perm_write" eval="False"/> + <field name="perm_create" eval="False"/> + <field name="perm_unlink" eval="False"/> + </record> <record id="hr_department_public" model="ir.rule"> <field name="name">Job department: Public</field> <field name="model_id" ref="hr.model_hr_department"/> diff --git a/addons/website_mail/controllers/main.py b/addons/website_mail/controllers/main.py index 0932794ee288523c8aa59a1487dab20225279224..de69fba490a318b0b8a19db39e552d4a8d9a274c 100644 --- a/addons/website_mail/controllers/main.py +++ b/addons/website_mail/controllers/main.py @@ -62,6 +62,7 @@ class WebsiteMail(http.Controller): @http.route(['/website_mail/is_follower'], type='json', auth="public", website=True) def call(self, model, id, **post): + id = int(id) cr, uid, context = request.cr, request.uid, request.context partner_obj = request.registry.get('res.partner') @@ -74,17 +75,28 @@ class WebsiteMail(http.Controller): partner_id = users_obj.browse(cr, SUPERUSER_ID, uid, context).partner_id elif request.session.get('partner_id'): partner_id = partner_obj.browse(cr, SUPERUSER_ID, request.session.get('partner_id'), context) - - email = "" - is_follower = False - if partner_id: - email = partner_id and partner_id.email - is_follower = partner_id.id in [ - fol.id for fol in obj.browse(cr, SUPERUSER_ID, id, context).message_follower_ids] + email = partner_id and partner_id.email or "" - return { + values = { 'is_user': uid != public_id, 'email': email, - 'is_follower': is_follower + 'is_follower': False, } + if not obj: + return values + obj_ids = obj.exists(cr, SUPERUSER_ID, [id], context=context) + if obj_ids: + if partner_id: + values['is_follower'] = len( + request.registry['mail.followers'].search( + cr, SUPERUSER_ID, [ + ('res_model', '=', 'mail.group'), + ('res_id', '=', obj_ids[0]), + ('partner_id', '=', partner_id.id) + ], context=context)) == 1 + if post.get('fields'): + record = obj.read(cr, SUPERUSER_ID, obj_ids[0], fields=post.get('fields'), context=context) + values.update(record) + + return values diff --git a/addons/website_mail/models/mail_message.py b/addons/website_mail/models/mail_message.py index 1e50611fa009cf2f2ce9e9a3f416cf4c641b344a..cff950f2962e7aabd4fa85d2bb7da1f1601a6244 100644 --- a/addons/website_mail/models/mail_message.py +++ b/addons/website_mail/models/mail_message.py @@ -34,7 +34,7 @@ class MailMessage(osv.Model): res[message.id] = message.subject else: plaintext_ct = html2plaintext(message.body) - res[message.id] = plaintext_ct + '%s' % (' [...]' if len(plaintext_ct) >= 20 else '') + res[message.id] = plaintext_ct[:30] + '%s' % (' [...]' if len(plaintext_ct) >= 30 else '') return res _columns = { diff --git a/addons/website_mail/static/src/js/website_mail.js b/addons/website_mail/static/src/js/website_mail.js index bd34b526ddd020cd9896818632a21403c07b2c25..d069f88ea453759e35cd0e50f383115264438116 100644 --- a/addons/website_mail/static/src/js/website_mail.js +++ b/addons/website_mail/static/src/js/website_mail.js @@ -7,17 +7,20 @@ selector: ".js_follow", start: function (editable_mode) { var self = this; + this.is_user = false; - // set value and display button - self.$target.find("input").removeClass("hidden"); openerp.jsonRpc('/website_mail/is_follower', 'call', { model: this.$target.data('object'), - id: +this.$target.data('id'), + id: this.$target.data('id'), + fields: ['name', 'alias_id'], }).always(function (data) { + self.is_user = data.is_user; + self.$target.find('.js_mg_email').attr('href', 'mailto:' + data.alias_id[1]); + self.$target.find('.js_mg_link').attr('href', '/groups/' + data.id); + self.toggle_subscription(data.is_follower); self.$target.find('input.js_follow_email') .val(data.email ? data.email : "") - .attr("disabled", data.is_follower && data.email.length ? "disabled" : false); - self.$target.attr("data-follow", data.is_follower ? 'on' : 'off'); + .attr("disabled", data.is_follower || (data.email.length && self.is_user) ? "disabled" : false); self.$target.removeClass("hidden"); }); @@ -30,10 +33,11 @@ self.on_click(); }); } + return; }, on_click: function () { var self = this; - var $email = this.$target.find(".js_follow_email:visible"); + var $email = this.$target.find(".js_follow_email"); if ($email.length && !$email.val().match(/.+@.+/)) { this.$target.addClass('has-error'); @@ -47,13 +51,27 @@ 'message_is_follower': this.$target.attr("data-follow") || "off", 'email': $email.length ? $email.val() : false, }).then(function (follow) { - if (follow) { - self.$target.find(".js_follow_email, .input-group-btn").addClass("hidden"); - self.$target.find(".alert").removeClass("hidden"); - } - self.$target.find('input.js_follow_email').attr("disabled", follow ? "disabled" : false); - self.$target.attr("data-follow", follow ? 'on' : 'off'); + self.toggle_subscription(follow); }); }, + toggle_subscription: function(follow) { + if (follow) { + this.$target.find(".js_mg_follow_form").addClass("hidden"); + this.$target.find(".js_mg_details").removeClass("hidden"); + } + else { + this.$target.find(".js_mg_follow_form").removeClass("hidden"); + this.$target.find(".js_mg_details").addClass("hidden"); + } + this.$target.find('input.js_follow_email').attr("disabled", follow || this.is_user ? "disabled" : false); + this.$target.attr("data-follow", follow ? 'on' : 'off'); + }, + }); + + $(document).ready(function () { + $('.js_follow_btn').on('click', function (ev) { + var email = $(ev.currentTarget).parents('.js_mg_follow_form').first().find('.js_follow_email').val(); + $(document).find('.js_follow_email').val(email); + }); }); })(); diff --git a/addons/website_mail/views/snippets.xml b/addons/website_mail/views/snippets.xml index 98a793932114088dea775dae5938f170536c8c92..987ccaa672e6ae082ce7d38caccec84336a6e34d 100644 --- a/addons/website_mail/views/snippets.xml +++ b/addons/website_mail/views/snippets.xml @@ -503,20 +503,25 @@ <span class="oe_snippet_thumbnail_title">Subscribe Button</span> </div> - <div class="oe_snippet_body input-group js_follow" + <div class="oe_snippet_body js_follow" data-id="0" data-object="mail.group" data-follow="off"> - <input - type="email" - name="email" - class="js_follow_email form-control" - placeholder="your email..."/> - <span class="input-group-btn"> - <a href="#" class="btn btn-default js_unfollow_btn">Unsubscribe</a> - <a href="#" class="btn btn-primary js_follow_btn">Subscribe</a> - </span> - <div class="alert alert-success hidden">thanks for your subscription!</div> + <div class="input-group js_mg_follow_form"> + <input + type="email" + name="email" + class="js_follow_email form-control" + placeholder="your email..."/> + <span class="input-group-btn"> + <button href="#" class="btn btn-primary js_follow_btn">Subscribe</button> + </span> + </div> + <p class="js_mg_details hidden well well-sm"> + <i class="fa fa-envelope-o"/><a href="#" class="js_mg_email"> send mail</a> - + <i class="fa fa-file-o"/><a href="#" class="js_mg_link"> archives</a> - + <i class="fa fa-times"/><a href="#" class="js_unfollow_btn"> unsubscribe</a> + </p> </div> </div> diff --git a/addons/website_mail_group/controllers/main.py b/addons/website_mail_group/controllers/main.py index 88eb1a7e9b73fda434b36c3554d036c514643ab2..7320ebc6548a2b4e2c64ae68f68054c38453f95f 100644 --- a/addons/website_mail_group/controllers/main.py +++ b/addons/website_mail_group/controllers/main.py @@ -9,13 +9,14 @@ from openerp.addons.web.http import request class MailGroup(http.Controller): - _thread_per_page = 10 + _thread_per_page = 20 + _replies_per_page = 10 def _get_archives(self, group_id): MailMessage = request.registry['mail.message'] groups = MailMessage.read_group( request.cr, request.uid, [('model', '=', 'mail.group'), ('res_id', '=', group_id)], ['subject', 'date'], - groupby="date", orderby="date asc", context=request.context) + groupby="date", orderby="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() @@ -27,7 +28,7 @@ class MailGroup(http.Controller): def view(self, **post): cr, uid, context = request.cr, request.uid, request.context group_obj = request.registry.get('mail.group') - group_ids = group_obj.search(cr, uid, [], context=context) + group_ids = group_obj.search(cr, uid, [('alias_id', '!=', False), ('alias_id.alias_name', '!=', False)], context=context) values = {'groups': group_obj.browse(cr, uid, group_ids, context)} return request.website.render('website_mail_group.mail_groups', values) @@ -73,6 +74,7 @@ class MailGroup(http.Controller): 'archives': self._get_archives(group.id), 'date_begin': date_begin, 'date_end': date_end, + 'replies_per_page': self._replies_per_page, } return request.website.render('website_mail_group.group_messages', values) @@ -81,11 +83,49 @@ class MailGroup(http.Controller): ], type='http', auth="public", website=True) def thread_discussion(self, group, message, mode='thread', date_begin=None, date_end=None, **post): cr, uid, context = request.cr, request.uid, request.context + Message = request.registry['mail.message'] + if mode == 'thread': + base_domain = [('model', '=', 'mail.group'), ('res_id', '=', group.id), ('parent_id', '=', message.parent_id and message.parent_id.id or False)] + else: + base_domain = [('model', '=', 'mail.group'), ('res_id', '=', group.id)] + next_message = None + next_message_ids = Message.search(cr, uid, base_domain + [('date', '<', message.date)], order="date DESC", limit=1, context=context) + if next_message_ids: + next_message = Message.browse(cr, uid, next_message_ids[0], context=context) + prev_message = None + prev_message_ids = Message.search(cr, uid, base_domain + [('date', '>', message.date)], order="date ASC", limit=1, context=context) + if prev_message_ids: + prev_message = Message.browse(cr, uid, prev_message_ids[0], context=context) values = { 'message': message, 'group': group, 'mode': mode, + 'archives': self._get_archives(group.id), 'date_begin': date_begin, 'date_end': date_end, + 'replies_per_page': self._replies_per_page, + 'next_message': next_message, + 'prev_message': prev_message, } return request.website.render('website_mail_group.group_message', values) + + @http.route( + '''/groups/<model('mail.group'):group>/<model('mail.message', "[('model','=','mail.group'), ('res_id','=',group[0])]"):message>/get_replies''', + type='json', auth="public", methods=['POST'], website=True) + def render_messages(self, group, message, **post): + last_displayed_id = post.get('last_displayed_id') + if not last_displayed_id: + return False + Message = request.registry['mail.message'] + replies_domain = [('id', '<', int(last_displayed_id)), ('parent_id', '=', message.id)] + msg_ids = Message.search(request.cr, request.uid, replies_domain, limit=self._replies_per_page, context=request.context) + msg_count = Message.search(request.cr, request.uid, replies_domain, count=True, context=request.context) + messages = Message.browse(request.cr, request.uid, msg_ids, context=request.context) + values = { + 'group': group, + 'thread_header': message, + 'messages': messages, + 'msg_more_count': msg_count - self._replies_per_page, + 'replies_per_page': self._replies_per_page, + } + return request.registry['ir.ui.view'].render(request.cr, request.uid, 'website_mail_group.messages_short', values, engine='ir.qweb', context=request.context) diff --git a/addons/website_mail_group/static/src/css/website_mail_group.css b/addons/website_mail_group/static/src/css/website_mail_group.css new file mode 100644 index 0000000000000000000000000000000000000000..27482ebd203e1e14f20604ed86a432ce4e3247c0 --- /dev/null +++ b/addons/website_mail_group/static/src/css/website_mail_group.css @@ -0,0 +1,12 @@ +.o_mg_avatar { + width: 40px; + height: 40px; +} + +.o_mg_link_show { + display: none; +} + +.o_mg_link_content { + display: none; +} diff --git a/addons/website_mail_group/static/src/js/website_mail_group.js b/addons/website_mail_group/static/src/js/website_mail_group.js new file mode 100644 index 0000000000000000000000000000000000000000..c9b8ae797ed2fa85fd05ce473ad2b4cec5fe932e --- /dev/null +++ b/addons/website_mail_group/static/src/js/website_mail_group.js @@ -0,0 +1,42 @@ +$(document).ready(function () { + + $('.o_mg_link_hide').on('click', function (ev) { + ev.preventDefault(); + var $link = $(ev.currentTarget); + var $container = $link.parents('div').first(); + $container.find('.o_mg_link_hide').first().hide(); + $container.find('.o_mg_link_show').first().show(); + $container.find('.o_mg_link_content').first().show(); + return false; + }); + + $('.o_mg_link_show').on('click', function (ev) { + ev.preventDefault(); + var $link = $(ev.currentTarget); + var $container = $link.parents('div').first(); + $container.find('.o_mg_link_hide').first().show(); + $container.find('.o_mg_link_show').first().hide(); + $container.find('.o_mg_link_content').first().hide(); + return false; + }); + + $('body').on('click', 'button.o_mg_read_more', function (ev) { + var $link = $(ev.target); + return openerp.jsonRpc($link.data('href'), 'call', { + 'last_displayed_id': $link.data('msg-id'), + }).then(function (data) { + if (! data) { + return true; + } + var $thread_container = $link.parents('.o_mg_replies').first().find('ul.media-list'); + if ($thread_container) { + var $last_msg = $thread_container.find('li.media').last(); + $(data).find('li.media').insertAfter($last_msg); + $(data).find('p.well').appendTo($thread_container); + } + var $show_more = $link.parents('p.well').first(); + $show_more.remove(); + return true; + }); + }); +}); \ No newline at end of file diff --git a/addons/website_mail_group/views/website_mail_group.xml b/addons/website_mail_group/views/website_mail_group.xml index 87f8755c04a8e20700ca94d497e723955a9f18f2..76edd5247536de81f18499a9932689dd495ec20d 100644 --- a/addons/website_mail_group/views/website_mail_group.xml +++ b/addons/website_mail_group/views/website_mail_group.xml @@ -1,152 +1,240 @@ <?xml version="1.0" encoding="utf-8"?> <openerp> <data> - <template id="footer_mailing_list" inherit_id="website.layout" name="Footer Mailing List Link"> - <xpath expr="//footer//div[@name='info']/ul" position="inside"> - <li><a t-attf-href="/groups">Mailing List</a></li> - </xpath> - </template> - <template id="mail_groups" name="Mailing Lists"> - <t t-call="website.layout"> - <div class="container"> - <h1> - Our Mailing Lists - </h1> - <div class="row"> - <div class="col-sm-4" style="height: 140px" t-foreach="groups" t-as="group"> - <img t-att-src="'/website/image?model=mail.group&field=image_small&id='+str(group['id'])" class="pull-left"/> - <div> - <strong><a t-attf-href="/groups/#{ slug(group) }" t-esc="group.name"/></strong> - <div t-esc="group.description" class="text-muted"/> - <t t-call="website_mail.follow"><t t-set="object" t-value="group"/></t> - </div> - </div> + +<template id="footer_mailing_list" inherit_id="website.layout" name="Footer Mailing List Link"> + <xpath expr="//footer//div[@name='info']/ul" position="inside"> + <li><a t-attf-href="/groups">Mailing List</a></li> + </xpath> +</template> + +<template id="mail_groups" name="Mailing Lists"> + <t t-call="website.layout"> + <div id="wrap" class="oe_structure oe_empty"> + <section class="bg-primary jumbotron mt0 mb0"> + <div class="container"> + <h1>Stay in touch with our Community</h1> + <p>Alone we can do so little, together we can do so much</p> + </div> + </section> + </div> + <div class="container mt32"> + <div class="row mt8" t-foreach="groups" t-as="group"> + <div class="col-md-3"> + <img t-att-src="'/website/image?model=mail.group&field=image_small&id='+str(group['id'])" class="pull-left"/> + <strong><a t-attf-href="/groups/#{ slug(group) }" t-esc="group.name"/></strong><br /> + <i class='fa fa-envelope-o'/> + <a t-attf-href="mailto:#{group.alias_id.alias_name}@#{group.alias_id.alias_domain}"><span t-field="group.alias_id"/></a> + </div> + <div class="col-md-4"> + <div t-esc="group.description" class="text-muted"/> + </div> + <div class="col-md-2"> + <i class='fa fa-user'/> <t t-esc="len(group.message_follower_ids)"/> participants<br /> + <i class='fa fa-envelope-o'/> <t t-esc="len(group.message_ids)"/> messages + </div> + <div class="col-md-3"> + <t t-call="website_mail.follow"><t t-set="object" t-value="group"/></t> </div> </div> - </t> - </template> + </div> + </t> +</template> - <template id="group_messages" name="Message Threads"> - <t t-call="website.layout"> - <section class="container"> - <div class="row mt8"> - <div class="col-md-5 mt16"> - <ol class="breadcrumb"> - <li><a href="/groups">Mailing Lists</a></li> - <li class="active" t-esc="group.name"/> - </ol> - </div> - <div class="col-md-5 pull-right"> - <t t-call="website.pager"/> - </div> +<template id="group_messages" name="Message Threads"> + <t t-call="website.layout"> + <t t-set="head"> + <link rel='stylesheet' href="/website_mail_group/static/src/css/website_mail_group.css"/> + <script type="text/javascript" src="/website_mail_group/static/src/js/website_mail_group.js"/> + </t> + <section class="container"> + <div class="row mt8"> + <ol class="breadcrumb pull-left"> + <li><a href="/groups">Mailing Lists</a></li> + <li> + <a t-attf-href="/groups/#{slug(group)}?#{mode and 'mode=%s' % mode or ''}#{date_begin and '&date_begin=%s' % date_begin or ''}#{date_end and '&date_end=%s' % date_end or ''}"><t t-esc="group.name"/></a> + </li> + </ol> + </div> + <div class="row"> + <h1 class="text-center"> + <t t-esc="group.name"/> mailing list archives + </h1><h4 class="text-center text-muted"> + <i class='fa fa-envelope-o'/> + <a t-attf-href="mailto:#{group.alias_id.alias_name}@#{group.alias_id.alias_domain}"><span t-field="group.alias_id"/></a> + </h4> + </div> + <div class="row"> + <div class="col-md-3"> + <h2>Archives</h2> + <ul class="nav nav-pills nav-stacked" id="group_mode"> + <li t-attf-class="#{mode=='thread' and 'active' or ''}"> + <a t-attf-href="/groups/#{ slug(group) }?mode=thread">By thread</a> + </li> + <li t-attf-class="#{mode=='date' and not date_begin and 'active' or ''}"> + <a t-attf-href="/groups/#{ slug(group) }?mode=date">By date</a> + <ul class="nav nav-pills nav-stacked" style="margin-left: 8px;"> + <t t-foreach="archives" t-as="month_archive"> + <li t-att-class="month_archive['date_begin'] == date_begin and 'active' or None"> + <a t-ignore="True" t-attf-href="/groups/#{ slug(group) }?mode=date&date_begin=#{ month_archive['date_begin'] }&date_end=#{month_archive['date_end']}"> + <t t-esc="month_archive['date']"/> + <span class="pull-right badge" t-esc="month_archive['date_count']"/> + </a> + </li> + </t> + </ul> + </li> + </ul> </div> - <h1 class="mt8 mb32"> - <span t-field="group.name"/> - <small>List Archive</small> - </h1> - <div class="row"> - <div class="col-md-3"> - <ul class="nav nav-pills nav-stacked" id="group_mode"> - <li t-attf-class="#{mode=='thread' and 'active' or ''}"> - <a t-attf-href="/groups/#{ slug(group) }?mode=thread">By thread</a> - </li> - <li t-attf-class="#{mode=='date' and not date_begin and 'active' or ''}"> - <a t-attf-href="/groups/#{ slug(group) }?mode=date">By date</a> - <ul class="nav nav-pills nav-stacked" style="margin-left: 8px;"> - <t t-foreach="archives" t-as="month_archive"> - <li t-att-class="month_archive['date_begin'] == date_begin and 'active' or None"> - <a t-ignore="True" t-attf-href="/groups/#{ slug(group) }?mode=date&date_begin=#{ month_archive['date_begin'] }&date_end=#{month_archive['date_end']}"> - <t t-esc="month_archive['date']"/> - <span class="pull-right badge" t-esc="month_archive['date_count']"/> - </a> - </li> - </t> - </ul> - </li> - </ul> - </div> - <div class="col-md-9"> - <t t-call="website_mail_group.messages_short"/> + <div class="col-md-9"> + <div> + <t t-call="website.pager"/> </div> - </div> - </section> - </t> - </template> - - <template id="group_message"> - <t t-call="website.layout"> - <div class="container"> - <div class="row mt8"> - <div class="col-md-5"> - <ol class="breadcrumb mb8"> - <li><a href="/groups">Mailing Lists</a></li> - <li> - <a t-attf-href="/groups/#{slug(group)}?mode=#{mode}&date_begin=#{date_begin}&date_end=#{date_end}"> - <span t-field="group.name"/> - </a> - </li> - <li class="active" t-esc="message.subject or 'Message'"/> - </ol> + <t t-call="website_mail_group.messages_short"> + <t t-set="messages" t-value="messages"/> + <t t-set="msg_more_count" t-value="0"/> + <t t-set="thread_header" t-value="None"/> + </t> + <div> + <t t-call="website.pager"/> </div> </div> + </div> + </section> + </t> +</template> - <h1 t-field="message.subject"/> - <img class="img-rounded pull-left" t-att-src="'/website/image?model=mail.message&field=author_avatar&id='+str(message.id)" style="width : 30px"/> - <h4 class="mt0 mb32"> - <t t-if="message.author_id"> - <span t-field="message.author_id" style="display: inline-block;" t-field-options='{ - "widget": "contact", - "fields": ["name"] - }'/> - </t> - <t t-if="not message.author_id"><t t-esc="message.email_from"/></t> - on <span t-field="message.date"/> +<template id="group_message"> + <t t-call="website.layout"> + <t t-set="head"> + <link rel='stylesheet' href="/website_mail_group/static/src/css/website_mail_group.css"/> + <script type="text/javascript" src="/website_mail_group/static/src/js/website_mail_group.js"/> + </t> + <section class="container"> + <div class="row mt8"> + <ol class="breadcrumb pull-left"> + <li><a href="/groups">Mailing Lists</a></li> + <li> + <a t-attf-href="/groups/#{slug(group)}?#{mode and 'mode=%s' % mode or ''}#{date_begin and '&date_begin=%s' % date_begin or ''}#{date_end and '&date_end=%s' % date_end or ''}"><t t-esc="group.name"/></a> + </li> + <li t-if="message" class="active"><t t-esc="message.description"/></li> + </ol> + </div> + <div class="row"> + <h1 class="text-center"> + <t t-esc="group.name"/> mailing list archives + </h1><h4 class="text-center text-muted"> + <i class='fa fa-envelope-o'/> + <a t-attf-href="mailto:#{group.alias_id.alias_name}@#{group.alias_id.alias_domain}"><span t-field="group.alias_id"/></a> </h4> - <div t-raw="message.body"/> - - <div class="row" t-if="message.attachment_ids"> - <h3 class="col-sm-12">Attachment(s):</h3> - <div class="col-md-2 col-sm-3 text-center" t-foreach='message.attachment_ids' t-as='attachment'> - <a t-att-href="'/mail/download_attachment?model=mail.message&id='+str(message.id)+'&method=download_attachment&attachment_id='+str(attachment.id)" target="_blank"> - <t t-if="attachment.file_type == 'webimage'"> - <img t-att-src="'/web/binary/image?model=ir.attachment&field=datas&id=' + str(attachment.id) + '&resize=100,80'" - class='oe_attachment_embedded'></img> - </t> - <t t-if="attachment.file_type != 'webimage'"> - <img t-att-src="'/mail/static/src/img/mimetypes/' + attachment.file_type + '.png'" - class='oe_attachment_webimage'></img> - </t> - <div class='oe_attachment_name'><t t-raw='attachment.name' /></div> - </a> - </div> - </div> - <div t-if="message.child_ids"> - <h2 class="page-header">Follow ups</h2> - <t t-set="messages" t-value="message.child_ids"/> - <t t-call="website_mail_group.messages_short"/> - </div> - <div t-if="message.parent_id"> - <h2 class="page-header">Reference</h2> - <t t-set="messages" t-value="[message.parent_id]"/> - <t t-call="website_mail_group.messages_short"/> + </div> + <div class="row"> + <div class="col-md-3"> + <h4>Browse archives</h4> + <ul class="nav nav-pills nav-stacked" id="group_mode"> + <li t-attf-class="#{mode=='thread' and 'active' or ''}"> + <a t-attf-href="/groups/#{ slug(group) }?mode=thread">By thread</a> + </li> + <li t-attf-class="#{mode=='date' and not date_begin and 'active' or ''}"> + <a t-attf-href="/groups/#{ slug(group) }?mode=date">By date</a> + <ul class="nav nav-pills nav-stacked" style="margin-left: 8px;"> + <t t-foreach="archives" t-as="month_archive"> + <li t-att-class="month_archive['date_begin'] == date_begin and 'active' or None"> + <a t-ignore="True" t-attf-href="/groups/#{ slug(group) }?mode=date&date_begin=#{ month_archive['date_begin'] }&date_end=#{month_archive['date_end']}"> + <t t-esc="month_archive['date']"/> + <span class="pull-right badge" t-esc="month_archive['date_count']"/> + </a> + </li> + </t> + </ul> + </li> + </ul> </div> + <div class="col-md-9"> + <div class="row"> + <h4 class="col-md-6"> + <t t-if="prev_message"><a t-attf-href='/groups/#{slug(group)}/#{slug(prev_message)}?#{mode and "mode=%s" % mode or ""}'> + <i class="fa fa-arrow-left"/> <t t-esc="prev_message.description"/> + </a></t> + </h4> + <h4 class="col-md-6"> + <t t-if="next_message"><a class="pull-right" t-attf-href='/groups/#{slug(group)}/#{slug(next_message)}?#{mode and "mode=%s" % mode or ""}'> + <t t-esc="next_message.description"/> <i class="fa fa-arrow-right"/> + </a></t> + </h4> + </div> + <div class="media"> + <img class="img-rounded pull-left mt0 media-object o_mg_avatar" + t-att-src="'/website/image?model=mail.message&field=author_avatar&id='+str(message.id)"/> + <div class="media-body"> + <h4 class="media-heading" t-esc="message.description"/> + <small> + by + <t t-if="message.author_id"> + <span t-field="message.author_id" style="display: inline-block;" t-field-options='{ + "widget": "contact", + "fields": ["name"] + }'/> + </t> + <t t-if="not message.author_id"><t t-esc="message.email_from"/></t> + - <i class="fa fa-calendar"/> <t t-esc="message.date"/> + </small> + <div t-raw="message.body"/> - <div class="jumbotron mt64"> - <h1>Join the discussion</h1> - <p> - Join this mailing list to follow or participate to this discussion.<br/> - <span t-field="group.name"/>: <i t-field="group.description"/> - </p> - <t t-call="website_mail.follow"><t t-set="object" t-value="group"/></t> + <div> + <p t-if="message.attachment_ids" class="mt8"> + <a href="#" class="o_mg_link_hide"> + <i class="fa fa-chevron-right"/> <t t-raw="len(message.attachment_ids)"/> attachments + </a> + <a href="#" class="o_mg_link_show"> + <i class="fa fa-chevron-down"/> <t t-raw="len(message.attachment_ids)"/> attachments + </a> + </p> + <div class="o_mg_link_content"> + <div class="col-md-2 col-sm-3 text-center" t-foreach='message.attachment_ids' t-as='attachment'> + <a t-att-href="'/mail/download_attachment?model=mail.message&id='+str(message.id)+'&method=download_attachment&attachment_id='+str(attachment.id)" target="_blank"> + <t t-if="attachment.file_type == 'webimage'"> + <img t-att-src="'/web/binary/image?model=ir.attachment&field=datas&id=' + str(attachment.id) + '&resize=100,80'" + class='oe_attachment_embedded' + t-att-title="attachment.name"/> + </t> + <t t-if="attachment.file_type != 'webimage'"> + <img t-att-src="'/mail/static/src/img/mimetypes/' + attachment.file_type + '.png'" + class='oe_attachment_webimage' + t-att-title="attachment.name"/> + </t> + <div class='oe_attachment_name'><t t-raw='attachment.name' /></div> + </a> + </div> + </div> + </div> + </div> + </div> + <div t-if="message.child_ids" class="o_mg_replies"> + <h4 class="page-header">Follow-Ups</h4> + <t t-call="website_mail_group.messages_short"> + <t t-set="messages" t-value="message.child_ids[:replies_per_page]"/> + <t t-set="msg_more_count" t-value="len(message.child_ids) - replies_per_page"/> + <t t-set="thread_header" t-value="message"/> + </t> + </div> + <div t-if="message.parent_id"> + <h4 class="page-header">Reference</h4> + <t t-call="website_mail_group.messages_short"> + <t t-set="messages" t-value="[message.parent_id]"/> + </t> + </div> </div> </div> - </t> - </template> + </section> + </t> +</template> - <template id="messages_short"> +<template id="messages_short"> + <div> <ul class="media-list"> <li t-foreach="messages" t-as="thread" class="media"> - <img class="img-rounded pull-left mt0 media-object" style="height: 40px" + <img class="img-rounded pull-left mt0 media-object o_mg_avatar" t-att-src="'/website/image?model=mail.message&field=author_avatar&id='+str(thread.id)"/> <div class="media-body"> <h4 class="media-heading"> @@ -161,14 +249,36 @@ }'/> </t> <t t-if="not thread.author_id"><t t-esc="thread.email_from"/></t> - <span class="fa fa-comment-o"> - <t t-raw="len(thread.child_ids)"/> replies - </span> + - <i class="fa fa-calendar"/> <t t-esc="thread.date"/> + - <i class="fa fa-paperclip"/> <t t-esc="len(thread.attachment_ids)"/> </small> + <p t-if="thread.child_ids" class="mt8"> + <a href="#" class="o_mg_link_hide"> + <i class="fa fa-chevron-right"/> <t t-raw="len(thread.child_ids)"/> replies + </a> + <a href="#" class="o_mg_link_show"> + <i class="fa fa-chevron-down"/> <t t-raw="len(thread.child_ids)"/> replies + </a> + </p> + <div class="o_mg_link_content o_mg_replies"> + <t t-call="website_mail_group.messages_short"> + <t t-set="messages" t-value="thread.child_ids[:replies_per_page]"/> + <t t-set="msg_more_count" t-value="len(thread.child_ids) - replies_per_page"/> + <t t-set="thread_header" t-value="thread"/> + </t> + </div> </div> </li> </ul> - </template> + <p t-if="messages and msg_more_count > 0 and thread_header" class="well well-sm"> + <button class="fa btn-link o_mg_read_more" + t-attf-data-href="/groups/#{slug(group)}/#{slug(thread_header)}/get_replies" + t-attf-data-msg-id="#{messages[-1].id}"> + show <t t-esc="msg_more_count"/> more replies + </button> + </p> + </div> +</template> </data> </openerp> diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index bbd8f83fd7d9afc577b7028bd4f712ff02cb4b00..6df34f232b4f6b211aa6d43bd1b157641999b2a8 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -35,8 +35,8 @@ class table_compute(object): index = 0 maxy = 0 for p in products: - x = p.website_size_x - y = p.website_size_y + x = min(max(p.website_size_x, 1), PPR) + y = min(max(p.website_size_y, 1), PPR) if index>PPG: x = y = 1 diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py index daa2a61c8518c19cf9e0918ccc6603d80f18bc43..0911887c8353e0ff067b935e41ded05f421bdee5 100644 --- a/addons/website_sale/models/product.py +++ b/addons/website_sale/models/product.py @@ -65,6 +65,7 @@ class product_template(osv.Model): _inherit = ["product.template", "website.seo.metadata"] _order = 'website_published desc, website_sequence desc, name' _name = 'product.template' + _mail_post_access = 'read' def _website_url(self, cr, uid, ids, field_name, arg, context=None): res = dict.fromkeys(ids, '') diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index 0c69ece50cc5a480ac8130bb89bf69339193cc85..8e96628c38bff5b70bf94b578ade03b23dcf3cf0 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -5,7 +5,7 @@ <!-- Layout and common templates --> <template id="debugger" inherit_id="website.debugger" name="Event Debugger"> - <xpath expr="//script[last()]" position="after"> + <xpath expr='//t[@t-set="debugger_hook"]' position="after"> <script type="text/javascript" src="/website_sale/static/src/js/website.tour.sale.js"></script> </xpath> </template> diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index 57f24c8864adde2546eabc35e42b2201424d46b2..e9de9dc70288ca814fdea448955b89ade36a3525 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -41,6 +41,7 @@ import openerp.workflow _logger = logging.getLogger(__name__) + class actions(osv.osv): _name = 'ir.actions.actions' _table = 'ir_actions' @@ -129,9 +130,15 @@ class ir_actions_report_xml(osv.osv): Look up a report definition and render the report for the provided IDs. """ new_report = self._lookup_report(cr, name) - # in order to use current yml test files with qweb reports - if isinstance(new_report, (str, unicode)): - return self.pool['report'].get_pdf(cr, uid, res_ids, new_report, data=data, context=context), 'pdf' + + if isinstance(new_report, (str, unicode)): # Qweb report + # The only case where a QWeb report is rendered with this method occurs when running + # yml tests originally written for RML reports. + if openerp.tools.config['test_enable'] and not tools.config['test_report_directory']: + # Only generate the pdf when a destination folder has been provided. + return self.pool['report'].get_html(cr, uid, res_ids, new_report, data=data, context=context), 'html' + else: + return self.pool['report'].get_pdf(cr, uid, res_ids, new_report, data=data, context=context), 'pdf' else: return new_report.create(cr, uid, res_ids, data, context) @@ -1182,7 +1189,4 @@ class ir_actions_act_client(osv.osv): } - - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - diff --git a/openerp/http.py b/openerp/http.py index 0627c7695550a6c77dd72e3dc2cb8a0812fd483f..d0e4cf4d2eb8d95bb9e38f5245aa348eb8b43cfd 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -269,7 +269,10 @@ class WebRequest(object): """Called within an except block to allow converting exceptions to abitrary responses. Anything returned (except None) will be used as response.""" - raise + self._failed = exception # prevent tx commit + if isinstance(exception, werkzeug.exceptions.HTTPException): + return exception + raise def _call_function(self, *args, **kwargs): request = self @@ -456,18 +459,20 @@ class JsonRequest(WebRequest): def _handle_exception(self, exception): """Called within an except block to allow converting exceptions to abitrary responses. Anything returned (except None) will - be used as response.""" - _logger.exception("Exception during JSON request handling.") - self._failed = exception # prevent tx commit - error = { - 'code': 200, - 'message': "OpenERP Server Error", - 'data': serialize_exception(exception) - } - if isinstance(exception, AuthenticationError): - error['code'] = 100 - error['message'] = "OpenERP Session Invalid" - return self._json_response(error=error) + be used as response.""" + try: + return super(JsonRequest, self)._handle_exception(exception) + except Exception: + _logger.exception("Exception during JSON request handling.") + error = { + 'code': 200, + 'message': "OpenERP Server Error", + 'data': serialize_exception(exception) + } + if isinstance(exception, AuthenticationError): + error['code'] = 100 + error['message'] = "OpenERP Session Invalid" + return self._json_response(error=error) def dispatch(self): """ Calls the method asked for by the JSON-RPC2 or JSONP request diff --git a/openerp/report/render/rml2pdf/trml2pdf.py b/openerp/report/render/rml2pdf/trml2pdf.py index 6ce69578a8e16e006d084f0cf621be67e0f7d48b..af046836192be2f56201c5276813349987f0b317 100644 --- a/openerp/report/render/rml2pdf/trml2pdf.py +++ b/openerp/report/render/rml2pdf/trml2pdf.py @@ -107,7 +107,7 @@ class NumberedCanvas(canvas.Canvas): self.setFont("Helvetica", 8) self.drawRightString((self._pagesize[0]-30), (self._pagesize[1]-40), " %(this)i / %(total)i" % { - 'this': self._pageNumber+1, + 'this': self._pageNumber, 'total': page_count, } ) diff --git a/openerp/tools/test_reports.py b/openerp/tools/test_reports.py index c008c20ae29887ece957f8dde79f77cb4957ec6f..305d2b0f115890c8f2195156cd7ea84b63b68df5 100644 --- a/openerp/tools/test_reports.py +++ b/openerp/tools/test_reports.py @@ -87,8 +87,7 @@ def try_report(cr, uid, rname, ids, data=None, context=None, our_module=None, re if ('[[' in line) or ('[ [' in line): _logger.error("Report %s may have bad expression near: \"%s\".", rname, line[80:]) # TODO more checks, what else can be a sign of a faulty report? - elif res_format == 'foobar': - # TODO + elif res_format == 'html': pass else: _logger.warning("Report %s produced a \"%s\" chunk, cannot examine it", rname, res_format)