-
+
@@ -139,8 +139,8 @@
-
+
diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py
index b48f0786704..0977f3729b4 100644
--- a/addons/hr_holidays/hr_holidays.py
+++ b/addons/hr_holidays/hr_holidays.py
@@ -115,10 +115,10 @@ class hr_holidays(osv.osv):
_columns = {
'name': fields.char('Description', size=64),
'state': fields.selection([('draft', 'To Submit'), ('cancel', 'Cancelled'),('confirm', 'To Approve'), ('refuse', 'Refused'), ('validate1', 'Second Approval'), ('validate', 'Approved')],
- 'State', readonly=True, help='The state is set to \'To Submit\', when a holiday request is created.\
- \nThe state is \'To Approve\', when holiday request is confirmed by user.\
- \nThe state is \'Refused\', when holiday request is refused by manager.\
- \nThe state is \'Approved\', when holiday request is approved by manager.'),
+ 'Status', readonly=True, help='The status is set to \'To Submit\', when a holiday request is created.\
+ \nThe status is \'To Approve\', when holiday request is confirmed by user.\
+ \nThe status is \'Refused\', when holiday request is refused by manager.\
+ \nThe status is \'Approved\', when holiday request is approved by manager.'),
'user_id':fields.related('employee_id', 'user_id', type='many2one', relation='res.users', string='User', store=True),
'date_from': fields.datetime('Start Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, select=True),
'date_to': fields.datetime('End Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml
index 1808e216906..7ccace1c536 100644
--- a/addons/hr_holidays/hr_holidays_view.xml
+++ b/addons/hr_holidays/hr_holidays_view.xml
@@ -121,8 +121,8 @@
-
+
@@ -158,8 +158,8 @@
-
+
diff --git a/addons/hr_payroll/hr_payroll.py b/addons/hr_payroll/hr_payroll.py
index b56b49d3d99..d1f38ad4a0f 100644
--- a/addons/hr_payroll/hr_payroll.py
+++ b/addons/hr_payroll/hr_payroll.py
@@ -272,10 +272,10 @@ class hr_payslip(osv.osv):
('done', 'Done'),
('cancel', 'Rejected'),
], 'Status', select=True, readonly=True,
- help='* When the payslip is created the state is \'Draft\'.\
- \n* If the payslip is under verification, the state is \'Waiting\'. \
- \n* If the payslip is confirmed then state is set to \'Done\'.\
- \n* When user cancel payslip the state is \'Rejected\'.'),
+ help='* When the payslip is created the status is \'Draft\'.\
+ \n* If the payslip is under verification, the status is \'Waiting\'. \
+ \n* If the payslip is confirmed then status is set to \'Done\'.\
+ \n* When user cancel payslip the status is \'Rejected\'.'),
# 'line_ids': fields.one2many('hr.payslip.line', 'slip_id', 'Payslip Line', required=False, readonly=True, states={'draft': [('readonly', False)]}),
'line_ids': one2many_mod2('hr.payslip.line', 'slip_id', 'Payslip Lines', readonly=True, states={'draft':[('readonly',False)]}),
'company_id': fields.many2one('res.company', 'Company', required=False, readonly=True, states={'draft': [('readonly', False)]}),
diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py
index 56c517620e0..049cb93ca1e 100644
--- a/addons/hr_recruitment/hr_recruitment.py
+++ b/addons/hr_recruitment/hr_recruitment.py
@@ -62,7 +62,7 @@ class hr_recruitment_stage(osv.osv):
'name': fields.char('Name', size=64, required=True, translate=True),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of stages."),
'department_id':fields.many2one('hr.department', 'Specific to a Department', help="Stages of the recruitment process may be different per department. If this stage is common to all departments, keep this field empty."),
- 'state': fields.selection(AVAILABLE_STATES, 'State', required=True, help="The related state for the stage. The state of your document will automatically change according to the selected stage. Example, a stage is related to the state 'Close', when your document reach this stage, it will be automatically closed."),
+ 'state': fields.selection(AVAILABLE_STATES, 'Status', required=True, help="The related status for the stage. The status of your document will automatically change according to the selected stage. Example, a stage is related to the status 'Close', when your document reach this stage, it will be automatically closed."),
'fold': fields.boolean('Hide in views if empty', help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."),
'requirements': fields.text('Requirements'),
}
@@ -189,11 +189,11 @@ class hr_applicant(base_stage, osv.Model):
'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage',
domain="['&', ('fold', '=', False), '|', ('department_id', '=', department_id), ('department_id', '=', False)]"),
'state': fields.related('stage_id', 'state', type="selection", store=True,
- selection=AVAILABLE_STATES, string="State", readonly=True,
- help='The state is set to \'Draft\', when a case is created.\
- If the case is in progress the state is set to \'Open\'.\
- When the case is over, the state is set to \'Done\'.\
- If the case needs to be reviewed then the state is \
+ selection=AVAILABLE_STATES, string="Status", readonly=True,
+ help='The status is set to \'Draft\', when a case is created.\
+ If the case is in progress the status is set to \'Open\'.\
+ When the case is over, the status is set to \'Done\'.\
+ If the case needs to be reviewed then the status is \
set to \'Pending\'.'),
'categ_ids': fields.many2many('hr.applicant_category', string='Tags'),
'company_id': fields.many2one('res.company', 'Company'),
diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml
index 12c552e7a6d..1028db60890 100644
--- a/addons/hr_recruitment/hr_recruitment_view.xml
+++ b/addons/hr_recruitment/hr_recruitment_view.xml
@@ -107,7 +107,7 @@
diff --git a/addons/hr_recruitment/report/hr_recruitment_report.py b/addons/hr_recruitment/report/hr_recruitment_report.py
index e888da9ead7..d3faa3f37be 100644
--- a/addons/hr_recruitment/report/hr_recruitment_report.py
+++ b/addons/hr_recruitment/report/hr_recruitment_report.py
@@ -41,7 +41,7 @@ class hr_recruitment_report(osv.osv):
_columns = {
'user_id': fields.many2one('res.users', 'User', readonly=True),
'nbr': fields.integer('# of Applications', readonly=True),
- 'state': fields.selection(AVAILABLE_STATES, 'State', size=16, readonly=True),
+ 'state': fields.selection(AVAILABLE_STATES, 'Status', size=16, readonly=True),
'month':fields.selection([('01', 'January'), ('02', 'February'), \
('03', 'March'), ('04', 'April'),\
('05', 'May'), ('06', 'June'), \
diff --git a/addons/hr_timesheet/hr_timesheet.py b/addons/hr_timesheet/hr_timesheet.py
index a28508be4db..216541501b1 100644
--- a/addons/hr_timesheet/hr_timesheet.py
+++ b/addons/hr_timesheet/hr_timesheet.py
@@ -154,7 +154,7 @@ class hr_analytic_timesheet(osv.osv):
'date': lambda self, cr, uid, ctx: ctx.get('date', fields.date.context_today(self,cr,uid,context=ctx)),
'user_id': lambda obj, cr, uid, ctx: ctx.get('user_id', uid),
}
- def on_change_account_id(self, cr, uid, ids, account_id):
+ def on_change_account_id(self, cr, uid, ids, account_id, context=None):
return {'value':{}}
def on_change_date(self, cr, uid, ids, date):
@@ -190,17 +190,14 @@ class hr_analytic_timesheet(osv.osv):
'journal_id': self._getAnalyticJournal(cr, uid, context),
}}
-hr_analytic_timesheet()
-
class account_analytic_account(osv.osv):
_inherit = 'account.analytic.account'
_description = 'Analytic Account'
-
_columns = {
- 'use_timesheets': fields.boolean('Timesheets', help="Check this field if this project manages timesheets"),
+ 'use_timesheets': fields.boolean('Use Timesheets', help="Check this field if this project manages timesheets"),
}
-
+
def on_change_template(self, cr, uid, ids, template_id, context=None):
res = super(account_analytic_account, self).on_change_template(cr, uid, ids, template_id, context=context)
if template_id and 'value' in res:
@@ -208,6 +205,4 @@ class account_analytic_account(osv.osv):
res['value']['use_timesheets'] = template.use_timesheets
return res
-account_analytic_account()
-
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/hr_timesheet/hr_timesheet_view.xml b/addons/hr_timesheet/hr_timesheet_view.xml
index c81cda8493b..ebdd05ed59c 100644
--- a/addons/hr_timesheet/hr_timesheet_view.xml
+++ b/addons/hr_timesheet/hr_timesheet_view.xml
@@ -6,12 +6,12 @@
hr.analytic.timesheet.tree
hr.analytic.timesheet
-
+
-
+
-
+
@@ -24,7 +24,7 @@
hr.analytic.timesheet.form
hr.analytic.timesheet
-
diff --git a/addons/l10n_ar/l10n_ar_wizard.xml b/addons/l10n_ar/l10n_ar_wizard.xml
index 9a2fa377155..3c94d3db7a8 100644
--- a/addons/l10n_ar/l10n_ar_wizard.xml
+++ b/addons/l10n_ar/l10n_ar_wizard.xml
@@ -2,13 +2,9 @@
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_at/l10n_chart_at_wizard.xml b/addons/l10n_at/l10n_chart_at_wizard.xml
index b23d9be6cb5..efc68ce13e9 100644
--- a/addons/l10n_at/l10n_chart_at_wizard.xml
+++ b/addons/l10n_at/l10n_chart_at_wizard.xml
@@ -1,12 +1,8 @@
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_be/l10n_be_wizard.yml b/addons/l10n_be/l10n_be_wizard.yml
index 178009be7ff..0b16827225f 100644
--- a/addons/l10n_be/l10n_be_wizard.yml
+++ b/addons/l10n_be/l10n_be_wizard.yml
@@ -1,10 +1,6 @@
--
- !record {model: ir.actions.todo, id: config_call_account_template}:
- action_id: account.action_wizard_multi_chart
- type: automatic
-
!python {model: ir.actions.todo}: |
- install_todo = self.browse(cr, uid, ref('l10n_be.config_call_account_template'))
+ install_todo = self.browse(cr, uid, ref('account.action_wizard_multi_chart_todo'))
if install_todo.state == 'open':
wiz = self.pool.get('wizard.multi.charts.accounts')
values = {
diff --git a/addons/l10n_ca/l10n_ca_wizard.xml b/addons/l10n_ca/l10n_ca_wizard.xml
index 3bdc380325d..efc68ce13e9 100644
--- a/addons/l10n_ca/l10n_ca_wizard.xml
+++ b/addons/l10n_ca/l10n_ca_wizard.xml
@@ -1,13 +1,9 @@
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_ch/wizard.xml b/addons/l10n_ch/wizard.xml
index 084159aa36d..792bd21a2fc 100644
--- a/addons/l10n_ch/wizard.xml
+++ b/addons/l10n_ch/wizard.xml
@@ -1,13 +1,7 @@
-
- Generate Chart of Accounts for l10n_ch
- Generate Chart of Accounts from a Chart Template. Please let the nuber to 0 for Swiss charts.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial
- Accounts/Generate Chart of Accounts from a Chart Template.
-
- 4
- automatic
+
+ open
diff --git a/addons/l10n_cl/l10n_cl_wizard.xml b/addons/l10n_cl/l10n_cl_wizard.xml
index fd0d29f1edb..bcbd9a43f27 100644
--- a/addons/l10n_cl/l10n_cl_wizard.xml
+++ b/addons/l10n_cl/l10n_cl_wizard.xml
@@ -1,14 +1,9 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_cn/l10n_chart_cn_wizard.xml b/addons/l10n_cn/l10n_chart_cn_wizard.xml
index 0a098120682..6919eb199b6 100644
--- a/addons/l10n_cn/l10n_chart_cn_wizard.xml
+++ b/addons/l10n_cn/l10n_chart_cn_wizard.xml
@@ -1,12 +1,7 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_cr/l10n_wizard.xml b/addons/l10n_cr/l10n_wizard.xml
index ef37667b318..6919eb199b6 100644
--- a/addons/l10n_cr/l10n_wizard.xml
+++ b/addons/l10n_cr/l10n_wizard.xml
@@ -1,10 +1,8 @@
-
-
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_de/l10n_de_wizard.xml b/addons/l10n_de/l10n_de_wizard.xml
index 3a0dcd34bd3..efc68ce13e9 100644
--- a/addons/l10n_de/l10n_de_wizard.xml
+++ b/addons/l10n_de/l10n_de_wizard.xml
@@ -1,12 +1,9 @@
-
- Generiert Kontenplan aus Vorlage
- Der Assistent generiert einen Kontenplan auf Basis eines Templates (Vorlage). Sie werden aufgefordert den Namen der Firma einzugeben, sowie die entsprechende Kontenvorlage zu wählen. Ausserdem können Sie für die Initialisierung der Journale die gewünschte Stellenanzahl der Konten, sowie die Hauptwährung Ihres Betriebes auswählen. Dieser Assistent ist identisch mit dem Open ERP Menü Finanzen/Konfiguration/Finanzkonten/Erzeuge Konten aus Vorlage.
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_ec/l10n_chart_ec_wizard.xml b/addons/l10n_ec/l10n_chart_ec_wizard.xml
index 9906cf9828c..6919eb199b6 100644
--- a/addons/l10n_ec/l10n_chart_ec_wizard.xml
+++ b/addons/l10n_ec/l10n_chart_ec_wizard.xml
@@ -1,13 +1,8 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_es/l10n_es_wizard.xml b/addons/l10n_es/l10n_es_wizard.xml
index ec30f9e9e25..52aaa88fdab 100644
--- a/addons/l10n_es/l10n_es_wizard.xml
+++ b/addons/l10n_es/l10n_es_wizard.xml
@@ -1,9 +1,7 @@
-
-
-
- automatic
+
+ open
diff --git a/addons/l10n_fr/l10n_fr_wizard.xml b/addons/l10n_fr/l10n_fr_wizard.xml
index 897da1d5414..bcbd9a43f27 100644
--- a/addons/l10n_fr/l10n_fr_wizard.xml
+++ b/addons/l10n_fr/l10n_fr_wizard.xml
@@ -1,12 +1,9 @@
-
-
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_gr/l10n_gr_wizard.xml b/addons/l10n_gr/l10n_gr_wizard.xml
index 4fa564e2155..6919eb199b6 100644
--- a/addons/l10n_gr/l10n_gr_wizard.xml
+++ b/addons/l10n_gr/l10n_gr_wizard.xml
@@ -1,12 +1,7 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_gt/l10n_gt_base.xml b/addons/l10n_gt/l10n_gt_base.xml
index 4ba27d6a5c4..8caf6ebe48c 100644
--- a/addons/l10n_gt/l10n_gt_base.xml
+++ b/addons/l10n_gt/l10n_gt_base.xml
@@ -1,14 +1,8 @@
-
- Generar la nomenclatura contable a partir de un modelo
- Generar la nomenclatura contable a partir de un modelo. Deberá seleccionar una compañía, el modelo a utilizar, el número de digitos a usar en la nomenclatura, la moneda para crear los diarios.
-
-
- automatic
+
+ open
diff --git a/addons/l10n_hn/l10n_hn_base.xml b/addons/l10n_hn/l10n_hn_base.xml
index a12d93db034..b8b85c3f2a7 100644
--- a/addons/l10n_hn/l10n_hn_base.xml
+++ b/addons/l10n_hn/l10n_hn_base.xml
@@ -2,13 +2,7 @@
-
- Generar la nomenclatura contable a partir de un modelo
- Generar la nomenclatura contable a partir de un modelo. Deberá seleccionar una compañía, el modelo a utilizar, el número de digitos a usar en la nomenclatura, la moneda para crear los diarios.
-
-
+
open
diff --git a/addons/l10n_in/l10n_in_wizard.xml b/addons/l10n_in/l10n_in_wizard.xml
index 20d35aa09a2..bcbd9a43f27 100644
--- a/addons/l10n_in/l10n_in_wizard.xml
+++ b/addons/l10n_in/l10n_in_wizard.xml
@@ -1,13 +1,8 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py
index 085a2679db2..b7c3926d727 100644
--- a/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py
+++ b/addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py
@@ -65,7 +65,7 @@ class payroll_advice(osv.osv):
('draft', 'Draft'),
('confirm', 'Confirmed'),
('cancel', 'Cancelled'),
- ], 'State', select=True, readonly=True),
+ ], 'Status', select=True, readonly=True),
'number':fields.char('Reference', size=16, readonly=True),
'line_ids':fields.one2many('hr.payroll.advice.line', 'advice_id', 'Employee Salary', states={'draft': [('readonly', False)]}, readonly=True),
'chaque_nos':fields.char('Cheque Numbers', size=256),
diff --git a/addons/l10n_in_hr_payroll/report/payment_advice_report.py b/addons/l10n_in_hr_payroll/report/payment_advice_report.py
index 838334913a5..a264368be94 100644
--- a/addons/l10n_in_hr_payroll/report/payment_advice_report.py
+++ b/addons/l10n_in_hr_payroll/report/payment_advice_report.py
@@ -38,7 +38,7 @@ class payment_advice_report(osv.osv):
('draft', 'Draft'),
('confirm', 'Confirmed'),
('cancel', 'Cancelled'),
- ], 'State', select=True, readonly=True),
+ ], 'Status', select=True, readonly=True),
'employee_id': fields.many2one('hr.employee', 'Employee', readonly=True),
'nbr': fields.integer('# Payment Lines', readonly=True),
'number':fields.char('Number', size=16, readonly=True),
diff --git a/addons/l10n_in_hr_payroll/report/payslip_report.py b/addons/l10n_in_hr_payroll/report/payslip_report.py
index b688af4329b..a78d9850667 100644
--- a/addons/l10n_in_hr_payroll/report/payslip_report.py
+++ b/addons/l10n_in_hr_payroll/report/payslip_report.py
@@ -39,7 +39,7 @@ class payslip_report(osv.osv):
('draft', 'Draft'),
('done', 'Done'),
('cancel', 'Rejected'),
- ], 'State', readonly=True),
+ ], 'Status', readonly=True),
'employee_id': fields.many2one('hr.employee', 'Employee', readonly=True),
'nbr': fields.integer('# Payslip lines', readonly=True),
'number': fields.char('Number', size=16, readonly=True),
diff --git a/addons/l10n_it/l10n_chart_it_generic.xml b/addons/l10n_it/l10n_chart_it_generic.xml
index 52269c71962..792bd21a2fc 100644
--- a/addons/l10n_it/l10n_chart_it_generic.xml
+++ b/addons/l10n_it/l10n_chart_it_generic.xml
@@ -1,12 +1,7 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_lu/l10n_lu_wizard.xml b/addons/l10n_lu/l10n_lu_wizard.xml
index 32351428bb6..6919eb199b6 100644
--- a/addons/l10n_lu/l10n_lu_wizard.xml
+++ b/addons/l10n_lu/l10n_lu_wizard.xml
@@ -1,10 +1,8 @@
-
-
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_ma/l10n_ma_wizard.xml b/addons/l10n_ma/l10n_ma_wizard.xml
index 428a17e081a..6919eb199b6 100644
--- a/addons/l10n_ma/l10n_ma_wizard.xml
+++ b/addons/l10n_ma/l10n_ma_wizard.xml
@@ -1,10 +1,8 @@
-
-
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_mx/i18n/es_MX.po b/addons/l10n_mx/i18n/es_MX.po
index e9822e23498..305ec408af2 100644
--- a/addons/l10n_mx/i18n/es_MX.po
+++ b/addons/l10n_mx/i18n/es_MX.po
@@ -1,28 +1,41 @@
-# Spanish translation for openobject-addons
-# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
+# Spanish (Mexico) translation for openobject-addons
+# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
-# FIRST AUTHOR , 2011.
+# FIRST AUTHOR , 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME \n"
-"POT-Creation-Date: 2011-01-07 06:07+0000\n"
-"PO-Revision-Date: 2011-02-15 15:37+0000\n"
+"POT-Creation-Date: 2011-12-23 09:56+0000\n"
+"PO-Revision-Date: 2012-10-22 22:58+0000\n"
"Last-Translator: FULL NAME \n"
-"Language-Team: Spanish \n"
+"Language-Team: Spanish (Mexico) \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2011-09-05 05:58+0000\n"
-"X-Generator: Launchpad (build 13830)\n"
+"X-Launchpad-Export-Date: 2012-10-24 04:55+0000\n"
+"X-Generator: Launchpad (build 16179)\n"
#. module: l10n_mx
-#: model:ir.module.module,description:l10n_mx.module_meta_information
-msgid ""
-"This is the module to manage the accounting chart for Mexico in Open ERP."
-msgstr ""
-"Este es el módulo para gestionar el plan contable para Méjico en Open ERP."
+#: model:account.account.type,name:l10n_mx.account_type_receivable
+msgid "Receivable"
+msgstr "Por cobrar"
+
+#. module: l10n_mx
+#: model:account.account.type,name:l10n_mx.account_type_equity
+msgid "Equity"
+msgstr "Capital"
+
+#. module: l10n_mx
+#: model:account.account.type,name:l10n_mx.account_type_tax
+msgid "Tax"
+msgstr "Impuesto"
+
+#. module: l10n_mx
+#: model:account.account.type,name:l10n_mx.account_type_cash
+msgid "Cash"
+msgstr "Efectivo"
#. module: l10n_mx
#: model:ir.actions.todo,note:l10n_mx.config_call_account_template_mx_chart
@@ -35,16 +48,28 @@ msgid ""
"Management/Configuration/Financial Accounting/Financial Accounts/Generate "
"Chart of Accounts from a Chart Template."
msgstr ""
-"Generar Plan Contable a partir de una plantilla de Plan. Se le preguntará "
-"por el nombre de la compañía, la plantilla de plan contable a seguir, el no. "
-"de dígitos para generar el código de sus cuentas y la cuenta bancaria, la "
-"divisa para crear Diarios. Por lo tanto, la copia exacta de la plantilla de "
-"plan contable será generada.\n"
-"Este es el mismo asistente que se ejecuta desde Gestión "
-"Financiera/Configuración/Contabilidad Financiera/Contabilidad "
-"Financiera/Generar Plan Contable a partir de una Plantilla de Plan."
#. module: l10n_mx
-#: model:ir.module.module,shortdesc:l10n_mx.module_meta_information
-msgid "Mexico - Chart of Account"
-msgstr "Méjico - Plan Contable"
+#: model:account.account.type,name:l10n_mx.account_type_payable
+msgid "Payable"
+msgstr "A pagar"
+
+#. module: l10n_mx
+#: model:account.account.type,name:l10n_mx.account_type_asset
+msgid "Asset"
+msgstr "Activo"
+
+#. module: l10n_mx
+#: model:account.account.type,name:l10n_mx.account_type_income
+msgid "Income"
+msgstr "Ingreso"
+
+#. module: l10n_mx
+#: model:account.account.type,name:l10n_mx.account_type_expense
+msgid "Expense"
+msgstr "Gasto"
+
+#. module: l10n_mx
+#: model:account.account.type,name:l10n_mx.account_type_view
+msgid "View"
+msgstr "Vista"
diff --git a/addons/l10n_mx/l10n_chart_mx_wizard.xml b/addons/l10n_mx/l10n_chart_mx_wizard.xml
index aeea1f3cd69..6919eb199b6 100644
--- a/addons/l10n_mx/l10n_chart_mx_wizard.xml
+++ b/addons/l10n_mx/l10n_chart_mx_wizard.xml
@@ -1,12 +1,7 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_nl/l10n_nl_wizard.xml b/addons/l10n_nl/l10n_nl_wizard.xml
index bcdd183b12c..6919eb199b6 100644
--- a/addons/l10n_nl/l10n_nl_wizard.xml
+++ b/addons/l10n_nl/l10n_nl_wizard.xml
@@ -1,19 +1,8 @@
-
-
- Genereer Grootboekrekingschema vanuit het Nederlandse Grootboek Template
- Na installatie van deze module word de configuratie wizard voor "Accounting" aangeroepen.
-* U krijgt een lijst met grootboektemplates aangeboden waarin zich ook het Nederlandse grootboekschema bevind.
-* Als de configuratie wizard start, wordt u gevraagd om de naam van uw bedrijf in te voeren, welke grootboekschema te installeren, uit hoeveel cijfers een grootboekrekening mag bestaan, het rekeningnummer van uw bank en de currency om Journalen te creeren.
-
-Let op!! -> De template van het Nederlandse rekeningschema is opgebouwd uit 4 cijfers. Dit is het minimale aantal welk u moet invullen, u mag het aantal verhogen. De extra cijfers worden dan achter het rekeningnummer aangevult met "nullen"
-
-* Dit is dezelfe configuratie wizard welke aangeroepen kan worden via Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_pe/l10n_pe_wizard.xml b/addons/l10n_pe/l10n_pe_wizard.xml
index e1189fbcc2d..3c94d3db7a8 100644
--- a/addons/l10n_pe/l10n_pe_wizard.xml
+++ b/addons/l10n_pe/l10n_pe_wizard.xml
@@ -2,12 +2,8 @@
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_pl/l10n_chart_pl_wizard.xml b/addons/l10n_pl/l10n_chart_pl_wizard.xml
index aded9e3b2a3..6919eb199b6 100644
--- a/addons/l10n_pl/l10n_chart_pl_wizard.xml
+++ b/addons/l10n_pl/l10n_chart_pl_wizard.xml
@@ -1,12 +1,7 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_ro/l10n_chart_ro_wizard.xml b/addons/l10n_ro/l10n_chart_ro_wizard.xml
index bafb7b26f82..6919eb199b6 100644
--- a/addons/l10n_ro/l10n_chart_ro_wizard.xml
+++ b/addons/l10n_ro/l10n_chart_ro_wizard.xml
@@ -1,12 +1,7 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_syscohada/l10n_syscohada_wizard.xml b/addons/l10n_syscohada/l10n_syscohada_wizard.xml
index 7174ef438b7..792bd21a2fc 100644
--- a/addons/l10n_syscohada/l10n_syscohada_wizard.xml
+++ b/addons/l10n_syscohada/l10n_syscohada_wizard.xml
@@ -1,11 +1,7 @@
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a SYSCOHADA Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_th/account_data.xml b/addons/l10n_th/account_data.xml
index d5885d4b92b..d730caa8404 100644
--- a/addons/l10n_th/account_data.xml
+++ b/addons/l10n_th/account_data.xml
@@ -643,14 +643,9 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
-This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
-
+
+ open
+
diff --git a/addons/l10n_tr/l10n_tr_wizard.xml b/addons/l10n_tr/l10n_tr_wizard.xml
index e7ae8dc2d82..59668347bed 100644
--- a/addons/l10n_tr/l10n_tr_wizard.xml
+++ b/addons/l10n_tr/l10n_tr_wizard.xml
@@ -1,16 +1,8 @@
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be
-asked to pass the name of the company, the chart template to follow, the no. of digits to generate
-the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of
-chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial
-Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_uk/l10n_uk_wizard.xml b/addons/l10n_uk/l10n_uk_wizard.xml
index cb947163ab3..1b8d528c0e8 100644
--- a/addons/l10n_uk/l10n_uk_wizard.xml
+++ b/addons/l10n_uk/l10n_uk_wizard.xml
@@ -1,41 +1,7 @@
-
-
- Generate Chart of Accounts from a Chart Template
- wizard.multi.charts.accounts
-
-
-
-
-
-
-
-
-
- Generate UK Chart of Accounts from a Chart Template
- ir.actions.act_window
- wizard.multi.charts.accounts
-
- form
- form
- new
-
-
-
-
- 10
- once
+
open
diff --git a/addons/l10n_us/l10n_us_wizard.xml b/addons/l10n_us/l10n_us_wizard.xml
index 8c306f155fe..6919eb199b6 100644
--- a/addons/l10n_us/l10n_us_wizard.xml
+++ b/addons/l10n_us/l10n_us_wizard.xml
@@ -1,12 +1,7 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
- This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
- automatic
+
+ open
diff --git a/addons/l10n_uy/l10n_uy_wizard.xml b/addons/l10n_uy/l10n_uy_wizard.xml
index 6543664b2b6..49cbeca89f9 100644
--- a/addons/l10n_uy/l10n_uy_wizard.xml
+++ b/addons/l10n_uy/l10n_uy_wizard.xml
@@ -1,9 +1,8 @@
-
-
- automatic
+
+ open
diff --git a/addons/l10n_ve/l10n_chart_ve_wizard.xml b/addons/l10n_ve/l10n_chart_ve_wizard.xml
index 823fa833313..6919eb199b6 100644
--- a/addons/l10n_ve/l10n_chart_ve_wizard.xml
+++ b/addons/l10n_ve/l10n_chart_ve_wizard.xml
@@ -1,15 +1,7 @@
-
-
- Generate Chart of Accounts from a Chart Template
- Generate Chart of Accounts from a Chart Template. You will be asked to pass the name of the company, the chart template to follow, the no. of digits to generate the code for your accounts and Bank account, currency to create Journals. Thus,the pure copy of chart Template is generated.
-This is the same wizard that runs from Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-Genere el Plan de cuentas de una Plantilla de Carta. Le pedirán pasar el nombre de la compania, la plantilla de carta para seguir, el no. de digitos para generar el codigo para sus cuentas y cuenta Bancaria, dinero para crear Diarios. Asi, la copia pura de la carta la Plantilla es generada.
-Esto es el mismo wizard que corre de la Financial Management/Configuration/Financial Accounting/Financial Accounts/Generate Chart of Accounts from a Chart Template.
-
-
- automatic
+
+ open
diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py
index 27565bc4102..0c32ba27d6f 100644
--- a/addons/mail/__openerp__.py
+++ b/addons/mail/__openerp__.py
@@ -84,7 +84,6 @@ Main Features
'css': [
'static/src/css/mail.css',
'static/src/css/mail_group.css',
- 'static/src/css/mail_compose_message.css',
],
'js': [
'static/lib/jquery.expander/jquery.expander.js',
diff --git a/addons/mail/data/mail_demo.xml b/addons/mail/data/mail_demo.xml
index 7bda32c002b..bda93fc485d 100644
--- a/addons/mail/data/mail_demo.xml
+++ b/addons/mail/data/mail_demo.xml
@@ -8,6 +8,7 @@
Your monthly meal vouchers arrived. You can get them at Christine's office.
This month you also get 250 EUR of eco-vouchers if you have been in the company for more than a year.
comment
+
+
+
-
-
diff --git a/addons/mail/i18n/hr.po b/addons/mail/i18n/hr.po
index 36de7acc0b1..4535063726e 100644
--- a/addons/mail/i18n/hr.po
+++ b/addons/mail/i18n/hr.po
@@ -8,45 +8,45 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME \n"
"POT-Creation-Date: 2012-02-09 00:36+0000\n"
-"PO-Revision-Date: 2011-12-22 17:13+0000\n"
-"Last-Translator: Tomislav Bosnjakovic \n"
+"PO-Revision-Date: 2012-10-28 19:42+0000\n"
+"Last-Translator: Marijan Rajic \n"
"Language-Team: Croatian
\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-10-19 05:36+0000\n"
-"X-Generator: Launchpad (build 16165)\n"
+"X-Launchpad-Export-Date: 2012-10-29 05:00+0000\n"
+"X-Generator: Launchpad (build 16194)\n"
#. module: mail
#: field:mail.compose.message,subtype:0 field:mail.message,subtype:0
#: field:mail.message.common,subtype:0
msgid "Message Type"
-msgstr ""
+msgstr "Tip poruke"
#. module: mail
#: help:mail.compose.message,auto_delete:0
msgid "Permanently delete emails after sending"
-msgstr ""
+msgstr "Trajno obrisati e-poštu nakon slanja"
#. module: mail
#: view:mail.message:0
msgid "Open Related Document"
-msgstr ""
+msgstr "Otvori povezani dokument"
#. module: mail
#: view:mail.message:0
msgid "Open Attachments"
-msgstr "Otvori privitke"
+msgstr "Otvori priloge"
#. module: mail
#: view:mail.message:0
msgid "Message Details"
-msgstr "Detalji poruke"
+msgstr "Detalji porukue"
#. module: mail
#: view:mail.thread:0
msgid "Communication History"
-msgstr ""
+msgstr "Povijest komunikacije"
#. module: mail
#: view:mail.message:0
@@ -57,73 +57,73 @@ msgstr "Grupiraj po..."
#: model:ir.actions.act_window,name:mail.action_email_compose_message_wizard
#: view:mail.compose.message:0
msgid "Compose Email"
-msgstr ""
+msgstr "Sastavi e-poštu"
#. module: mail
#: help:mail.compose.message,body_text:0 help:mail.message,body_text:0
#: help:mail.message.common,body_text:0
msgid "Plain-text version of the message"
-msgstr ""
+msgstr "Verzija poruke u običnom tekstu"
#. module: mail
#: view:mail.compose.message:0
msgid "Body"
-msgstr ""
+msgstr "Sadržaj"
#. module: mail
#: help:mail.compose.message,email_to:0 help:mail.message,email_to:0
#: help:mail.message.common,email_to:0
msgid "Message recipients"
-msgstr ""
+msgstr "Primatelji poruke"
#. module: mail
#: field:mail.compose.message,body_text:0 field:mail.message,body_text:0
#: field:mail.message.common,body_text:0
msgid "Text Contents"
-msgstr ""
+msgstr "Sadržaj"
#. module: mail
#: view:mail.message:0 selection:mail.message,state:0
msgid "Received"
-msgstr ""
+msgstr "Primljeno"
#. module: mail
#: view:mail.message:0
msgid "Thread"
-msgstr ""
+msgstr "Nit"
#. module: mail
#: field:mail.message,mail_server_id:0
msgid "Outgoing mail server"
-msgstr ""
+msgstr "Izlazni poslužitelj e-pošte"
#. module: mail
#: selection:mail.message,state:0
msgid "Cancelled"
-msgstr ""
+msgstr "Otkazano"
#. module: mail
#: field:mail.compose.message,reply_to:0 field:mail.message,reply_to:0
#: field:mail.message.common,reply_to:0
msgid "Reply-To"
-msgstr ""
+msgstr "Odgovori na"
#. module: mail
#: help:mail.compose.message,body_html:0 help:mail.message,body_html:0
#: help:mail.message.common,body_html:0
msgid "Rich-text/HTML version of the message"
-msgstr ""
+msgstr "Verzija poruke u Rich-text/HTML formatu"
#. module: mail
#: field:mail.compose.message,auto_delete:0 field:mail.message,auto_delete:0
msgid "Auto Delete"
-msgstr ""
+msgstr "Auto brisanje"
#. module: mail
#: help:mail.compose.message,email_bcc:0 help:mail.message,email_bcc:0
#: help:mail.message.common,email_bcc:0
msgid "Blind carbon copy message recipients"
-msgstr ""
+msgstr "Primatelji skrivene kopije poruke"
#. module: mail
#: model:ir.model,name:mail.model_res_partner view:mail.message:0
@@ -134,13 +134,13 @@ msgstr "Partner"
#: field:mail.compose.message,subject:0 field:mail.message,subject:0
#: field:mail.message.common,subject:0
msgid "Subject"
-msgstr ""
+msgstr "Naslov"
#. module: mail
#: code:addons/mail/wizard/mail_compose_message.py:152
#, python-format
msgid "On %(date)s, "
-msgstr ""
+msgstr "Na %(date)s, "
#. module: mail
#: field:mail.compose.message,email_from:0 field:mail.message,email_from:0
@@ -151,32 +151,32 @@ msgstr "Od"
#. module: mail
#: view:mail.message:0
msgid "Email message"
-msgstr ""
+msgstr "Poruka e-pošte"
#. module: mail
#: view:mail.compose.message:0
msgid "Send"
-msgstr ""
+msgstr "Pošalji"
#. module: mail
#: view:mail.message:0
msgid "Failed"
-msgstr ""
+msgstr "Neuspjelo"
#. module: mail
#: view:mail.message:0 field:mail.message,state:0
msgid "State"
-msgstr ""
+msgstr "Stanje"
#. module: mail
#: view:mail.message:0
msgid "Reply"
-msgstr ""
+msgstr "Odgovor"
#. module: mail
#: view:mail.message:0 selection:mail.message,state:0
msgid "Sent"
-msgstr ""
+msgstr "Poslano"
#. module: mail
#: help:mail.compose.message,subtype:0 help:mail.message,subtype:0
@@ -185,39 +185,41 @@ msgid ""
"Type of message, usually 'html' or 'plain', used to select plaintext or rich "
"text contents accordingly"
msgstr ""
+"Tip poruke, obično 'html' ili 'običan tekst', koristi se za odabir običnog "
+"ili bogatije formatiranog teksta"
#. module: mail
#: view:mail.message:0
msgid "Recipients"
-msgstr ""
+msgstr "Primatelji"
#. module: mail
#: model:ir.model,name:mail.model_mail_compose_message
msgid "Email composition wizard"
-msgstr ""
+msgstr "Čarobnjak za sastavljanje e-pošte"
#. module: mail
#: field:mail.compose.message,res_id:0 field:mail.message,res_id:0
#: field:mail.message.common,res_id:0
msgid "Related Document ID"
-msgstr ""
+msgstr "Povezani ID dokumenta"
#. module: mail
#: view:mail.message:0
msgid "Advanced"
-msgstr ""
+msgstr "Napredno"
#. module: mail
#: code:addons/mail/wizard/mail_compose_message.py:157
#, python-format
msgid "Re:"
-msgstr ""
+msgstr "Re:"
#. module: mail
#: field:mail.compose.message,model:0 field:mail.message,model:0
#: field:mail.message.common,model:0
msgid "Related Document Model"
-msgstr ""
+msgstr "Povezani model dokumenta"
#. module: mail
#: view:mail.message:0
@@ -227,42 +229,42 @@ msgstr "Mjesec"
#. module: mail
#: view:mail.message:0
msgid "Email Search"
-msgstr "Pretraživanje e-mailova"
+msgstr "Pretraga e-pošte"
#. module: mail
#: help:mail.message,original:0
msgid "Original version of the message, as it was sent on the network"
-msgstr ""
+msgstr "Originalna varijanta poruke, kakva je poslana na mrežu"
#. module: mail
#: view:mail.message:0
msgid "Partner Name"
-msgstr "Naziv partnera"
+msgstr "Ime partnera"
#. module: mail
#: view:mail.message:0
msgid "Retry"
-msgstr ""
+msgstr "Pokušaj ponovo"
#. module: mail
#: view:mail.message:0 selection:mail.message,state:0
msgid "Outgoing"
-msgstr ""
+msgstr "Odlazno"
#. module: mail
#: view:mail.message:0
msgid "Send Now"
-msgstr ""
+msgstr "Pošalji odmah"
#. module: mail
#: field:mail.message,partner_id:0
msgid "Related partner"
-msgstr ""
+msgstr "Povezani partner"
#. module: mail
#: view:mail.message:0
msgid "User"
-msgstr ""
+msgstr "Korisnik"
#. module: mail
#: field:mail.compose.message,date:0 field:mail.message,date:0
@@ -273,19 +275,19 @@ msgstr "Datum"
#. module: mail
#: view:mail.message:0
msgid "Extended Filters..."
-msgstr ""
+msgstr "Prošireni filteri..."
#. module: mail
#: code:addons/mail/wizard/mail_compose_message.py:153
#, python-format
msgid "%(sender_name)s wrote:"
-msgstr ""
+msgstr "%(sender_name)s je napisao:"
#. module: mail
#: field:mail.compose.message,body_html:0 field:mail.message,body_html:0
#: field:mail.message.common,body_html:0
msgid "Rich-text Contents"
-msgstr ""
+msgstr "Formatirani tekst"
#. module: mail
#: field:mail.message,original:0
@@ -346,18 +348,18 @@ msgstr ""
#. module: mail
#: view:mail.message:0
msgid "Open"
-msgstr ""
+msgstr "Otvori"
#. module: mail
#: code:addons/mail/mail_thread.py:434
#, python-format
msgid "[OpenERP-Forward-Failed] %s"
-msgstr ""
+msgstr "[OpenERP-Forward-Failed] %s"
#. module: mail
#: field:mail.message,user_id:0
msgid "Related User"
-msgstr ""
+msgstr "Povezani korisnik"
#. module: mail
#: help:mail.compose.message,headers:0 help:mail.message,headers:0
@@ -366,11 +368,13 @@ msgid ""
"Full message headers, e.g. SMTP session headers (usually available on "
"inbound messages only)"
msgstr ""
+"Cjelovito zaglavlje poruke, npr. zaglavlje SMTP sesije (obično dostupno samo "
+"kod dolaznih poruka)"
#. module: mail
#: view:mail.message:0
msgid "Creation Month"
-msgstr ""
+msgstr "Mjesec kreiranja"
#. module: mail
#: field:mail.compose.message,email_to:0 field:mail.message,email_to:0
@@ -387,7 +391,7 @@ msgstr "Detalji"
#: model:ir.actions.act_window,name:mail.action_view_mailgate_thread
#: view:mail.thread:0
msgid "Email Threads"
-msgstr ""
+msgstr "Niti e-pošte"
#. module: mail
#: help:mail.compose.message,email_from:0 help:mail.message,email_from:0
@@ -396,28 +400,30 @@ msgid ""
"Message sender, taken from user preferences. If empty, this is not a mail "
"but a message."
msgstr ""
+"Pošiljatelj poruke, preuzet iz korisničkih preferenci. Ukoliko je prazno, "
+"biti će poruka a ne e-pošta."
#. module: mail
#: view:mail.message:0
msgid "Body (Plain)"
-msgstr ""
+msgstr "Tijelo (obično)"
#. module: mail
#: code:addons/mail/wizard/mail_compose_message.py:153
#, python-format
msgid "You"
-msgstr ""
+msgstr "Vi"
#. module: mail
#: help:mail.compose.message,message_id:0 help:mail.message,message_id:0
#: help:mail.message.common,message_id:0
msgid "Message unique identifier"
-msgstr ""
+msgstr "Jedinstveni identifikator poruke"
#. module: mail
#: view:mail.message:0
msgid "Body (Rich)"
-msgstr ""
+msgstr "Tijelo (formatirano)"
#. module: mail
#: code:addons/mail/mail_message.py:155
@@ -427,6 +433,9 @@ msgid ""
" Subject: %s \n"
"\t"
msgstr ""
+"%s napisano %s: \n"
+" Predmet: %s \n"
+"\t"
#. module: mail
#: model:ir.actions.act_window,name:mail.act_res_partner_emails
@@ -445,64 +454,64 @@ msgstr "Poruke"
#: field:mail.compose.message,headers:0 field:mail.message,headers:0
#: field:mail.message.common,headers:0
msgid "Message Headers"
-msgstr ""
+msgstr "Zaglavlja poruke"
#. module: mail
#: field:mail.compose.message,email_bcc:0 field:mail.message,email_bcc:0
#: field:mail.message.common,email_bcc:0
msgid "Bcc"
-msgstr "Bcc"
+msgstr "Skrivena kopija"
#. module: mail
#: model:ir.model,name:mail.model_mail_message_common
msgid "mail.message.common"
-msgstr ""
+msgstr "mail.message.common"
#. module: mail
#: help:mail.compose.message,references:0 help:mail.message,references:0
#: help:mail.message.common,references:0
msgid "Message references, such as identifiers of previous messages"
-msgstr ""
+msgstr "Reference poruke, poput identifikatora prethodnih poruka"
#. module: mail
#: constraint:res.partner:0
msgid "Error ! You cannot create recursive associated members."
-msgstr ""
+msgstr "Greška! Nije moguće kreirati pridružene članove rekurzivno."
#. module: mail
#: help:mail.compose.message,email_cc:0 help:mail.message,email_cc:0
#: help:mail.message.common,email_cc:0
msgid "Carbon copy message recipients"
-msgstr ""
+msgstr "Primatelji skrivene kopije"
#. module: mail
#: selection:mail.message,state:0
msgid "Delivery Failed"
-msgstr ""
+msgstr "Isporuka nije uspjela"
#. module: mail
#: model:ir.model,name:mail.model_mail_message
msgid "Email Message"
-msgstr ""
+msgstr "Poruka e-pošte"
#. module: mail
#: model:ir.model,name:mail.model_mail_thread view:mail.thread:0
msgid "Email Thread"
-msgstr ""
+msgstr "Nit e-pošte"
#. module: mail
#: field:mail.compose.message,filter_id:0
msgid "Filters"
-msgstr ""
+msgstr "Filteri"
#. module: mail
#: code:addons/mail/mail_thread.py:220
#, python-format
msgid "Mail attachment"
-msgstr ""
+msgstr "Prilog e-pošte"
#. module: mail
#: help:mail.compose.message,reply_to:0 help:mail.message,reply_to:0
#: help:mail.message.common,reply_to:0
msgid "Preferred response address for the message"
-msgstr ""
+msgstr "Željena povratna adresa za poruku"
diff --git a/addons/mail/i18n/hu.po b/addons/mail/i18n/hu.po
index 467485a589d..86e15e751b3 100644
--- a/addons/mail/i18n/hu.po
+++ b/addons/mail/i18n/hu.po
@@ -7,21 +7,20 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-02-09 00:36+0000\n"
-"PO-Revision-Date: 2011-01-21 12:01+0000\n"
-"Last-Translator: NOVOTRADE RENDSZERHÁZ ( novotrade.hu ) "
-"\n"
+"PO-Revision-Date: 2012-10-26 12:29+0000\n"
+"Last-Translator: Herczeg Péter \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-10-19 05:36+0000\n"
-"X-Generator: Launchpad (build 16165)\n"
+"X-Launchpad-Export-Date: 2012-10-27 04:59+0000\n"
+"X-Generator: Launchpad (build 16194)\n"
#. module: mail
#: field:mail.compose.message,subtype:0 field:mail.message,subtype:0
#: field:mail.message.common,subtype:0
msgid "Message Type"
-msgstr ""
+msgstr "Üzenettípus"
#. module: mail
#: help:mail.compose.message,auto_delete:0
@@ -31,7 +30,7 @@ msgstr ""
#. module: mail
#: view:mail.message:0
msgid "Open Related Document"
-msgstr ""
+msgstr "Kapcsolódó dokumentum megnyitása"
#. module: mail
#: view:mail.message:0
@@ -57,7 +56,7 @@ msgstr "Csoportosítás..."
#: model:ir.actions.act_window,name:mail.action_email_compose_message_wizard
#: view:mail.compose.message:0
msgid "Compose Email"
-msgstr ""
+msgstr "Email írás"
#. module: mail
#: help:mail.compose.message,body_text:0 help:mail.message,body_text:0
@@ -68,7 +67,7 @@ msgstr ""
#. module: mail
#: view:mail.compose.message:0
msgid "Body"
-msgstr ""
+msgstr "Levéltörzs"
#. module: mail
#: help:mail.compose.message,email_to:0 help:mail.message,email_to:0
@@ -85,7 +84,7 @@ msgstr ""
#. module: mail
#: view:mail.message:0 selection:mail.message,state:0
msgid "Received"
-msgstr ""
+msgstr "Fogadott"
#. module: mail
#: view:mail.message:0
@@ -95,18 +94,18 @@ msgstr ""
#. module: mail
#: field:mail.message,mail_server_id:0
msgid "Outgoing mail server"
-msgstr ""
+msgstr "Kimenő levelező szerver"
#. module: mail
#: selection:mail.message,state:0
msgid "Cancelled"
-msgstr ""
+msgstr "Megszakítva"
#. module: mail
#: field:mail.compose.message,reply_to:0 field:mail.message,reply_to:0
#: field:mail.message.common,reply_to:0
msgid "Reply-To"
-msgstr ""
+msgstr "Válaszcím"
#. module: mail
#: help:mail.compose.message,body_html:0 help:mail.message,body_html:0
@@ -117,7 +116,7 @@ msgstr ""
#. module: mail
#: field:mail.compose.message,auto_delete:0 field:mail.message,auto_delete:0
msgid "Auto Delete"
-msgstr ""
+msgstr "Automatikus törlés"
#. module: mail
#: help:mail.compose.message,email_bcc:0 help:mail.message,email_bcc:0
@@ -146,37 +145,37 @@ msgstr ""
#: field:mail.compose.message,email_from:0 field:mail.message,email_from:0
#: field:mail.message.common,email_from:0
msgid "From"
-msgstr ""
+msgstr "Feladó"
#. module: mail
#: view:mail.message:0
msgid "Email message"
-msgstr ""
+msgstr "Email üzenet"
#. module: mail
#: view:mail.compose.message:0
msgid "Send"
-msgstr ""
+msgstr "Küldés"
#. module: mail
#: view:mail.message:0
msgid "Failed"
-msgstr ""
+msgstr "Sikertelen"
#. module: mail
#: view:mail.message:0 field:mail.message,state:0
msgid "State"
-msgstr ""
+msgstr "Állapot"
#. module: mail
#: view:mail.message:0
msgid "Reply"
-msgstr ""
+msgstr "Válasz"
#. module: mail
#: view:mail.message:0 selection:mail.message,state:0
msgid "Sent"
-msgstr ""
+msgstr "Elküldött"
#. module: mail
#: help:mail.compose.message,subtype:0 help:mail.message,subtype:0
@@ -189,7 +188,7 @@ msgstr ""
#. module: mail
#: view:mail.message:0
msgid "Recipients"
-msgstr ""
+msgstr "Címzettek"
#. module: mail
#: model:ir.model,name:mail.model_mail_compose_message
@@ -205,7 +204,7 @@ msgstr ""
#. module: mail
#: view:mail.message:0
msgid "Advanced"
-msgstr ""
+msgstr "Speciális"
#. module: mail
#: code:addons/mail/wizard/mail_compose_message.py:157
@@ -242,17 +241,17 @@ msgstr "Partner neve"
#. module: mail
#: view:mail.message:0
msgid "Retry"
-msgstr ""
+msgstr "Újra"
#. module: mail
#: view:mail.message:0 selection:mail.message,state:0
msgid "Outgoing"
-msgstr ""
+msgstr "Kimenő"
#. module: mail
#: view:mail.message:0
msgid "Send Now"
-msgstr ""
+msgstr "Küldés most"
#. module: mail
#: field:mail.message,partner_id:0
@@ -262,7 +261,7 @@ msgstr ""
#. module: mail
#: view:mail.message:0
msgid "User"
-msgstr ""
+msgstr "Felhasználó"
#. module: mail
#: field:mail.compose.message,date:0 field:mail.message,date:0
@@ -273,7 +272,7 @@ msgstr "Dátum"
#. module: mail
#: view:mail.message:0
msgid "Extended Filters..."
-msgstr ""
+msgstr "Kiterjesztett szűrők…"
#. module: mail
#: code:addons/mail/wizard/mail_compose_message.py:153
@@ -290,7 +289,7 @@ msgstr ""
#. module: mail
#: field:mail.message,original:0
msgid "Original"
-msgstr ""
+msgstr "Eredeti"
#. module: mail
#: code:addons/mail/mail_thread.py:247 view:res.partner:0
@@ -341,12 +340,12 @@ msgstr ""
#. module: mail
#: view:mail.compose.message:0 view:mail.message:0
msgid "Cancel"
-msgstr ""
+msgstr "Mégsem"
#. module: mail
#: view:mail.message:0
msgid "Open"
-msgstr ""
+msgstr "Megnyitás"
#. module: mail
#: code:addons/mail/mail_thread.py:434
@@ -357,7 +356,7 @@ msgstr ""
#. module: mail
#: field:mail.message,user_id:0
msgid "Related User"
-msgstr ""
+msgstr "Kapcsolódó felhasználó"
#. module: mail
#: help:mail.compose.message,headers:0 help:mail.message,headers:0
diff --git a/addons/mail/i18n/ru.po b/addons/mail/i18n/ru.po
index d85c3660ed3..61b01489c39 100644
--- a/addons/mail/i18n/ru.po
+++ b/addons/mail/i18n/ru.po
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME \n"
"POT-Creation-Date: 2012-02-09 00:36+0000\n"
-"PO-Revision-Date: 2011-01-25 08:25+0000\n"
-"Last-Translator: Alexey Y. Fedotov \n"
+"PO-Revision-Date: 2012-10-26 17:02+0000\n"
+"Last-Translator: Chertykov Denis \n"
"Language-Team: Russian \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2012-10-19 05:36+0000\n"
-"X-Generator: Launchpad (build 16165)\n"
+"X-Launchpad-Export-Date: 2012-10-27 04:59+0000\n"
+"X-Generator: Launchpad (build 16194)\n"
#. module: mail
#: field:mail.compose.message,subtype:0 field:mail.message,subtype:0
@@ -26,12 +26,12 @@ msgstr ""
#. module: mail
#: help:mail.compose.message,auto_delete:0
msgid "Permanently delete emails after sending"
-msgstr ""
+msgstr "Не сохранять сообщения после отправки"
#. module: mail
#: view:mail.message:0
msgid "Open Related Document"
-msgstr ""
+msgstr "Открыть связанный документ"
#. module: mail
#: view:mail.message:0
@@ -46,7 +46,7 @@ msgstr "Подробности сообщения"
#. module: mail
#: view:mail.thread:0
msgid "Communication History"
-msgstr ""
+msgstr "История общения"
#. module: mail
#: view:mail.message:0
@@ -57,24 +57,24 @@ msgstr "Объеденить по..."
#: model:ir.actions.act_window,name:mail.action_email_compose_message_wizard
#: view:mail.compose.message:0
msgid "Compose Email"
-msgstr ""
+msgstr "Написать письмо"
#. module: mail
#: help:mail.compose.message,body_text:0 help:mail.message,body_text:0
#: help:mail.message.common,body_text:0
msgid "Plain-text version of the message"
-msgstr ""
+msgstr "Простая текстовая версия сообщения"
#. module: mail
#: view:mail.compose.message:0
msgid "Body"
-msgstr ""
+msgstr "Содержимое"
#. module: mail
#: help:mail.compose.message,email_to:0 help:mail.message,email_to:0
#: help:mail.message.common,email_to:0
msgid "Message recipients"
-msgstr ""
+msgstr "Получатели сообщения"
#. module: mail
#: field:mail.compose.message,body_text:0 field:mail.message,body_text:0
@@ -85,7 +85,7 @@ msgstr ""
#. module: mail
#: view:mail.message:0 selection:mail.message,state:0
msgid "Received"
-msgstr ""
+msgstr "Получено"
#. module: mail
#: view:mail.message:0
@@ -95,40 +95,40 @@ msgstr "Цепочка"
#. module: mail
#: field:mail.message,mail_server_id:0
msgid "Outgoing mail server"
-msgstr ""
+msgstr "Сервер исходящей почты"
#. module: mail
#: selection:mail.message,state:0
msgid "Cancelled"
-msgstr ""
+msgstr "Отменено"
#. module: mail
#: field:mail.compose.message,reply_to:0 field:mail.message,reply_to:0
#: field:mail.message.common,reply_to:0
msgid "Reply-To"
-msgstr ""
+msgstr "Адрес ответа"
#. module: mail
#: help:mail.compose.message,body_html:0 help:mail.message,body_html:0
#: help:mail.message.common,body_html:0
msgid "Rich-text/HTML version of the message"
-msgstr ""
+msgstr "Форматный текст/HTML версия сообщения"
#. module: mail
#: field:mail.compose.message,auto_delete:0 field:mail.message,auto_delete:0
msgid "Auto Delete"
-msgstr ""
+msgstr "Авто удаление"
#. module: mail
#: help:mail.compose.message,email_bcc:0 help:mail.message,email_bcc:0
#: help:mail.message.common,email_bcc:0
msgid "Blind carbon copy message recipients"
-msgstr ""
+msgstr "Получатели скрытой копии сообщения"
#. module: mail
#: model:ir.model,name:mail.model_res_partner view:mail.message:0
msgid "Partner"
-msgstr "Контрагент"
+msgstr "Партнёр"
#. module: mail
#: field:mail.compose.message,subject:0 field:mail.message,subject:0
@@ -140,7 +140,7 @@ msgstr "Тема"
#: code:addons/mail/wizard/mail_compose_message.py:152
#, python-format
msgid "On %(date)s, "
-msgstr ""
+msgstr "За %(date)s, "
#. module: mail
#: field:mail.compose.message,email_from:0 field:mail.message,email_from:0
@@ -151,32 +151,32 @@ msgstr "От"
#. module: mail
#: view:mail.message:0
msgid "Email message"
-msgstr ""
+msgstr "Сообщение эл. почты"
#. module: mail
#: view:mail.compose.message:0
msgid "Send"
-msgstr ""
+msgstr "Отправить"
#. module: mail
#: view:mail.message:0
msgid "Failed"
-msgstr ""
+msgstr "Ошибка"
#. module: mail
#: view:mail.message:0 field:mail.message,state:0
msgid "State"
-msgstr ""
+msgstr "Состояние"
#. module: mail
#: view:mail.message:0
msgid "Reply"
-msgstr ""
+msgstr "Ответить"
#. module: mail
#: view:mail.message:0 selection:mail.message,state:0
msgid "Sent"
-msgstr ""
+msgstr "Отправлено"
#. module: mail
#: help:mail.compose.message,subtype:0 help:mail.message,subtype:0
@@ -185,11 +185,13 @@ msgid ""
"Type of message, usually 'html' or 'plain', used to select plaintext or rich "
"text contents accordingly"
msgstr ""
+"Тип сообщения, обычно 'html' или 'простой', используемый для выбора "
+"содержимого соответственно простого текста или форматного текста"
#. module: mail
#: view:mail.message:0
msgid "Recipients"
-msgstr ""
+msgstr "Получатели"
#. module: mail
#: model:ir.model,name:mail.model_mail_compose_message
@@ -200,18 +202,18 @@ msgstr ""
#: field:mail.compose.message,res_id:0 field:mail.message,res_id:0
#: field:mail.message.common,res_id:0
msgid "Related Document ID"
-msgstr ""
+msgstr "ID связанного документа"
#. module: mail
#: view:mail.message:0
msgid "Advanced"
-msgstr ""
+msgstr "Дополнительно"
#. module: mail
#: code:addons/mail/wizard/mail_compose_message.py:157
#, python-format
msgid "Re:"
-msgstr ""
+msgstr "Ответ:"
#. module: mail
#: field:mail.compose.message,model:0 field:mail.message,model:0
@@ -232,37 +234,37 @@ msgstr "Искать эл. почту"
#. module: mail
#: help:mail.message,original:0
msgid "Original version of the message, as it was sent on the network"
-msgstr ""
+msgstr "Оригинальная версия сообщения, как было отправлено в сеть"
#. module: mail
#: view:mail.message:0
msgid "Partner Name"
-msgstr "Имя партнера"
+msgstr "Название партнера"
#. module: mail
#: view:mail.message:0
msgid "Retry"
-msgstr ""
+msgstr "Повторить"
#. module: mail
#: view:mail.message:0 selection:mail.message,state:0
msgid "Outgoing"
-msgstr ""
+msgstr "Исходящее"
#. module: mail
#: view:mail.message:0
msgid "Send Now"
-msgstr ""
+msgstr "Отправить сейчас"
#. module: mail
#: field:mail.message,partner_id:0
msgid "Related partner"
-msgstr ""
+msgstr "Связанный партнер"
#. module: mail
#: view:mail.message:0
msgid "User"
-msgstr ""
+msgstr "Пользователь"
#. module: mail
#: field:mail.compose.message,date:0 field:mail.message,date:0
@@ -273,13 +275,13 @@ msgstr "Дата"
#. module: mail
#: view:mail.message:0
msgid "Extended Filters..."
-msgstr ""
+msgstr "Расширенные фильтры..."
#. module: mail
#: code:addons/mail/wizard/mail_compose_message.py:153
#, python-format
msgid "%(sender_name)s wrote:"
-msgstr ""
+msgstr "%(sender_name)s написал:"
#. module: mail
#: field:mail.compose.message,body_html:0 field:mail.message,body_html:0
@@ -290,7 +292,7 @@ msgstr ""
#. module: mail
#: field:mail.message,original:0
msgid "Original"
-msgstr ""
+msgstr "Оригинал"
#. module: mail
#: code:addons/mail/mail_thread.py:247 view:res.partner:0
@@ -302,13 +304,13 @@ msgstr "История"
#: field:mail.compose.message,message_id:0 field:mail.message,message_id:0
#: field:mail.message.common,message_id:0
msgid "Message-Id"
-msgstr ""
+msgstr "ID сообщения"
#. module: mail
#: view:mail.compose.message:0 field:mail.compose.message,attachment_ids:0
#: view:mail.message:0 field:mail.message,attachment_ids:0
msgid "Attachments"
-msgstr "Прикрепленные файлы"
+msgstr "Вложения"
#. module: mail
#: field:mail.compose.message,email_cc:0 field:mail.message,email_cc:0
@@ -325,7 +327,7 @@ msgstr " на "
#. module: mail
#: help:mail.message,auto_delete:0
msgid "Permanently delete this email after sending it, to save space"
-msgstr ""
+msgstr "Навсегда удалить это письмо после отправки, для экономии места"
#. module: mail
#: field:mail.compose.message,references:0 field:mail.message,references:0
@@ -341,18 +343,18 @@ msgstr "Вывести текст"
#. module: mail
#: view:mail.compose.message:0 view:mail.message:0
msgid "Cancel"
-msgstr ""
+msgstr "Отмена"
#. module: mail
#: view:mail.message:0
msgid "Open"
-msgstr ""
+msgstr "Открыть"
#. module: mail
#: code:addons/mail/mail_thread.py:434
#, python-format
msgid "[OpenERP-Forward-Failed] %s"
-msgstr ""
+msgstr "[OpenERP-Forward-Failed] %s"
#. module: mail
#: field:mail.message,user_id:0
@@ -366,11 +368,13 @@ msgid ""
"Full message headers, e.g. SMTP session headers (usually available on "
"inbound messages only)"
msgstr ""
+"Полные заголовки сообщения, т.е. заголовки сессии SMTP (обычно доступные "
+"только во входящих сообщениях)"
#. module: mail
#: view:mail.message:0
msgid "Creation Month"
-msgstr ""
+msgstr "Месяц создания"
#. module: mail
#: field:mail.compose.message,email_to:0 field:mail.message,email_to:0
@@ -387,7 +391,7 @@ msgstr "Подробности"
#: model:ir.actions.act_window,name:mail.action_view_mailgate_thread
#: view:mail.thread:0
msgid "Email Threads"
-msgstr ""
+msgstr "Цепочки эл.писем"
#. module: mail
#: help:mail.compose.message,email_from:0 help:mail.message,email_from:0
@@ -396,28 +400,30 @@ msgid ""
"Message sender, taken from user preferences. If empty, this is not a mail "
"but a message."
msgstr ""
+"Отправитель сообщения, взятый из параметров пользователя. Если пусто, то это "
+"не почта, а сообщение."
#. module: mail
#: view:mail.message:0
msgid "Body (Plain)"
-msgstr ""
+msgstr "Текст (простой)"
#. module: mail
#: code:addons/mail/wizard/mail_compose_message.py:153
#, python-format
msgid "You"
-msgstr ""
+msgstr "Вы"
#. module: mail
#: help:mail.compose.message,message_id:0 help:mail.message,message_id:0
#: help:mail.message.common,message_id:0
msgid "Message unique identifier"
-msgstr ""
+msgstr "Уникальный идентификатор сообщения"
#. module: mail
#: view:mail.message:0
msgid "Body (Rich)"
-msgstr ""
+msgstr "Текст (форматный)"
#. module: mail
#: code:addons/mail/mail_message.py:155
@@ -427,6 +433,9 @@ msgid ""
" Subject: %s \n"
"\t"
msgstr ""
+"%s написал на %s: \n"
+" Тема: %s \n"
+"\t"
#. module: mail
#: model:ir.actions.act_window,name:mail.act_res_partner_emails
@@ -456,53 +465,74 @@ msgstr "Скрытая копия"
#. module: mail
#: model:ir.model,name:mail.model_mail_message_common
msgid "mail.message.common"
-msgstr ""
+msgstr "mail.message.common"
#. module: mail
#: help:mail.compose.message,references:0 help:mail.message,references:0
#: help:mail.message.common,references:0
msgid "Message references, such as identifiers of previous messages"
-msgstr ""
+msgstr "Ссылки сообщения, такие как идентификаторы предыдущих сообщений"
#. module: mail
#: constraint:res.partner:0
msgid "Error ! You cannot create recursive associated members."
-msgstr ""
+msgstr "Ошибка! Вы не можете создавать рекурсивные ссылки на участников."
#. module: mail
#: help:mail.compose.message,email_cc:0 help:mail.message,email_cc:0
#: help:mail.message.common,email_cc:0
msgid "Carbon copy message recipients"
-msgstr ""
+msgstr "Получатели скрытой копии сообщения"
#. module: mail
#: selection:mail.message,state:0
msgid "Delivery Failed"
-msgstr ""
+msgstr "Доставка не удалась"
#. module: mail
#: model:ir.model,name:mail.model_mail_message
msgid "Email Message"
-msgstr ""
+msgstr "Сообщение эл.почты"
#. module: mail
#: model:ir.model,name:mail.model_mail_thread view:mail.thread:0
msgid "Email Thread"
-msgstr ""
+msgstr "Цепочка эл.почты"
#. module: mail
#: field:mail.compose.message,filter_id:0
msgid "Filters"
-msgstr ""
+msgstr "Фильтры"
#. module: mail
#: code:addons/mail/mail_thread.py:220
#, python-format
msgid "Mail attachment"
-msgstr ""
+msgstr "Почтовое вложение"
#. module: mail
#: help:mail.compose.message,reply_to:0 help:mail.message,reply_to:0
#: help:mail.message.common,reply_to:0
msgid "Preferred response address for the message"
-msgstr ""
+msgstr "Предпочтительный адрес ответа для сообщения"
+
+#~ msgid "Message type"
+#~ msgstr "Тип сообщения"
+
+#~ msgid "Text contents"
+#~ msgstr "Текстовое содержимое"
+
+#~ msgid "Related Document model"
+#~ msgstr "Модель связанного документа"
+
+#~ msgid "E-mail composition wizard"
+#~ msgstr "Мастер создания электронной почты"
+
+#~ msgid "Rich-text contents"
+#~ msgstr "Форматное текстовое сообщение"
+
+#~ msgid "Related user"
+#~ msgstr "Связанный пользователь"
+
+#~ msgid "Message headers"
+#~ msgstr "Заголовки сообщения"
diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py
index 35603d2d720..e8cf2648f29 100644
--- a/addons/mail/mail_followers.py
+++ b/addons/mail/mail_followers.py
@@ -84,9 +84,16 @@ class mail_notification(osv.Model):
return False
def set_message_read(self, cr, uid, msg_ids, read=None, context=None):
- """ TDE note: add a comment, verify method calls, because js seems obfuscated. """
+ """ Set a message and its child messages as (un)read for uid.
+
+ :param bool read: read / unread
+ """
+ # TDE note: use child_of or front-end send correct values ?
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
- notif_ids = self.search(cr, uid, [('partner_id', '=', user_pid), ('message_id', 'in', msg_ids)], context=context)
+ notif_ids = self.search(cr, uid, [
+ ('partner_id', '=', user_pid),
+ ('message_id', 'in', msg_ids)
+ ], context=context)
# all message have notifications: already set them as (un)read
if len(notif_ids) == len(msg_ids):
@@ -130,7 +137,8 @@ class mail_notification(osv.Model):
def _notify(self, cr, uid, msg_id, context=None):
""" Send by email the notification depending on the user preferences """
- context = context or {}
+ if context is None:
+ context = {}
# mail_noemail (do not send email) or no partner_ids: do not send, return
if context.get('mail_noemail'):
return True
@@ -140,9 +148,15 @@ class mail_notification(osv.Model):
if not notify_partner_ids:
return True
+ # add the context in the email
+ # TDE FIXME: commented, to be improved in a future branch
+ # quote_context = self.pool.get('mail.message').message_quote_context(cr, uid, msg_id, context=context)
+
mail_mail = self.pool.get('mail.mail')
# add signature
body_html = msg.body
+ # if quote_context:
+ # body_html = tools.append_content_to_html(body_html, quote_context, plaintext=False)
signature = msg.author_id and msg.author_id.user_ids[0].signature or ''
if signature:
body_html = tools.append_content_to_html(body_html, signature)
diff --git a/addons/mail/mail_group_view.xml b/addons/mail/mail_group_view.xml
index 6268cd425b1..2028e8d1b18 100644
--- a/addons/mail/mail_group_view.xml
+++ b/addons/mail/mail_group_view.xml
@@ -82,9 +82,8 @@
-
+
diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index 8719d59bf04..bd0ade31b68 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -24,11 +24,17 @@ import tools
from email.header import decode_header
from openerp import SUPERUSER_ID
-from osv import osv, orm, fields
-from tools.translate import _
+from openerp.osv import osv, orm, fields
+from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
+try:
+ from mako.template import Template as MakoTemplate
+except ImportError:
+ _logger.warning("payment_acquirer: mako templates not available, payment acquirer will not work!")
+
+
""" Some tools for parsing / creating email fields """
def decode(text):
"""Returns unicode() string conversion of the the given encoded smtp header text"""
@@ -46,7 +52,7 @@ class mail_message(osv.Model):
_order = 'id desc'
_message_read_limit = 10
- _message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read',
+ _message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read', 'email_from',
'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name', 'favorite_user_ids']
_message_record_name_length = 18
_message_read_more_limit = 1024
@@ -57,17 +63,14 @@ class mail_message(osv.Model):
return name[:self._message_record_name_length] + '...'
def _get_record_name(self, cr, uid, ids, name, arg, context=None):
- """ Return the related document name, using name_get. It is included in
- a try/except statement, because if uid cannot read the related
- document, he should see a void string instead of crashing. """
+ """ Return the related document name, using name_get. It is done using
+ SUPERUSER_ID, to be sure to have the record name correctly stored. """
+ # TDE note: regroup by model/ids, to have less queries to perform
result = dict.fromkeys(ids, False)
for message in self.read(cr, uid, ids, ['model', 'res_id'], context=context):
- if not message['model'] or not message['res_id']:
+ if not message.get('model') or not message.get('res_id'):
continue
- try:
- result[message['id']] = self._shorten_name(self.pool.get(message['model']).name_get(cr, uid, [message['res_id']], context=context)[0][1])
- except (orm.except_orm, osv.except_osv):
- pass
+ result[message['id']] = self._shorten_name(self.pool.get(message['model']).name_get(cr, SUPERUSER_ID, [message['res_id']], context=context)[0][1])
return result
def _get_to_read(self, cr, uid, ids, name, arg, context=None):
@@ -78,10 +81,10 @@ class mail_message(osv.Model):
notif_ids = notif_obj.search(cr, uid, [
('partner_id', 'in', [partner_id]),
('message_id', 'in', ids),
- ('read', '=', False)
+ ('read', '=', False),
], context=context)
for notif in notif_obj.browse(cr, uid, notif_ids, context=context):
- res[notif.message_id.id] = not notif.read
+ res[notif.message_id.id] = True
return res
def _search_to_read(self, cr, uid, obj, name, domain, context=None):
@@ -115,16 +118,21 @@ class mail_message(osv.Model):
], 'Type',
help="Message type: email for email message, notification for system "\
"message, comment for other messages such as user replies"),
- 'author_id': fields.many2one('res.partner', 'Author', required=True),
- 'partner_ids': fields.many2many('res.partner', 'mail_notification', 'message_id', 'partner_id', 'Recipients'),
+ 'email_from': fields.char('From',
+ help="Email address of the sender. This field is set when no matching partner is found for incoming emails."),
+ 'author_id': fields.many2one('res.partner', 'Author',
+ help="Author of the message. If not set, email_from may hold an email address that did not match any partner."),
+ 'partner_ids': fields.many2many('res.partner', string='Recipients'),
+ 'notified_partner_ids': fields.many2many('res.partner', 'mail_notification',
+ 'message_id', 'partner_id', 'Recipients'),
'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel',
'message_id', 'attachment_id', 'Attachments'),
'parent_id': fields.many2one('mail.message', 'Parent Message', select=True, ondelete='set null', help="Initial thread message."),
'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'),
'model': fields.char('Related Document Model', size=128, select=1),
'res_id': fields.integer('Related Document ID', select=1),
- 'record_name': fields.function(_get_record_name, type='string',
- string='Message Record Name',
+ 'record_name': fields.function(_get_record_name, type='char',
+ store=True, string='Message Record Name',
help="Name get of the related document."),
'notification_ids': fields.one2many('mail.notification', 'message_id', 'Notifications'),
'subject': fields.char('Subject'),
@@ -198,19 +206,33 @@ class mail_message(osv.Model):
:param dict message: read result of a mail.message
"""
+ # TDE note: this method should be optimized, to lessen the number of queries, will be done ASAP
+ is_author = False
+ if message['author_id']:
+ is_author = message['author_id'][0] == self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
+ author_id = message['author_id']
+ elif message['email_from']:
+ author_id = (0, message['email_from'])
+
has_voted = False
- if uid in message['vote_user_ids']:
+ if uid in message.get('vote_user_ids'):
has_voted = True
is_favorite = False
- if uid in message['favorite_user_ids']:
+ if uid in message.get('favorite_user_ids'):
is_favorite = True
+ is_private = True
+ if message.get('model') and message.get('res_id'):
+ is_private = False
+
try:
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, message['attachment_ids'], context=context)]
except (orm.except_orm, osv.except_osv):
attachment_ids = []
+ # TDE note: should we send partner_ids ?
+ # TDE note: shouldn't we separated followers and other partners ? costly to compute maybe ,
try:
partner_ids = self.pool.get('res.partner').name_get(cr, uid, message['partner_ids'], context=context)
except (orm.except_orm, osv.except_osv):
@@ -226,95 +248,106 @@ class mail_message(osv.Model):
'record_name': message['record_name'],
'subject': message['subject'],
'date': message['date'],
- 'author_id': message['author_id'],
- 'is_author': message['author_id'] and message['author_id'][0] == uid,
- # TDE note: is this useful ? to check
+ 'author_id': author_id,
+ 'is_author': is_author,
'partner_ids': partner_ids,
- 'parent_id': message['parent_id'] and message['parent_id'][0] or False,
- # TDE note: see with CHM about votes, how they are displayed (only number, or name_get ?)
- # vote: should only use number of votes
+ 'parent_id': False,
'vote_nb': len(message['vote_user_ids']),
'has_voted': has_voted,
- 'is_private': message['model'] and message['res_id'],
+ 'is_private': is_private,
'is_favorite': is_favorite,
'to_read': message['to_read'],
}
- def _message_read_expandable(self, cr, uid, message_list, read_messages,
- message_loaded_ids=[], domain=[], context=None, parent_id=False, limit=None):
- """ Create the expandable message for all parent message read
- this function is used by message_read
+ def _message_read_add_expandables(self, cr, uid, message_list, read_messages,
+ thread_level=0, message_loaded_ids=[], domain=[], parent_id=False, context=None, limit=None):
+ """ Create expandables for message_read, to load new messages.
+ 1. get the expandable for new threads
+ if display is flat (thread_level == 0):
+ fetch message_ids < min(already displayed ids), because we
+ want a flat display, ordered by id
+ else:
+ fetch message_ids that are not childs of already displayed
+ messages
+ 2. get the expandables for new messages inside threads if display
+ is not flat
+ for each thread header, search for its childs
+ for each hole in the child list based on message displayed,
+ create an expandable
- :param list message_list: list of messages given by message_read to
- which we have to add expandables
+ :param list message_list:list of message structure for the Chatter
+ widget to which expandables are added
:param dict read_messages: dict [id]: read result of the messages to
easily have access to their values, given their ID
+ :return bool: True
"""
- # sort for group items / TDE: move to message_read
- # result = sorted(result, key=lambda k: k['id'])
- tree_not = []
- # expandable for not show message
+ def _get_expandable(domain, message_nb, parent_id, id, model):
+ return {
+ 'domain': domain,
+ 'nb_messages': message_nb,
+ 'type': 'expandable',
+ 'parent_id': parent_id,
+ 'id': id,
+ # TDE note: why do we need model sometimes, and sometimes not ???
+ 'model': model,
+ }
+
+ # all_not_loaded_ids = []
id_list = sorted(read_messages.keys())
+ if not id_list:
+ return message_list
+
+ # 1. get the expandable for new threads
+ if thread_level == 0:
+ exp_domain = domain + [('id', '<', min(message_loaded_ids + id_list))]
+ else:
+ exp_domain = domain + ['!', ('id', 'child_of', message_loaded_ids + id_list)]
+ ids = self.search(cr, uid, exp_domain, context=context, limit=1)
+ if ids:
+ message_list.append(_get_expandable(exp_domain, -1, parent_id, -1, None))
+
+ # 2. get the expandables for new messages inside threads if display is not flat
+ if thread_level == 0:
+ return True
for message_id in id_list:
message = read_messages[message_id]
+ # message is not a thread header (has a parent_id)
+ # TDE note: parent_id is false is there is a parent we can not see -> ok
+ if message.get('parent_id'):
+ continue
+
# TDE note: check search is correctly implemented in mail.message
not_loaded_ids = self.search(cr, uid, [
- ('parent_id', '=', message['id']),
+ ('id', 'child_of', message['id']),
('id', 'not in', message_loaded_ids),
], context=context, limit=self._message_read_more_limit)
- # group childs not read
- id_min = None
- id_max = None
- nb = 0
+ if not not_loaded_ids:
+ continue
+ # all_not_loaded_ids += not_loaded_ids
+ # group childs not read
+ id_min, id_max, nb = max(not_loaded_ids), 0, 0
for not_loaded_id in not_loaded_ids:
if not read_messages.get(not_loaded_id):
nb += 1
- if id_min == None or id_min > not_loaded_id:
+ if id_min > not_loaded_id:
id_min = not_loaded_id
- if id_max == None or id_max < not_loaded_id:
+ if id_max < not_loaded_id:
id_max = not_loaded_id
- tree_not.append(not_loaded_id)
+ elif nb > 0:
+ exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)]
+ message_list.append(_get_expandable(exp_domain, nb, message_id, id_min, message.get('model')))
+ id_min, id_max, nb = max(not_loaded_ids), 0, 0
else:
- if nb > 0:
- message_list.append({
- 'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', message_id)],
- 'nb_messages': nb,
- 'type': 'expandable',
- 'parent_id': message_id,
- 'id': id_min,
- 'model': message['model']
- })
- id_min = None
- id_max = None
- nb = 0
+ id_min, id_max, nb = max(not_loaded_ids), 0, 0
if nb > 0:
- message_list.append({
- 'domain': [('id', '>=', id_min), ('id', '<=', id_max), ('parent_id', '=', message_id)],
- 'nb_messages': nb,
- 'type': 'expandable',
- 'parent_id': message_id,
- 'id': id_min,
- 'model': message['model'],
- })
+ exp_domain = [('id', '>=', id_min), ('id', '<=', id_max), ('id', 'child_of', message_id)]
+ message_list.append(_get_expandable(exp_domain, nb, message_id, id_min, message.get('model')))
- for msg_id in read_messages.keys() + tree_not:
- message_loaded_ids.append(msg_id)
+ # message_loaded_ids = list(set(message_loaded_ids + read_messages.keys() + all_not_loaded_ids))
- # expandable for limit max
- ids = self.search(cr, uid, domain + [('id', 'not in', message_loaded_ids)], context=context, limit=1)
- if len(ids) > 0:
- message_list.append({
- 'domain': domain,
- 'nb_messages': 0,
- 'type': 'expandable',
- 'parent_id': parent_id,
- 'id': -1,
- 'max_limit': True,
- })
-
- return message_list
+ return True
def _get_parent(self, cr, uid, message, context=None):
""" Tools method that tries to get the parent of a mail.message. If
@@ -331,57 +364,70 @@ class mail_message(osv.Model):
except (orm.except_orm, osv.except_osv):
return False
- def message_read(self, cr, uid, ids=False, domain=[], message_loaded_ids=[], context=None, parent_id=False, limit=None):
- """ Read messages from mail.message, and get back a structured tree
- of messages to be displayed as discussion threads. If IDs is set,
+ def message_read(self, cr, uid, ids=None, domain=None, message_unload_ids=None, thread_level=0, context=None, parent_id=False, limit=None):
+ """ Read messages from mail.message, and get back a list of structured
+ messages to be displayed as discussion threads. If IDs is set,
fetch these records. Otherwise use the domain to fetch messages.
- After having fetch messages, their parents & child will be added to obtain
- well formed threads.
+ After having fetch messages, their ancestors will be added to obtain
+ well formed threads, if uid has access to them.
- TDE note: update this comment after final method implementation
+ After reading the messages, expandable messages are added in the
+ message list (see ``_message_read_add_expandables``). It consists
+ in messages holding the 'read more' data: number of messages to
+ read, domain to apply.
- :param domain: optional domain for searching ids
- :param limit: number of messages to fetch
- :param parent_id: if parent_id reached, stop searching for
- further parents
- :return list: list of trees of messages
+ :param list ids: optional IDs to fetch
+ :param list domain: optional domain for searching ids if ids not set
+ :param list message_unload_ids: optional ids we do not want to fetch,
+ because i.e. they are already displayed somewhere
+ :param int parent_id: context of parent_id
+ - if parent_id reached when adding ancestors, stop going further
+ in the ancestor search
+ - if set in flat mode, ancestor_id is set to parent_id
+ :param int limit: number of messages to fetch, before adding the
+ ancestors and expandables
+ :return list: list of message structure for the Chatter widget
"""
- if message_loaded_ids:
- domain += [('id', 'not in', message_loaded_ids)]
+ # print 'message_read', ids, domain, message_unload_ids, thread_level, context, parent_id, limit
+ assert thread_level in [0, 1], 'message_read() thread_level should be 0 (flat) or 1 (1 level of thread); given %s.' % thread_level
+ domain = domain if domain is not None else []
+ message_unload_ids = message_unload_ids if message_unload_ids is not None else []
+ if message_unload_ids:
+ domain += [('id', 'not in', message_unload_ids)]
limit = limit or self._message_read_limit
read_messages = {}
message_list = []
- # specific IDs given: fetch those ids and return directly the message list
- if ids:
- for message in self.read(cr, uid, ids, self._message_read_fields, context=context):
- message_list.append(self._message_get_dict(cr, uid, message, context=context))
- message_list = sorted(message_list, key=lambda k: k['id'])
- return message_list
-
- # TDE FIXME: check access rights on search are implemented for mail.message
- # fetch messages according to the domain, add their parents if uid has access to
- ids = self.search(cr, uid, domain, context=context, limit=limit)
+ # no specific IDS given: fetch messages according to the domain, add their parents if uid has access to
+ if ids is None:
+ ids = self.search(cr, uid, domain, context=context, limit=limit)
for message in self.read(cr, uid, ids, self._message_read_fields, context=context):
- # if not in tree and not in message_loded list
- if not read_messages.get(message.get('id')) and message.get('id') not in message_loaded_ids:
- read_messages[message.get('id')] = message
+ message_id = message['id']
+
+ # if not in tree and not in message_loaded list
+ if not message_id in read_messages and not message_id in message_unload_ids:
+ read_messages[message_id] = message
message_list.append(self._message_get_dict(cr, uid, message, context=context))
- # get all parented message if the user have the access
+ # get the older ancestor the user can read, update its ancestor field
+ if not thread_level:
+ message_list[-1]['parent_id'] = parent_id
+ continue
parent = self._get_parent(cr, uid, message, context=context)
while parent and parent.get('id') != parent_id:
- if not read_messages.get(parent.get('id')) and parent.get('id') not in message_loaded_ids:
- read_messages[parent.get('id')] = parent
- message_list.append(self._message_get_dict(cr, uid, parent, context=context))
- parent = self._get_parent(cr, uid, parent, context=context)
+ message_list[-1]['parent_id'] = parent.get('id')
+ message = parent
+ parent = self._get_parent(cr, uid, message, context=context)
+ # if in thread: add its ancestor to the list of messages
+ if not message['id'] in read_messages and not message['id'] in message_unload_ids:
+ read_messages[message['id']] = message
+ message_list.append(self._message_get_dict(cr, uid, message, context=context))
# get the child expandable messages for the tree
message_list = sorted(message_list, key=lambda k: k['id'])
- message_list = self._message_read_expandable(cr, uid, message_list, read_messages,
- message_loaded_ids=message_loaded_ids, domain=domain, context=context, parent_id=parent_id, limit=limit)
+ self._message_read_add_expandables(cr, uid, message_list, read_messages, thread_level=thread_level,
+ message_loaded_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context, limit=limit)
- # message_list = sorted(message_list, key=lambda k: k['id'])
return message_list
# TDE Note: do we need this ?
@@ -394,7 +440,7 @@ class mail_message(osv.Model):
# return attachment_list
#------------------------------------------------------
- # Email api
+ # mail_message internals
#------------------------------------------------------
def init(self, cr):
@@ -402,23 +448,75 @@ class mail_message(osv.Model):
if not cr.fetchone():
cr.execute("""CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id)""")
+ def _search(self, cr, uid, args, offset=0, limit=None, order=None,
+ context=None, count=False, access_rights_uid=None):
+ """ Override that adds specific access rights of mail.message, to remove
+ ids uid could not see according to our custom rules. Please refer
+ to check_access_rule for more details about those rules.
+
+ After having received ids of a classic search, keep only:
+ - if author_id == pid, uid is the author, OR
+ - a notification (id, pid) exists, uid has been notified, OR
+ - uid have read access to the related document is model, res_id
+ - otherwise: remove the id
+ """
+ # Rules do not apply to administrator
+ # print '_search', uid, args
+ if uid == SUPERUSER_ID:
+ return super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order,
+ context=context, count=count, access_rights_uid=access_rights_uid)
+ # Perform a super with count as False, to have the ids, not a counter
+ ids = super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order,
+ context=context, count=False, access_rights_uid=access_rights_uid)
+ if not ids and count:
+ return 0
+ elif not ids:
+ return ids
+
+ pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'])['partner_id'][0]
+ author_ids, partner_ids, allowed_ids = set([]), set([]), set([])
+ model_ids = {}
+
+ messages = super(mail_message, self).read(cr, uid, ids, ['author_id', 'model', 'res_id', 'notified_partner_ids'], context=context)
+ for message in messages:
+ if message.get('author_id') and message.get('author_id')[0] == pid:
+ author_ids.add(message.get('id'))
+ elif pid in message.get('notified_partner_ids'):
+ partner_ids.add(message.get('id'))
+ elif message.get('model') and message.get('res_id'):
+ model_ids.setdefault(message.get('model'), {}).setdefault(message.get('res_id'), set()).add(message.get('id'))
+
+ model_access_obj = self.pool.get('ir.model.access')
+ for doc_model, doc_dict in model_ids.iteritems():
+ if not model_access_obj.check(cr, uid, doc_model, 'read', False):
+ continue
+ doc_ids = doc_dict.keys()
+ allowed_doc_ids = self.pool.get(doc_model).search(cr, uid, [('id', 'in', doc_ids)], context=context)
+ allowed_ids |= set([message_id for allowed_doc_id in allowed_doc_ids for message_id in doc_dict[allowed_doc_id]])
+
+ final_ids = author_ids | partner_ids | allowed_ids
+ if count:
+ return len(final_ids)
+ else:
+ return list(final_ids)
+
def check_access_rule(self, cr, uid, ids, operation, context=None):
""" Access rules of mail.message:
- read: if
- - notification exist (I receive pushed message) OR
- - author_id = pid (I am the author) OR
- - I can read the related document if res_model, res_id
- - Otherwise: raise
+ - author_id == pid, uid is the author, OR
+ - mail_notification (id, pid) exists, uid has been notified, OR
+ - uid have read access to the related document if model, res_id
+ - otherwise: raise
- create: if
- - I am in the document message_follower_ids OR
- - I can write on the related document if res_model, res_id OR
- - I create a private message (no model, no res_id)
- - Otherwise: raise
+ - no model, no res_id, I create a private message
+ - pid in message_follower_ids if model, res_id OR
+ - uid have write access on the related document if model, res_id, OR
+ - otherwise: raise
- write: if
- - I can write on the related document if res_model, res_id
+ - uid has write access on the related document if model, res_id
- Otherwise: raise
- unlink: if
- - I can write on the related document if res_model, res_id
+ - uid has write access on the related document if model, res_id
- Otherwise: raise
"""
if uid == SUPERUSER_ID:
@@ -428,15 +526,25 @@ class mail_message(osv.Model):
partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=None)['partner_id'][0]
# Read mail_message.ids to have their values
- model_record_ids = {}
message_values = dict.fromkeys(ids)
+ model_record_ids = {}
cr.execute('SELECT DISTINCT id, model, res_id, author_id FROM "%s" WHERE id = ANY (%%s)' % self._table, (ids,))
for id, rmod, rid, author_id in cr.fetchall():
message_values[id] = {'res_model': rmod, 'res_id': rid, 'author_id': author_id}
if rmod:
- model_record_ids.setdefault(rmod, set()).add(rid)
+ model_record_ids.setdefault(rmod, dict()).setdefault(rid, set()).add(id)
- # Read: Check for received notifications -> could become an ir.rule, but not till we do not have a many2one variable field
+ # Author condition, for read and create (private message) -> could become an ir.rule, but not till we do not have a many2one variable field
+ if operation == 'read':
+ author_ids = [mid for mid, message in message_values.iteritems()
+ if message.get('author_id') and message.get('author_id') == partner_id]
+ elif operation == 'create':
+ author_ids = [mid for mid, message in message_values.iteritems()
+ if not message.get('model') and not message.get('res_id')]
+ else:
+ author_ids = []
+
+ # Notification condition, for read (check for received notifications and create (in message_follower_ids)) -> could become an ir.rule, but not till we do not have a many2one variable field
if operation == 'read':
not_obj = self.pool.get('mail.notification')
not_ids = not_obj.search(cr, SUPERUSER_ID, [
@@ -444,38 +552,24 @@ class mail_message(osv.Model):
('message_id', 'in', ids),
], context=context)
notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, SUPERUSER_ID, not_ids, context=context)]
- else:
- notified_ids = []
- # Read: Check messages you are author -> could become an ir.rule, but not till we do not have a many2one variable field
- if operation == 'read':
- author_ids = [mid for mid, message in message_values.iteritems()
- if message.get('author_id') and message.get('author_id') == partner_id]
- # Create: Check messages you create that are private messages -> ir.rule ?
elif operation == 'create':
- author_ids = [mid for mid, message in message_values.iteritems()
- if not message.get('model') and not message.get('res_id')]
- else:
- author_ids = []
-
- # Create: Check message_follower_ids
- if operation == 'create':
- doc_follower_ids = []
- for model, mids in model_record_ids.items():
+ notified_ids = []
+ for doc_model, doc_dict in model_record_ids.items():
fol_obj = self.pool.get('mail.followers')
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [
- ('res_model', '=', model),
- ('res_id', 'in', list(mids)),
+ ('res_model', '=', doc_model),
+ ('res_id', 'in', list(doc_dict.keys())),
('partner_id', '=', partner_id),
], context=context)
fol_mids = [follower.res_id for follower in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context)]
- doc_follower_ids += [mid for mid, message in message_values.iteritems()
- if message.get('res_model') == model and message.get('res_id') in fol_mids]
+ notified_ids += [mid for mid, message in message_values.iteritems()
+ if message.get('res_model') == doc_model and message.get('res_id') in fol_mids]
else:
- doc_follower_ids = []
+ notified_ids = []
# Calculate remaining ids, and related model/res_ids
model_record_ids = {}
- other_ids = set(ids).difference(set(notified_ids), set(author_ids), set(doc_follower_ids))
+ other_ids = set(ids).difference(set(author_ids), set(notified_ids))
for id in other_ids:
if message_values[id]['res_model']:
model_record_ids.setdefault(message_values[id]['res_model'], set()).add(message_values[id]['res_id'])
@@ -495,7 +589,7 @@ class mail_message(osv.Model):
if message.get('res_model') == model and message.get('res_id') in mids]
# Calculate remaining ids: if not void, raise an error
- other_ids = set(ids).difference(set(notified_ids), set(author_ids), set(doc_follower_ids), set(document_related_ids))
+ other_ids = other_ids - set(document_related_ids)
if not other_ids:
return
raise orm.except_orm(_('Access Denied'),
@@ -529,50 +623,123 @@ class mail_message(osv.Model):
self.pool.get('ir.attachment').unlink(cr, uid, attachments_to_delete, context=context)
return super(mail_message, self).unlink(cr, uid, ids, context=context)
- def _notify_followers(self, cr, uid, newid, message, context=None):
- """ Add the related record followers to the destination partner_ids.
+ def copy(self, cr, uid, id, default=None, context=None):
+ """ Overridden to avoid duplicating fields that are unique to each email """
+ if default is None:
+ default = {}
+ default.update(message_id=False, headers=False)
+ return super(mail_message, self).copy(cr, uid, id, default=default, context=context)
+
+ #------------------------------------------------------
+ # Messaging API
+ #------------------------------------------------------
+
+ # TDE note: this code is not used currently, will be improved in a future merge, when quoted context
+ # will be added to email send for notifications. Currently only WIP.
+ MAIL_TEMPLATE = """
+ % if message:
+ ${display_message(message)}
+ % endif
+ % for ctx_msg in context_messages:
+ ${display_message(ctx_msg)}
+ % endfor
+ % if add_expandable:
+ ${display_expandable()}
+ % endif
+ ${display_message(header_message)}
+
+
+ <%def name="display_message(message)">
+
+ Subject: ${message.subject}
+ Body: ${message.body}
+
+ %def>
+
+ <%def name="display_expandable()">
+ This is an expandable.
+ %def>
+ """
+
+ def message_quote_context(self, cr, uid, id, context=None, limit=3, add_original=False):
"""
- partners_to_notify = set([])
- # message has no subtype_id: pure log message -> no partners, no one notified
- if not message.subtype_id:
- message.write({'partner_ids': [5]})
- return True
- # all partner_ids of the mail.message have to be notified
- if message.partner_ids:
- partners_to_notify |= set(partner.id for partner in message.partner_ids)
- # all followers of the mail.message document have to be added as partners and notified
- if message.model and message.res_id:
- fol_obj = self.pool.get("mail.followers")
- fol_ids = fol_obj.search(cr, uid, [('res_model', '=', message.model), ('res_id', '=', message.res_id), ('subtype_ids', 'in', message.subtype_id.id)], context=context)
- fol_objs = fol_obj.browse(cr, uid, fol_ids, context=context)
- extra_notified = set(fol.partner_id.id for fol in fol_objs)
- missing_notified = extra_notified - partners_to_notify
- if missing_notified:
- self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(4, p_id) for p_id in missing_notified]}, context=context)
+ 1. message.parent_id = False: new thread, no quote_context
+ 2. get the lasts messages in the thread before message
+ 3. get the message header
+ 4. add an expandable between them
+
+ :param dict quote_context: options for quoting
+ :return string: html quote
+ """
+ add_expandable = False
+
+ message = self.browse(cr, uid, id, context=context)
+ if not message.parent_id:
+ return ''
+ context_ids = self.search(cr, uid, [
+ ('parent_id', '=', message.parent_id.id),
+ ('id', '<', message.id),
+ ], limit=limit, context=context)
+
+ if len(context_ids) >= limit:
+ add_expandable = True
+ context_ids = context_ids[0:-1]
+
+ context_ids.append(message.parent_id.id)
+ context_messages = self.browse(cr, uid, context_ids, context=context)
+ header_message = context_messages.pop()
+
+ try:
+ if not add_original:
+ message = False
+ result = MakoTemplate(self.MAIL_TEMPLATE).render_unicode(message=message,
+ context_messages=context_messages,
+ header_message=header_message,
+ add_expandable=add_expandable,
+ # context kw would clash with mako internals
+ ctx=context,
+ format_exceptions=True)
+ result = result.strip()
+ return result
+ except Exception:
+ _logger.exception("failed to render mako template for quoting message")
+ return ''
+ return result
def _notify(self, cr, uid, newid, context=None):
""" Add the related record followers to the destination partner_ids if is not a private message.
Call mail_notification.notify to manage the email sending
"""
- message = self.browse(cr, uid, newid, context=context)
- if message.model and message.res_id:
- self._notify_followers(cr, uid, newid, message, context=context)
+ message = self.read(cr, uid, newid, ['model', 'res_id', 'author_id', 'subtype_id', 'partner_ids'], context=context)
- # add myself if I wrote on my wall, otherwise remove myself author
- if ((message.model == "res.partner" and message.res_id == message.author_id.id)):
- self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(4, message.author_id.id)]}, context=context)
- else:
- self.write(cr, SUPERUSER_ID, [newid], {'partner_ids': [(3, message.author_id.id)]}, context=context)
+ partners_to_notify = set([])
+ # message has no subtype_id: pure log message -> no partners, no one notified
+ if not message.get('subtype_id'):
+ return True
+ # all partner_ids of the mail.message have to be notified
+ if message.get('partner_ids'):
+ partners_to_notify |= set(message.get('partner_ids'))
+ # all followers of the mail.message document have to be added as partners and notified
+ if message.get('model') and message.get('res_id'):
+ fol_obj = self.pool.get("mail.followers")
+ fol_ids = fol_obj.search(cr, uid, [
+ ('res_model', '=', message.get('model')),
+ ('res_id', '=', message.get('res_id')),
+ ('subtype_ids', 'in', message.get('subtype_id')[0])
+ ], context=context)
+ fol_objs = fol_obj.read(cr, uid, fol_ids, ['partner_id'], context=context)
+ partners_to_notify |= set(fol['partner_id'][0] for fol in fol_objs)
+ # when writing to a wall
+ if message.get('author_id') and message.get('model') == "res.partner" and message.get('res_id') == message.get('author_id')[0]:
+ partners_to_notify |= set([message.get('author_id')[0]])
+ elif message.get('author_id'):
+ partners_to_notify = partners_to_notify - set([message.get('author_id')[0]])
+
+ if partners_to_notify:
+ self.write(cr, SUPERUSER_ID, [newid], {'notified_partner_ids': [(4, p_id) for p_id in partners_to_notify]}, context=context)
self.pool.get('mail.notification')._notify(cr, uid, newid, context=context)
- def copy(self, cr, uid, id, default=None, context=None):
- """Overridden to avoid duplicating fields that are unique to each email"""
- if default is None:
- default = {}
- default.update(message_id=False, headers=False)
- return super(mail_message, self).copy(cr, uid, id, default=default, context=context)
-
#------------------------------------------------------
# Tools
#------------------------------------------------------
diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml
index 4677beee511..93b35797fd5 100644
--- a/addons/mail/mail_message_view.xml
+++ b/addons/mail/mail_message_view.xml
@@ -29,6 +29,7 @@
+
@@ -38,6 +39,7 @@
+
@@ -63,6 +65,9 @@
+
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index 04da686b49d..e986f12b822 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -554,7 +554,11 @@ class mail_thread(osv.AbstractModel):
('file2', 'bytes')}
}
"""
- msg_dict = {}
+ msg_dict = {
+ 'type': 'email',
+ 'subtype': 'mail.mt_comment',
+ 'author_id': False,
+ }
if not isinstance(message, Message):
if isinstance(message, unicode):
# Warning: message_from_string doesn't always work correctly on unicode,
@@ -572,7 +576,7 @@ class mail_thread(osv.AbstractModel):
if 'Subject' in message:
msg_dict['subject'] = decode(message.get('Subject'))
- # Envelope fields not stored in mail.message but made available for message_new()
+ # Envelope fields not stored in mail.message but made available for message_new()
msg_dict['from'] = decode(message.get('from'))
msg_dict['to'] = decode(message.get('to'))
msg_dict['cc'] = decode(message.get('cc'))
@@ -581,6 +585,8 @@ class mail_thread(osv.AbstractModel):
author_ids = self._message_find_partners(cr, uid, message, ['From'], context=context)
if author_ids:
msg_dict['author_id'] = author_ids[0]
+ else:
+ msg_dict['email_from'] = message.get('from')
partner_ids = self._message_find_partners(cr, uid, message, ['From', 'To', 'Cc'], context=context)
msg_dict['partner_ids'] = partner_ids
@@ -670,6 +676,18 @@ class mail_thread(osv.AbstractModel):
if self._mail_flat_thread and not parent_id and thread_id:
message_ids = mail_message.search(cr, uid, ['&', ('res_id', '=', thread_id), ('model', '=', model)], context=context, order="id ASC", limit=1)
parent_id = message_ids and message_ids[0] or False
+ # we want to set a parent: force to set the parent_id to the oldest ancestor, to avoid having more than 1 level of thread
+ elif parent_id:
+ message_ids = mail_message.search(cr, SUPERUSER_ID, [('id', '=', parent_id), ('parent_id', '!=', False)], context=context)
+ # avoid loops when finding ancestors
+ processed_list = []
+ if message_ids:
+ _counter, _counter_max = 0, 200
+ message = mail_message.browse(cr, SUPERUSER_ID, message_ids[0], context=context)
+ while (message.parent_id and message.parent_id.id not in processed_list):
+ processed_list.append(message.parent_id.id)
+ message = message.parent_id
+ parent_id = message.id
values = kwargs
values.update({
@@ -689,27 +707,28 @@ class mail_thread(osv.AbstractModel):
return mail_message.create(cr, uid, values, context=context)
- def message_post_api(self, cr, uid, thread_id, body='', subject=False, type='notification',
- subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
- # TDE FIXME: body is plaintext: convert it into html
- # when writing on res.partner, without specific thread_id -> redirect to the user's partner
- if self._name == 'res.partner' and not thread_id:
- thread_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+ def message_post_api(self, cr, uid, thread_id, body='', subject=False, parent_id=False, attachment_ids=None, context=None):
+ """ Wrapper on message_post, used only in Chatter (JS). The purpose is
+ to handle attachments.
+ # TDE FIXME: body is plaintext: convert it into html
+ """
+ new_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type='comment',
+ subtype='mail.mt_comment', parent_id=parent_id, context=context)
- new_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type=type,
- subtype=subtype, parent_id=parent_id, context=context)
-
- # Chatter: attachments linked to the document (not done JS-side), load the message
- if attachments:
+ # HACK FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
+ if attachment_ids:
ir_attachment = self.pool.get('ir.attachment')
mail_message = self.pool.get('mail.message')
- attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [('res_model', '=', 'mail.message'), ('res_id', '=', 0), ('create_uid', '=', uid), ('id', 'in', attachments)], context=context)
- if attachment_ids:
+ filtered_attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [
+ ('res_model', '=', 'mail.compose.message'),
+ ('res_id', '=', 0),
+ ('create_uid', '=', uid),
+ ('id', 'in', attachment_ids)], context=context)
+ if filtered_attachment_ids:
ir_attachment.write(cr, SUPERUSER_ID, attachment_ids, {'res_model': self._name, 'res_id': thread_id}, context=context)
mail_message.write(cr, SUPERUSER_ID, [new_message_id], {'attachment_ids': [(6, 0, [pid for pid in attachment_ids])]}, context=context)
- new_message = self.pool.get('mail.message').message_read(cr, uid, [new_message_id], context=context)
- return new_message
+ return new_message_id
#------------------------------------------------------
# Followers API
diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml
index 5ef5e14b846..07b24725b05 100644
--- a/addons/mail/mail_thread_view.xml
+++ b/addons/mail/mail_thread_view.xml
@@ -39,13 +39,13 @@
-
-
+
@@ -79,4 +79,4 @@
-
\ No newline at end of file
+
diff --git a/addons/mail/res_partner_view.xml b/addons/mail/res_partner_view.xml
index 2a8430926d5..a4aa56ae971 100644
--- a/addons/mail/res_partner_view.xml
+++ b/addons/mail/res_partner_view.xml
@@ -9,9 +9,8 @@
-
+
diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py
index 54b8a76703f..1b6388111ba 100644
--- a/addons/mail/res_users.py
+++ b/addons/mail/res_users.py
@@ -111,8 +111,7 @@ class res_users(osv.Model):
alias_pool.unlink(cr, uid, alias_ids, context=context)
return res
- def message_post_api(self, cr, uid, thread_id, body='', subject=False, type='notification',
- subtype=None, parent_id=False, attachments=None, context=None, **kwargs):
+ def message_post_api(self, cr, uid, thread_id, body='', subject=False, parent_id=False, attachment_ids=None, context=None):
""" Redirect the posting of message on res.users to the related partner.
This is done because when giving the context of Chatter on the
various mailboxes, we do not have access to the current partner_id.
@@ -124,7 +123,7 @@ class res_users(osv.Model):
thread_id = thread_id[0]
partner_id = self.pool.get('res.users').read(cr, uid, thread_id, ['partner_id'], context=context)['partner_id'][0]
return self.pool.get('res.partner').message_post_api(cr, uid, partner_id, body=body, subject=subject,
- type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, **kwargs)
+ parent_id=parent_id, attachment_ids=attachment_ids, context=context)
def message_post(self, cr, uid, thread_id, context=None, **kwargs):
""" Redirect the posting of message on res.users to the related partner.
diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv
index c609667c26e..52c6c605109 100644
--- a/addons/mail/security/ir.model.access.csv
+++ b/addons/mail/security/ir.model.access.csv
@@ -2,6 +2,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mail_message_all,mail.message.all,model_mail_message,,1,0,1,0
access_mail_message_group_user,mail.message.group.user,model_mail_message,base.group_user,1,1,1,1
access_mail_mail_all,mail.mail.all,model_mail_mail,,0,0,1,0
+access_mail_mail_user,mail.mail,model_mail_mail,base.group_user,1,1,1,0
access_mail_mail_system,mail.mail.system,model_mail_mail,base.group_system,1,1,1,1
access_mail_followers_all,mail.followers.all,model_mail_followers,,1,0,0,0
access_mail_followers_system,mail.followers.system,model_mail_followers,base.group_system,1,1,1,1
@@ -12,6 +13,6 @@ access_mail_group_user,mail.group.user,model_mail_group,base.group_user,1,1,1,1
access_mail_alias_all,mail.alias.all,model_mail_alias,,1,0,0,0
access_mail_alias_user,mail.alias,model_mail_alias,base.group_user,1,1,1,0
access_mail_alias_system,mail.alias,model_mail_alias,base.group_system,1,1,1,1
-access_mail_message_subtype,mail.message.subtype,model_mail_message_subtype,,1,1,1,1
-access_mail_mail_user,mail.mail,model_mail_mail,base.group_user,1,1,1,0
+access_mail_message_subtype_all,mail.message.subtype.all,model_mail_message_subtype,,1,0,0,0
access_mail_vote_all,mail.vote.all,model_mail_vote,,1,1,1,1
+access_mail_favorite_all,mail.favorite.all,model_mail_favorite,,1,1,1,1
\ No newline at end of file
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index 50ce0e000a9..af3cc3ed359 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -1,439 +1,382 @@
-/* ------------------------------------------------------------ */
-/* Reset because of ugly display of end of August
-/* ------------------------------------------------------------ */
-.openerp .oe_mail_wall ul, .openerp .oe_mail_wall li {
- list-style-type: none;
- padding: 0;
- margin: 0;
+/* ------------ MAIL WIDGET --------------- */
+.openerp .oe_mail, .openerp .oe_mail *{
+ box-sizing: border-box;
}
-
-.openerp .oe_chatter ul, .openerp .oe_chatter li {
- list-style-type: none;
- padding: 0;
- margin: 0;
-}
-
-/* ------------------------------------------------------------ */
-/* Wall
-/* ------------------------------------------------------------ */
-
-.openerp div.oe_mail_wall {
- overflow: auto;
- padding: 0;
- background: white;
-}
-
-.openerp div.oe_mail_wall div.oe_mail_wall_aside {
- margin-left: 565px;
- margin: 8px;
-}
-
-.openerp div.oe_mail_wall ul.oe_mail_wall_threads {
- float: left;
- width: 560px;
- margin: 8px;
- list-style-type: none;
-}
-
-/* ------------------------------------------------------------ */
-/* Followers
-/* ------------------------------------------------------------ */
-
-.openerp div.oe_mail_recthread_aside h4 {
- display: inline-block;
-}
-.openerp div.oe_mail_recthread_aside button {
+.openerp .oe_mail {
+ display: block;
position: relative;
+ margin: 0px;
}
-.openerp div.oe_mail_recthread_aside label,
-.openerp div.oe_mail_recthread_aside input {
- cursor:pointer;
+.openerp .oe_mail .oe_thread{
+ margin-left: 32px;
+}
+.openerp .oe_mail > .oe_thread{
+ margin-left: 0px;
}
-/* Specific display of threads in the wall */
-/* ------------------------------------------------------------ */
+/* ---------------- MESSAGES ------------------ */
-.openerp ul.oe_mail_wall_threads .oe_msg_content textarea.oe_mail_compose_textarea {
- width: 434px;
- height: 30px;
- padding: 4px;
-}
-
-.openerp li.oe_mail_wall_thread:first .oe_msg_notification {
- border-top: 0;
-}
-
-.openerp div.oe_thread_placeholder img {
- width: 28px;
- height: 28px;
-}
-
-.openerp div.oe_thread_placeholder div.oe_msg_content {
- width: 440px;
-}
-
-
-/* ------------------------------------------------------------ */
-/* RecordThread
-/* ------------------------------------------------------------ */
-
-.openerp .oe_form div.oe_chatter {
- overflow: auto;
-}
-
-.openerp .oe_mail_record_wall {
- margin: auto;
- width: 560px;
-}
-
-.openerp .oe_mail_record_wall > .oe_mail_wall_threads {
- float: left;
-}
-
-.openerp div.oe_mail_recthread_aside {
- float: right;
- width: 250px;
-}
-
-.openerp div.oe_mail_recthread_actions {
- margin-bottom: 8px;
-}
-
-.openerp div.oe_mail_recthread_actions button {
- width: 120px;
-}
-
-.openerp .oe_mail_recthread_aside .oe_follower.oe_follow {
- color: white;
- background-color: #8a89ba;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#8a89ba), to(#807fb4));
- background-image: -webkit-linear-gradient(top, #8a89ba, #807fb4);
- background-image: -moz-linear-gradient(top, #8a89ba, #807fb4);
- background-image: -ms-linear-gradient(top, #8a89ba, #807fb4);
- background-image: -o-linear-gradient(top, #8a89ba, #807fb4);
- background-image: linear-gradient(to bottom, #8a89ba, #807fb4);
-}
-.openerp .oe_mail_recthread_aside .oe_follower.oe_following {
- color: white;
- background-color: #dc5f59;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
- background-image: -webkit-linear-gradient(top, #dc5f59, #b33630);
- background-image: -moz-linear-gradient(top, #dc5f59, #b33630);
- background-image: -ms-linear-gradient(top, #dc5f59, #b33630);
- background-image: -o-linear-gradient(top, #dc5f59, #b33630);
- background-image: linear-gradient(to bottom, #dc5f59, #b33630);
-}
-
-
-.openerp .oe_mail_recthread_aside .oe_follower span {
- display:none;
-}
-.openerp .oe_mail_recthread_aside .oe_following span.oe_following,
-.openerp .oe_mail_recthread_aside .oe_notfollow span.oe_follow {
- display:block;
-}
-
-.openerp div.oe_mail_recthread_followers {
- margin-bottom: 8px;
-}
-
-
-/* ------------------------------------------------------------ */
-/* subtypes
-/* ------------------------------------------------------------ */
-
-.openerp .oe_mail_subtypes {
- display:inline-block;
+.openerp .oe_mail .oe_msg{
position: relative;
- z-index: 5;
+ background: #F4F5FA;
+ border-radius: 2px;
+ margin-bottom: 2px;
+ min-height: 42px;
+ border: solid 1px rgba(0,0,0,0.03);
}
-.openerp .oe_mail_subtypes .oe_recthread_subtypes {
- background: #fff;
- padding: 2px;
- border: 1px solid #aaaaaa;
- border-top: 0px;
+.openerp .oe_mail .oe_msg .oe_msg_left{
position: absolute;
- z-index: 2;
+ left:0; top: 0; bottom: 0; width: 40px;
+ overflow: hidden;
}
-.openerp .oe_mail_subtypes.oe_mouseout .oe_recthread_subtypes {
- display: none;
+.openerp .oe_mail .oe_msg .oe_msg_icon{
+ width: 32px;
+ margin: 4px;
+ border-radius: 2px;
}
-.openerp .oe_mail_subtypes.oe_mouseover .oe_recthread_subtypes {
- display: block;
-}
-
-/* ------------------------------------------------------------ */
-/* Thread
-/* ------------------------------------------------------------ */
-
-.openerp div.oe_mail_thread_action {
- white-space: normal;
- padding: 8px;
- z-index:5;
- background: #fff;
-}
-
-.openerp div.oe_mail_thread_action:after {
- content: "";
- display: block;
- clear: both;
-}
-
-/* default textarea (oe_mail_compose_textarea), and body textarea for compose form view */
-.openerp .oe_msg_content textarea.oe_mail_compose_textarea:focus,
-.openerp .oe_msg_content div.oe_mail_compose_message_body textarea:focus {
- outline: 0;
- border-color: rgba(82, 168, 236, 0.8);
- -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
- -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
- -box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6);
-}
-
-.openerp .oe_mail_vote_count,
-.openerp .oe_msg_vote{
- vertical-align: bottom;
-}
-
-.openerp button.oe_mail_starbox{
- background: #ff0000;
-}
-.openerp button.oe_mail_starbox.oe_stared{
- background: #00FF00;
-}
-
-.openerp div.oe_mail_thread_display {
- white-space: normal;
-}
-
-.openerp div.oe_thread_placeholder {
- margin-left: 66px;
-}
-
-.openerp li.oe_mail_thread_msg {
- width: 560px;
-}
-
-.openerp div.oe_thread_placeholder li.oe_mail_thread_msg:last-child {
- margin-bottom: 8px;
-}
-
-.openerp div.oe_mail_thread_more {
- display: none;
- border-bottom: 1px solid #D2D9E7;
-}
-
-.openerp li.oe_mail_thread_msg:after {
- content: "";
- display: block;
- clear: both;
-}
-
-.openerp li.oe_mail_thread_msg.oe_mail_read,
-.openerp li.oe_mail_thread_msg.oe_mail_read div {
- border-left: #F0F0F0;
-}
-.openerp li.oe_mail_thread_msg.oe_mail_read li.oe_mail_thread_msg.oe_mail_unread,
-.openerp li.oe_mail_thread_msg.oe_mail_read li.oe_mail_thread_msg.oe_mail_unread div {
- background-color: #F6F6F6;
-}
-
-.openerp li.oe_mail_thread_msg.oe_mail_unread>div>ul>li.oe_unread,
-.openerp li.oe_mail_thread_msg.oe_mail_read>div>ul>li.oe_read {
- display: none;
-}
-
-.openerp li.oe_mail_thread_msg > div:after {
- content: "";
- display: block;
- clear: both;
-}
-
-.openerp div.oe_mail_msg {
- padding: 0;
- margin: 0 0 4px 0;
-}
-
-.openerp .oe_msg_notification,
-.openerp .oe_msg_expandable,
-.openerp .oe_msg_comment,
-.openerp .oe_msg_email {
- padding: 8px;
- background: white;
+.openerp .oe_mail .oe_msg .oe_msg_center{
position: relative;
-}
-
-.openerp .oe_msg_notification:after,
-.openerp .oe_msg_comment:after,
-.openerp .oe_msg_email:after {
- content: "";
display: block;
- clear: both;
+ margin-left: 40px;
+}
+.openerp .oe_mail .oe_msg .oe_msg_footer{
+ padding-left: 4px;
+ overflow: hidden;
+ opacity:0.8;
+ -webkit-transition: opacity 0.2s linear;
+}
+.openerp .oe_mail .oe_msg .oe_msg_content{
+ display: block;
+ overflow: hidden;
+ padding: 4px;
+ padding-bottom:1px;
+}
+.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_title{
+ font-size: 13px;
+ margin-bottom: 0px;
+ margin-top: 2px;
}
-.openerp div.oe_msg_content {
- float: left;
+/* a) Indented Messages */
+
+.openerp .oe_mail .oe_msg_indented{
+ background: #FFF;
+ border: none;
+ margin-bottom:0px;
+ min-height:38px;
+}
+.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_icon{
+ width:32px;
+ margin:2px;
+ border-radius:2px;
+}
+.openerp .oe_mail .oe_msg .oe_subtle{
+ color: #B7B7D5;
+}
+.openerp .oe_mail .oe_msg_indented .oe_msg_center{
+ margin-left:34px;
+}
+.openerp .oe_mail .oe_msg.oe_msg_indented .oe_msg_content{
+ padding-top:2px;
+}
+/* b) Votes (likes) */
+.openerp .oe_mail .oe_mail_vote_count{
+ display: inline;
position: relative;
- width: 486px;
-}
-
-.openerp div.oe_msg_content > li {
- float: left;
+ background: #7C7BAD;
+ color: white;
+ text-shadow: none;
+ border-radius: 3px;
+ margin: 0px;
+ padding-left: 3px;
+ padding-right: 18px;
margin-right: 3px;
}
-
-.openerp .oe_msg_content:after {
- content: "";
- display: block;
- clear: both;
+.openerp .oe_mail .oe_mail_vote_count .oe_e{
+ position: absolute;
+ bottom: 1px;
+ right: 2px;
+ font-size: 26px;
}
-.openerp .oe_chatter a {
- cursor: pointer;
-}
+/* c) Message action icons */
-.openerp img.oe_mail_icon {
- width: 50px;
- height: 50px;
+.openerp .oe_mail .oe_msg.oe_msg_unread .oe_unread{
+ display:none;
}
-
-.openerp img.oe_mail_thumbnail {
- width: 28px;
- height: 28px;
- margin: 4px;
+.openerp .oe_mail .oe_msg.oe_msg_read .oe_read{
+ display:none;
}
-
-.openerp img.oe_mail_frame {
+.openerp .oe_mail .oe_msg .oe_msg_icons{
+ float: right;
+ margin-top: 4px;
+ margin-right: 8px;
+ margin-left: 8px;
+ height: 24px;
+ -webkit-user-select: none;
+}
+.openerp .oe_mail .oe_msg .oe_msg_icons span{
+ float:right;
+ width:24px;
+ height:24px;
+ line-height:24px;
text-align: center;
- overflow: hidden;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- -o-border-radius: 3px;
- -ms-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- -o-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
- clip: rect(5px, 40px, 45px, 0px);
+}
+.openerp .oe_mail .oe_msg .oe_msg_icons a {
+ text-decoration: none;
+ color: #FFF;
+ text-shadow: 0px 1px #AAA,0px -1px #AAA, -1px 0px #AAA, 1px 0px #AAA, 0px 3px 3px rgba(0,0,0,0.1);
+ -webkit-transition: all 0.2s linear;
+}
+.openerp .oe_mail .oe_msg:hover .oe_msg_icons a{
+ opacity: 1;
+ -webkit-transition: all 0.1s linear;
+}
+.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star:hover a{
+ color: #FFF6C0;
+ text-shadow: 0px 1px #FFA162,0px -1px #FFA162, -1px 0px #FFA162, 1px 0px #FFA162, 0px 3px 3px rgba(0,0,0,0.1);
+}
+.openerp .oe_mail .oe_msg .oe_msg_icons .oe_star.oe_starred a{
+ color: #FFE41F;
+ text-shadow: 0px 1px #DF6200,0px -1px #DF6200, -1px 0px #DF6200, 1px 0px #DF6200, 0px 3px 3px rgba(0,0,0,0.1);
+}
+.openerp .oe_mail .oe_msg .oe_msg_icons .oe_reply:hover a{
+ color: #1fc0ff;
+ text-shadow: 0px 1px #184fc5,0px -1px #184fc5, -1px 0px #184fc5, 1px 0px #184fc5, 0px 3px 3px rgba(0,0,0,0.1);
+}
+.openerp .oe_mail .oe_msg .oe_msg_icons .oe_read:hover a{
+ color: #bbbaff;
+ text-shadow: 0px 1px #7c7bad,0px -1px #7c7bad, -1px 0px #7c7bad, 1px 0px #7c7bad, 0px 3px 3px rgba(0,0,0,0.1);
+}
+.openerp .oe_mail .oe_msg .oe_msg_icons .oe_unread:hover a{
+ color: #c2ff00;
+ text-shadow: 0px 1px #009441,0px -1px #009441, -1px 0px #009441, 1px 0px #009441, 0px 3px 3px rgba(0,0,0,0.1);
+}
+.openerp .oe_mail .oe_msg .oe_msg_content textarea{
+ width: 100%;
+ height: 32px;
+ margin: 0px;
+ padding: 0px;
+ resize: vertical;
+
+ padding: 4px;
+}
+.openerp .oe_mail .oe_msg.oe_msg_composer_compact, .openerp .oe_mail .oe_msg.oe_msg_expandable{
+ padding:4px;
+ min-height:0px;
+}
+.openerp .oe_mail .oe_msg.oe_msg_composer_compact textarea{
+ height: 24px;
+ width: 100%;
}
-.openerp .oe_mail_invisible {
+/* ---------------- MESSAGE QUICK COMPOSER --------------- */
+
+.openerp .oe_mail .oe_msg_composer .oe_msg_footer{
+ padding-right:4px;
+ padding-top: 2px;
+ padding-bottom:6px;
+}
+.openerp .oe_mail .oe_msg_attachments.oe_hidden,
+.openerp .oe_mail .oe_msg_images.oe_hidden{
+ margin:0px;
+ border: none;
display: none;
}
-
-/* ------------------------------------------------------------ */
-/* Messages layout
-/* ------------------------------------------------------------ */
-
-.openerp .oe_mail_msg .oe_msg_title {
- margin: 0;
- font-size: 1.3em;
- font-weight: bold;
+.openerp .oe_mail .oe_msg_attachments{
+ margin-bottom: 4px;
+ margin-right: 0px;
+ font-size: 12px;
+ border-radius: 2px;
+ border: solid 1px rgba(124,123,173,0.14);
}
-.openerp .oe_mail_msg .oe_msg_title a:link,
-.openerp .oe_mail_msg .oe_msg_title a:visited {
- color: #4C4C4C;
+.openerp .oe_mail .oe_msg_attachments .oe_attachment{
+ padding: 2px;
+ padding-left: 4px;
+ padding-right: 4px;
+}
+.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e{
+ font-size: 23px;
+ margin-top: -5px;
+}
+.openerp .oe_mail .oe_msg_attachments .oe_attachment .oe_e:hover{
+ text-decoration: none;
+}
+.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(odd){
+ background:white;
+}
+.openerp .oe_mail .oe_msg_attachments .oe_attachment:nth-child(even){
+ background: #F4F5FA;
+}
+.openerp .oe_mail .oe_msg_images {
+ display: block;
+}
+.openerp .oe_mail .oe_msg_footer button{
+ display: inline;
+ height: 24px;
+ font-size: 12px;
+ line-height: 12px;
+ vertical-align: middle;
+}
+.openerp .oe_mail .oe_msg_footer button.oe_attach{
+ width: 24px;
+ overflow: hidden;
+}
+.openerp .oe_mail .oe_msg_footer button.oe_attach .oe_e{
+ position: relative;
+ top: -1px;
+ left: -9px;
+}
+.openerp .oe_mail .oe_hidden_input_file, .openerp .oe_mail .oe_hidden_input_file form{
+ display:inline;
+}
+.openerp .oe_mail .oe_msg_footer button.oe_full{
+ width:24px;
+ overflow:hidden;
+ float: right;
+}
+.openerp .oe_mail .oe_msg_footer button.oe_full .oe_e{
+ position: relative;
+ top: -1px;
+ left: -9px;
+}
+.openerp .oe_mail button.oe_attach, .openerp .oe_mail button.oe_full{
+ background: transparent;
+ color: #7C7BAD;
+ box-shadow: none;
+ border: none;
+ text-shadow: none;
+}
+.openerp .oe_mail .oe_attach_label{
+ color: #7C7BAD;
+ margin-left: -3px;
+}
+.openerp .oe_mail .oe_msg_footer .oe_attachment_file .oe_form_binary_file{
+ display: inline-block;
+ margin-left: -47px;
+ height: 28px;
+ width: 52px;
+ margin-top: -6px;
+}
+.openerp .oe_mail .oe_mail_list_recipients{
+ font-size: 12px;
+ margin-top: 4px;
+ margin-bottom: 4px;
+}
+
+/* ---------------- HIDDEN MESSAGES ------------------ */
+
+.openerp .oe_mail .oe_msg_content.oe_msg_more_message{
+ text-align: right;
+}
+.openerp .oe_mail .oe_msg_content.oe_msg_more_message .oe_separator{
+ height: 0;
+ border-bottom: dashed 1px #e6e6e6;
+ margin-left: -4px;
+ margin-right: 8px;
+ margin-top: 6px;
+ margin-bottom: -9px;
+}
+.openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more {
+ background: white;
+ margin-right: 280px;
+ padding-left: 8px;
+ padding-right: 8px;
+ text-decoration: none;
+ color: #b4b4b4;
+}
+.openerp .oe_mail .oe_msg_more_message .oe_msg_fetch_more:hover{
text-decoration: none;
}
-.openerp .oe_mail_msg .oe_msg_body {
- margin-bottom: .5em;
- text-align: justify;
+/* ---------------- FOLLOWERS ------------------ */
+
+.openerp .oe_followers{
+ position: relative;
+ display: inline-block;
+ padding-top: 5px;
+ width: 160px;
+ float: right;
}
-.openerp .oe_mail_msg .oe_msg_body pre {
- font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
- margin: 0px;
- white-space: pre-wrap;
+/* a) THE FOLLOW BUTTON */
+
+.openerp .oe_followers button.oe_follower{
+ display: block;
+ text-align: center;
+ width:100%;
+}
+.openerp .oe_followers button.oe_follower.oe_following{
+ background-color: #3465A4;
+ background-image: -webkit-linear-gradient(top, #729FCF, #3465A4);
+ color: white;
+}
+
+.openerp .oe_followers button.oe_follower .oe_follow,
+.openerp .oe_followers button.oe_follower .oe_unfollow,
+.openerp .oe_followers button.oe_follower .oe_following{
+ display: none;
+}
+/* a.1) when following, show 'following' */
+.openerp .oe_followers button.oe_follower.oe_following .oe_following{
+ display: inline;
+}
+/* a.2) when following and hovering, show 'unfollow' */
+.openerp .oe_followers button.oe_follower.oe_following:hover .oe_following{
+ display: none;
+}
+.openerp .oe_followers button.oe_follower.oe_following:hover .oe_unfollow{
+ display: inline;
+}
+/* a.3) when not following show 'follow' */
+.openerp .oe_followers button.oe_follower.oe_notfollow .oe_follow{
display: inline;
}
-/* Read more/less link */
-.openerp .oe_mail_msg span.oe_mail_reduce {
- position: absolute;
- right: 0;
+.openerp .oe_followers .oe_subtype_list{
+ margin-top: 4px;
}
-/* Dropdown menu */
-
-.openerp .oe_mail ul.oe_mail_thread_display ul.oe_mail_thread_display {
- position: relative;
- border-left: 1px #DDD dashed;
+/* b) THE FOLLOWERS */
+.openerp .oe_followers .oe_follower_title{
+ display: inline;
}
-
-.openerp .oe_mail ul.oe_header {
- position: absolute;
- right: 3px;
- top: -6px;
- z-index: 10;
+.openerp .oe_followers .oe_follower_title_box{
+ margin-top: 12px;
+ margin-bottom: 4px;
}
-
-.openerp .oe_mail ul.oe_header a {
- text-decoration: none;
-}
-
-.openerp .oe_mail ul.oe_header>li {
- display: inline-block;
- height: 20px;
- text-align: right;
-}
-
-/* Message footer */
-.openerp .oe_mail_msg .oe_msg_footer {
- color: #888;
-}
-.openerp .oe_mail_msg .oe_msg_footer li {
- float: left;
- margin-right: 3px;
-}
-.openerp .oe_mail_msg .oe_msg_footer li:after {
- content: " · ";
-}
-.openerp .oe_mail_msg .oe_msg_footer li:last-child:after {
- content: "";
-}
-
-/* Attachments list */
-.openerp .oe_msg_content ul.oe_msg_attachments {
- width: 100%;
- margin: .5em 0 0 0;
- padding: .5em 0;
- list-style-position: inside;
-}
-.openerp .oe_msg_content ul.oe_msg_attachments.oe_hidden {
- display: none;
-}
-.openerp .oe_msg_content ul.oe_msg_attachments li {
- float: none;
- height: 20px;
- line-height: 20px;
- margin: 0;
- padding: 0;
- list-style-type: square;
-}
-.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process {
+.openerp .oe_followers .oe_invite{
float: right;
- width: 200px;
- height: 16px;
}
-.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process div {
- float: left;
- width: 38px;
- height: 16px;
- margin-right: 2px;
- background: #66FF66;
+.openerp .oe_followers .oe_partner {
+ height: 32px;
+ overflow: hidden;
}
-.openerp .oe_msg_content ul.oe_msg_attachments .oe_upload_in_process span {
- color: #aaaaaa;
- position: absolute;
+.openerp .oe_followers .oe_partner img{
+ width: 32px;
+ margin-right:4px;
+ border-radius: 2px;
}
-/* ------------------------------------------------------------ */
-/* Topbar button
-/* ------------------------------------------------------------ */
+/* ---------------- MESSAGES BODY ------------------ */
+.openerp .oe_mail .oe_msg_content .oe_blockquote,
+.openerp .oe_mail .oe_msg_content blockquote {
+ padding: 4px;
+ border-radius: 2px;
+ border: solid 1px rgba(124,123,173,0.14);
+}
-.openerp .oe_topbar .oe_topbar_compose_full_email {
- float: right;
- margin: 3px 25px 0 0;
-}
\ No newline at end of file
+/* ----------- FORM INTEGRATION ------------ */
+
+.openerp .oe_record_thread{
+ display: block;
+ margin-right: 180px;
+}
+
+/* ----------- INBOX INTEGRATION ----------- */
+
+.openerp .oe_mail_wall .oe_mail{
+ margin: 16px;
+ width: 720px;
+}
diff --git a/addons/mail/static/src/css/mail_compose_message.css b/addons/mail/static/src/css/mail_compose_message.css
deleted file mode 100644
index c706c55bcde..00000000000
--- a/addons/mail/static/src/css/mail_compose_message.css
+++ /dev/null
@@ -1,166 +0,0 @@
-/* ------------------------------ */
-/* Compose Message */
-/* ------------------------------ */
-
-.openerp .oe_msg_content .oe_mail_compose_message_footer {
- height: 24px;
-}
-
-.openerp .oe_msg_content .oe_mail_compose_message_footer button.oe_mail_compose_message_button_send {
- float: left;
-}
-
-.openerp .oe_mail .oe_mail_compose_textarea
-{
- display: none;
-}
-
-.openerp .oe_mail .oe_mail_compose_textarea .oe_mail_post_header,
-.openerp .oe_mail .oe_mail_compose_textarea .oe_mail_post_footer,
-{
- position: relative;
-}
-
-.openerp .oe_mail .oe_mail_compose_textarea a.oe_cancel {
- position: absolute;
- right: -8px;
- top: -8px;
-}
-.openerp .oe_mail .oe_mail_compose_textarea a.oe_cancel:first-of-type {
- display:none;
-}
-
-.openerp .oe_mail .oe_mail_compose_textarea button.oe_full {
- float: right;
- position: relative;
- right: -10px;
-}
-
-/* ------------------------------------------------------------ */
-/* mail.compose.message : list_recipients
-/* ------------------------------------------------------------ */
-
-.openerp .oe_mail .oe_mail_list_recipients {
- display: inline;
-}
-.openerp .oe_mail .oe_mail_list_recipients .oe_all_follower {
- color: blue;
-}
-.openerp .oe_mail .oe_mail_list_recipients .oe_partner_follower a {
- color: red;
-}
-.openerp .oe_mail .oe_mail_list_recipients .oe_hidden,
-.openerp .oe_mail .oe_mail_list_recipients .oe_more_hidden {
- display: none;
-}
-
-/* ------------------------------------------------------------ */
-/* mail.compose.message : attachment
-/* ------------------------------------------------------------ */
-
-.openerp .oe_mail .oe_attachment_file {
- display: inline-block;
-}
-
-.openerp .oe_mail .oe_attachment_file .oe_add {
- float: left;
- width: 24px;
- height: 24px;
- position: relative;
- z-index: 10;
- left: +2px;
- top: +7px;
- overflow: hidden;
-}
-
-/* attachment button: override of openerp values */
-.openerp .oe_mail .oe_attachment_file .oe_add button,
-.openerp .oe_mail .oe_attachment_file .oe_add input.oe_insert_file {
- position: absolute;
- bottom: +0px;
- left: +0px;
- height: 24px;
- width: 24px;
- margin: 0px;
- padding: 0px;
-}
-.openerp .oe_mail .oe_attachment_file .oe_add input.oe_insert_file {
- z-index:2;
- width: 300px;
- left: -100px;
- background: transparent;
- border: 0;
- color: transparent;
-}
-.openerp .oe_mail .oe_attachment_file .oe_add button span {
- position: relative;
- bottom: +4px;
- font-size: 30px;
-}
-
-.openerp .oe_mail .oe_msg_attachments input {
- visibility: hidden;
-}
-
-.openerp .oe_mail .oe_mail_compose_attachment_list {
- clear: both;
-}
-
-/* ------------------------------------------------------------ */
-/* mail.compose.message
-/* ------------------------------------------------------------ */
-
-
-/* default textarea (oe_mail_compose_textarea), and body textarea for compose form view */
-.openerp .oe_mail.oe_semantic_html_override .oe_mail_compose_textarea textarea.field_text,
-.openerp .oe_mail div.oe_mail_compose_message_body textarea.field_text {
- width: 100%;
- min-height: 120px;
- height: auto;
- padding: 4px;
- font-size: 12px;
- border: 1px solid #cccccc;
-}
-
-/* not top textarea */
-.openerp .oe_mail.oe_semantic_html_override .oe_semantic_html_override .oe_mail_compose_textarea textarea.field_text {
- height: 60px;
-}
-
-/* form_view: delete white background */
-.openerp .oe_msg_content div.oe_formview {
- background-color: transparent;
-}
-
-.openerp .oe_msg_content div.oe_form_nosheet {
- margin: 0px;
-}
-
-.openerp .oe_msg_content table.oe_form_group {
- margin: 0px;
-}
-
-.openerp .oe_msg_content table.oe_form_field,
-.openerp .oe_msg_content div.oe_form_field {
- padding: 0px;
-}
-
-.openerp .oe_msg_content td.oe_form_group_cell {
- vertical-align: bottom;
-}
-
-/* subject: change width */
-.openerp .oe_msg_content .oe_form .oe_form_field input[type='text'] {
- width: 472px;
-}
-
-/* body_html: cleditor */
-.openerp .oe_msg_content div.cleditorMain {
- border: 1px solid #cccccc;
-}
-
-/* destination_partner_ids */
-.openerp .oe_msg_content div.text-core {
- height: 22px !important;
- width: 472px;
-}
\ No newline at end of file
diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js
index 7501eabe104..9133e5de257 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -1,4 +1,4 @@
-openerp.mail = function(session) {
+openerp.mail = function (session) {
var _t = session.web._t,
_lt = session.web._lt;
@@ -16,15 +16,16 @@ openerp.mail = function(session) {
*/
session.web.FormView = session.web.FormView.extend({
- do_action: function(action) {
+ do_action: function (action) {
if (action.res_model == 'mail.compose.message') {
/* hack for stop context propagation of wrong value
* delete this hack when a global method to clean context is create
*/
var context_keys = ['default_template_id', 'default_composition_mode',
'default_use_template', 'default_partner_ids', 'default_model',
- 'default_res_id', 'default_content_subtype', 'active_id', 'lang',
- 'bin_raw', 'tz', 'active_model', 'edi_web_url_view', 'active_ids']
+ 'default_res_id', 'default_content_subtype', , 'default_subject',
+ 'default_body', 'active_id', 'lang', 'bin_raw', 'tz',
+ 'active_model', 'edi_web_url_view', 'active_ids']
for (var key in action.context) {
if (_.indexOf(context_keys, key) == -1) {
action.context[key] = null;
@@ -51,17 +52,18 @@ openerp.mail = function(session) {
mail.ChatterUtils = {
- /** Get an image in /web/binary/image?... */
- get_image: function(session, model, field, id) {
+ /* Get an image in /web/binary/image?... */
+ get_image: function (session, model, field, id) {
return session.prefix + '/web/binary/image?session_id=' + session.session_id + '&model=' + model + '&field=' + field + '&id=' + (id || '');
},
- /** Get the url of an attachment {'id': id} */
+ /* Get the url of an attachment {'id': id} */
get_attachment_url: function (session, attachment) {
return session.origin + '/web/binary/saveas?session_id=' + session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id=' + attachment['id'];
},
- /** Replaces some expressions
+ /**
+ * Replaces some expressions
* - :name - shortcut to an image
*/
do_replace_expressions: function (string) {
@@ -78,14 +80,36 @@ openerp.mail = function(session) {
return string;
},
- /* replace textarea text into html text
- * (add , )
- * TDE note : should not be here, but server-side I think ...
- */
- get_text2html: function(text){
+ /**
+ * Replaces textarea text into html text (add , )
+ * TDE note : should be done server-side, in Python -> use mail.compose.message ?
+ */
+ get_text2html: function (text) {
return text
.replace(/[\n\r]/g,'
')
.replace(/((?:https?|ftp):\/\/[\S]+)/g,'$1 ')
+ },
+
+ /* Returns the complete domain with "&"
+ * TDE note: please add some comments to explain how/why
+ */
+ expand_domain: function (domain) {
+ var new_domain = [];
+ var nb_and = -1;
+ // TDE note: smarted code maybe ?
+ for ( var k = domain.length-1; k >= 0 ; k-- ) {
+ if ( typeof domain[k] != 'array' && typeof domain[k] != 'object' ) {
+ nb_and -= 2;
+ continue;
+ }
+ nb_and += 1;
+ }
+
+ for (var k = 0; k < nb_and ; k++) {
+ domain.unshift('&');
+ }
+
+ return domain;
}
};
@@ -97,6 +121,8 @@ openerp.mail = function(session) {
*
* This widget handles the display of a form to compose a new message.
* This form is a mail.compose.message form_view.
+ * On first time : display a compact textarea that is not the compose form.
+ * When the user focuses the textarea, the compose message is instantiated.
*/
mail.ThreadComposeMessage = session.web.Widget.extend({
@@ -108,60 +134,50 @@ openerp.mail = function(session) {
* @param {Object} [context] context passed to the
* mail.compose.message DataSetSearch. Please refer to this model
* for more details about fields and default values.
- * @param {Boolean} [show_attachment_delete]
*/
- init: function (parent, options) {
+
+ init: function (parent, datasets, options) {
var self = this;
this._super(parent);
this.context = options.context || {};
+ this.options = options.options;
- this.datasets = {
- 'attachment_ids' : [],
- 'id': options.datasets.id,
- 'model': options.datasets.model,
- 'res_model': options.datasets.res_model,
- 'is_private': options.datasets.is_private || false,
- 'partner_ids': options.datasets.partner_ids || []
- };
- this.options={};
- this.options.thread={};
- this.options.thread.show_header_compose = options.options.thread.show_header_compose;
- this.options.thread.display_on_thread = options.options.thread.display_on_thread;
- this.options.thread.show_attachment_delete = true;
- this.options.thread.show_attachment_link = true;
+ this.show_compact_message = false;
+ // data of this compose message
+ this.attachment_ids = [];
+ this.id = datasets.id;
+ this.model = datasets.model;
+ this.res_model = datasets.res_model;
+ this.is_private = datasets.is_private || false;
+ this.partner_ids = datasets.partner_ids || [];
+ this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
+ this.thread_level = datasets.thread_level;
this.parent_thread= parent.messages!= undefined ? parent : false;
this.ds_attachment = new session.web.DataSetSearch(this, 'ir.attachment');
+ this.show_delete_attachment = true;
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
$(window).on(self.fileupload_id, self.on_attachment_loaded);
},
- start: function(){
+ start: function () {
this.display_attachments();
this.bind_events();
-
- //load avatar img
- var user_avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
- this.$('img.oe_mail_icon').attr('src', user_avatar);
},
/* upload the file on the server, add in the attachments list and reload display
*/
- display_attachments: function(){
- var self = this;
- var render = $(session.web.qweb.render('mail.thread.message.attachments', {'widget': self}));
- if(!this.list_attachment){
- this.$('.oe_mail_compose_attachment_list').replaceWith( render );
- } else {
- this.list_attachment.replaceWith( render );
- }
- this.list_attachment = this.$("ul.oe_msg_attachments");
-
+ display_attachments: function () {
+ this.$(".oe_msg_attachment_list").html(
+ session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) );
// event: delete an attachment
- this.$el.on('click', '.oe_mail_attachment_delete', self.on_attachment_delete);
+ this.$(".oe_msg_attachment_list").on('click', '.oe_mail_attachment_delete', this.on_attachment_delete);
},
+
+ /* when a user click on the upload button, send file read on_attachment_loaded
+ */
on_attachment_change: function (event) {
event.stopPropagation();
var self = this;
@@ -172,25 +188,24 @@ openerp.mail = function(session) {
// if the files exits for this answer, delete the file before upload
var attachments=[];
- for(var i in this.datasets.attachment_ids){
- if((this.datasets.attachment_ids[i].filename || this.datasets.attachment_ids[i].name) == filename){
- if(this.datasets.attachment_ids[i].upload){
+ for (var i in this.attachment_ids) {
+ if ((this.attachment_ids[i].filename || this.attachment_ids[i].name) == filename) {
+ if (this.attachment_ids[i].upload) {
return false;
}
- this.ds_attachment.unlink([this.datasets.attachment_ids[i].id]);
+ this.ds_attachment.unlink([this.attachment_ids[i].id]);
} else {
- attachments.push(this.datasets.attachment_ids[i]);
+ attachments.push(this.attachment_ids[i]);
}
}
- this.datasets.attachment_ids = attachments;
+ this.attachment_ids = attachments;
// submit file
- //session.web.blockUI();
- self.$('form.oe_form_binary_form').submit();
+ this.$('form.oe_form_binary_form').submit();
this.$(".oe_attachment_file").hide();
- this.datasets.attachment_ids.push({
+ this.attachment_ids.push({
'id': 0,
'name': filename,
'filename': filename,
@@ -201,11 +216,12 @@ openerp.mail = function(session) {
}
},
+ /* when the file is uploaded
+ */
on_attachment_loaded: function (event, result) {
- //session.web.unblockUI();
- for(var i in this.datasets.attachment_ids){
- if(this.datasets.attachment_ids[i].filename == result.filename && this.datasets.attachment_ids[i].upload) {
- this.datasets.attachment_ids[i]={
+ for (var i in this.attachment_ids) {
+ if (this.attachment_ids[i].filename == result.filename && this.attachment_ids[i].upload) {
+ this.attachment_ids[i]={
'id': result.id,
'name': result.name,
'filename': result.filename,
@@ -219,6 +235,7 @@ openerp.mail = function(session) {
$input.after($input.clone(true)).remove();
this.$(".oe_attachment_file").show();
},
+
/* unlink the file on the server and reload display
*/
on_attachment_delete: function (event) {
@@ -226,61 +243,58 @@ openerp.mail = function(session) {
var attachment_id=$(event.target).data("id");
if (attachment_id) {
var attachments=[];
- for(var i in this.datasets.attachment_ids){
- if(attachment_id!=this.datasets.attachment_ids[i].id){
- attachments.push(this.datasets.attachment_ids[i]);
+ for (var i in this.attachment_ids) {
+ if (attachment_id!=this.attachment_ids[i].id) {
+ attachments.push(this.attachment_ids[i]);
}
else {
this.ds_attachment.unlink([attachment_id]);
}
}
- this.datasets.attachment_ids = attachments;
+ this.attachment_ids = attachments;
this.display_attachments();
}
},
- /* to avoid having unsorted file on the server.
- we will show the users files of the first message post
- TDE note: unnecessary call to server I think
- */
- // set_free_attachments: function(){
- // var self=this;
- // this.parent_thread.ds_message.call('user_free_attachment').then(function(attachments){
- // this.attachment_ids=[];
- // for(var i in attachments){
- // self.attachment_ids[i]={
- // 'id': attachments[i].id,
- // 'name': attachments[i].name,
- // 'filename': attachments[i].filename,
- // 'url': mail.ChatterUtils.get_attachment_url(self.session, attachments[i])
- // };
- // }
- // self.display_attachments();
- // });
- // },
-
- bind_events: function() {
+ bind_events: function () {
var self = this;
+ this.$('textarea.oe_compact').on('focus', _.bind( this.on_compose_expandable, this));
+
// set the function called when attachments are added
- this.$el.on('change', 'input.oe_form_binary_file', self.on_attachment_change );
- this.$el.on('click', 'a.oe_cancel', self.on_cancel );
- this.$el.on('click', 'button.oe_post', function(){self.on_message_post()} );
- this.$el.on('click', 'button.oe_full', function(){self.on_compose_fullmail()} );
+ this.$el.on('change', 'input.oe_form_binary_file', _.bind( this.on_attachment_change, this) );
+
+ this.$el.on('click', '.oe_cancel', _.bind( this.on_cancel, this) );
+ this.$el.on('click', '.oe_post', _.bind( this.on_message_post, this) );
+ this.$el.on('click', '.oe_full', _.bind( this.on_compose_fullmail, this, 'reply') );
+
+ /* stack for don't close the compose form if the user click on a button */
+ this.$el.on('mousedown', '.oe_msg_footer', _.bind( function () { this.stay_open = true; }, this));
+ this.$('textarea:not(.oe_compact):first').on('focus, mouseup, keydown', _.bind( function () { this.stay_open = false; }, this));
+ this.$('textarea:not(.oe_compact):first').autosize();
+
+ // auto close
+ this.$el.on('blur', 'textarea:not(.oe_compact):first', _.bind( this.on_compose_expandable, this));
},
- on_compose_fullmail: function(){
- var attachments=[];
- for(var i in this.datasets.attachment_ids){
- attachments.push(this.datasets.attachment_ids[i].id);
- }
- /* TDE note: I think this is not necessary, because
- * 1/ post on a document: followers added server-side in _notify
- * 2/ reply to a message: mail.compose.message should add the previous partners
- */
- var partner_ids=[];
- for(var i in this.datasets.partner_ids){
- partner_ids.push(this.datasets.partner_ids[i][0]);
+ on_compose_fullmail: function (default_composition_mode) {
+ if (default_composition_mode == 'reply') {
+ var context = {
+ 'default_composition_mode': default_composition_mode,
+ 'default_parent_id': this.id,
+ 'default_body': mail.ChatterUtils.get_text2html(this.$el ? (this.$el.find('textarea:not(.oe_compact)').val() || '') : ''),
+ 'default_attachment_ids': this.attachment_ids,
+ };
+ } else {
+ var context = {
+ 'default_model': this.context.default_model,
+ 'default_res_id': this.context.default_res_id,
+ 'default_content_subtype': 'html',
+ 'default_composition_mode': default_composition_mode,
+ 'default_parent_id': this.id,
+ 'default_body': mail.ChatterUtils.get_text2html(this.$el ? (this.$el.find('textarea:not(.oe_compact)').val() || '') : ''),
+ 'default_attachment_ids': this.attachment_ids,
+ };
}
var action = {
type: 'ir.actions.act_window',
@@ -290,70 +304,108 @@ openerp.mail = function(session) {
action_from: 'mail.ThreadComposeMessage',
views: [[false, 'form']],
target: 'new',
- context: {
- 'default_model': this.context.default_model,
- 'default_res_id': this.context.default_res_id,
- 'default_content_subtype': 'html',
- 'default_parent_id': this.context.default_parent_id,
- 'default_body': mail.ChatterUtils.get_text2html(this.$('textarea').val() || ''),
- 'default_attachment_ids': attachments,
- 'default_partner_ids': partner_ids
- },
+ context: context,
};
+
this.do_action(action);
+ this.on_cancel();
},
- on_cancel: function(event){
- if(event) event.stopPropagation();
- this.$('textarea').val("");
- this.$('input[data-id]').remove();
- //this.attachment_ids=[];
+ reinit: function() {
+ var $render = $( session.web.qweb.render('mail.compose_message', {'widget': this}) );
+
+ $render.insertAfter(this.$el.last());
+ this.$el.remove();
+ this.$el = $render;
+
this.display_attachments();
- if(!this.options.thread.show_header_compose || !this.options.thread.display_on_thread[0]){
- this.$el.hide();
- }
+ this.bind_events();
+ },
+
+ on_cancel: function (event) {
+ if (event) event.stopPropagation();
+ this.attachment_ids=[];
+ this.stay_open = false;
+ this.show_composer = false;
+ this.reinit();
},
/*post a message and fetch the message*/
- on_message_post: function (body) {
+ on_message_post: function (event) {
var self = this;
- if (! body) {
- var comment_node = this.$('textarea');
- var body = comment_node.val();
- comment_node.val('');
- }
+ var comment_node = this.$('textarea');
+ var body = comment_node.val();
+ comment_node.val('');
var attachments=[];
- for(var i in this.datasets.attachment_ids){
- if(this.datasets.attachment_ids[i].upload){
+ for (var i in this.attachment_ids) {
+ if (this.attachment_ids[i].upload) {
session.web.dialog($('
' + session.web.qweb.render('CrashManager.warning', {message: 'Please, wait while the file is uploading.'}) + '
'));
return false;
}
- attachments.push(this.datasets.attachment_ids[i].id);
+ attachments.push(this.attachment_ids[i].id);
}
- if(body.match(/\S+/)) {
+ if (body.match(/\S+/)) {
+ //session.web.blockUI();
this.parent_thread.ds_thread.call('message_post_api', [
this.context.default_res_id,
mail.ChatterUtils.get_text2html(body),
false,
- 'comment',
- 'mail.mt_comment',
this.context.default_parent_id,
attachments,
this.parent_thread.context
- ]).then(function(records){
- self.parent_thread.switch_new_message(records);
- self.datasets.attachment_ids=[];
- self.on_cancel();
+ ]).then(function (record) {
+ var thread = self.parent_thread;
+ // create object and attach to the thread object
+ thread.message_fetch(false, false, [record], function (arg, data) {
+ var message = thread.create_message_object( data[0] );
+ // insert the message on dom
+ thread.insert_message( message, self.$el );
+ if (thread.parent_message) {
+ self.$el.remove();
+ self.parent_thread.compose_message = null;
+ } else {
+ self.on_cancel();
+ }
+ });
+ //session.web.unblockUI();
});
return true;
}
},
+
+ /* convert the compact mode into the compose message
+ */
+ on_compose_expandable: function (event) {
+
+ if (!this.stay_open && (!this.show_composer || !this.$('textarea:not(.oe_compact)').val().match(/\S+/))) {
+ this.show_composer = !this.show_composer || this.stay_open;
+ this.reinit();
+ }
+ if (!this.stay_open && this.show_composer) {
+ this.$('textarea:not(.oe_compact):first').focus();
+ }
+ return true;
+ },
+
+ do_hide_compact: function () {
+ this.show_compact_message = false;
+ if (!this.show_composer) {
+ this.reinit();
+ }
+ },
+
+ do_show_compact: function () {
+ this.show_compact_message = true;
+ if (!this.show_composer) {
+ this.reinit();
+ }
+ }
});
- /**
+ /**
* ------------------------------------------------------------
* Thread Message Expandable Widget
* ------------------------------------------------------------
@@ -369,71 +421,81 @@ openerp.mail = function(session) {
mail.ThreadExpandable = session.web.Widget.extend({
template: 'mail.thread.expandable',
- init: function(parent, options) {
+ init: function (parent, datasets, context) {
this._super(parent);
- this.domain = options.domain || [];
+ this.domain = datasets.domain || [];
+ this.options = datasets.options;
this.context = _.extend({
default_model: 'mail.thread',
default_res_id: 0,
- default_parent_id: false }, options.context || {});
+ default_parent_id: false }, context || {});
- this.datasets = {
- 'id' : options.datasets.id || -1,
- 'model' : options.datasets.model || false,
- 'parent_id' : options.datasets.parent_id || false,
- 'nb_messages' : options.datasets.nb_messages || 0,
- 'type' : 'expandable',
- 'max_limit' : options.datasets.max_limit || false,
- 'flag_used' : false,
- };
-
- // record options and data
- this.parent_thread= parent.messages!= undefined ? parent : options.options.thread._parents[0] ;
+ // data of this expandable message
+ this.id = datasets.id || -1,
+ this.model = datasets.model || false,
+ this.parent_id = datasets.parent_id || false,
+ this.nb_messages = datasets.nb_messages || 0,
+ this.thread_level = datasets.thread_level || 0,
+ this.type = 'expandable',
+ this.max_limit = this.id < 0 || false,
+ this.flag_used = false,
+ this.parent_thread= parent.messages!= undefined ? parent : this.options._parents[0];
},
- start: function() {
+ start: function () {
this._super.apply(this, arguments);
this.bind_events();
},
+ reinit: function () {
+ var $render = $(session.web.qweb.render('mail.thread.expandable', {'widget': this}));
+ this.$el.replaceWith( $render );
+ this.$el = $render;
+ this.bind_events();
+ },
+
/**
* Bind events in the widget. Each event is slightly described
* in the function. */
- bind_events: function() {
- var self = this;
- this.$el.on('click', 'a.oe_mail_fetch_more', self.on_expandable);
+ bind_events: function () {
+ this.$el.on('click', 'a.oe_msg_fetch_more', this.on_expandable);
},
- animated_destroy: function(options) {
+ animated_destroy: function (fadeTime) {
var self=this;
- //graphic effects
- if(options && options.fadeTime) {
- self.$el.fadeOut(options.fadeTime, function(){
- self.destroy();
- });
- } else {
+ this.$el.fadeOut(fadeTime, function () {
self.destroy();
- }
+ });
},
/*The selected thread and all childs (messages/thread) became read
* @param {object} mouse envent
*/
on_expandable: function (event) {
- if(event)event.stopPropagation();
- if(this.datasets.flag_used) {
+ if (event)event.stopPropagation();
+ if (this.flag_used) {
return false
}
- this.datasets.flag_used = true;
+ this.flag_used = true;
- this.animated_destroy({'fadeTime':300});
- this.parent_thread.message_fetch(false, this.domain, this.context);
+ this.animated_destroy(200);
+ this.parent_thread.message_fetch(this.domain, this.context);
return false;
},
+
+ /**
+ * call on_message_delete on his parent thread
+ */
+ destroy: function () {
+
+ this._super();
+ this.parent_thread.on_message_detroy(this);
+
+ }
});
- /**
+ /**
* ------------------------------------------------------------
* Thread Message Widget
* ------------------------------------------------------------
@@ -463,80 +525,56 @@ openerp.mail = function(session) {
* @param {Object} [options]
* @param {Object} [thread] read obout mail.Thread object
* @param {Object} [message]
- * @param {Number} [message_ids=null] ids for message_fetch
- * @param {Number} [message_data=null] already formatted message data,
- * for subthreads getting data from their parent
* @param {Number} [truncate_limit=250] number of character to
* display before having a "show more" link; note that the text
* will not be truncated if it does not have 110% of the parameter
* @param {Boolean} [show_record_name]
- * @param {Boolean} [show_dd_delete]
- * @param {Array [A,B]} [show_reply] display the reply button on the
- * message for thread level between A and B. -1 for no begin or no end.
- * @param {Array [A,B]} [show_read_unread] display the read/unread button on the
- * message for thread level between A and B. -1 for no begin or no end.
+ *... @param {int} [show_reply_button] number thread level to display the reply button
+ *... @param {int} [show_read_unread_button] number thread level to display the read/unread button
*/
- init: function(parent, options) {
+ init: function (parent, datasets, context) {
this._super(parent);
- // record datasets
- var param = options.datasets;
- this.datasets = _.extend({
- 'id' : -1,
- 'model' : false,
- 'parent_id': false,
- 'res_id' : false,
- 'type' : false,
- 'is_author' : false,
- 'is_private' : false,
- 'subject' : false,
- 'name' : false,
- 'record_name' : false,
- 'body' : false,
- 'vote_user_ids' :[],
- 'has_voted' : false,
- 'is_favorite' : false,
- 'thread_level' : 0,
- 'to_read' : true,
- 'author_id' : [],
- 'attachment_ids' : [],
- }, param || {});
- this.datasets._date = param.date;
-
// record domain and context
- this.domain = options.domain || [];
+ this.domain = datasets.domain || [];
this.context = _.extend({
default_model: 'mail.thread',
default_res_id: 0,
- default_parent_id: false }, options.context || {});
+ default_parent_id: false }, context || {});
// record options
- this.options={
- 'thread' : options.options.thread,
- 'message' : {
- 'message_ids': options.options.message.message_ids || null,
- 'message_data': options.options.message.message_data || null,
- 'show_record_name': options.options.message.show_record_name != undefined ? options.options.message.show_record_name: true,
- 'show_dd_delete': options.options.message.show_dd_delete || false,
- 'truncate_limit': options.options.message.truncate_limit || 250,
- 'show_reply': options.options.message.show_reply || [0,-1],
- 'show_read_unread': options.options.message.show_read_unread || [0,-1],
- }
- };
+ this.options = datasets.options;
- this.datasets.show_reply = this.options.message.show_reply[0]>=0 &&
- this.options.message.show_reply[0]<=this.datasets.thread_level &&
- (this.options.message.show_reply[1]<0 || this.options.message.show_reply[1]>=this.datasets.thread_level);
+ // data of this message
+ this.id = datasets.id || -1,
+ this.model = datasets.model || false,
+ this.parent_id = datasets.parent_id || false,
+ this.res_id = datasets.res_id || false,
+ this.type = datasets.type || false,
+ this.is_author = datasets.is_author || false,
+ this.is_private = datasets.is_private || false,
+ this.subject = datasets.subject || false,
+ this.name = datasets.name || false,
+ this.record_name = datasets.record_name || false,
+ this.body = datasets.body || false,
+ this.vote_nb = datasets.vote_nb || 0,
+ this.has_voted = datasets.has_voted || false,
+ this.is_favorite = datasets.is_favorite || false,
+ this.thread_level = datasets.thread_level || 0,
+ this.to_read = datasets.to_read || false,
+ this.author_id = datasets.author_id || [],
+ this.attachment_ids = datasets.attachment_ids || [],
+ this._date = datasets.date;
- this.datasets.show_read_unread = this.options.message.show_read_unread[0]>=0 &&
- this.options.message.show_read_unread[0]<=this.datasets.thread_level &&
- (this.options.message.show_read_unread[1]<0 || this.options.message.show_read_unread[1]>=this.datasets.thread_level);
+
+ this.show_reply_button = this.options.show_compose_message && this.options.show_reply_button > this.thread_level;
+ this.show_read_unread_button = this.options.show_read_unread_button > this.thread_level;
// record options and data
- this.parent_thread= parent.messages!= undefined ? parent : options.options.thread._parents[0];
+ this.parent_thread= parent.messages!= undefined ? parent : this.options._parents[0];
this.thread = false;
- if( this.datasets.id > 0 ) {
+ if ( this.id > 0 ) {
this.formating_data();
}
@@ -545,63 +583,93 @@ openerp.mail = function(session) {
this.ds_follow = new session.web.DataSetSearch(this, 'mail.followers');
},
- formating_data: function(){
+ /* Convert date, timerelative and avatar in displayable data. */
+ formating_data: function () {
//formating and add some fields for render
- this.datasets.date = session.web.format_value(this.datasets._date, {type:"datetime"});
- this.datasets.timerelative = $.timeago(this.datasets.date);
- if (this.datasets.type == 'email') {
- this.datasets.avatar = ('/mail/static/src/img/email_icon.png');
+ this.date = session.web.format_value(this._date, {type:"datetime"});
+ this.timerelative = $.timeago(this.date);
+ if (this.type == 'email') {
+ this.avatar = ('/mail/static/src/img/email_icon.png');
} else {
- this.datasets.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.datasets.author_id[0]);
+ this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]);
}
- for (var l in this.datasets.attachment_ids) {
- var attach = this.datasets.attachment_ids[l];
+ for (var l in this.attachment_ids) {
+ var attach = this.attachment_ids[l];
attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
+
+ if ((attach.filename || attach.name).match(/[.](jpg|jpg|gif|png|tif|svg)$/i)) {
+ attach.is_image = true;
+ attach['url'] = mail.ChatterUtils.get_image(this.session, 'ir.attachment', 'datas', attach.id);
+ }
}
},
- start: function() {
+ start: function () {
this._super.apply(this, arguments);
this.expender();
- this.$el.hide().fadeIn(750);
+ this.$el.hide().fadeIn(750, function () {$(this).css('display', '');});
+ this.resize_img();
this.bind_events();
- this.create_thread();
+ if(this.thread_level < this.options.display_indented_thread) {
+ this.create_thread();
+ }
+ this.$('.oe_msg_attachments, .oe_msg_images').addClass("oe_hidden");
+ },
+
+ resize_img: function () {
+ var resize = function () {
+ var h = $(this).height();
+ var w = $(this).width();
+ if ( h > 100 || w >100 ) {
+ var ratio = 100 / (h > w ? h : w);
+ $(this).attr("width", parseInt( w*ratio )).attr("height", parseInt( h*ratio ));
+ }
+ };
+ this.$("img").load(resize).each(resize);
},
/**
* Bind events in the widget. Each event is slightly described
* in the function. */
- bind_events: function() {
+ bind_events: function () {
var self = this;
// event: click on 'Attachment(s)' in msg
- this.$('a.oe_msg_view_attachments:first').on('click', function (event) {
- self.$('.oe_msg_attachments:first').toggle();
+ this.$('.oe_mail_msg_view_attachments').on('click', function (event) {
+ var attach = self.$('.oe_msg_attachments:first, .oe_msg_images:first');
+ if ( self.$('.oe_msg_attachments:first').hasClass("oe_hidden") ) {
+ attach.removeClass("oe_hidden");
+ } else {
+ attach.addClass("oe_hidden");
+ }
+ self.resize_img();
});
// event: click on icone 'Read' in header
- this.$el.on('click', 'a.oe_read', this.on_message_read_unread);
+ this.$el.on('click', '.oe_read', this.on_message_read_unread);
// event: click on icone 'UnRead' in header
- this.$el.on('click', 'a.oe_unread', this.on_message_read_unread);
+ this.$el.on('click', '.oe_unread', this.on_message_read_unread);
// event: click on 'Delete' in msg side menu
- this.$el.on('click', 'a.oe_msg_delete', this.on_message_delete);
+ this.$el.on('click', '.oe_msg_delete', this.on_message_delete);
// event: click on 'Reply' in msg
- this.$el.on('click', 'a.oe_reply', this.on_message_reply);
+ this.$el.on('click', '.oe_reply', this.on_message_reply);
// event: click on 'Vote' button
- this.$el.on('click', 'button.oe_msg_vote', this.on_vote);
- // event: click on 'Star' button
- this.$el.on('click', 'button.oe_mail_starbox', this.on_star);
+ this.$el.on('click', '.oe_msg_vote', this.on_vote);
+ // event: click on 'starred/favorite' button
+ this.$el.on('click', '.oe_star', this.on_star);
},
- on_message_reply:function(event){
+ /* Call the on_compose_message on the thread of this message. */
+ on_message_reply:function (event) {
event.stopPropagation();
+ this.create_thread();
this.thread.on_compose_message();
return false;
},
- expender: function(){
- this.$('div.oe_msg_body:first').expander({
+ expender: function () {
+ this.$('.oe_msg_body:first').expander({
slicePoint: this.options.truncate_limit,
expandText: 'read more',
userCollapseText: '[^]',
@@ -611,52 +679,55 @@ openerp.mail = function(session) {
});
},
- create_thread: function(){
- var self=this;
- if(this.thread){
+ /**
+ * Instantiate the thread object of this message.
+ * Each message have only one thread.
+ */
+ create_thread: function () {
+ if (this.thread) {
return false;
}
-
/*create thread*/
- self.thread = new mail.Thread(self, {
- 'domain': self.domain,
+ this.thread = new mail.Thread(this, this, {
+ 'domain': this.domain,
'context':{
- 'default_model': self.datasets.model,
- 'default_res_id': self.datasets.res_id,
- 'default_parent_id': self.datasets.id
+ 'default_model': this.model,
+ 'default_res_id': this.res_id,
+ 'default_parent_id': this.id
},
- 'options': {
- 'thread' : self.options.thread,
- 'message' : self.options.message
- },
- 'datasets': self.datasets
+ 'options': this.options
}
);
/*insert thread in parent message*/
- self.thread.appendTo(self.$el.find('div.oe_thread_placeholder'));
+ this.thread.insertAfter(this.$el);
},
- animated_destroy: function(options) {
+ /**
+ * Fade out the message and his child thread.
+ * Then this object is destroyed.
+ */
+ animated_destroy: function (fadeTime) {
var self=this;
- //graphic effects
- if(options && options.fadeTime) {
- self.$el.fadeOut(options.fadeTime, function(){
- self.destroy();
- });
- } else {
- self.destroy();
+ this.$el.fadeOut(fadeTime, function () {
+ self.parent_thread.message_to_expandable(self);
+ });
+ if (this.thread) {
+ this.thread.$el.fadeOut(fadeTime);
}
},
+ /**
+ * Wait a confirmation for delete the message on the DB.
+ * Make an animate destroy
+ */
on_message_delete: function (event) {
event.stopPropagation();
if (! confirm(_t("Do you really want to delete this message?"))) { return false; }
- this.animated_destroy({fadeTime:250});
+ this.animated_destroy(150);
// delete this message and his childs
- var ids = [this.datasets.id].concat( this.get_child_ids() );
+ var ids = [this.id].concat( this.get_child_ids() );
this.ds_message.unlink(ids);
- this.animated_destroy();
return false;
},
@@ -664,117 +735,125 @@ openerp.mail = function(session) {
* @param {object} mouse envent
*/
on_message_read_unread: function (event) {
- // TDE note: code here seems complicated... just check that current message is read (value coming from server)
- // and send its opposite to set_message_read
event.stopPropagation();
- // if this message is read, all childs message display is read
- var ids = [this.datasets.id].concat( this.get_child_ids() );
- var read = $(event.srcElement).hasClass("oe_read");
- this.$el.removeClass("oe_mail_" + (read?"un":"") + "read").addClass("oe_mail_" + (read?"":"un") + "read");
+ var self=this;
- if( (read && this.options.thread.typeof_thread == 'inbox') ||
- (!read && this.options.thread.typeof_thread == 'archives')) {
- this.animated_destroy({fadeTime:250});
+ if ( (this.to_read && this.options.typeof_thread == 'inbox') ||
+ (!this.to_read && this.options.typeof_thread == 'archives')) {
+ this.animated_destroy(150);
}
- // TDE note: should have a context here
- this.ds_notification.call('set_message_read', [ids, read]);
+
+ // if this message is read, all childs message display is read
+ this.ds_notification.call('set_message_read', [ [this.id].concat( this.get_child_ids() ) , this.to_read, this.context]).pipe(function () {
+ self.$el.removeClass(self.to_read ? 'oe_msg_unread':'oe_msg_read').addClass(self.to_read ? 'oe_msg_read':'oe_msg_unread');
+ self.to_read = !self.to_read;
+ });
return false;
},
- /** browse message
+ /**
+ * search a message in all thread and child thread.
+ * This method return an object message.
* @param {object}{int} option.id
* @param {object}{string} option.model
* @param {object}{boolean} option._go_thread_wall
* private for check the top thread
* @return thread object
*/
- browse_message: function(options){
+ browse_message: function (options) {
// goto the wall thread for launch browse
- if(!options._go_thread_wall) {
+ if (!options._go_thread_wall) {
options._go_thread_wall = true;
- for(var i in this.options.thread._parents[0].messages){
- var res=this.options.thread._parents[0].messages[i].browse_message(options);
- if(res) return res;
+ for (var i in this.options._parents[0].messages) {
+ var res=this.options._parents[0].messages[i].browse_message(options);
+ if (res) return res;
}
}
- if(this.datasets.id==options.id)
+ if (this.id==options.id)
return this;
- for(var i in this.thread.messages){
- if(this.thread.messages[i].thread){
+ for (var i in this.thread.messages) {
+ if (this.thread.messages[i].thread) {
var res=this.thread.messages[i].browse_message(options);
- if(res) return res;
+ if (res) return res;
}
}
return false;
},
- /* get all child message/thread id linked
+ /* get all child message id linked.
+ * @return array of id
*/
- get_child_ids: function(){
+ get_child_ids: function () {
var res=[]
- if(arguments[0]) res.push(this.datasets.id);
- if(this.thread){
+ if (arguments[0]) res.push(this.id);
+ if (this.thread) {
res = res.concat( this.thread.get_child_ids(true) );
}
return res;
},
+ /**
+ * add or remove a vote for a message and display the result
+ */
on_vote: function (event) {
event.stopPropagation();
var self=this;
- return this.ds_message.call('vote_toggle', [[self.datasets.id]]).pipe(function(vote){
- // TDE note: to update, because vote_user_ids is about to disappear to be replaced by vote_nb (number of votes)
- self.datasets.has_voted=vote;
- if (!self.datasets.has_voted) {
- var votes=[];
- for(var i in self.datasets.vote_user_ids){
- if(self.datasets.vote_user_ids[i][0]!=self.datasets.session.uid)
- vote.push(self.datasets.vote_user_ids[i]);
- }
- self.datasets.vote_user_ids=votes;
- }
- else {
- self.datasets.vote_user_ids.push([self.session.uid, 'You']);
- }
+ return this.ds_message.call('vote_toggle', [[self.id]]).pipe(function (vote) {
+ self.has_voted = vote;
+ self.vote_nb += self.has_voted ? 1 : -1;
self.display_vote();
});
return false;
},
- // Render vote Display template.
+ /**
+ * Display the render of this message's vote
+ */
display_vote: function () {
var self = this;
var vote_element = session.web.qweb.render('mail.thread.message.vote', {'widget': self});
- self.$(".placeholder-mail-vote:first").empty();
- self.$(".placeholder-mail-vote:first").html(vote_element);
+ self.$(".oe_msg_vote:first").remove();
+ self.$(".oe_mail_vote_count:first").replaceWith(vote_element);
},
- // Stared/unstared + Render star.
+ /**
+ * add or remove a favorite (or starred) for a message and change class on the DOM
+ */
on_star: function (event) {
event.stopPropagation();
var self=this;
- var button = self.$('button.oe_mail_starbox:first');
- return this.ds_message.call('favorite_toggle', [[self.datasets.id]]).pipe(function(star){
- self.datasets.is_favorite=star;
- if(self.datasets.is_favorite){
- button.addClass('oe_stared');
+ var button = self.$('.oe_star:first');
+ return this.ds_message.call('favorite_toggle', [[self.id]]).pipe(function (star) {
+ self.is_favorite=star;
+ if (self.is_favorite) {
+ button.addClass('oe_starred');
} else {
- button.removeClass('oe_stared');
- if( self.options.thread.typeof_thread == 'stared' ) {
- self.animated_destroy({fadeTime:250});
+ button.removeClass('oe_starred');
+ if ( self.options.typeof_thread == 'stared' ) {
+ self.animated_destroy(150);
}
}
});
return false;
},
+ /**
+ * call on_message_delete on his parent thread
+ */
+ destroy: function () {
+
+ this._super();
+ this.parent_thread.on_message_detroy(this);
+
+ }
+
});
- /**
- * ------------------------------------------------------------
+ /**
+ * ------------------------------------------------------------
* Thread Widget
* ------------------------------------------------------------
*
@@ -799,23 +878,15 @@ openerp.mail = function(session) {
* @param {Object} [options]
* @param {Object} [message] read about mail.ThreadMessage object
* @param {Object} [thread]
- * @param {Boolean} [use_composer] use the advanced composer, or
- * the default basic textarea if not set
- * @param {Number} [expandable_number=5] number message show
- * for each click on "show more message"
- * @param {Number} [expandable_default_number=5] number message show
- * on begin before the first click on "show more message"
- * @param {Array [A,B]} [display_on_thread] display the threads (hierarchy)
- * for the thread level between A and B. -1 for no begin or no end.
- * All thread before A are insert in the root thread.
- * All thread after B are insert in parent thread on B level.
+ * @param {int} [display_indented_thread] number thread level to indented threads.
+ * other are on flat mode
* @param {Select} [typeof_thread] inbox/archives/stared/sent
* type of thread and option for user application like animate
* destroy for read/unread
* @param {Array} [parents] liked with the parents thread
* use with browse, fetch... [O]= top parent
*/
- init: function(parent, options) {
+ init: function (parent, datasets, options) {
this._super(parent);
this.domain = options.domain || [];
this.context = _.extend({
@@ -823,154 +894,132 @@ openerp.mail = function(session) {
default_res_id: 0,
default_parent_id: false }, options.context || {});
- // options
- this.options={
- 'thread' : {
- 'show_header_compose': (options.options.thread.show_header_compose != undefined ? options.options.thread.show_header_compose: false),
- 'use_composer': options.options.thread.use_composer || false,
- 'expandable_number': options.options.thread.expandable_number || 5,
- 'expandable_default_number': options.options.thread.expandable_default_number || 5,
- '_expandable_max': options.options.thread.expandable_default_number || 5,
- 'display_on_thread': options.options.thread.display_on_thread || [0,-1],
- 'typeof_thread': options.options.thread.typeof_thread || 'inbox',
- '_parents': (options.options.thread._parents != undefined ? options.options.thread._parents : []).concat( [this] )
- },
- 'message' : options.options.message
- };
+ this.options = options.options;
+ this.options._parents = (options.options._parents != undefined ? options.options._parents : []).concat( [this] );
// record options and data
this.parent_message= parent.thread!= undefined ? parent : false ;
- var param = options.datasets
- // datasets and internal vars
- this.datasets = {
- 'id' : param.id || false,
- 'model' : param.model || false,
- 'parent_id' : param.parent_id || false,
- 'is_private' : param.is_private || false,
- 'author_id' : param.author_id || false,
- 'thread_level' : (param.thread_level+1) || 0,
- 'partner_ids' : []
- };
-
- for(var i in param.partner_ids){
- if(param.partner_ids[i][0]!=(param.author_id ? param.author_id[0] : -1)){
- this.datasets.partner_ids.push(param.partner_ids[i]);
- }
- }
-
+ // data of this thread
+ this.id = datasets.id || false,
+ this.model = datasets.model || false,
+ this.parent_id = datasets.parent_id || false,
+ this.is_private = datasets.is_private || false,
+ this.author_id = datasets.author_id || false,
+ this.thread_level = (datasets.thread_level+1) || 0,
+ this.partner_ids = _.filter(datasets.partner_ids, function (partner) { return partner[0]!=datasets.author_id[0]; } )
this.messages = [];
- this.ComposeMessage = false;
+ this.show_compose_message = this.options.show_compose_message && (this.options.show_reply_button > this.thread_level || !this.thread_level);
+
+ // object compose message
+ this.compose_message = false;
this.ds_thread = new session.web.DataSetSearch(this, this.context.default_model || 'mail.thread');
this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
},
- start: function() {
+ start: function () {
this._super.apply(this, arguments);
-
- this.list_ul = this.$('ul.oe_mail_thread_display:first');
- this.more_msg = this.$(">.oe_msg_more_message:first");
-
- this.display_user_avatar();
- var display_done = compose_done = false;
-
this.bind_events();
-
- if(this.options.thread._parents[0]==this){
- this.on_root_thread();
- }
-
- return display_done && compose_done;
},
- instantiate_ComposeMessage: function() {
- // add message composition form view
- this.ComposeMessage = new mail.ThreadComposeMessage(this,{
- 'context': this.context,
- 'datasets': this.datasets,
- 'options': this.options,
- 'show_attachment_delete': true,
- });
- this.ComposeMessage.appendTo(this.$(".oe_mail_thread_action:first"));
- },
-
- /* this method is runing for first parent thread
+ /* instantiate the compose message object and insert this on the DOM.
+ * The compose message is display in compact form.
*/
- on_root_thread: function(){
- var self=this;
- // fetch and display message, using message_ids if set
- this.message_fetch();
-
- $(document).scroll( self.on_scroll );
- $(window).resize( self.on_scroll );
- window.setTimeout( self.on_scroll, 500 );
-
- $(session.web.qweb.render('mail.wall_no_message', {})).appendTo(this.$('ul.oe_mail_thread_display'));
-
- this.instantiate_ComposeMessage();
- this.ComposeMessage.datasets.is_private=true;
-
- if(this.options.thread.show_header_compose){
- this.ComposeMessage.$el.show();
- //this.ComposeMessage.set_free_attachments();
+ instantiate_compose_message: function () {
+ // add message composition form view
+ if (!this.compose_message) {
+ this.compose_message = new mail.ThreadComposeMessage(this, this, {
+ 'context': this.context,
+ 'options': this.options,
+ });
+ if (!this.thread_level) {
+ // root view
+ this.compose_message.insertBefore(this.$el);
+ } else if (this.thread_level > this.options.display_indented_thread) {
+ this.compose_message.insertAfter(this.$el);
+ } else {
+ this.compose_message.appendTo(this.$el);
+ }
}
-
- this.$el.addClass("oe_mail_root_thread");
},
/* When the expandable object is visible on screen (with scrolling)
* then the on_expandable function is launch
*/
- on_scroll: function(event){
- if(event)event.stopPropagation();
- var message = this.messages[0];
- if(message && message.datasets.type=="expandable" && message.datasets.max_limit){
+ on_scroll: function (event) {
+ if (event)event.stopPropagation();
+ this.$('.oe_msg_expandable:last');
+
+ var message = this.messages[this.messages.length-1];
+ if (message && message.type=="expandable" && message.max_limit) {
var pos = message.$el.position();
- if(pos.top){
+ if (pos.top) {
/* bottom of the screen */
var bottom = $(window).scrollTop()+$(window).height()+200;
- if(bottom - pos.top > 0){
+ if (bottom > pos.top) {
message.on_expandable();
}
}
-
}
},
/**
* Bind events in the widget. Each event is slightly described
* in the function. */
- bind_events: function() {
+ bind_events: function () {
var self = this;
- self.$('.oe_mail_compose_textarea .oe_more').click(function () { var p=$(this).parent(); p.find('.oe_more_hidden, .oe_hidden').show(); p.find('.oe_more').hide(); });
- self.$('.oe_mail_compose_textarea .oe_more_hidden').click(function () { var p=$(this).parent(); p.find('.oe_more_hidden, .oe_hidden').hide(); p.find('.oe_more').show(); });
+ self.$el.on('click', '.oe_mail_list_recipients .oe_more', self.on_show_recipients);
+ self.$el.on('click', '.oe_mail_compose_textarea .oe_more_hidden', self.on_hide_recipients);
},
- /* get all child message/thread id linked
+ /**
+ *show all the partner list of this parent message
*/
- get_child_ids: function(){
+ on_show_recipients: function () {
+ var p=$(this).parent();
+ p.find('.oe_more_hidden, .oe_hidden').show();
+ p.find('.oe_more').hide();
+ },
+
+ /**
+ *hide a part of the partner list of this parent message
+ */
+ on_hide_recipients: function () {
+ var p=$(this).parent();
+ p.find('.oe_more_hidden, .oe_hidden').hide();
+ p.find('.oe_more').show();
+ },
+
+ /* get all child message/thread id linked.
+ * @return array of id
+ */
+ get_child_ids: function () {
var res=[];
- _(this.get_childs()).each(function (val, key) { res.push(val.datasets.id); });
+ _(this.get_childs()).each(function (val, key) { res.push(val.id); });
return res;
},
- /* get all child message/thread linked
+ /* get all child message/thread linked.
+ * @param {int} nb_thread_level, number of traversed thread level for this search
+ * @return array of thread object
*/
- get_childs: function(nb_thread_level){
+ get_childs: function (nb_thread_level) {
var res=[];
- if(arguments[1]) res.push(this);
- if(isNaN(nb_thread_level) || nb_thread_level>0){
+ if (arguments[1]) res.push(this);
+ if (isNaN(nb_thread_level) || nb_thread_level>0) {
_(this.messages).each(function (val, key) {
- if(val.thread){
- res = res.concat( val.thread.get_childs((isNaN(nb_thread_level) ? null : nb_thread_level-1), true) )
+ if (val.thread) {
+ res = res.concat( val.thread.get_childs((isNaN(nb_thread_level) ? undefined : nb_thread_level-1), true) );
}
});
}
return res;
},
- /** browse thread
+ /**
+ *search a thread in all thread and child thread.
+ * This method return an object thread.
* @param {object}{int} option.id
* @param {object}{string} option.model
* @param {object}{boolean} option._go_thread_wall
@@ -979,252 +1028,469 @@ openerp.mail = function(session) {
* return the top thread (wall) if no thread found
* @return thread object
*/
- browse_thread: function(options){
+ browse_thread: function (options) {
// goto the wall thread for launch browse
- if(!options._go_thread_wall) {
+ if (!options._go_thread_wall) {
options._go_thread_wall = true;
- return this.options.thread._parents[0].browse_thread(options);
+ return this.options._parents[0].browse_thread(options);
}
- if(this.datasets.id==options.id){
+ if (this.id==options.id) {
return this;
}
- if(options.id)
- for(var i in this.messages){
- if(this.messages[i].thread){
- var res=this.messages[i].thread.browse_thread({'id':options.id, '_go_thread_wall':true});
- if(res) return res;
- }
- }
-
- //if option default_return_top_thread, return the top if no found thread
- if(options.default_return_top_thread){
- return this;
- }
-
- return false;
- },
-
- /** browse message
- * @param {object}{int} option.id
- * @param {object}{string} option.model
- * @param {object}{boolean} option._go_thread_wall
- * private for check the top thread
- * @return thread object
- */
- browse_message: function(options){
- if(this.options.thread._parents[0].messages[0])
- return this.options.thread._parents[0].messages[0].browse_message(options);
- },
-
- /* this function is launch when a user click on "Reply" button
- */
- on_compose_message: function(){
- if(!this.ComposeMessage){
- this.instantiate_ComposeMessage();
- }
- this.ComposeMessage.$el.toggle();
- return false;
- },
-
- /** Fetch messages
- * @param {Bool} initial_mode: initial mode: try to use message_data or
- * message_ids, if nothing available perform a message_read; otherwise
- * directly perform a message_read
- * @param {Array} replace_domain: added to this.domain
- * @param {Object} replace_context: added to this.context
- */
- message_fetch: function (initial_mode, replace_domain, replace_context, ids, callback) {
- var self = this;
-
- // initial mode: try to use message_data or message_ids
- if (initial_mode && this.options.thread.message_data) {
- return this.create_message_object(this.options.message_data);
- }
- // domain and context: options + additional
- fetch_domain = replace_domain ? replace_domain : this.domain;
- fetch_context = replace_context ? replace_context : this.context;
- var message_loaded = [this.datasets.id||0].concat( self.options.thread._parents[0].get_child_ids() );
-
- return this.ds_message.call('message_read', [ids, fetch_domain, message_loaded, fetch_context, this.context.default_parent_id || undefined]
- ).then(this.proxy('switch_new_message'));
- },
-
- /* create record object and linked him
- */
- create_message_object: function (data) {
- var self = this;
-
- if(data.type=='expandable'){
- var message = new mail.ThreadExpandable(self, {
- 'domain': data.domain,
- 'context': {
- 'default_model': data.model || self.context.default_model,
- 'default_res_id': data.res_id || self.context.default_res_id,
- 'default_parent_id': self.datasets.id },
- 'datasets': data
- });
- } else {
- var message = new mail.ThreadMessage(self, {
- 'domain': data.domain,
- 'context': {
- 'default_model': data.model,
- 'default_res_id': data.res_id,
- 'default_parent_id': data.id },
- 'options':{
- 'thread': self.options.thread,
- 'message': self.options.message
- },
- 'datasets': _.extend(data, {'thread_level': self.datasets.thread_level})
- });
- var data = _.extend(data, {'thread_level': self.datasets.thread_level});
- }
-
- // check if the message is already create
- for(var i in self.messages){
- if(self.messages[i].datasets.id==message.datasets.id){
- self.messages[i].destroy();
- self.messages[i]=self.insert_message(message);
- return true;
- }
- }
- self.messages.push( self.insert_message(message) );
- },
-
- /** Displays a message or an expandable message */
- insert_message: function (message) {
- var self=this;
-
- this.$("li.oe_wall_no_message").remove();
-
- // insert on hierarchy display => insert in self child
- var thread_messages = self.messages;
- var thread = self;
- var flat = false;
- var hierarchy = self.options.thread.display_on_thread;
- if( hierarchy[0] < 0 ||
- hierarchy[0] > self.datasets.thread_level ||
- (hierarchy[1]>0 && hierarchy[1] < self.datasets.thread_level) ) {
-
- var flat = true;
-
- if(hierarchy[0]<0){
-
- // all is in flat mode
- thread = self.options.thread._parents[0];
- var nb_thread_level = null;
-
- } else if(hierarchy[0] > self.datasets.thread_level) {
-
- // list all childs messages for flat display before the hierarchy
- thread = self.options.thread._parents[0];
- var nb_thread_level = hierarchy[0];
-
- } else if(hierarchy[1] < self.datasets.thread_level) {
-
- // list all childs messages for flat display after the hierarchy
- thread = self.options.thread._parents[hierarchy[1]];
- var nb_thread_level = hierarchy[1]>0 ? hierarchy[1]-hierarchy[0] : null;
- } else {
-
- thread = self.options.thread._parents[0];
- var nb_thread_level = null;
- }
-
- var thread_messages = [];
- _(thread.get_childs( nb_thread_level )).each(function (val, key) { thread_messages.push(val.parent_message); });
- }
-
-
- // check older and newer message for insert
- var parent_newer = false;
- var parent_older = false;
- if ( message.datasets.id > 0 ){
- for(var i in thread_messages){
- if(thread_messages[i].datasets.id > message.datasets.id){
- if(!parent_newer || parent_newer.datasets.id>=thread_messages[i].datasets.id)
- parent_newer = thread_messages[i];
- } else if(thread_messages[i].datasets.id>0 && thread_messages[i].datasets.id < message.datasets.id) {
- if(!parent_older || parent_older.id=1);
+ //if option default_return_top_thread, return the top if no found thread
+ if (options.default_return_top_thread) {
+ return this;
+ }
- if(parent_older){
- if(sort){
- message.insertBefore(parent_older.$el);
- } else {
- message.insertAfter(parent_older.$el);
+ return false;
+ },
+
+ /**
+ *search a message in all thread and child thread.
+ * This method return an object message.
+ * @param {object}{int} option.id
+ * @param {object}{string} option.model
+ * @param {object}{boolean} option._go_thread_wall
+ * private for check the top thread
+ * @return message object
+ */
+ browse_message: function (options) {
+ if (this.options._parents[0].messages[0])
+ return this.options._parents[0].messages[0].browse_message(options);
+ },
+
+ /**
+ *If compose_message doesn't exist, instantiate the compose message.
+ * Call the on_compose_expandable method to allow the user to write his message.
+ * (Is call when a user click on "Reply" button)
+ */
+ on_compose_message: function () {
+ this.instantiate_compose_message();
+ this.compose_message.on_compose_expandable();
+ },
+
+ /**
+ *display the message "there are no message" on the thread
+ */
+ no_message: function () {
+ var no_message = $(session.web.qweb.render('mail.wall_no_message', {}));
+ if (this.options.no_message) {
+ no_message.html(this.options.no_message);
+ }
+ no_message.appendTo(this.$el);
+ },
+
+ /**
+ *make a request to read the message (calling RPC to "message_read").
+ * The result of this method is send to the switch message for sending ach message to
+ * his parented object thread.
+ * @param {Array} replace_domain: added to this.domain
+ * @param {Object} replace_context: added to this.context
+ * @param {Array} ids read (if the are some ids, the method don't use the domain)
+ */
+ message_fetch: function (replace_domain, replace_context, ids, callback) {
+ var self = this;
+
+ // domain and context: options + additional
+ fetch_domain = replace_domain ? replace_domain : this.domain;
+ fetch_context = replace_context ? replace_context : this.context;
+ var message_loaded_ids = this.id ? [this.id].concat( self.get_child_ids() ) : self.get_child_ids();
+
+ // CHM note : option for sending in flat mode by server
+ var thread_level = this.options.display_indented_thread > this.thread_level ? this.options.display_indented_thread - this.thread_level : 0;
+
+ return this.ds_message.call('message_read', [ids, fetch_domain, message_loaded_ids, thread_level, fetch_context, this.context.default_parent_id || undefined])
+ .then(callback ? _.bind(callback, this, arguments) : this.proxy('switch_new_message'));
+ },
+
+ /**
+ *create the message object and attached on this thread.
+ * When the message object is create, this method call insert_message for,
+ * displaying this message on the DOM.
+ * @param : {object} data from calling RPC to "message_read"
+ */
+ create_message_object: function (data) {
+ var self = this;
+
+ var data = _.extend(data, {'thread_level': data.thread_level ? data.thread_level : self.thread_level});
+ data.options = _.extend(self.options, data.options);
+
+ if (data.type=='expandable') {
+ var message = new mail.ThreadExpandable(self, data, {
+ 'default_model': data.model || self.context.default_model,
+ 'default_res_id': data.res_id || self.context.default_res_id,
+ 'default_parent_id': self.id,
+ });
+ } else {
+ var message = new mail.ThreadMessage(self, data, {
+ 'default_model': data.model,
+ 'default_res_id': data.res_id,
+ 'default_parent_id': data.id,
+ });
+ }
+
+ // check if the message is already create
+ for (var i in self.messages) {
+ if (self.messages[i] && self.messages[i].id == message.id) {
+ self.messages[i].destroy();
}
- } else if(parent_newer){
- if(sort){
- message.insertAfter(parent_newer.$el);
+ }
+ self.messages.push( message );
+
+ return message;
+ },
+
+ /**
+ *insert the message on the DOM.
+ * All message (and expandable message) are sorted. The method get the
+ * older and newer message to insert the message (before, after).
+ * If there are no older or newer, the message is prepend or append to
+ * the thread (parent object or on root thread for flat view).
+ * The sort is define by the thread_level (O for newer on top).
+ * @param : {object} ThreadMessage object
+ */
+ insert_message: function (message, dom_insert_after) {
+ var self=this;
+
+ if (this.show_compose_message && this.options.show_compact_message) {
+ this.instantiate_compose_message();
+ this.compose_message.do_show_compact();
+ }
+
+ this.$('.oe_wall_no_message').remove();
+
+
+ if (dom_insert_after) {
+ message.insertAfter(dom_insert_after);
+ return message
+ }
+
+ // check older and newer message for insertion
+ var message_newer = false;
+ var message_older = false;
+ if (message.id > 0) {
+ for (var i in self.messages) {
+ if (self.messages[i].id > message.id) {
+ if (!message_newer || message_newer.id > self.messages[i].id) {
+ message_newer = self.messages[i];
+ }
+ } else if (self.messages[i].id > 0 && self.messages[i].id < message.id) {
+ if (!message_older || message_older.id < self.messages[i].id) {
+ message_older = self.messages[i];
+ }
+ }
+ }
+ }
+
+ var sort = (!!self.thread_level || message.id<0);
+
+ if (sort) {
+ if (message_older) {
+
+ message.insertAfter(message_older.thread ? (message_older.thread.compose_message ? message_older.thread.compose_message.$el : message_older.thread.$el) : message_older.$el);
+
+ } else if (message_newer) {
+
+ message.insertBefore(message_newer.$el);
+
+ } else if (message.id < 0) {
+
+ message.appendTo(self.$el);
+
} else {
- message.insertBefore(parent_newer.$el);
+
+ message.prependTo(self.$el);
}
} else {
- if(sort && message.id > 0){
- message.prependTo(thread.list_ul);
+ if (message_older) {
+
+ message.insertBefore(message_older.$el);
+
+ } else if (message_newer) {
+
+ message.insertAfter(message_newer.thread ? (message_newer.thread.compose_message ? message_newer.thread.compose_message.$el : message_newer.thread.$el) : message_newer.$el );
+
+ } else if (message.id < 0) {
+
+ message.prependTo(self.$el);
+
} else {
- message.appendTo(thread.list_ul);
+
+ message.appendTo(self.$el);
+
}
}
return message
},
-
- display_user_avatar: function () {
- var avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
- return this.$('img.oe_mail_icon').attr('src', avatar);
- },
- /* Send the records to his parent thread */
- switch_new_message: function(records) {
+ /**
+ *get the parent thread of the messages.
+ * Each message is send to his parent object (or parent thread flat mode) for creating the object message.
+ * @param : {Array} datas from calling RPC to "message_read"
+ */
+ switch_new_message: function (records) {
var self=this;
- _(records).each(function(record){
- self.browse_thread({
+ _(records).each(function (record) {
+ var thread = self.browse_thread({
'id': record.parent_id,
'default_return_top_thread':true
- }).create_message_object( record );
+ });
+ // create object and attach to the thread object
+ var message = thread.create_message_object( record );
+ // insert the message on dom
+ thread.insert_message( message );
});
},
+
+ /**
+ * this method is call when the widget of a message or an expandable message is destroy
+ * in this thread. The this.messages array is filter to remove this message
+ */
+ on_message_detroy: function (message) {
+
+ this.messages = _.filter(this.messages, function (val) { return !val.isDestroyed(); });
+
+ },
+
+ /**
+ * Convert a destroyed message into a expandable message
+ */
+ message_to_expandable: function (message) {
+
+ if (!this.thread_level || message.isDestroyed()) {
+ message.destroy();
+ return false;
+ }
+
+ var messages = _.sortBy( this.messages, function (val) { return val.id; });
+ var it = _.indexOf( messages, message );
+
+ var msg_up = messages[it-1];
+ var msg_down = messages[it+1];
+
+ var message_dom = [ ["id", "=", message.id] ];
+
+ if ( msg_up && msg_up.type == "expandable" && msg_down && msg_down.type == "expandable") {
+ // concat two expandable message and add this message to this dom
+ msg_up.domain = mail.ChatterUtils.expand_domain( msg_up.domain );
+ msg_down.domain = mail.ChatterUtils.expand_domain( msg_down.domain );
+
+ msg_down.domain = ['|','|'].concat( msg_up.domain ).concat( message_dom ).concat( msg_down.domain );
+
+ if ( !msg_down.max_limit ) {
+ msg_down.nb_messages += 1 + msg_up.nb_messages;
+ }
+
+ msg_up.$el.remove();
+ msg_up.destroy();
+
+ msg_down.reinit();
+
+ } else if ( msg_up && msg_up.type == "expandable") {
+ // concat preview expandable message and this message to this dom
+ msg_up.domain = mail.ChatterUtils.expand_domain( msg_up.domain );
+ msg_up.domain = ['|'].concat( msg_up.domain ).concat( message_dom );
+
+ msg_up.nb_messages++;
+
+ msg_up.reinit();
+
+ } else if ( msg_down && msg_down.type == "expandable") {
+ // concat next expandable message and this message to this dom
+ msg_down.domain = mail.ChatterUtils.expand_domain( msg_down.domain );
+ msg_down.domain = ['|'].concat( msg_down.domain ).concat( message_dom );
+
+ // it's maybe a message expandable for the max limit read message
+ if ( !msg_down.max_limit ) {
+ msg_down.nb_messages++;
+ }
+
+ msg_down.reinit();
+
+ } else {
+ // create a expandable message
+ var expandable = new mail.ThreadExpandable(this, {
+ 'id': message.id,
+ 'model': message.model,
+ 'parent_id': message.parent_id,
+ 'nb_messages': 1,
+ 'thread_level': message.thread_level,
+ 'parent_id': message.parent_id,
+ 'domain': message_dom,
+ 'options': message.options,
+ }, {
+ 'default_model': message.model || this.context.default_model,
+ 'default_res_id': message.res_id || this.context.default_res_id,
+ 'default_parent_id': this.id,
+ });
+
+ // add object on array and DOM
+ this.messages.push(expandable);
+ expandable.insertAfter(message.$el);
+ }
+
+ // destroy message
+ message.destroy();
+
+ return true;
+ },
});
-
- /**
+ /**
+ * ------------------------------------------------------------
+ * mail : root Widget
* ------------------------------------------------------------
+ *
+ * This widget handles the display of messages with thread options. Its main
+ * use is to receive a context and a domain, and to delegate the message
+ * fetching and displaying to the Thread widget.
+ */
+ session.web.client_actions.add('mail.Widget', 'session.mail.Widget');
+ mail.Widget = session.web.Widget.extend({
+ template: 'mail.Widget',
+
+ /**
+ * @param {Object} parent parent
+ * @param {Array} [domain]
+ * @param {Object} [context] context of the thread. It should
+ * contain at least default_model, default_res_id. Please refer to
+ * the compose_message widget for more information about it.
+ * ... @param {Select} [typeof_thread=(mail|stared|archives|send|other)]
+ * options for destroy message when the user click on a button
+ * @param {Object} [options]
+ *... @param {Number} [truncate_limit=250] number of character to
+ * display before having a "show more" link; note that the text
+ * will not be truncated if it does not have 110% of the parameter
+ *... @param {Boolean} [show_record_name] display the name and link for do action
+ *... @param {int} [show_reply_button] number thread level to display the reply button
+ *... @param {int} [show_read_unread_button] number thread level to display the read/unread button
+ *... @param {int} [display_indented_thread] number thread level to indented threads.
+ * other are on flat mode
+ *... @param {Boolean} [show_compose_message] allow to display the composer
+ *... @param {Boolean} [show_compact_message] display the compact message on the thread
+ * when the user clic on this compact mode, the composer is open
+ *... @param {Array} [message_ids] List of ids to fetch by the root thread.
+ * When you use this option, the domain is not used for the fetch root.
+ * @param {String} [no_message] Message to display when there are no message
+ */
+ init: function (parent, options) {
+ this._super(parent);
+ this.domain = options.domain || [];
+ this.context = options.context || {};
+ this.search_results = {'domain': [], 'context': {}, 'groupby': {}};
+
+ this.options = _.extend({
+ 'typeof_thread' : 'inbox',
+ 'display_indented_thread' : -1,
+ 'show_reply_button' : -1,
+ 'show_read_unread_button' : -1,
+ 'truncate_limit' : 250,
+ 'show_record_name' : false,
+ 'show_compose_message' : false,
+ 'show_compact_message' : false,
+ 'message_ids': undefined,
+ 'no_message': false
+ }, options);
+
+ if (this.display_indented_thread === false) {
+ this.display_indented_thread = -1;
+ }
+ if (this.show_reply_button === false) {
+ this.show_reply_button = -1;
+ }
+ if (this.show_read_unread_button === false) {
+ this.show_read_unread_button = -1;
+ }
+
+ },
+
+ start: function (options) {
+ this._super.apply(this, arguments);
+ this.message_render();
+ this.bind_events();
+ },
+
+
+ /**
+ *Create the root thread and display this object in the DOM.
+ * Call the no_message method then c all the message_fetch method
+ * of this root thread to display the messages.
+ */
+ message_render: function (search) {
+
+ this.thread = new mail.Thread(this, {}, {
+ 'domain' : this.domain,
+ 'context' : this.context,
+ 'options': this.options,
+ });
+
+ this.thread.appendTo( this.$el );
+ this.thread.no_message();
+ this.thread.message_fetch(null, null, this.options.message_ids);
+
+ if (this.options.show_compose_message) {
+ this.thread.instantiate_compose_message();
+ if (this.options.show_compact_message) {
+ this.thread.compose_message.do_show_compact();
+ } else {
+ this.thread.compose_message.do_hide_compact();
+ }
+ }
+ },
+
+ bind_events: function () {
+ if (this.context['typeof_thread']!='other') {
+ $(document).scroll( this.thread.on_scroll );
+ $(window).resize( this.thread.on_scroll );
+ window.setTimeout( this.thread.on_scroll, 500 );
+ }
+ }
+ });
+
+
+ /**
+ * ------------------------------------------------------------
* mail_thread Widget
* ------------------------------------------------------------
*
* This widget handles the display of messages on a document. Its main
* use is to receive a context and a domain, and to delegate the message
* fetching and displaying to the Thread widget.
+ * Use Help on the field to display a custom "no message loaded"
*/
session.web.form.widgets.add('mail_thread', 'openerp.mail.RecordThread');
mail.RecordThread = session.web.form.AbstractField.extend({
template: 'mail.record_thread',
- init: function() {
+ init: function () {
this._super.apply(this, arguments);
this.options.domain = this.options.domain || [];
this.options.context = {'default_model': 'mail.thread', 'default_res_id': false};
},
- start: function() {
+ start: function () {
this._super.apply(this, arguments);
// NB: check the actual_mode property on view to know if the view is in create mode anymore
this.view.on("change:actual_mode", this, this._check_visibility);
this._check_visibility();
},
- _check_visibility: function() {
+ _check_visibility: function () {
this.$el.toggle(this.view.get("actual_mode") !== "create");
},
- render_value: function() {
+ render_value: function () {
var self = this;
if (! this.view.datarecord.id || session.web.BufferedDataSet.virtual_id_regex.test(this.view.datarecord.id)) {
this.$('oe_mail_thread').hide();
@@ -1237,39 +1503,37 @@ openerp.mail = function(session) {
default_is_private: false });
// update domain
var domain = this.options.domain.concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]);
- // create and render Thread widget
- // TDE note: replace message_is_follower by a check in message_follower_ids, as message_is_follower is not used in views anymore
- var show_header_compose = this.view.is_action_enabled('edit') ||
+
+ var show_compose_message = this.view.is_action_enabled('edit') ||
(this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value());
- if(this.thread){
- this.thread.destroy();
+ var message_ids = this.getParent().fields.message_ids && this.getParent().fields.message_ids.get_value();
+
+ if (this.root) {
+ this.root.destroy();
}
- this.thread = new mail.Thread(self, {
- 'domain': domain,
- 'context': this.options.context,
- 'options':{
- 'thread':{
- 'show_header_compose': show_header_compose,
- 'use_composer': show_header_compose,
- 'display_on_thread':[-1,-1]
- },
- 'message':{
- 'show_reply': [-1,-1],
- 'show_read_unread': [-1,-1],
- 'show_dd_delete': false
- }
- },
- 'datasets': {},
+ // create and render Thread widget
+ this.root = new mail.Widget(this, {
+ 'domain' : domain,
+ 'context' : this.options.context,
+ 'typeof_thread': this.options.context['typeof_thread'] || 'other',
+ 'display_indented_thread': -1,
+ 'show_reply_button': 0,
+ 'show_read_unread_button': -1,
+ 'show_compose_message': show_compose_message,
+ 'message_ids': message_ids,
+ 'show_compact_message': true,
+ 'no_message': this.node.attrs.help
}
);
- return this.thread.appendTo( this.$('.oe_mail_wall_threads:first') );
+
+ return this.root.replace(this.$('.oe_mail-placeholder'));
},
});
- /**
- * ------------------------------------------------------------
+ /**
+ * ------------------------------------------------------------
* Wall Widget
* ------------------------------------------------------------
*
@@ -1287,8 +1551,6 @@ openerp.mail = function(session) {
* @param {Array} [options.domain] domain on the Wall
* @param {Object} [options.context] context, is an object. It should
* contain default_model, default_res_id, to give it to the threads.
- * @param {Number} [options.thread_level] number of thread levels to display
- * 0 being flat.
*/
init: function (parent, options) {
this._super(parent);
@@ -1305,7 +1567,7 @@ openerp.mail = function(session) {
var thread_displayed = this.message_render();
this.options.domain = this.options.domain.concat(this.search_results['domain']);
this.bind_events();
- return (searchview_ready && thread_displayed);
+ return $.when(searchview_ready, thread_displayed);
},
/**
@@ -1328,7 +1590,7 @@ openerp.mail = function(session) {
* @param {Array} contexts
* @param {Array} groupbys
*/
- do_searchview_search: function(domains, contexts, groupbys) {
+ do_searchview_search: function (domains, contexts, groupbys) {
var self = this;
this.rpc('/web/session/eval_domain_and_context', {
domains: domains || [],
@@ -1337,51 +1599,43 @@ openerp.mail = function(session) {
}).then(function (results) {
self.search_results['context'] = results.context;
self.search_results['domain'] = results.domain;
- self.thread.destroy();
+ self.root.destroy();
return self.message_render();
});
},
/**
- * Display the threads
+ *Create the root thread widget and display this object in the DOM
*/
message_render: function (search) {
var domain = this.options.domain.concat(this.search_results['domain']);
var context = _.extend(this.options.context, search&&search.search_results['context'] ? search.search_results['context'] : {});
- this.thread = new mail.Thread(this, {
- 'domain' : domain,
- 'context' : context,
- 'options': {
- 'thread' :{
- 'use_composer': true,
- 'show_header_compose': false,
- 'typeof_thread': context.typeof_thread || 'inbox',
- 'display_on_thread': [0,1]
- },
- 'message': {
- 'show_reply': [0,0],
- 'show_read_unread': [0,-1],
- 'show_dd_delete': false,
- },
- },
- 'datasets': {},
+ this.root = new mail.Widget(this, {
+ 'domain' : domain,
+ 'context' : context,
+ 'typeof_thread': context['typeof_thread'] || 'other',
+ 'display_indented_thread': 1,
+ 'show_reply_button': 10,
+ 'show_read_unread_button': 11,
+ 'show_compose_message': true,
+ 'show_compact_message': false,
}
);
- return this.thread.appendTo( this.$('.oe_mail_wall_threads:first') );
+ return this.root.replace(this.$('.oe_mail-placeholder'));
},
- bind_events: function(){
+ bind_events: function () {
var self=this;
- this.$("button.oe_write_full:first").click(function(){ self.thread.ComposeMessage.on_compose_fullmail(); });
- this.$("button.oe_write_onwall:first").click(function(){ self.thread.ComposeMessage.$el.toggle(); });
+ this.$(".oe_write_full").click(function(){ self.root.thread.compose_message.on_compose_fullmail(); });
+ this.$(".oe_write_onwall").click(function(){ self.root.thread.on_compose_message(); });
}
});
/**
- * ------------------------------------------------------------
+ * ------------------------------------------------------------
* UserMenu
* ------------------------------------------------------------
*
@@ -1401,13 +1655,13 @@ openerp.mail = function(session) {
};
},
- start: function(parent, params) {
+ start: function (parent, params) {
var self = this;
this.$el.on('click', 'button', self.on_compose_message );
this._super(parent, params);
},
- on_compose_message: function(event){
+ on_compose_message: function (event) {
event.stopPropagation();
var action = {
type: 'ir.actions.act_window',
@@ -1417,7 +1671,11 @@ openerp.mail = function(session) {
action_from: 'mail.ThreadComposeMessage',
views: [[false, 'form']],
target: 'new',
- context: this.options.context,
+ context: _.extend(this.options.context, {
+ 'default_model': this.context.default_model,
+ 'default_res_id': this.context.default_res_id,
+ 'default_content_subtype': 'html',
+ }),
};
session.client.action_manager.do_action(action);
},
@@ -1425,7 +1683,7 @@ openerp.mail = function(session) {
});
session.web.UserMenu = session.web.UserMenu.extend({
- start: function(parent, params) {
+ start: function (parent, params) {
var render = new session.web.ComposeMessageTopButton();
render.insertAfter(this.$el);
this._super(parent, params);
diff --git a/addons/mail/static/src/js/mail_followers.js b/addons/mail/static/src/js/mail_followers.js
index 806bf28e7b2..a13d2048bf3 100644
--- a/addons/mail/static/src/js/mail_followers.js
+++ b/addons/mail/static/src/js/mail_followers.js
@@ -24,10 +24,9 @@ openerp_mail_followers = function(session, mail) {
init: function() {
this._super.apply(this, arguments);
- this.options.image = this.node.attrs.image || 'image_small';
- this.options.title = this.node.attrs.title || 'Followers';
- this.options.comment = this.node.attrs.help || false;
- this.options.displayed_nb = this.node.attrs.displayed_nb || 10;
+ this.image = this.node.attrs.image || 'image_small';
+ this.comment = this.node.attrs.help || false;
+ this.displayed_nb = this.node.attrs.displayed_nb || 10;
this.ds_model = new session.web.DataSetSearch(this, this.view.model);
this.ds_follow = new session.web.DataSetSearch(this, this.field.relation);
this.ds_users = new session.web.DataSetSearch(this, 'res.users');
@@ -61,7 +60,7 @@ openerp_mail_followers = function(session, mail) {
self.do_unfollow();
});
// event: click on a subtype, that (un)subscribe for this subtype
- this.$el.on('click', 'ul.oe_subtypes input', self.do_update_subscription);
+ this.$el.on('click', '.oe_subtype_list input', self.do_update_subscription);
// event: click on 'invite' button, that opens the invite wizard
this.$('.oe_invite').on('click', function (event) {
action = {
@@ -115,20 +114,24 @@ openerp_mail_followers = function(session, mail) {
self.message_is_follower = (_.indexOf(self.get('value'), pid) != -1);
}).pipe(self.proxy('display_generic'));
},
-
+ _format_followers: function(count){
+ // TDE note: why redefining _t ?
+ function _t(str) { return str; }
+ var str = '';
+ if(count <= 0){
+ str = _t('No followers');
+ }else if(count === 1){
+ str = _t('One follower');
+ }else{
+ str = ''+count+' '+_t('followers');
+ }
+ return str;
+ },
/* Display generic info about follower, for people not having access to res_partner */
display_generic: function () {
var self = this;
- var node_user_list = this.$('ul.oe_mail_followers_display').empty();
- // format content: Followers (You and 0 other) // Followers (3)
- var content = this.options.title;
- if (this.message_is_follower) {
- content += ' (You and ' + (this.get('value').length-1) + ' other)';
- }
- else {
- content += ' (' + this.get('value').length + ')'
- }
- this.$('div.oe_mail_recthread_followers h4').html(content);
+ var node_user_list = this.$('.oe_follower_list').empty();
+ this.$('.oe_follower_title').html(this._format_followers(this.get('value').length));
},
/** Display the followers */
@@ -137,16 +140,17 @@ openerp_mail_followers = function(session, mail) {
records = records || [];
this.message_is_follower = this.set_is_follower(records);
// clean and display title
- var node_user_list = this.$('ul.oe_mail_followers_display').empty();
- this.$('div.oe_mail_recthread_followers h4').html(this.options.title + ' (' + records.length + ')');
+ var node_user_list = this.$('.oe_follower_list').empty();
+ this.$('.oe_follower_title').html(this._format_followers(records.length));
// truncate number of displayed followers
- truncated = records.splice(0, this.options.displayed_nb);
+ truncated = records.splice(0, this.displayed_nb);
_(truncated).each(function (record) {
record.avatar_url = mail.ChatterUtils.get_image(self.session, 'res.partner', 'image_small', record.id);
$(session.web.qweb.render('mail.followers.partner', {'record': record})).appendTo(node_user_list);
});
+ // FVA note: be sure it is correctly translated
if (truncated.length < records.length) {
- $('And ' + (records.length - truncated.length) + ' more.').appendTo(node_user_list);
+ $('And ' + (records.length - truncated.length) + ' more.
').appendTo(node_user_list);
}
},
@@ -172,7 +176,7 @@ openerp_mail_followers = function(session, mail) {
/** Fetch subtypes, only if current user is follower */
fetch_subtypes: function () {
- var subtype_list_ul = this.$('.oe_subtypes').empty();
+ var subtype_list_ul = this.$('.oe_subtype_list').empty();
if (! this.message_is_follower) return;
var context = new session.web.CompoundContext(this.build_context(), {});
this.ds_model.call('message_get_subscription_data', [[this.view.datarecord.id], context]).pipe(this.proxy('display_subtypes'));
@@ -181,13 +185,12 @@ openerp_mail_followers = function(session, mail) {
/** Display subtypes: {'name': default, followed} */
display_subtypes:function (data) {
var self = this;
- var subtype_list_ul = this.$('.oe_subtypes');
- var records = data[this.view.datarecord.id].message_subtype_data;
-
+ var subtype_list_ul = this.$('.oe_subtype_list');
+ var records = data[this.view.datarecord.id || this.view.dataset.ids[0]].message_subtype_data;
_(records).each(function (record, record_name) {
record.name = record_name;
record.followed = record.followed || undefined;
- $(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo( self.$('ul.oe_subtypes') );
+ $(session.web.qweb.render('mail.followers.subtype', {'record': record})).appendTo( self.$('.oe_subtype_list') );
});
},
@@ -210,7 +213,7 @@ openerp_mail_followers = function(session, mail) {
var self = this;
var checklist = new Array();
- _(this.$('.oe_mail_recthread_actions input[type="checkbox"]')).each(function (record) {
+ _(this.$('.oe_actions input[type="checkbox"]')).each(function (record) {
if ($(record).is(':checked')) {
checklist.push(parseInt($(record).data('id')));
}
diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml
index fbb64cc6772..caa85cd3deb 100644
--- a/addons/mail/static/src/xml/mail.xml
+++ b/addons/mail/static/src/xml/mail.xml
@@ -1,33 +1,46 @@
+
+
+
+
+
+
-
-
-
-
-
-