- Receivable Accounts[[ data['form']['result_selection'] == 'customer' or removeParentNode('para') ]]
- Payable Accounts[[ data['form']['result_selection'] == 'supplier' or removeParentNode('para') ]]
- Receivable and Payable Accounts[[ data['form']['result_selection'] == 'customer_supplier' or removeParentNode('para') ]]
-
-
- [[ data['form']['direction_selection'] ]]
-
-
- [[ get_target_move(data) ]]
-
-
-
-
-
-
-
-
-
- Partners
-
-
- Due[[ data['form']['direction_selection'] == 'future' and ' ' or removeParentNode('para') ]]
- Not due[[ data['form']['direction_selection'] != 'future' and ' ' or removeParentNode('para') ]]
-
-
- [[ data['form']['4']['name'] ]]
-
-
- [[ data['form']['3']['name'] ]]
-
-
- [[ data['form']['2']['name'] ]]
-
-
- [[ data['form']['1']['name'] ]]
-
-
- [[ data['form']['0']['name'] ]]
-
-
- Total
-
-
-
-
-
-
- [[ (get_lines(data['form']), 'partner') == False or removeParentNode('para') ]]
- [[ (get_lines_with_out_partner(data['form']), 'not_partner') == False or removeParentNode('para') ]]
- Account Total
-
- Company[[ data['model']=='account.account' and ' ' or removeParentNode('para') ]]
- Chart of Accounts[[ data['model']=='ir.ui.menu' and ' ' or removeParentNode('para') ]]
-
-
- Fiscal Year
-
-
- Display Account
-
-
- Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]]
-
-
- Target Moves
-
-
-
-
- [[ get_account(data) or '' ]]
-
-
- [[ get_fiscalyear(data) or '' ]]
-
-
- All[[ data['form']['display_account']=='all' and ' ' or removeParentNode('para') ]]
- With movements[[ data['form']['display_account']=='movement' and ' ' or removeParentNode('para') ]]
- With balance is not equal to 0[[ data['form']['display_account']=='not_zero' and ' ' or removeParentNode('para') ]]
-
-
[[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]]
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
-
- [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]]
-
-
- Start Period
-
-
- End Period
-
-
-
-
- [[ get_start_period(data) or removeParentNode('para') ]]
-
-
- [[ get_end_period(data) or removeParentNode('para') ]]
-
-
-
-
-
- [[ get_target_move(data) ]]
-
-
-
-
-
-
-
-
-
-
-
-
Code
-
Account
-
Debit
-
Credit
-
Balance
-
-
-
[[ repeatIn(lines(data['form']), 'a') ]][[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font') ]][[ a['code'] or removeParentNode('tr') ]]
-
[[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font') ]][[ '..'*(a['level']-1) ]][[ a['name'] ]]
-
[[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font') ]][[ a['type']=='view' and removeParentNode('font') ]][[ formatLang(a['debit']) ]][[ a['type']<>'view' and removeParentNode('font') ]] [[formatLang(a['debit']) ]]
-
[[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font')]][[ a['type']=='view' and removeParentNode('font') ]][[ formatLang(a['credit']) ]][[ a['type']<>'view' and removeParentNode('font') ]] [[ formatLang(a['credit']) ]]
-
[[ (a['type']<>'view' and setTag('para','para',{'fontName':"Helvetica"})) or removeParentNode('font') ]][[ a['type']=='view' and removeParentNode('font') ]][[ formatLang(a['balance'], currency_obj=company.currency_id) ]][[ a['type']<>'view' and removeParentNode('font') ]] [[ formatLang(a['balance'], currency_obj=company.currency_id) ]]
Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]]
-
Target Moves
-
-
-
[[ get_account(data) or removeParentNode('para') ]]
-
[[ get_fiscalyear(data) or '' ]]
-
[[o.journal_id.name ]]
-
[[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]]
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
-
Start Date
-
End Date
-
-
-
[[ formatLang(get_start_date(data),date=True) ]]
-
[[ formatLang(get_end_date(data),date=True) ]]
-
-
- [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]]
-
-
Start Period
-
End Period
-
-
-
[[ get_start_period(data) or removeParentNode('para') ]]
-
[[ get_end_period(data) or removeParentNode('para') ]]
Filter By [[ get_filter(data)!='No Filters' and get_filter(data) ]]
-
Target Moves
-
-
-
[[ get_account(data) or removeParentNode('para') ]]
-
[[ get_fiscalyear(data) or '' ]]
-
[[ get_filter(data)=='No Filters' and get_filter(data) or removeParentNode('para') ]]
- [[ get_filter(data)=='Date' or removeParentNode('blockTable') ]]
-
-
Start Date
-
End Date
-
-
-
[[ formatLang(get_start_date(data),date=True) ]]
-
[[ formatLang(get_end_date(data),date=True) ]]
-
-
- [[ get_filter(data)=='Periods' or removeParentNode('blockTable') ]]
-
-
Start Period
-
End Period
-
-
-
[[ get_start_period(data) or removeParentNode('para') ]]
-
[[ get_end_period(data) or removeParentNode('para') ]]
- [[ repeatIn(get_lines(data), 'a') ]]
- [[ (a.get('level') <> 0) or removeParentNode('tr') ]]
- [[ setTag('tr','tr',{'style': 'Table'+str(min(3,'level' in a and a.get('level') or 1))}) ]]
-
[[ (a.get('account_type') =='view' and a.get('level') <> 1) or removeParentNode('td') ]]
- [[ setTag('para','para',{'style': 'terp_level_'+str(min(6,a.get('level')))+'_balance'}) ]][[ formatLang(a.get('balance'), currency_obj = company.currency_id) ]]
-
[[ (a.get('account_type') <>'view' or a.get('level') == 1) or removeParentNode('td') ]]
- [[ setTag('para','para',{'style': 'terp_level_'+str(min(6,a.get('level')))+'_balance'}) ]][[ formatLang(a.get('balance'), currency_obj = company.currency_id) ]]
-
-
-
-
-
- [[ (not data['form']['enable_filter'] and not data['form']['debit_credit']) or removeParentNode('blockTable') ]]
-
-
- Name
-
-
- Balance
-
-
-
- [[ repeatIn(get_lines(data), 'a') ]]
- [[ (a.get('level') <> 0) or removeParentNode('tr') ]]
- [[ setTag('tr','tr',{'style': 'Table'+str(min(3,'level' in a and a.get('level') or 1))}) ]]
-
[[ (a.get('account_type') =='view' and a.get('level') <> 1) or removeParentNode('td') ]]
- [[ setTag('para','para',{'style': 'terp_level_'+str(min(6,a.get('level')))+'_balance'}) ]][[ formatLang(a.get('balance'), currency_obj = company.currency_id) ]]
-
[[ (a.get('account_type') <>'view' or a.get('level') == 1) or removeParentNode('td') ]]
- [[ setTag('para','para',{'style': 'terp_level_'+str(min(6,a.get('level')))+'_balance'}) ]][[ formatLang(a.get('balance'), currency_obj = company.currency_id) ]]
-
-
-
-
-
-
-
-
- [[ (data['form']['enable_filter'] == 1 and not data['form']['debit_credit']) or removeParentNode('blockTable') ]]
-
-
- Name
-
-
- Balance
-
-
- [[ data['form']['label_filter'] ]]
-
-
-
- [[ repeatIn(get_lines(data), 'a') ]]
- [[ (a.get('level') <> 0) or removeParentNode('tr') ]]
- [[ setTag('tr','tr',{'style': 'Table'+str(min(3,'level' in a and a.get('level') or 1))}) ]]
-
Company[[ data['model']=='account.journal.period' and ' ' or removeParentNode('para') ]]
- Chart of Accounts[[ data['model']=='ir.ui.menu' and ' ' or removeParentNode('para') ]]
-
Fiscal Year
-
Journals
-
Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]]
-
Target Moves
-
-
-
[[ get_account(data) or removeParentNode('para') ]]
-
[[ get_fiscalyear(data) or '' ]]
-
[[', '.join([ lt or '' for lt in get_journal(data) ]) ]]
-
[[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]]
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
-
Start Date
-
End Date
-
-
-
[[ formatLang(get_start_date(data),date=True)]]
-
[[ formatLang(get_end_date(data),date=True) ]]
-
-
- [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]]
-
-
Start Period
-
End Period
-
-
-
[[ get_start_period(data) or removeParentNode('para') ]]
-
[[ get_end_period(data) or removeParentNode('para') ]]
- Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]]
-
-
- Target Moves
-
-
-
-
-
-
- [[ get_account(data) or '' ]]
-
-
- [[ get_fiscalyear(data) or '' ]]
-
-
- [[', '.join([ lt or '' for lt in get_journal(data) ]) ]]
-
-
- [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]]
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
-
- Start Date
-
-
- End Date
-
-
-
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
- [[ (line.has_key('currency_id') and line['currency_id']==None or line['amount_currency']==None) and removeParentNode('font') ]] [[ formatLang(line['amount_currency'])]] [[ line['currency_code'] or '']]
-
- Company[[ data['model']=='account.account' and ' ' or removeParentNode('para') ]]
- Chart of Accounts[[ data['model']=='ir.ui.menu' and ' ' or removeParentNode('para') ]]
-
- Fiscal Year
-
-
- Journals
-
-
- Display Account
-
-
- Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]]
-
-
- Entries Sorted By
-
-
- Target Moves
-
-
-
-
-
-
- [[ get_account(data) or removeParentNode('para') ]]
-
-
- [[ get_fiscalyear(data) or '' ]]
-
-
- [[', '.join([ lt or '' for lt in get_journal(data) ]) ]]
-
-
- All[[ data['form']['display_account']=='all' and ' ' or removeParentNode('para') ]]
- With movements[[ data['form']['display_account']=='movement' and ' ' or removeParentNode('para') ]]
- With balance is not equal to 0[[ data['form']['display_account']=='not_zero' and ' ' or removeParentNode('para') ]]
-
-
- [[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]]
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
-
- Start Date
-
-
- End Date
-
-
-
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
- [[ (line.has_key('currency_id') and line['currency_id']==None or line['amount_currency']==None) and removeParentNode('font') ]] [[ formatLang(line['amount_currency'])]] [[ line['currency_code'] or '']]
-
Company[[ data['model']=='account.journal.period'and ' ' or removeParentNode('para') ]]
- Chart of Accounts[[ data['model']=='ir.ui.menu' and ' ' or removeParentNode('para') ]]
-
Fiscal Year
-
Journal
-
Period
-
Entries Sorted By
-
Target Moves
-
-
-
[[ get_account(data) or '' ]]
-
[[ get_fiscalyear(data) or '' ]]
-
[[ o.journal_id.name ]]
-
[[ o.period_id.name ]]
-
- Date[[ data['form'].get('sort_selection', 'date') == 'date' and ' ' or removeParentNode('para') ]]
- Reference Number[[ data['form'].get('sort_selection', 'date') == 'ref' and ' ' or removeParentNode('para') ]]
-
[[ data['model']=='account.journal.period'and 'Company' or removeParentNode('para') ]]
- [[ data['model']=='ir.ui.menu' and 'Chart of Accounts' or removeParentNode('para') ]]
Filter By [[ data['form']['filter']!='filter_no' and get_filter(data) ]]
-
Partner's
-
Target Moves
-
-
-
[[ get_account(data) or removeParentNode('para') ]]
-
[[ get_fiscalyear(data) or '' ]]
-
[[', '.join([ lt or '' for lt in get_journal(data) ]) ]]
-
-
[[ data['form']['filter']=='filter_no' and get_filter(data) or removeParentNode('para') ]]
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
-
Start Date
-
End Date
-
-
-
[[ formatLang(get_start_date(data),date=True) ]]
-
[[ formatLang(get_end_date(data),date=True) ]]
-
-
- [[ data['form']['filter']=='filter_period' or removeParentNode('blockTable') ]]
-
-
Start Period
-
End Period
-
-
-
[[ get_start_period(data) or removeParentNode('para') ]]
-
[[ get_end_period(data) or removeParentNode('para') ]]
[[ repeatIn(lines(), 'a') ]][[ (a['type']==3 and setTag('para','para',{'fontName':'Helvetica-Bold'})) or removeParentNode('font') ]][[ a['ref'] ]] [[ a['type']==3 and a['code'] ]]
-
[[ (a['type']==3 and setTag('para','para',{'fontName':'Helvetica-Bold'})) or removeParentNode('font') ]][[ a['name'] ]]
-
[[ (a['type']==3 and setTag('para','para',{'fontName':'Helvetica-Bold'})) or removeParentNode('font') ]][[ formatLang(a['debit']) ]]
-
[[ (a['type']==3 and setTag('para','para',{'fontName':'Helvetica-Bold'})) or removeParentNode('font') ]][[ formatLang(a['credit']) ]]
-
[[ (a['type']==3 and setTag('para','para',{'fontName':'Helvetica-Bold'})) or removeParentNode('font') ]][[ formatLang(a['balance'], currency_obj=company.currency_id) ]]
-
[[ (a['type']==3 and setTag('para','para',{'fontName':'Helvetica-Bold'})) or removeParentNode('font') ]][[ formatLang(a['enlitige'] or 0.0, currency_obj=company.currency_id) ]]
- Filters By [[ data['form']['filter'] not in ('filter_no','unreconciled') and get_filter(data) ]]
-
-
- Partner's
-
-
- Target Moves
-
-
-
-
-
-
- [[ get_account(data) or '' ]]
-
-
- [[ get_fiscalyear(data) or '' ]]
-
-
- [[', '.join([ lt or '' for lt in get_journal(data) ]) ]]
-
-
- [[ data['form']['filter'] in ('filter_no','unreconciled') and get_filter(data) or removeParentNode('para') ]]
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
-
- Start Date
-
-
- End Date
-
-
-
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
- Filters By [[ data['form']['filter'] not in ('filter_no','unreconciled') and get_filter(data) ]]
-
-
- Partner's
-
-
- Target Moves
-
-
-
-
-
-
- [[ get_account(data) or '' ]]
-
-
- [[ get_fiscalyear(data) or '' ]]
-
-
- [[', '.join([ lt or '' for lt in get_journal(data) ]) ]]
-
-
- [[ data['form']['filter'] in ('filter_no','unreconciled') and get_filter(data) or removeParentNode('para') ]]
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
-
- Start Date
-
-
- End Date
-
-
-
- [[ data['form']['filter']=='filter_date' or removeParentNode('blockTable') ]]
-
- [[ line['date_maturity'] and formatLang(line['date_maturity'],date=True) or '' ]]
-
-
- [[ (line['account_id']['type'] == 'receivable' and formatLang(line['debit']) or 0) or (line['account_id']['type'] == 'payable' and formatLang(line['credit'] * -1) or ' ') ]]
-
-
- [[ (line['account_id']['type'] == 'receivable' and formatLang(line['credit']) or 0) or (line['account_id']['type'] == 'payable' and formatLang(line['debit'] * -1) or 0) ]]
-
- [[ formatLang((reduce(lambda x, y: x + ((y['account_id']['type'] == 'receivable' and y['debit'] or 0) or (y['account_id']['type'] == 'payable' and y['credit'] * -1 or 0)), getLines(o), 0))) ]]
-
-
- [[ formatLang((reduce(lambda x, y: x + ((y['account_id']['type'] == 'receivable' and y['credit'] or 0) or (y['account_id']['type'] == 'payable' and y['debit'] * -1 or 0)), getLines(o), 0))) ]]
-
- [[ o.mode and o.mode.bank_id.bank and o.mode.bank_id.bank.name or '']]
- [[ o.mode and o.mode.bank_id.bank and o.mode.bank_id.bank.street or '']]
- [[ o.mode and o.mode.bank_id.bank and o.mode.bank_id.bank.street2 or removeParentNode('para')]]
- [[ o.mode and o.mode.bank_id.bank and o.mode.bank_id.bank.zip or '']] [[ o.mode and o.mode.bank_id.bank and o.mode.bank_id.bank.city or '']]
- [[ o.mode and o.mode.bank_id.bank and o.mode.bank_id.bank.state and o.mode.bank_id.bank.state.name or removeParentNode('para') ]]
- [[ o.mode and o.mode.bank_id.bank and o.mode.bank_id.bank.country and o.mode.bank_id.bank.country.name or '']]
-
-
-
-
-
-
- Payment Order / Payment
-
-
-
-
-
-
- Payment Type
-
-
- Reference
-
-
- Used Account
-
-
- Execution Type
-
-
- Company Currency
-
-
-
-
-
-
- [[ o.mode and o.mode.name or '-' ]]
-
-
- [[ o.reference or '-' ]]
-
-
- [[get_account_name(o.mode.bank_id.id)]]
-
-
- [[ o.date_prefered == 'now' and 'Now' or removeParentNode('para') ]]
- [[ o.date_prefered == 'due' and 'Due date' or removeParentNode('para') ]]
- [[ o.date_prefered == 'fixed' and 'Fixed date' or removeParentNode('para') ]]
-
-
- [[ o.user_id and o.user_id.company_id and o.user_id.company_id.currency_id and o.user_id.company_id.currency_id.name or '' ]]
-
-
-
-
-
-
-
-
-
- Partner
-
-
- Bank Account
-
-
- Invoice Ref
-
-
- Value Date
-
-
- Amount
-
-
- Currency
-
-
-
-
- [[repeatIn(o.line_ids, 'line') ]]
-
-
-
- [[line.partner_id and line.partner_id.name or '-' ]]
-
-
- [[get_account_name(line.bank_id.id) or '-']]
-
-
- [[ get_invoice_name(line.ml_inv_ref.id) or '-' ]]
-
-
- [[not line.date and '-' or formatLang(line.date,date=True) ]]
-
-
- [[ formatLang(line.amount or 0.0, currency_obj=line.company_currency) ]]
-
');
self.$('.oe_pad_readonly').html(data);
}).fail(function() {
diff --git a/addons/payment/__openerp__.py b/addons/payment/__openerp__.py
index bc5d3896ac2..c192bec7c1b 100644
--- a/addons/payment/__openerp__.py
+++ b/addons/payment/__openerp__.py
@@ -7,11 +7,12 @@
'version': '1.0',
'description': """Payment Acquirer Base Module""",
'author': 'OpenERP SA',
- 'depends': ['mail', 'account'],
+ 'depends': ['account'],
'data': [
'views/payment_acquirer.xml',
'views/res_config_view.xml',
'security/ir.model.access.csv',
],
'installable': True,
+ 'auto_install': True,
}
diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py
index d82fa94c132..b86a799e19c 100644
--- a/addons/procurement/schedulers.py
+++ b/addons/procurement/schedulers.py
@@ -59,7 +59,7 @@ class procurement_order(osv.osv):
context = {}
try:
if use_new_cursor:
- cr = openerp.registry(use_new_cursor).db.cursor()
+ cr = openerp.registry(use_new_cursor).cursor()
procurement_obj = self.pool.get('procurement.order')
if not skip_exception:
@@ -199,7 +199,7 @@ class procurement_order(osv.osv):
if context is None:
context = {}
if use_new_cursor:
- cr = openerp.registry(use_new_cursor).db.cursor()
+ cr = openerp.registry(use_new_cursor).cursor()
orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
procurement_obj = self.pool.get('procurement.order')
diff --git a/addons/procurement/wizard/orderpoint_procurement.py b/addons/procurement/wizard/orderpoint_procurement.py
index afc8e2a721f..c8d41c5c655 100644
--- a/addons/procurement/wizard/orderpoint_procurement.py
+++ b/addons/procurement/wizard/orderpoint_procurement.py
@@ -49,7 +49,7 @@ class procurement_compute(osv.osv_memory):
"""
proc_obj = self.pool.get('procurement.order')
#As this function is in a new thread, I need to open a new cursor, because the old one may be closed
- new_cr = self.pool.db.cursor()
+ new_cr = self.pool.cursor()
for proc in self.browse(new_cr, uid, ids, context=context):
proc_obj._procure_orderpoint_confirm(new_cr, uid, automatic=proc.automatic, use_new_cursor=new_cr.dbname, context=context)
#close the new cursor
diff --git a/addons/procurement/wizard/schedulers_all.py b/addons/procurement/wizard/schedulers_all.py
index aab9657ae5d..c0f1cfcc2df 100644
--- a/addons/procurement/wizard/schedulers_all.py
+++ b/addons/procurement/wizard/schedulers_all.py
@@ -45,7 +45,7 @@ class procurement_compute_all(osv.osv_memory):
"""
proc_obj = self.pool.get('procurement.order')
#As this function is in a new thread, i need to open a new cursor, because the old one may be closed
- new_cr = self.pool.db.cursor()
+ new_cr = self.pool.cursor()
for proc in self.browse(new_cr, uid, ids, context=context):
proc_obj.run_scheduler(new_cr, uid, automatic=proc.automatic, use_new_cursor=new_cr.dbname,\
context=context)
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index 4946a71f80b..c547a611cd4 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -118,6 +118,7 @@
+ /', type='http', auth='user', website=True, multilang=True)
- def report_html(self, reportname, docids):
- cr, uid, context = request.cr, request.uid, request.context
- docids = self._eval_params(docids)
- return request.registry['report'].get_html(cr, uid, docids, reportname, context=context)
-
- @route('/report/pdf/report//', type='http', auth="user", website=True)
- def report_pdf(self, reportname, docids):
- cr, uid, context = request.cr, request.uid, request.context
- docids = self._eval_params(docids)
- pdf = request.registry['report'].get_pdf(cr, uid, docids, reportname, context=context)
- pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
- return request.make_response(pdf, headers=pdfhttpheaders)
-
- #------------------------------------------------------
- # Particular reports controller
- #------------------------------------------------------
- @route('/report/', type='http', auth='user', website=True, multilang=True)
- def report_html_particular(self, reportname, **data):
- cr, uid, context = request.cr, request.uid, request.context
+ @route([
+ '/report//',
+ '/report///',
+ ], type='http', auth='user', website=True, multilang=True)
+ def report_routes(self, reportname, docids=None, converter=None, **data):
report_obj = request.registry['report']
- data = self._eval_params(data) # Sanitizing
- return report_obj.get_html(cr, uid, [], reportname, data=data, context=context)
-
- @route('/report/pdf/report/', type='http', auth='user', website=True, multilang=True)
- def report_pdf_particular(self, reportname, **data):
cr, uid, context = request.cr, request.uid, request.context
- report_obj = request.registry['report']
- data = self._eval_params(data) # Sanitizing
- pdf = report_obj.get_pdf(cr, uid, [], reportname, data=data, context=context)
- pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
- return request.make_response(pdf, headers=pdfhttpheaders)
+
+ if docids:
+ docids = [int(i) for i in docids.split(',')]
+ options_data = None
+ if data.get('options'):
+ options_data = simplejson.loads(data['options'])
+ if data.get('context'):
+ context.update(simplejson.loads(data['context']))
+
+ if converter == 'html':
+ html = report_obj.get_html(cr, uid, docids, reportname, data=options_data, context=context)
+ return request.make_response(html)
+ elif converter == 'pdf':
+ pdf = report_obj.get_pdf(cr, uid, docids, reportname, data=options_data, context=context)
+ pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
+ return request.make_response(pdf, headers=pdfhttpheaders)
+ else:
+ raise exceptions.HTTPException(description='Converter %s not implemented.' % converter)
#------------------------------------------------------
# Misc. route utils
@@ -94,7 +89,7 @@ class ReportController(Controller):
@route(['/report/download'], type='http', auth="user", website=True)
def report_download(self, data, token):
"""This function is used by 'qwebactionmanager.js' in order to trigger the download of
- a pdf report.
+ a pdf/controller report.
:param data: a javascript array JSON.stringified containg report internal url ([0]) and
type [1]
@@ -102,26 +97,26 @@ class ReportController(Controller):
"""
requestcontent = simplejson.loads(data)
url, type = requestcontent[0], requestcontent[1]
- if type == 'qweb-pdf':
- reportname = url.split('/report/pdf/report/')[1].split('?')[0].split('/')[0]
- if '?' not in url:
+ if type == 'qweb-pdf':
+ reportname = url.split('/report/pdf/')[1].split('?')[0]
+
+ docids = None
+ if '/' in reportname:
+ reportname, docids = reportname.split('/')
+
+ if docids:
# Generic report:
- docids = url.split('/')[-1]
- response = self.report_pdf(reportname, docids)
+ response = self.report_routes(reportname, docids=docids, converter='pdf')
else:
# Particular report:
- querystring = url.split('?')[1]
- querystring = dict(urlparse.parse_qsl(querystring))
- response = self.report_pdf_particular(reportname, **querystring)
+ data = url_decode(url.split('?')[1]).items() # decoding the args represented in JSON
+ response = self.report_routes(reportname, converter='pdf', **dict(data))
response.headers.add('Content-Disposition', 'attachment; filename=%s.pdf;' % reportname)
response.set_cookie('fileToken', token)
return response
elif type =='controller':
- from werkzeug.test import Client
- from werkzeug.wrappers import BaseResponse
- from werkzeug.datastructures import Headers
reqheaders = Headers(request.httprequest.headers)
response = Client(request.httprequest.app, BaseResponse).get(url, headers=reqheaders, follow_redirects=True)
response.set_cookie('fileToken', token)
@@ -132,28 +127,3 @@ class ReportController(Controller):
@route(['/report/check_wkhtmltopdf'], type='json', auth="user")
def check_wkhtmltopdf(self):
return request.registry['report']._check_wkhtmltopdf()
-
- def _eval_params(self, param):
- """Parse a dict generated by the webclient (javascript) into a python dict.
- """
- if isinstance(param, dict):
- for key, value in param.iteritems():
- if value.lower() == 'false':
- param[key] = False
- elif value.lower() == 'true':
- param[key] = True
- elif ',' in value:
- param[key] = [int(i) for i in value.split(',')]
- else:
- try:
- param[key] = int(value)
- except (ValueError, TypeError):
- pass
- else:
- if isinstance(param, (str, unicode)):
- param = [int(i) for i in param.split(',')]
- if isinstance(param, list):
- param = list(set(param))
- if isinstance(param, int):
- param = [param]
- return param
diff --git a/addons/report/models/__init__.py b/addons/report/models/__init__.py
index e048e10eb91..d21f03e5bcb 100644
--- a/addons/report/models/__init__.py
+++ b/addons/report/models/__init__.py
@@ -1,2 +1,3 @@
import report
import report_paperformat
+import abstract_report
diff --git a/addons/report/models/abstract_report.py b/addons/report/models/abstract_report.py
new file mode 100644
index 00000000000..2da32b6c0f6
--- /dev/null
+++ b/addons/report/models/abstract_report.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2014-Today OpenERP SA ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+from openerp.osv import osv
+
+
+class AbstractReport(osv.AbstractModel):
+ """Model used to embed old style reports"""
+ _name = 'report.abstract_report'
+ _template = None
+ _wrapped_report_class = None
+
+ def render_html(self, cr, uid, ids, data=None, context=None):
+ if context is None:
+ context = {}
+
+ # If the key 'landscape' is present in data['form'], passing it into the context
+ if data and data.get('form', {}).get('landscape'):
+ context['landscape'] = True
+
+ if context and context.get('active_ids'):
+ # Browse the selected objects via their reference in context
+ model = context.get('active_model') or context.get('model')
+ objects_model = self.pool[model]
+ objects = objects_model.browse(cr, uid, context['active_ids'], context=context)
+ else:
+ # If no context is set (for instance, during test execution), build one
+ model = self.pool['report']._get_report_from_name(cr, uid, self._template).model
+ objects_model = self.pool[model]
+ objects = objects_model.browse(cr, uid, ids, context=context)
+ context['active_model'] = model
+ context['active_ids'] = ids
+
+ # Generate the old style report
+ wrapped_report = self._wrapped_report_class(cr, uid, '', context=context)
+ wrapped_report.set_context(objects, data, context['active_ids'])
+
+ # Rendering self._template with the wrapped report instance localcontext as
+ # rendering environment
+ docargs = wrapped_report.localcontext
+ docargs['docs'] = docargs.get('objects')
+
+ # Used in template translating (see render_doc method from report model)
+ docargs['doc_ids'] = context['active_ids']
+ docargs['doc_model'] = model
+
+ return self.pool['report'].render(cr, uid, [], self._template, docargs, context=context)
diff --git a/addons/report/models/report.py b/addons/report/models/report.py
index 8abe8e544eb..8dd8a4dce6f 100644
--- a/addons/report/models/report.py
+++ b/addons/report/models/report.py
@@ -20,9 +20,10 @@
##############################################################################
from openerp.osv import osv
+from openerp.tools import config
from openerp.tools.translate import _
-from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, config
-from openerp.osv.fields import float as float_field, function as function_field, datetime as datetime_field
+from openerp.addons.web.http import request
+from openerp.tools.safe_eval import safe_eval as eval
import os
import time
@@ -34,8 +35,6 @@ import tempfile
import lxml.html
import cStringIO
import subprocess
-from datetime import datetime
-from functools import partial
from distutils.version import LooseVersion
try:
from pyPdf import PdfFileWriter, PdfFileReader
@@ -46,7 +45,7 @@ except ImportError:
_logger = logging.getLogger(__name__)
-"""Check the presence of wkhtmltopdf and return its version."""
+"""Check the presence of wkhtmltopdf and return its version at OpnerERP start-up."""
wkhtmltopdf_state = 'install'
try:
process = subprocess.Popen(
@@ -70,98 +69,26 @@ class Report(osv.Model):
public_user = None
+ MINIMAL_HTML_PAGE = """
+
+
+
+
+
+
+
+
+
+ {subst}
+
+
+ {body}
+
+"""
+
#--------------------------------------------------------------------------
# Extension of ir_ui_view.render with arguments frequently used in reports
#--------------------------------------------------------------------------
-
- def _get_digits(self, cr, uid, obj=None, f=None, dp=None):
- d = DEFAULT_DIGITS = 2
- if dp:
- decimal_precision_obj = self.pool['decimal.precision']
- ids = decimal_precision_obj.search(cr, uid, [('name', '=', dp)])
- if ids:
- d = decimal_precision_obj.browse(cr, uid, ids)[0].digits
- elif obj and f:
- res_digits = getattr(obj._columns[f], 'digits', lambda x: ((16, DEFAULT_DIGITS)))
- if isinstance(res_digits, tuple):
- d = res_digits[1]
- else:
- d = res_digits(cr)[1]
- elif (hasattr(obj, '_field') and
- isinstance(obj._field, (float_field, function_field)) and
- obj._field.digits):
- d = obj._field.digits[1] or DEFAULT_DIGITS
- return d
-
- def _get_lang_dict(self, cr, uid):
- pool_lang = self.pool['res.lang']
- lang = self.localcontext.get('lang', 'en_US') or 'en_US'
- lang_ids = pool_lang.search(cr, uid, [('code', '=', lang)])[0]
- lang_obj = pool_lang.browse(cr, uid, lang_ids)
- lang_dict = {
- 'lang_obj': lang_obj,
- 'date_format': lang_obj.date_format,
- 'time_format': lang_obj.time_format
- }
- self.lang_dict.update(lang_dict)
- self.default_lang[lang] = self.lang_dict.copy()
- return True
-
- def formatLang(self, value, digits=None, date=False, date_time=False, grouping=True, monetary=False, dp=False, currency_obj=False, cr=None, uid=None):
- """
- Assuming 'Account' decimal.precision=3:
- formatLang(value) -> digits=2 (default)
- formatLang(value, digits=4) -> digits=4
- formatLang(value, dp='Account') -> digits=3
- formatLang(value, digits=5, dp='Account') -> digits=5
- """
- def get_date_length(date_format=DEFAULT_SERVER_DATE_FORMAT):
- return len((datetime.now()).strftime(date_format))
-
- if digits is None:
- if dp:
- digits = self._get_digits(cr, uid, dp=dp)
- else:
- digits = self._get_digits(cr, uid, value)
-
- if isinstance(value, (str, unicode)) and not value:
- return ''
-
- if not self.lang_dict_called:
- self._get_lang_dict(cr, uid)
- self.lang_dict_called = True
-
- if date or date_time:
- if not str(value):
- return ''
-
- date_format = self.lang_dict['date_format']
- parse_format = DEFAULT_SERVER_DATE_FORMAT
- if date_time:
- value = value.split('.')[0]
- date_format = date_format + " " + self.lang_dict['time_format']
- parse_format = DEFAULT_SERVER_DATETIME_FORMAT
- if isinstance(value, basestring):
- # FIXME: the trimming is probably unreliable if format includes day/month names
- # and those would need to be translated anyway.
- date = datetime.strptime(value[:get_date_length(parse_format)], parse_format)
- elif isinstance(value, time.struct_time):
- date = datetime(*value[:6])
- else:
- date = datetime(*value.timetuple()[:6])
- if date_time:
- # Convert datetime values to the expected client/context timezone
- date = datetime_field.context_timestamp(cr, uid, timestamp=date, context=self.localcontext)
- return date.strftime(date_format.encode('utf-8'))
-
- res = self.lang_dict['lang_obj'].format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary)
- if currency_obj:
- if currency_obj.position == 'after':
- res = '%s %s' % (res, currency_obj.symbol)
- elif currency_obj and currency_obj.position == 'before':
- res = '%s %s' % (currency_obj.symbol, res)
- return res
-
def render(self, cr, uid, ids, template, values=None, context=None):
"""Allow to render a QWeb template python-side. This function returns the 'ir.ui.view'
render but embellish it with some variables/methods used in reports.
@@ -175,28 +102,19 @@ class Report(osv.Model):
if context is None:
context = {}
- self.lang_dict = self.default_lang = {}
- self.lang_dict_called = False
- self.localcontext = {
- 'lang': context.get('lang'),
- 'tz': context.get('tz'),
- 'uid': context.get('uid'),
- }
- self._get_lang_dict(cr, uid)
-
view_obj = self.pool['ir.ui.view']
- def render_doc(doc_id, model, template):
- """Helper used when a report should be translated into the associated
- partner's lang.
+ def translate_doc(doc_id, model, lang_field, template):
+ """Helper used when a report should be translated into a specific lang.
-
+
:param doc_id: id of the record to translate
:param model: model of the record to translate
- :param template: name of the template to translate into the partner's lang
+ :param lang_field': field of the record containing the lang
+ :param template: name of the template to translate into the lang_field
"""
ctx = context.copy()
doc = self.pool[model].browse(cr, uid, doc_id, context=ctx)
@@ -205,26 +123,28 @@ class Report(osv.Model):
if ctx.get('translatable') is True:
qcontext['o'] = doc
else:
- ctx['lang'] = doc.partner_id.lang
+ # Reach the lang we want to translate the doc into
+ ctx['lang'] = eval('doc.%s' % lang_field, {'doc': doc})
qcontext['o'] = self.pool[model].browse(cr, uid, doc_id, context=ctx)
return view_obj.render(cr, uid, template, qcontext, context=ctx)
+ user = self.pool['res.users'].browse(cr, uid, uid)
+ website = None
+ if request and hasattr(request, 'website'):
+ website = request.website
values.update({
'time': time,
- 'formatLang': partial(self.formatLang, cr=cr, uid=uid),
- 'get_digits': self._get_digits,
- 'render_doc': render_doc,
+ 'translate_doc': translate_doc,
'editable': True, # Will active inherit_branding
- 'res_company': self.pool['res.users'].browse(cr, uid, uid).company_id,
- 'website': False, # Will be overidden by ir.ui.view if the request has website enabled
+ 'user': user,
+ 'res_company': user.company_id,
+ 'website': website,
})
-
return view_obj.render(cr, uid, template, values, context=context)
#--------------------------------------------------------------------------
- # Main reports methods
+ # Main report methods
#--------------------------------------------------------------------------
-
def get_html(self, cr, uid, ids, report_name, data=None, context=None):
"""This method generates and returns html version of a report.
"""
@@ -233,7 +153,7 @@ class Report(osv.Model):
try:
report_model_name = 'report.%s' % report_name
particularreport_obj = self.pool[report_model_name]
- return particularreport_obj.render_html(cr, uid, ids, data={'form': data}, context=context)
+ return particularreport_obj.render_html(cr, uid, ids, data=data, context=context)
except KeyError:
report = self._get_report_from_name(cr, uid, report_name)
report_obj = self.pool[report.model]
@@ -254,15 +174,122 @@ class Report(osv.Model):
if html is None:
html = self.get_html(cr, uid, ids, report_name, data=data, context=context)
- html = html.decode('utf-8')
+ html = html.decode('utf-8') # Ensure the current document is utf-8 encoded.
# Get the ir.actions.report.xml record we are working on.
report = self._get_report_from_name(cr, uid, report_name)
+ # Check if we have to save the report or if we have to get one from the db.
+ save_in_attachment = self._check_attachment_use(cr, uid, ids, report)
+ # Get the paperformat associated to the report, otherwise fallback on the company one.
+ if not report.paperformat_id:
+ user = self.pool['res.users'].browse(cr, uid, uid)
+ paperformat = user.company_id.paperformat_id
+ else:
+ paperformat = report.paperformat_id
- # Check attachment_use field. If set to true and an existing pdf is already saved, load
- # this one now. Else, mark save it.
+ # Preparing the minimal html pages
+ subst = " "
+ css = '' # Will contain local css
+ headerhtml = []
+ contenthtml = []
+ footerhtml = []
+ base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url')
+
+ # The received html report must be simplified. We convert it in a xml tree
+ # in order to extract headers, bodies and footers.
+ try:
+ root = lxml.html.fromstring(html)
+
+ for node in root.xpath("//html/head/style"):
+ css += node.text
+
+ for node in root.xpath("//div[@class='header']"):
+ body = lxml.html.tostring(node)
+ header = self.MINIMAL_HTML_PAGE.format(css=css, subst=subst, body=body, base_url=base_url)
+ headerhtml.append(header)
+
+ for node in root.xpath("//div[@class='footer']"):
+ body = lxml.html.tostring(node)
+ footer = self.MINIMAL_HTML_PAGE.format(css=css, subst=subst, body=body, base_url=base_url)
+ footerhtml.append(footer)
+
+ for node in root.xpath("//div[@class='page']"):
+ # Previously, we marked some reports to be saved in attachment via their ids, so we
+ # must set a relation between report ids and report's content. We use the QWeb
+ # branding in order to do so: searching after a node having a data-oe-model
+ # attribute with the value of the current report model and read its oe-id attribute
+ oemodelnode = node.find(".//*[@data-oe-model='%s']" % report.model)
+ if oemodelnode is not None:
+ reportid = oemodelnode.get('data-oe-id')
+ if reportid:
+ reportid = int(reportid)
+ else:
+ reportid = False
+
+ body = lxml.html.tostring(node)
+ reportcontent = self.MINIMAL_HTML_PAGE.format(css=css, subst='', body=body, base_url=base_url)
+
+ # FIXME: imo the best way to extract record id from html reports is by using the
+ # qweb branding. As website editor is not yet splitted in a module independant from
+ # website, when we print a unique report we can use the id passed in argument to
+ # identify it.
+ if ids and len(ids) == 1:
+ reportid = ids[0]
+
+ contenthtml.append(tuple([reportid, reportcontent]))
+
+ except lxml.etree.XMLSyntaxError:
+ contenthtml = []
+ contenthtml.append(html)
+ save_in_attachment = {} # Don't save this potentially malformed document
+
+ # Get paperformat arguments set in the root html tag. They are prioritized over
+ # paperformat-record arguments.
+ specific_paperformat_args = {}
+ for attribute in root.items():
+ if attribute[0].startswith('data-report-'):
+ specific_paperformat_args[attribute[0]] = attribute[1]
+
+ # Run wkhtmltopdf process
+ pdf = self._generate_wkhtml_pdf(
+ cr, uid, headerhtml, footerhtml, contenthtml, context.get('landscape'),
+ paperformat, specific_paperformat_args, save_in_attachment
+ )
+ return pdf
+
+ def get_action(self, cr, uid, ids, report_name, data=None, context=None):
+ """Return an action of type ir.actions.report.xml.
+
+ :param report_name: Name of the template to generate an action for
+ """
+ if context is None:
+ context = {}
+
+ report_obj = self.pool['ir.actions.report.xml']
+ idreport = report_obj.search(cr, uid, [('report_name', '=', report_name)], context=context)
+ try:
+ report = report_obj.browse(cr, uid, idreport[0], context=context)
+ except IndexError:
+ raise osv.except_osv(_('Bad Report'), _('This report is not loaded into the database.'))
+
+ action = {
+ 'context': context,
+ 'data': data,
+ 'type': 'ir.actions.report.xml',
+ 'report_name': report.report_name,
+ 'report_type': report.report_type,
+ 'report_file': report.report_file,
+ }
+ return action
+
+ #--------------------------------------------------------------------------
+ # Report generation helpers
+ #--------------------------------------------------------------------------
+ def _check_attachment_use(self, cr, uid, ids, report):
+ """ Check attachment_use field. If set to true and an existing pdf is already saved, load
+ this one now. Else, mark save it.
+ """
save_in_attachment = {}
-
if report.attachment_use is True:
save_in_attachment['model'] = report.model
save_in_attachment['loaded_documents'] = {}
@@ -287,132 +314,8 @@ class Report(osv.Model):
_logger.info('The PDF document %s was loaded from the database' % filename)
else:
# Mark current document to be saved
- save_in_attachment[id] = filename
-
- # Get the paperformat associated to the report, otherwise fallback on the company one.
- if not report.paperformat_id:
- user = self.pool['res.users'].browse(cr, uid, uid)
- paperformat = user.company_id.paperformat_id
- else:
- paperformat = report.paperformat_id
-
- # Preparing the minimal html pages
- #subst = self._get_url_content('/report/static/src/js/subst.js')[0] # Used in age numbering
- subst = " "
- css = '' # Will contain local css
-
- headerhtml = []
- contenthtml = []
- footerhtml = []
- base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url')
-
- minimalhtml = """
-
-
-
-
-
-
-
-
-
- {subst}
-
-
- {body}
-
-"""
-
- # The retrieved html report must be simplified. We convert it into a xml tree
- # via lxml in order to extract headers, footers and content.
- try:
- root = lxml.html.fromstring(html)
-
- for node in root.xpath("//html/head/style"):
- css += node.text
-
- for node in root.xpath("//div[@class='header']"):
- body = lxml.html.tostring(node)
- header = minimalhtml.format(css=css, subst=subst, body=body, base_url=base_url)
- headerhtml.append(header)
-
- for node in root.xpath("//div[@class='footer']"):
- body = lxml.html.tostring(node)
- footer = minimalhtml.format(css=css, subst=subst, body=body, base_url=base_url)
- footerhtml.append(footer)
-
- for node in root.xpath("//div[@class='page']"):
- # Previously, we marked some reports to be saved in attachment via their ids, so we
- # must set a relation between report ids and report's content. We use the QWeb
- # branding in order to do so: searching after a node having a data-oe-model
- # attribute with the value of the current report model and read its oe-id attribute
- oemodelnode = node.find(".//*[@data-oe-model='%s']" % report.model)
- if oemodelnode is not None:
- reportid = oemodelnode.get('data-oe-id')
- if reportid:
- reportid = int(reportid)
- else:
- reportid = False
-
- body = lxml.html.tostring(node)
- reportcontent = minimalhtml.format(css=css, subst='', body=body, base_url=base_url)
- contenthtml.append(tuple([reportid, reportcontent]))
-
- except lxml.etree.XMLSyntaxError:
- contenthtml = []
- contenthtml.append(html)
- save_in_attachment = {} # Don't save this potentially malformed document
-
- # Get paperformat arguments set in the root html tag. They are prioritized over
- # paperformat-record arguments.
- specific_paperformat_args = {}
- for attribute in root.items():
- if attribute[0].startswith('data-report-'):
- specific_paperformat_args[attribute[0]] = attribute[1]
-
- # Run wkhtmltopdf process
- pdf = self._generate_wkhtml_pdf(
- cr, uid, headerhtml, footerhtml, contenthtml, context.get('landscape'),
- paperformat, specific_paperformat_args, save_in_attachment
- )
- return pdf
-
- def get_action(self, cr, uid, ids, report_name, datas=None, context=None):
- """Return an action of type ir.actions.report.xml.
-
- :param report_name: Name of the template to generate an action for
- """
- # TODO: return the action for the ids passed in args
- if context is None:
- context = {}
-
- if datas is None:
- datas = {}
-
- report_obj = self.pool.get('ir.actions.report.xml')
- idreport = report_obj.search(cr, uid, [('report_name', '=', report_name)], context=context)
-
- try:
- report = report_obj.browse(cr, uid, idreport[0], context=context)
- except IndexError:
- raise osv.except_osv(_('Bad Report'),
- _('This report is not loaded into the database.'))
-
- action = {
- 'type': 'ir.actions.report.xml',
- 'report_name': report.report_name,
- 'report_type': report.report_type,
- 'report_file': report.report_file,
- }
-
- if datas:
- action['datas'] = datas
-
- return action
-
- #--------------------------------------------------------------------------
- # Report generation helpers
- #--------------------------------------------------------------------------
+ save_in_attachment[record_id] = filename
+ return save_in_attachment
def _check_wkhtmltopdf(self):
return wkhtmltopdf_state
@@ -436,8 +339,8 @@ class Report(osv.Model):
# Passing the cookie to wkhtmltopdf in order to resolve URL.
try:
- from openerp.addons.web.http import request
- command_args.extend(['--cookie', 'session_id', request.session.sid])
+ if request:
+ command_args.extend(['--cookie', 'session_id', request.session.sid])
except AttributeError:
pass
@@ -445,6 +348,8 @@ class Report(osv.Model):
if paperformat:
command_args.extend(self._build_wkhtmltopdf_args(paperformat, spec_paperformat_args))
+ command_args.extend(['--load-error-handling', 'ignore'])
+
if landscape and '--orientation' in command_args:
command_args_copy = list(command_args)
for index, elem in enumerate(command_args_copy):
@@ -464,7 +369,7 @@ class Report(osv.Model):
# Directly load the document if we have it
if save_in_attachment and save_in_attachment['loaded_documents'].get(reporthtml[0]):
pdfreport.write(save_in_attachment['loaded_documents'].get(reporthtml[0]))
- pdfreport.flush()
+ pdfreport.seek(0)
pdfdocuments.append(pdfreport)
continue
@@ -473,7 +378,7 @@ class Report(osv.Model):
head_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.header.tmp.',
dir=tmp_dir, mode='w+')
head_file.write(headers[index])
- head_file.flush()
+ head_file.seek(0)
command_arg_local.extend(['--header-html', head_file.name])
# Footer stuff
@@ -481,14 +386,14 @@ class Report(osv.Model):
foot_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.footer.tmp.',
dir=tmp_dir, mode='w+')
foot_file.write(footers[index])
- foot_file.flush()
+ foot_file.seek(0)
command_arg_local.extend(['--footer-html', foot_file.name])
# Body stuff
content_file = tempfile.NamedTemporaryFile(suffix='.html', prefix='report.body.tmp.',
dir=tmp_dir, mode='w+')
content_file.write(reporthtml[1])
- content_file.flush()
+ content_file.seek(0)
try:
# If the server is running with only one worker, ask to create a secund to be able
@@ -525,7 +430,7 @@ class Report(osv.Model):
_logger.info('The PDF document %s is now saved in the '
'database' % attachment['name'])
- pdfreport.flush()
+ pdfreport.seek(0)
pdfdocuments.append(pdfreport)
if headers:
@@ -566,15 +471,24 @@ class Report(osv.Model):
command_args.extend(['--page-size', paperformat.format])
if paperformat.page_height and paperformat.page_width and paperformat.format == 'custom':
- command_args.extend(['--page-width', str(paperformat.page_width) + 'in'])
- command_args.extend(['--page-height', str(paperformat.page_height) + 'in'])
+ command_args.extend(['--page-width', str(paperformat.page_width) + 'mm'])
+ command_args.extend(['--page-height', str(paperformat.page_height) + 'mm'])
- if specific_paperformat_args and specific_paperformat_args['data-report-margin-top']:
- command_args.extend(['--margin-top',
- str(specific_paperformat_args['data-report-margin-top'])])
+ if specific_paperformat_args and specific_paperformat_args.get('data-report-margin-top'):
+ command_args.extend(['--margin-top', str(specific_paperformat_args['data-report-margin-top'])])
elif paperformat.margin_top:
command_args.extend(['--margin-top', str(paperformat.margin_top)])
+ if specific_paperformat_args and specific_paperformat_args.get('data-report-dpi'):
+ command_args.extend(['--dpi', str(specific_paperformat_args['data-report-dpi'])])
+ elif paperformat.dpi:
+ command_args.extend(['--dpi', str(paperformat.dpi)])
+
+ if specific_paperformat_args and specific_paperformat_args.get('data-report-header-spacing'):
+ command_args.extend(['--header-spacing', str(specific_paperformat_args['data-report-header-spacing'])])
+ elif paperformat.header_spacing:
+ command_args.extend(['--header-spacing', str(paperformat.header_spacing)])
+
if paperformat.margin_left:
command_args.extend(['--margin-left', str(paperformat.margin_left)])
if paperformat.margin_bottom:
@@ -583,12 +497,8 @@ class Report(osv.Model):
command_args.extend(['--margin-right', str(paperformat.margin_right)])
if paperformat.orientation:
command_args.extend(['--orientation', str(paperformat.orientation)])
- if paperformat.header_spacing:
- command_args.extend(['--header-spacing', str(paperformat.header_spacing)])
if paperformat.header_line:
command_args.extend(['--header-line'])
- if paperformat.dpi:
- command_args.extend(['--dpi', str(paperformat.dpi)])
return command_args
diff --git a/addons/report/models/report_paperformat.py b/addons/report/models/report_paperformat.py
index e3e747b81a4..6b84f3d3363 100644
--- a/addons/report/models/report_paperformat.py
+++ b/addons/report/models/report_paperformat.py
@@ -69,8 +69,8 @@ class report_paperformat(osv.Model):
'margin_bottom': fields.integer('Bottom Margin (mm)'),
'margin_left': fields.integer('Left Margin (mm)'),
'margin_right': fields.integer('Right Margin (mm)'),
- 'page_height': fields.integer('Page height (in)'),
- 'page_width': fields.integer('Page width (in)'),
+ 'page_height': fields.integer('Page height (mm)'),
+ 'page_width': fields.integer('Page width (mm)'),
'orientation': fields.selection([('Landscape', 'Landscape'),
('Portrait', 'Portrait')],
'Orientation'),
diff --git a/addons/report/static/src/js/qwebactionmanager.js b/addons/report/static/src/js/qwebactionmanager.js
index b75003942d2..7e55661be86 100644
--- a/addons/report/static/src/js/qwebactionmanager.js
+++ b/addons/report/static/src/js/qwebactionmanager.js
@@ -1,13 +1,20 @@
openerp.report = function(instance) {
var wkhtmltopdf_state;
+ var trigger_download = function(session, response, c) {
+ session.get_file({
+ url: '/report/download',
+ data: {data: JSON.stringify(response)},
+ complete: openerp.web.unblockUI,
+ error: c.rpc_error.bind(c)
+ });
+ }
+
instance.web.ActionManager = instance.web.ActionManager.extend({
ir_actions_report_xml: function(action, options) {
var self = this;
instance.web.blockUI();
action = _.clone(action);
- var eval_contexts = ([instance.session.user_context] || []).concat([action.context]);
- action.context = instance.web.pyeval.eval('contexts',eval_contexts);
_t = instance.web._t;
// QWeb reports
@@ -15,49 +22,40 @@ openerp.report = function(instance) {
var report_url = '';
switch (action.report_type) {
case 'qweb-html':
- report_url = '/report/' + action.report_name;
+ report_url = '/report/html/' + action.report_name;
break;
case 'qweb-pdf':
- report_url = '/report/pdf/report/' + action.report_name;
+ report_url = '/report/pdf/' + action.report_name;
break;
case 'controller':
report_url = action.report_file;
break;
default:
- report_url = '/report/' + action.report_name;
+ report_url = '/report/html/' + action.report_name;
break;
}
- // single/multiple id(s): no query string
- // wizard: query string of action.datas.form
- if (!('datas' in action)) {
+ // generic report: no query string
+ // particular: query string of action.data.form and context
+ if (!('data' in action) || !(action.data)) {
if ('active_ids' in action.context) {
report_url += "/" + action.context.active_ids.join(',');
}
} else {
- _.each(action.datas.form, function(value, key) {
- // will be erased when all wizards are rewritten
- if (key.substring(0, 12) === 'used_context') {
- delete action.datas.form[key];
- }
-
- if ($.type(value) === 'array') {
- action.datas.form[key] = value.join(',');
- }
- });
- report_url += "?" + $.param(action.datas.form);
+ report_url += "?options=" + encodeURIComponent(JSON.stringify(action.data));
+ report_url += "&context=" + encodeURIComponent(JSON.stringify(action.context));
}
+
+ var response = new Array();
+ response[0] = report_url;
+ response[1] = action.report_type;
+ var c = openerp.webclient.crashmanager;
+
if (action.report_type == 'qweb-html') {
- // Open the html report in a popup
window.open(report_url, '_blank', 'height=900,width=1280');
instance.web.unblockUI();
- return;
} else {
// Trigger the download of the pdf/controller report
- var c = openerp.webclient.crashmanager;
- var response = new Array();
- response[0] = report_url;
- response[1] = action.report_type;
if (action.report_type == 'qweb-pdf') {
(wkhtmltopdf_state = wkhtmltopdf_state || openerp.session.rpc('/report/check_wkhtmltopdf')).then(function (presence) {
@@ -66,8 +64,10 @@ openerp.report = function(instance) {
self.do_notify(_t('Report'), _t('Unable to find Wkhtmltopdf on this \
system. The report will be shown in html.
\
wkhtmltopdf.org'), true);
- window.open(report_url.substring(12), '_blank', 'height=768,width=1024');
+ report_url = report_url.substring(12)
+ window.open('/report/html/' + report_url, '_blank', 'height=768,width=1024');
instance.web.unblockUI();
+ return;
} else {
if (presence == 'upgrade') {
self.do_notify(_t('Report'), _t('You should upgrade your version of\
@@ -75,22 +75,13 @@ openerp.report = function(instance) {
support for table-breaking between pages.
wkhtmltopdf.org'), true);
}
- self.session.get_file({
- url: '/report/download',
- data: {data: JSON.stringify(response)},
- complete: openerp.web.unblockUI,
- error: c.rpc_error.bind(c)
- });
}
+ return trigger_download(self.session, response, c);
});
- } else {
- self.session.get_file({
- url: '/report/download',
- data: {data: JSON.stringify(response)},
- complete: openerp.web.unblockUI,
- error: c.rpc_error.bind(c)
- });
- }
+ }
+ else if (action.report_type == 'controller') {
+ return trigger_download(self.session, response, c);
+ }
}
} else {
return self._super(action, options);
diff --git a/addons/report/tests/test_reports.py b/addons/report/tests/test_reports.py
index dda14442709..b1853489573 100644
--- a/addons/report/tests/test_reports.py
+++ b/addons/report/tests/test_reports.py
@@ -1,5 +1,23 @@
# -*- coding: utf-8 -*-
-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2014-Today OpenERP SA ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
import logging
import openerp
@@ -11,26 +29,24 @@ _logger = logging.getLogger(__name__)
@openerp.tests.common.post_install(True)
class TestReports(openerp.tests.TransactionCase):
def test_reports(self):
- return # commented out until post_install tests are working
-
registry, cr, uid = self.registry, self.cr, self.uid
r_model = registry('ir.actions.report.xml')
domain = [('report_type', 'like', 'qweb')]
for r in r_model.browse(cr, uid, r_model.search(cr, uid, domain)):
report_model = 'report.%s' % r.report_name
- particular_model = registry('ir.model').search(cr, uid, [('model', '=', report_model)])
-
+ try:
+ registry(report_model)
+ except KeyError:
# Only test the generic reports here
- if particular_model:
+ _logger.info("testing report %s", r.report_name)
+ report_model = registry(r.model)
+ report_model_ids = report_model.search(cr, uid, [], limit=10)
+ if not report_model_ids:
+ _logger.info("no record found skipping report %s", r.report_name)
+ if not r.multi:
+ report_model_ids = report_model_ids[:1]
+
+ # Test report generation
+ registry('report').get_html(cr, uid, report_model_ids, r.report_name)
+ else:
continue
-
- _logger.info("testing report %s", r.report_name)
- report_model = registry(r.model)
- report_model_ids = report_model.search(cr, uid, [], limit=10)
- if not report_model_ids:
- _logger.info("no record found skipping report %s", r.report_name)
- if not r.multi:
- report_model_ids = report_model_ids[:1]
-
- # Test report generation
- registry('report').get_html(cr, uid, report_model_ids, r.report_name)
diff --git a/addons/report/views/layouts.xml b/addons/report/views/layouts.xml
index 588775fc41e..4f5104c726b 100644
--- a/addons/report/views/layouts.xml
+++ b/addons/report/views/layouts.xml
@@ -6,7 +6,10 @@
+ t-att-data-main-object="repr(main_object) if editable else None"
+ t-att-data-report-margin-top="data_report_margin_top if data_report_margin_top else None"
+ t-att-data-report-header-spacing="data_report_header_spacing if data_report_header_spacing else None"
+ t-att-data-report-dpi="data_report_dpi if data_report_dpi else None">
@@ -64,6 +67,9 @@
.table-condensed > thead > tr > th {
border-bottom: 1px solid black !important;
}
+ .zero_min_height {
+ min-height: 0px !important;
+ }
@@ -84,11 +90,11 @@
-
+
-
+
@@ -158,5 +164,6 @@
+
diff --git a/addons/report_intrastat/__init__.py b/addons/report_intrastat/__init__.py
index d2f598434a2..119777f2c70 100644
--- a/addons/report_intrastat/__init__.py
+++ b/addons/report_intrastat/__init__.py
@@ -20,7 +20,5 @@
##############################################################################
import report_intrastat
-import report
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/addons/report_intrastat/__openerp__.py b/addons/report_intrastat/__openerp__.py
index 0fa57977db1..4682a777ccc 100644
--- a/addons/report_intrastat/__openerp__.py
+++ b/addons/report_intrastat/__openerp__.py
@@ -19,7 +19,6 @@
#
##############################################################################
-
{
'name': 'Intrastat Reporting',
'version': '1.0',
@@ -37,7 +36,8 @@ European Union.""",
'security/ir.model.access.csv',
'report_intrastat_view.xml',
'intrastat_report.xml',
- 'report_intrastat_data.xml'
+ 'report_intrastat_data.xml',
+ 'views/report_intrastatinvoice.xml'
],
'demo': [],
'test': ['test/report_intrastat_report.yml'],
@@ -45,4 +45,5 @@ European Union.""",
'auto_install': False,
'images': ['images/country_intrastat_code.jpeg','images/intrastat_code.jpeg'],
}
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/report_intrastat/intrastat_report.xml b/addons/report_intrastat/intrastat_report.xml
index 124cf899efb..c5a8e9e7f84 100644
--- a/addons/report_intrastat/intrastat_report.xml
+++ b/addons/report_intrastat/intrastat_report.xml
@@ -1,13 +1,13 @@
-
-
-
+
+
+
-
-
diff --git a/addons/report_intrastat/report/__init__.py b/addons/report_intrastat/report/__init__.py
deleted file mode 100644
index 64e530bc278..00000000000
--- a/addons/report_intrastat/report/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-import invoice
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/report_intrastat/report/invoice.py b/addons/report_intrastat/report/invoice.py
deleted file mode 100644
index c38a62774ac..00000000000
--- a/addons/report_intrastat/report/invoice.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-import time
-from openerp.report import report_sxw
-
-class account_invoice_intrastat(report_sxw.rml_parse):
- def __init__(self, cr, uid, name, context):
- super(account_invoice_intrastat, self).__init__(cr, uid, name, context=context)
- self.total=0
- self.localcontext.update({
- 'time': time,
- })
-
-report_sxw.report_sxw('report.account.invoice.intrastat', 'account.invoice', 'addons/report_intrastat/report/invoice.rml', parser=account_invoice_intrastat)
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/report_intrastat/report/invoice.rml b/addons/report_intrastat/report/invoice.rml
deleted file mode 100644
index fc410e04ce0..00000000000
--- a/addons/report_intrastat/report/invoice.rml
+++ /dev/null
@@ -1,401 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [[ repeatIn(objects,'o') ]]
- [[ setLang(o.partner_id.lang) ]]
-
-
-
-
-
-
-
-
- [[ (o.partner_id.title and o.partner_id.title.name) or '' ]] [[ (o.partner_id and o.partner_id.name) or '' ]]
- [[ o.partner_id and display_address(o.partner_id) ]]
-
-
-
- Tel. : [[ (o.partner_id.phone) or removeParentNode('para') ]]
- Fax : [[ (o.partner_id.fax) or removeParentNode('para') ]]
- VAT : [[ (o.partner_id.vat) or removeParentNode('para') ]]
-
-
-
- Invoice [[ ((o.type == 'out_invoice' and (o.state == 'open' or o.state == 'paid')) or removeParentNode('para')) and '' ]] [[ o.number ]]
- PRO-FORMA [[ ((o.type == 'out_invoice' and o.state == 'proforma2') or removeParentNode('para')) and '' ]]
- Draft Invoice [[ ((o.type == 'out_invoice' and o.state == 'draft') or removeParentNode('para')) and '' ]]
- Cancelled Invoice [[ ((o.type == 'out_invoice' and o.state == 'cancel') or removeParentNode('para')) and '' ]] [[ o.number ]]
- Refund [[ (o.type=='out_refund' or removeParentNode('para')) and '' ]] [[ o.number ]]
- Supplier Refund [[ (o.type=='in_refund' or removeParentNode('para')) and '' ]] [[ o.number ]]
- Supplier Invoice [[ (o.type=='in_invoice' or removeParentNode('para')) and '' ]] [[ o.number ]]
-
-
-
-
-
-
- Document
-
-
- Invoice Date
-
-
- Partner Ref.
-
-
-
-
-
-
- [[ o.name or ' ' ]]
-
-
- [[ formatLang(o.date_invoice,date=True) ]]
-
-
- [[ (o.partner_id.ref) or ' ' ]]
-
-
-
-
-
-
-
-
-
- Description
-
-
- Taxes
-
-
- Intrastat
-
-
- Weight
-
-
- Quantity
-
-
- Unit Price
-
-
- Disc. (%)
-
-
- Price
-
-
-
-
- [[ repeatIn(o.invoice_line,'l') ]]
-
-
-
- [[ l.name ]]
-
-
- [[ ', '.join([lt.name for lt in l.invoice_line_tax_id]) ]]
-
-
- [[l.product_id and l.product_id.intrastat_id and l.product_id.intrastat_id.name or '']]
-
-
- [[ l.product_id and l.product_id.weight or '']]
-
-
- [[ formatLang(l.quantity) ]] [[ (l.uos_id and l.uos_id.name) or '' ]]
-
-
- [[ formatLang(l.price_unit) ]]
-
-
- [[ l.discount and formatLang (l.discount) or '' ]]
-
');
+ transfo.$center = transfo.$markup.find(".transfo-scaler-mc");
+
+ // init setting and get css to set wrap
+ _setOptions($this, transfo);
+ _overwriteOptions ($this, transfo, settings);
+
+ // append controls to container
+ $("body").append(transfo.$markup);
+
+ // set transfo container and markup
+ setTimeout(function () {
+ _targetCss($this, transfo);
+ },0);
+
+ _bind($this, transfo);
+
+ _targetCss($this, transfo);
+ }
+
+ function _overwriteOptions ($this, transfo, settings) {
+ transfo.settings = $.extend(transfo.settings, settings || {});
+ }
+
+ function _setOptions ($this, transfo) {
+ var style = $this.attr("style") || "";
+ var transform = style.match(/transform\s*:([^;]+)/) ? style.match(/transform\s*:([^;]+)/)[1] : "";
+
+ transfo.settings = {};
+
+ transfo.settings.angle= transform.indexOf('rotate') != -1 ? parseFloat(transform.match(/rotate\(([^)]+)deg\)/)[1]) : 0;
+ transfo.settings.scalex= transform.indexOf('scaleX') != -1 ? parseFloat(transform.match(/scaleX\(([^)]+)\)/)[1]) : 1;
+ transfo.settings.scaley= transform.indexOf('scaleY') != -1 ? parseFloat(transform.match(/scaleY\(([^)]+)\)/)[1]) : 1;
+
+ transfo.settings.style = style.replace(/[^;]*transform[^;]+/g, '').replace(/;+/g, ';');
+ $this.attr("style", transfo.settings.style);
+
+ transfo.settings.height = $this.innerHeight();
+ transfo.settings.width = $this.innerWidth();
+
+ var translatex = transform.match(/translateX\(([0-9.-]+)(%|px)\)/);
+ var translatey = transform.match(/translateY\(([0-9.-]+)(%|px)\)/);
+ transfo.settings.translate = "%";
+
+ if (translatex && translatex[2] === "%") {
+ transfo.settings.translatexp = parseFloat(translatex[1]);
+ transfo.settings.translatex = transfo.settings.translatexp / 100 * transfo.settings.width;
+ } else {
+ transfo.settings.translatex = translatex ? parseFloat(translatex[1]) : 0;
+ }
+ if (translatey && translatey[2] === "%") {
+ transfo.settings.translateyp = parseFloat(translatey[1]);
+ transfo.settings.translatey = transfo.settings.translateyp / 100 * transfo.settings.height;
+ } else {
+ transfo.settings.translatey = translatey ? parseFloat(translatey[1]) : 0;
+ }
+
+ transfo.settings.css = window.getComputedStyle($this[0], null);
+ transfo.settings.pos = $this.offset();
+
+ transfo.settings.rotationStep = 5;
+ transfo.settings.hide = false;
+ transfo.settings.callback = function () {};
+ }
+
+ function _bind ($this, transfo) {
+ function mousedown (event) {
+ _mouseDown($this, this, transfo, event);
+ $(document).on("mousemove", mousemove).on("mouseup", mouseup);
+ }
+ function mousemove (event) {
+ _mouseMove($this, this, transfo, event);
+ }
+ function mouseup (event) {
+ _mouseUp($this, this, transfo, event);
+ $(document).off("mousemove", mousemove).off("mouseup", mouseup);
+ }
+
+ transfo.$markup.off().on("mousedown", mousedown);
+ transfo.$markup.find(">:not(.transfo-scaler-mc)").off().on("mousedown", mousedown);
+ }
+
+ function _mouseDown($this, div, transfo, event) {
+ event.preventDefault();
+ if (transfo.active || event.which !== 1) return;
+
+ var type = "position", $e = $(div);
+ if ($e.hasClass("transfo-rotator")) type = "rotator";
+ else if ($e.hasClass("transfo-scaler-tl")) type = "tl";
+ else if ($e.hasClass("transfo-scaler-tr")) type = "tr";
+ else if ($e.hasClass("transfo-scaler-br")) type = "br";
+ else if ($e.hasClass("transfo-scaler-bl")) type = "bl";
+ else if ($e.hasClass("transfo-scaler-tc")) type = "tc";
+ else if ($e.hasClass("transfo-scaler-bc")) type = "bc";
+ else if ($e.hasClass("transfo-scaler-ml")) type = "ml";
+ else if ($e.hasClass("transfo-scaler-mr")) type = "mr";
+
+ transfo.active = {
+ "type": type,
+ "scalex": transfo.settings.scalex,
+ "scaley": transfo.settings.scaley,
+ "pageX": event.pageX,
+ "pageY": event.pageY,
+ "center": transfo.$center.offset(),
+ };
+ }
+ function _mouseUp($this, div, transfo, event) {
+ transfo.active = null;
+ }
+
+ function _mouseMove($this, div, transfo, event) {
+ event.preventDefault();
+ if (!transfo.active) return;
+ var settings = transfo.settings;
+ var center = transfo.active.center;
+ var cdx = center.left - event.pageX;
+ var cdy = center.top - event.pageY;
+
+ if (transfo.active.type == "rotator") {
+ var ang, dang = Math.atan((settings.width * settings.scalex) / (settings.height * settings.scaley)) / rad;
+
+ if (cdy) ang = Math.atan(- cdx / cdy) / rad;
+ else ang = 0;
+ if (event.pageY >= center.top && event.pageX >= center.left) ang += 180;
+ else if (event.pageY >= center.top && event.pageX < center.left) ang += 180;
+ else if (event.pageY < center.top && event.pageX < center.left) ang += 360;
+
+ ang -= dang;
+ if (settings.scaley < 0 && settings.scalex < 0) ang += 180;
+
+ if (!event.ctrlKey) {
+ settings.angle = Math.round(ang / transfo.settings.rotationStep) * transfo.settings.rotationStep;
+ } else {
+ settings.angle = ang;
+ }
+
+ // reset position : don't move center
+ _targetCss($this, transfo);
+ var new_center = transfo.$center.offset();
+ var x = center.left - new_center.left;
+ var y = center.top - new_center.top;
+ var angle = ang * rad;
+ settings.translatex += x*Math.cos(angle) - y*Math.sin(-angle);
+ settings.translatey += - x*Math.sin(angle) + y*Math.cos(-angle);
+ }
+ else if (transfo.active.type == "position") {
+ var angle = settings.angle * rad;
+ var x = event.pageX - transfo.active.pageX;
+ var y = event.pageY - transfo.active.pageY;
+ transfo.active.pageX = event.pageX;
+ transfo.active.pageY = event.pageY;
+ var dx = x*Math.cos(angle) - y*Math.sin(-angle);
+ var dy = - x*Math.sin(angle) + y*Math.cos(-angle);
+
+ settings.translatex += dx;
+ settings.translatey += dy;
+ }
+ else if (transfo.active.type.length === 2) {
+ var angle = settings.angle * rad;
+ var dx = cdx*Math.cos(angle) - cdy*Math.sin(-angle);
+ var dy = - cdx*Math.sin(angle) + cdy*Math.cos(-angle);
+ if (transfo.active.type.indexOf("t") != -1) {
+ settings.scaley = dy / (settings.height/2);
+ }
+ if (transfo.active.type.indexOf("b") != -1) {
+ settings.scaley = - dy / (settings.height/2);
+ }
+ if (transfo.active.type.indexOf("l") != -1) {
+ settings.scalex = dx / (settings.width/2);
+ }
+ if (transfo.active.type.indexOf("r") != -1) {
+ settings.scalex = - dx / (settings.width/2);
+ }
+ if (settings.scaley > 0 && settings.scaley < 0.05) settings.scaley = 0.05;
+ if (settings.scalex > 0 && settings.scalex < 0.05) settings.scalex = 0.05;
+ if (settings.scaley < 0 && settings.scaley > -0.05) settings.scaley = -0.05;
+ if (settings.scalex < 0 && settings.scalex > -0.05) settings.scalex = -0.05;
+
+ if (event.shiftKey &&
+ (transfo.active.type === "tl" || transfo.active.type === "bl" ||
+ transfo.active.type === "tr" || transfo.active.type === "br")) {
+ settings.scaley = settings.scalex;
+ }
+ }
+
+ settings.angle = Math.round(settings.angle);
+ settings.translatex = Math.round(settings.translatex);
+ settings.translatey = Math.round(settings.translatey);
+ settings.scalex = Math.round(settings.scalex*100)/100;
+ settings.scaley = Math.round(settings.scaley*100)/100;
+
+ _targetCss($this, transfo);
+ return false;
+ }
+
+ function _setCss($this, css, settings) {
+ var transform = "";
+ var trans = false;
+ if (settings.angle !== 0) {
+ trans = true;
+ transform += " rotate("+settings.angle+"deg) ";
+ }
+ if (settings.translatex) {
+ trans = true;
+ transform += " translateX("+(settings.translate === "%" ? settings.translatexp+"%" : settings.translatex+"px")+") ";
+ }
+ if (settings.translatey) {
+ trans = true;
+ transform += " translateY("+(settings.translate === "%" ? settings.translateyp+"%" : settings.translatey+"px")+") ";
+ }
+ if (settings.scalex != 1) {
+ trans = true;
+ transform += " scaleX("+settings.scalex+") ";
+ }
+ if (settings.scaley != 1){
+ trans = true;
+ transform += " scaleY("+settings.scaley+") ";
+ }
+
+ if (trans) {
+ css += ";"
+ /* Safari */
+ css += "-webkit-transform:" + transform + ";"
+ /* Firefox */
+ + "-moz-transform:" + transform + ";"
+ /* IE */
+ + "-ms-transform:" + transform + ";"
+ /* Opera */
+ + "-o-transform:" + transform + ";"
+ /* Other */
+ + "transform:" + transform + ";";
+ }
+
+ css = css.replace(/(\s*;)+/g, ';').replace(/^\s*;|;\s*$/g, '');
+
+ $this.attr("style", css);
+ }
+
+ function _targetCss ($this, transfo) {
+ var settings = transfo.settings;
+ var width = parseFloat(settings.css.width);
+ var height = parseFloat(settings.css.height);
+ settings.translatexp = Math.round(settings.translatex/width*1000)/10;
+ settings.translateyp = Math.round(settings.translatey/height*1000)/10;
+
+ _setCss($this, settings.style, settings);
+
+ _setCss(transfo.$markup,
+ "position: absolute;" +
+ "top:" + settings.pos.top + "px;" +
+ "left:" + settings.pos.left + "px;" +
+ "width:" + width + "px;" +
+ "height:" + height + "px;" +
+ "cursor: move;",
+ settings);
+ transfo.$markup.find(">").css("transform", "scaleX("+(1/settings.scalex)+") scaleY("+(1/settings.scaley)+")");
+
+ _showHide($this, transfo);
+
+ transfo.settings.callback.call($this[0], $this);
+ }
+
+ function _showHide ($this, transfo) {
+ transfo.$markup.css("z-index", transfo.settings.hide ? -1 : 1000);
+ if (transfo.settings.hide) {
+ transfo.$markup.find(">").hide();
+ transfo.$markup.find(".transfo-scaler-mc").show();
+ } else {
+ transfo.$markup.find(">").show();
+ }
+ }
+
+ function _destroy ($this) {
+ $this.data('transfo').$markup.remove();
+ $this.removeData('transfo');
+ }
+
+ function _reset ($this) {
+ var transfo = $this.data('transfo');
+ _destroy($this);
+ $this.transfo(transfo.settings);
+ }
+
+})(jQuery);
diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js
index c965c5ef834..365b8cf46ea 100644
--- a/addons/website/static/src/js/website.editor.js
+++ b/addons/website/static/src/js/website.editor.js
@@ -64,7 +64,7 @@
return new website.editor.RTELinkDialog(editor).appendTo(document.body);
}
function image_dialog(editor, image) {
- return new website.editor.RTEImageDialog(editor, image).appendTo(document.body);
+ return new website.editor.MediaDialog(editor, image).appendTo(document.body);
}
// only enable editors manually
@@ -80,14 +80,19 @@
website.init_editor = function () {
CKEDITOR.plugins.add('customdialogs', {
-// requires: 'link,image',
+ // requires: 'link,image',
init: function (editor) {
editor.on('doubleclick', function (evt) {
var element = evt.data.element;
- if (element.is('img') && is_editable_node(element)) {
+ if ((element.is('img') || element.$.className.indexOf(' fa-') != -1) && is_editable_node(element)) {
image_dialog(editor, element);
return;
}
+ var parent = new CKEDITOR.dom.element(element.$.parentNode);
+ if (parent.$.className.indexOf('media_iframe_video') != -1 && is_editable_node(parent)) {
+ image_dialog(editor, parent);
+ return;
+ }
element = get_selected_link(editor) || evt.data.element;
if (!(element.is('a') && is_editable_node(element))) {
@@ -109,7 +114,7 @@
context: 'a',
});
//noinspection JSValidateTypes
- editor.addCommand('image', {
+ editor.addCommand('cimage', {
exec: function (editor) {
image_dialog(editor);
return true;
@@ -126,7 +131,7 @@
});
editor.ui.addButton('Image', {
label: 'Image',
- command: 'image',
+ command: 'cimage',
toolbar: 'insert,10',
});
@@ -400,7 +405,7 @@
init: function () {
this.on('edit', function () {
- new website.editor.FontIconsDialog(editor, this.element.$)
+ new website.editor.MediaDialog(editor, this.element)
.appendTo(document.body);
});
},
@@ -629,22 +634,48 @@
},
/**
- * Creates a "hover" button for image and link edition
+ * Creates a "hover" button for link edition
*
- * @param {String} label the button's label
* @param {Function} editfn edition function, called when clicking the button
- * @param {String} [classes] additional classes to set on the button
* @returns {jQuery}
*/
- make_hover_button: function (label, editfn, classes) {
- return $(openerp.qweb.render('website.editor.hoverbutton', {
- label: label,
- classes: classes,
- })).hide().appendTo(document.body).click(function (e) {
+ make_hover_button_link: function (editfn) {
+ return $(openerp.qweb.render('website.editor.hoverbutton.link', {}))
+ .hide()
+ .click(function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ editfn.call(this, e);
+ })
+ .appendTo(document.body);
+ },
+
+ /**
+ * Creates a "hover" button for image
+ *
+ * @param {Function} editfn edition function, called when clicking the button
+ * @param {Function} stylefn edition style function, called when clicking the button
+ * @returns {jQuery}
+ */
+ make_hover_button_image: function (editfn, stylefn) {
+ var $div = $(openerp.qweb.render('website.editor.hoverbutton.media', {}))
+ .hide()
+ .appendTo(document.body);
+
+ $div.find('[data-toggle="dropdown"]').dropdown();
+ $div.find(".hover-edition-button").click(function (e) {
e.preventDefault();
e.stopPropagation();
editfn.call(this, e);
});
+ if (stylefn) {
+ $div.find(".hover-style-button").click(function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ stylefn.call(this, e);
+ });
+ }
+ return $div;
},
/**
* For UI clarity, during RTE edition when the user hovers links and
@@ -653,23 +684,18 @@
*/
setup_hover_buttons: function () {
var editor = this.rte.editor;
- var $link_button = this.make_hover_button(_t("Change"), function () {
+ var $link_button = this.make_hover_button_link(function () {
var sel = new CKEDITOR.dom.element(previous);
editor.getSelection().selectElement(sel);
if(sel.hasClass('fa')) {
- new website.editor.FontIconsDialog(editor, previous)
+ new website.editor.MediaDialog(editor, previous)
.appendTo(document.body);
} else if (previous.tagName.toUpperCase() === 'A') {
link_dialog(editor);
}
$link_button.hide();
previous = null;
- }, 'btn-xs');
- var $image_button = this.make_hover_button(_t("Change"), function () {
- image_dialog(editor, new CKEDITOR.dom.element(previous));
- $image_button.hide();
- previous = null;
- }, 'btn-sm');
+ });
function is_icons_widget(element) {
var w = editor.widgets.getByElement(element);
@@ -681,7 +707,7 @@
// -ish, because when moving to the button itself ``previous`` is
// still set to the element having triggered showing the button.
var previous;
- $(editor.element.$).on('mouseover', 'a, img, .fa', function () {
+ $(editor.element.$).on('mouseover', 'a', function () {
// Back from edit button -> ignore
if (previous && previous === this) { return; }
@@ -700,34 +726,19 @@
previous = this;
var $selected = $(this);
var position = $selected.offset();
- if ($selected.is('img')) {
- $link_button.hide();
- // center button on image
- $image_button.show().offset({
- top: $selected.outerHeight() / 2
- + position.top
- - $image_button.outerHeight() / 2,
- left: $selected.outerWidth() / 2
- + position.left
- - $image_button.outerWidth() / 2,
- });
- } else {
- $image_button.hide();
- // put button below link, horizontally centered
- $link_button.show().offset({
- top: $selected.outerHeight()
- + position.top,
- left: $selected.outerWidth() / 2
- + position.left
- - $link_button.outerWidth() / 2
- })
- }
+ $link_button.show().offset({
+ top: $selected.outerHeight()
+ + position.top,
+ left: $selected.outerWidth() / 2
+ + position.left
+ - $link_button.outerWidth() / 2
+ })
}).on('mouseleave', 'a, img, .fa', function (e) {
var current = document.elementFromPoint(e.clientX, e.clientY);
- if (current === $link_button[0] || current === $image_button[0]) {
+ if (current === $link_button[0] || $(current).parent()[0] === $link_button[0]) {
return;
}
- $image_button.add($link_button).hide();
+ $link_button.hide();
previous = null;
});
}
@@ -1041,6 +1052,7 @@
},
save: function () {
this.close();
+ this.trigger("saved");
},
cancel: function () {
},
@@ -1290,6 +1302,128 @@
},
});
+ website.editor.Media = openerp.Widget.extend({
+ init: function (parent, editor, media) {
+ this._super();
+ this.parent = parent;
+ this.editor = editor;
+ this.media = media;
+ },
+ start: function () {
+ this.$preview = this.$('.preview-container').detach();
+ return this._super();
+ },
+ search: function (needle) {
+ },
+ save: function () {
+ },
+ clear: function () {
+ },
+ cancel: function () {
+ },
+ close: function () {
+ },
+ });
+ website.editor.MediaDialog = website.editor.Dialog.extend({
+ template: 'website.editor.dialog.media',
+ events : _.extend({}, website.editor.Dialog.prototype.events, {
+ 'input input#icon-search': 'search',
+ }),
+
+ init: function (editor, media) {
+ this._super(editor);
+ this.editor = editor;
+ this.page = 0;
+ this.media = media;
+ },
+ start: function () {
+ var self = this;
+
+ this.imageDialog = new website.editor.RTEImageDialog(this, this.editor, this.media);
+ this.imageDialog.appendTo(this.$("#editor-media-image"));
+ this.iconDialog = new website.editor.FontIconsDialog(this, this.editor, this.media);
+ this.iconDialog.appendTo(this.$("#editor-media-icon"));
+ this.videoDialog = new website.editor.VideoDialog(this, this.editor, this.media);
+ this.videoDialog.appendTo(this.$("#editor-media-video"));
+
+ this.active = this.imageDialog;
+
+ $('a[data-toggle="tab"]').on('shown.bs.tab', function (event) {
+ if ($(event.target).is('[href="#editor-media-image"]')) {
+ self.active = self.imageDialog;
+ self.$('li.search, li.previous, li.next').removeClass("hidden");
+ } else if ($(event.target).is('[href="#editor-media-icon"]')) {
+ self.active = self.iconDialog;
+ self.$('li.search, li.previous, li.next').removeClass("hidden");
+ self.$('.nav-tabs li.previous, .nav-tabs li.next').addClass("hidden");
+ } else if ($(event.target).is('[href="#editor-media-video"]')) {
+ self.active = self.videoDialog;
+ self.$('.nav-tabs li.search').addClass("hidden");
+ }
+ });
+
+ if (this.media) {
+ if (this.media.$.nodeName === "IMG") {
+ this.$('[href="#editor-media-image"]').tab('show');
+ } else if (this.media.$.className.match(/(^|\s)media_iframe_video($|\s)/)) {
+ this.$('[href="#editor-media-video"]').tab('show');
+ } else if (this.media.$.className.match(/(^|\s)fa($|\s)/)) {
+ this.$('[href="#editor-media-icon"]').tab('show');
+ }
+
+ if ($(this.media.$).parent().data("oe-field") === "image") {
+ this.$('[href="#editor-media-video"], [href="#editor-media-icon"]').addClass('hidden');
+ }
+ }
+
+ return this._super();
+ },
+ save: function () {
+ var self = this;
+ if (self.media) {
+ this.media.$.innerHTML = "";
+ if (this.active !== this.imageDialog) {
+ this.imageDialog.clear();
+ }
+ if (this.active !== this.iconDialog) {
+ this.iconDialog.clear();
+ }
+ if (this.active !== this.videoDialog) {
+ this.videoDialog.clear();
+ }
+ } else {
+ var selection = this.editor.getSelection();
+ var range = selection.getRanges(true)[0];
+ this.media = new CKEDITOR.dom.element("img");
+ range.insertNode(this.media);
+ range.selectNodeContents(this.media);
+ this.active.media = this.media;
+ }
+
+ var $el = $(self.active.media.$);
+
+ this.active.save();
+
+ this.media.$.className = this.media.$.className.replace(/\s+/g, ' ');
+
+ setTimeout(function () {
+ $el.trigger("saved", self.active.media.$);
+ $(document.body).trigger("media-saved", [$el[0], self.active.media.$]);
+ },0);
+
+ this._super();
+ },
+ searchTimer: null,
+ search: function () {
+ var self = this;
+ var needle = this.$("input#icon-search").val();
+ clearTimeout(this.searchTimer);
+ this.searchTimer = setTimeout(function () {
+ self.active.search(needle || "");
+ },250);
+ }
+ });
+
/**
* ImageDialog widget. Lets users change an image, including uploading a
* new image in OpenERP or selecting the image style (if supported by
@@ -1307,73 +1441,118 @@
* selected by the users (or possibly the ones
* originally passed in)
*/
- website.editor.ImageDialog = website.editor.Dialog.extend({
+ var IMAGES_PER_ROW = 6;
+ var IMAGES_ROWS = 2;
+ website.editor.ImageDialog = website.editor.Media.extend({
template: 'website.editor.dialog.image',
events: _.extend({}, website.editor.Dialog.prototype.events, {
- 'change .url-source': function (e) { this.changed($(e.target)); },
+ 'change .url-source': function (e) {
+ this.changed($(e.target));
+ },
'click button.filepicker': function () {
this.$('input[type=file]').click();
},
'change input[type=file]': 'file_selection',
- 'change input.url': 'preview_image',
- 'click a[href=#existing]': 'browse_existing',
- 'change select.image-style': 'preview_image',
+ 'submit form': 'form_submit',
+ 'change input.url': "change_input",
+ 'keyup input.url': "change_input",
+ //'change select.image-style': 'preview_image',
+ 'click .existing-attachments img': 'select_existing',
+ 'click .existing-attachment-remove': 'try_remove',
}),
+ init: function (parent, editor, media) {
+ this.page = 0;
+ this._super(parent, editor, media);
+ },
start: function () {
- this.$('button.wait').text("Uploading…");
- var $options = this.$('.image-style').children();
- this.image_styles = $options.map(function () { return this.value; }).get();
+ var self = this;
+ var res = this._super();
- var o = { url: null, style: null, };
+ var o = { url: null };
// avoid typos, prevent addition of new properties to the object
Object.preventExtensions(o);
this.trigger('start', o);
- if (o.url) {
- if (o.style) {
- this.$('.image-style').val(o.style);
+ this.parent.$(".pager > li").click(function (e) {
+ e.preventDefault();
+ var $target = $(e.currentTarget);
+ if ($target.hasClass('disabled')) {
+ return;
}
- this.set_image(o.url);
- }
+ self.page += $target.hasClass('previous') ? -1 : 1;
+ self.display_attachments();
+ });
- return this._super();
+ this.set_image(o.url);
+
+ return res;
},
save: function () {
+ if (!this.link) {
+ this.link = this.$(".existing-attachments img:first").attr('src');
+ }
this.trigger('save', {
- url: this.$('input.url').val(),
- style: this.$('.image-style').val(),
+ url: this.link
});
+ this.media.renameNode("img");
+ this.media.$.attributes.src = this.link;
return this._super();
},
+ clear: function () {
+ this.media.$.className = this.media.$.className.replace(/(^|\s)(img(\s|$)|img-[^\s]*)/g, ' ');
+ },
cancel: function () {
this.trigger('cancel');
},
- /**
- * Sets the provided image url as the dialog's value-to-save and
- * refreshes the preview element to use it.
- */
- set_image: function (url, error) {
- this.$('input.url').val(
- error ? '' : url);
- this.$('input.url').val(url);
- this.preview_image();
+ change_input: function (e) {
+ var $input = $(e.target);
+ var $button = $input.parent().find("button");
+ if ($input.val() === "") {
+ $button.addClass("btn-default").removeClass("btn-primary");
+ } else {
+ $button.removeClass("btn-default").addClass("btn-primary");
+ }
},
- file_selection: function () {
- this.$el.addClass('nosave');
- this.$('form').removeClass('has-error').find('.help-block').empty();
- this.$('button.filepicker').removeClass('btn-danger btn-success');
-
+ search: function (needle) {
var self = this;
+ this.fetch_existing(needle).then(function () {
+ self.selected_existing(self.$('input.url').val());
+ });
+ },
+
+ set_image: function (url, error) {
+ var self = this;
+ if (url) this.link = url;
+ this.$('input.url').val('');
+ this.fetch_existing().then(function () {
+ self.selected_existing(url);
+ });
+ },
+
+ form_submit: function (event) {
+ var self = this;
+ var $form = this.$('form[action="/website/attach"]');
+ if (!$form.find('input[name="upload"]').val().length) {
+ var url = $form.find('input[name="url"]').val();
+ if (this.selected_existing(url).size()) {
+ event.preventDefault();
+ return false;
+ }
+ }
var callback = _.uniqueId('func_');
this.$('input[name=func]').val(callback);
-
window[callback] = function (url, error) {
delete window[callback];
self.file_selected(url, error);
};
+ },
+ file_selection: function () {
+ this.$el.addClass('nosave');
+ this.$('form').removeClass('has-error').find('.help-block').empty();
+ this.$('button.filepicker').removeClass('btn-danger btn-success');
this.$('form').submit();
},
file_selected: function(url, error) {
@@ -1387,125 +1566,27 @@
$button.addClass('btn-danger');
}
this.set_image(url, error);
+ // auto save and close popup
+ this.parent.save();
},
- preview_image: function () {
- var loaded = function () {
- this.$el.removeClass('nosave');
- }.bind(this);
- var image = this.$('input.url').val();
- if (!image) { loaded(); return; }
- var $img = this.$('img.image-preview')
- .attr('src', image)
- .removeClass(this.image_styles.join(' '))
- .addClass(this.$('select.image-style').val());
-
- if ($img.prop('complete')) {
- loaded();
- } else {
- $img.load(loaded)
+ fetch_existing: function (needle) {
+ var domain = [['res_model', '=', 'ir.ui.view'], '|',
+ ['mimetype', '=', false], ['mimetype', '=like', 'image/%']];
+ if (needle && needle.length) {
+ domain.push('|', ['datas_fname', 'ilike', needle], ['name', 'ilike', needle]);
}
- },
- browse_existing: function (e) {
- e.preventDefault();
- this.$('form').removeClass('has-error').find('.help-block').empty();
- this.$('button.filepicker').removeClass('btn-danger btn-success');
- new website.editor.ExistingImageDialog(this).appendTo(document.body);
- },
- });
- website.editor.RTEImageDialog = website.editor.ImageDialog.extend({
- init: function (editor, image) {
- this._super(editor);
-
- this.element = image;
-
- this.on('start', this, this.proxy('started'));
- this.on('save', this, this.proxy('saved'));
- },
- started: function (holder) {
- if (!this.element) {
- var selection = this.editor.getSelection();
- this.element = selection && selection.getSelectedElement();
- }
-
- var el = this.element;
- if (!el || !el.is('img')) {
- return;
- }
- _(this.image_styles).each(function (style) {
- if (el.hasClass(style)) {
- holder.style = style;
- }
- });
- holder.url = el.getAttribute('src');
- },
- saved: function (data) {
- var element, editor = this.editor;
- if (!(element = this.element)) {
- element = editor.document.createElement('img');
- element.addClass('img');
- element.addClass('img-responsive');
- // focus event handler interactions between bootstrap (modal)
- // and ckeditor (RTE) lead to blowing the stack in Safari and
- // Chrome (but not FF) when this is done synchronously =>
- // defer insertion so modal has been hidden & destroyed before
- // it happens
- setTimeout(function () {
- editor.insertElement(element);
- }, 0);
- }
-
- var style = data.style;
- element.setAttribute('src', data.url);
- element.removeAttribute('data-cke-saved-src');
- $(element.$).removeClass(this.image_styles.join(' '));
- if (style) { element.addClass(style); }
- },
- });
-
- var IMAGES_PER_ROW = 6;
- var IMAGES_ROWS = 4;
- website.editor.ExistingImageDialog = website.editor.Dialog.extend({
- template: 'website.editor.dialog.image.existing',
- events: _.extend({}, website.editor.Dialog.prototype.events, {
- 'click .existing-attachments img': 'select_existing',
- 'click .pager > li': function (e) {
- e.preventDefault();
- var $target = $(e.currentTarget);
- if ($target.hasClass('disabled')) {
- return;
- }
- this.page += $target.hasClass('previous') ? -1 : 1;
- this.display_attachments();
- },
- 'click .existing-attachment-remove': 'try_remove',
- }),
- init: function (parent) {
- this.image = null;
- this.page = 0;
- this.parent = parent;
- this._super(parent.editor);
- },
-
- start: function () {
- return $.when(
- this._super(),
- this.fetch_existing().then(this.proxy('fetched_existing')));
- },
-
- fetch_existing: function () {
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
model: 'ir.attachment',
method: 'search_read',
args: [],
kwargs: {
fields: ['name', 'website_url'],
- domain: [['res_model', '=', 'ir.ui.view'], '|',
- ['mimetype', '=', false], ['mimetype', '=like', 'image/%']],
+ domain: domain,
order: 'id desc',
context: website.get_context(),
}
- });
+ }).then(this.proxy('fetched_existing'));
},
fetched_existing: function (records) {
this.records = records;
@@ -1528,24 +1609,33 @@
this.$('.existing-attachments').replaceWith(
openerp.qweb.render(
'website.editor.dialog.image.existing.content', {rows: rows}));
- this.$('.pager')
+ this.parent.$('.pager')
.find('li.previous').toggleClass('disabled', (from === 0)).end()
.find('li.next').toggleClass('disabled', (from + per_screen >= records.length));
-
},
select_existing: function (e) {
var link = $(e.currentTarget).attr('src');
- if (link) {
- this.parent.set_image(link);
- }
- this.close()
+ this.link = link;
+ this.selected_existing(link);
+ },
+ selected_existing: function (link) {
+ this.$('.existing-attachment-cell.media_selected').removeClass("media_selected");
+ var $select = this.$('.existing-attachment-cell img').filter(function () {
+ return $(this).attr("src") == link;
+ }).first();
+ $select.parent().addClass("media_selected");
+ return $select;
},
try_remove: function (e) {
var $help_block = this.$('.help-block').empty();
var self = this;
- var id = parseInt($(e.target).data('id'), 10);
+ var $a = $(e.target);
+ var id = parseInt($a.data('id'), 10);
var attachment = _.findWhere(this.records, {id: id});
+ var $both = $a.parent().children();
+
+ $both.css({borderWidth: "5px", borderColor: "#f00"});
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
model: 'ir.attachment',
@@ -1561,6 +1651,7 @@
self.display_attachments();
return;
}
+ $both.css({borderWidth: "", borderColor: ""});
$help_block.replaceWith(openerp.qweb.render(
'website.editor.dialog.image.existing.error', {
views: prevented[id]
@@ -1570,6 +1661,48 @@
},
});
+ website.editor.RTEImageDialog = website.editor.ImageDialog.extend({
+ init: function (parent, editor, media) {
+ this._super(parent, editor, media);
+
+ this.on('start', this, this.proxy('started'));
+ this.on('save', this, this.proxy('saved'));
+ },
+ started: function (holder) {
+ if (!this.media) {
+ var selection = this.editor.getSelection();
+ this.media = selection && selection.getSelectedElement();
+ }
+
+ var el = this.media;
+ if (!el || !el.is('img')) {
+ return;
+ }
+ holder.url = el.getAttribute('src');
+ },
+ saved: function (data) {
+ var element, editor = this.editor;
+ if (!(element = this.media)) {
+ element = editor.document.createElement('img');
+ element.addClass('img');
+ element.addClass('img-responsive');
+ // focus event handler interactions between bootstrap (modal)
+ // and ckeditor (RTE) lead to blowing the stack in Safari and
+ // Chrome (but not FF) when this is done synchronously =>
+ // defer insertion so modal has been hidden & destroyed before
+ // it happens
+ setTimeout(function () {
+ editor.insertElement(element);
+ }, 0);
+ }
+
+ var style = data.style;
+ element.setAttribute('src', data.url);
+ element.removeAttribute('data-cke-saved-src');
+ if (style) { element.addClass(style); }
+ },
+ });
+
function get_selected_link(editor) {
var sel = editor.getSelection(),
el = sel.getSelectedElement();
@@ -1581,7 +1714,7 @@
range.shrink(CKEDITOR.SHRINK_TEXT);
var commonAncestor = range.getCommonAncestor();
var viewRoot = editor.elementPath(commonAncestor).contains(function (element) {
- return element.data('oe-model') === 'ir.ui.view'
+ return element.data('oe-model') === 'ir.ui.view';
});
if (!viewRoot) { return null; }
// if viewRoot is the first link, don't edit it.
@@ -1589,7 +1722,7 @@
.contains('a', true);
}
- website.editor.FontIconsDialog = website.editor.Dialog.extend({
+ website.editor.FontIconsDialog = website.editor.Media.extend({
template: 'website.editor.dialog.font-icons',
events : _.extend({}, website.editor.Dialog.prototype.events, {
change: 'update_preview',
@@ -1607,20 +1740,6 @@
this.$('#fa-size').val(e.target.getAttribute('data-size'));
this.update_preview();
},
- 'input input#icon-search': function () {
- var needle = this.$('#icon-search').val();
- var icons = this.icons;
- if (needle) {
- icons = _(icons).filter(function (icon) {
- return icon.id.substring(3).indexOf(needle) !== -1;
- });
- }
-
- this.$('div.font-icons-icons').html(
- openerp.qweb.render(
- 'website.editor.dialog.font-icons.icons',
- {icons: icons}));
- },
}),
// List of FontAwesome icons in 4.0.3, extracted from the cheatsheet.
@@ -1630,10 +1749,6 @@
// where we still need to implement ``initSelection``)
// TODO: add id/name to the text in order to allow FAYT selection of icons?
icons: [{"text": "\uf000", "id": "fa-glass"}, {"text": "\uf001", "id": "fa-music"}, {"text": "\uf002", "id": "fa-search"}, {"text": "\uf003", "id": "fa-envelope-o"}, {"text": "\uf004", "id": "fa-heart"}, {"text": "\uf005", "id": "fa-star"}, {"text": "\uf006", "id": "fa-star-o"}, {"text": "\uf007", "id": "fa-user"}, {"text": "\uf008", "id": "fa-film"}, {"text": "\uf009", "id": "fa-th-large"}, {"text": "\uf00a", "id": "fa-th"}, {"text": "\uf00b", "id": "fa-th-list"}, {"text": "\uf00c", "id": "fa-check"}, {"text": "\uf00d", "id": "fa-times"}, {"text": "\uf00e", "id": "fa-search-plus"}, {"text": "\uf010", "id": "fa-search-minus"}, {"text": "\uf011", "id": "fa-power-off"}, {"text": "\uf012", "id": "fa-signal"}, {"text": "\uf013", "id": "fa-cog"}, {"text": "\uf014", "id": "fa-trash-o"}, {"text": "\uf015", "id": "fa-home"}, {"text": "\uf016", "id": "fa-file-o"}, {"text": "\uf017", "id": "fa-clock-o"}, {"text": "\uf018", "id": "fa-road"}, {"text": "\uf019", "id": "fa-download"}, {"text": "\uf01a", "id": "fa-arrow-circle-o-down"}, {"text": "\uf01b", "id": "fa-arrow-circle-o-up"}, {"text": "\uf01c", "id": "fa-inbox"}, {"text": "\uf01d", "id": "fa-play-circle-o"}, {"text": "\uf01e", "id": "fa-repeat"}, {"text": "\uf021", "id": "fa-refresh"}, {"text": "\uf022", "id": "fa-list-alt"}, {"text": "\uf023", "id": "fa-lock"}, {"text": "\uf024", "id": "fa-flag"}, {"text": "\uf025", "id": "fa-headphones"}, {"text": "\uf026", "id": "fa-volume-off"}, {"text": "\uf027", "id": "fa-volume-down"}, {"text": "\uf028", "id": "fa-volume-up"}, {"text": "\uf029", "id": "fa-qrcode"}, {"text": "\uf02a", "id": "fa-barcode"}, {"text": "\uf02b", "id": "fa-tag"}, {"text": "\uf02c", "id": "fa-tags"}, {"text": "\uf02d", "id": "fa-book"}, {"text": "\uf02e", "id": "fa-bookmark"}, {"text": "\uf02f", "id": "fa-print"}, {"text": "\uf030", "id": "fa-camera"}, {"text": "\uf031", "id": "fa-font"}, {"text": "\uf032", "id": "fa-bold"}, {"text": "\uf033", "id": "fa-italic"}, {"text": "\uf034", "id": "fa-text-height"}, {"text": "\uf035", "id": "fa-text-width"}, {"text": "\uf036", "id": "fa-align-left"}, {"text": "\uf037", "id": "fa-align-center"}, {"text": "\uf038", "id": "fa-align-right"}, {"text": "\uf039", "id": "fa-align-justify"}, {"text": "\uf03a", "id": "fa-list"}, {"text": "\uf03b", "id": "fa-outdent"}, {"text": "\uf03c", "id": "fa-indent"}, {"text": "\uf03d", "id": "fa-video-camera"}, {"text": "\uf03e", "id": "fa-picture-o"}, {"text": "\uf040", "id": "fa-pencil"}, {"text": "\uf041", "id": "fa-map-marker"}, {"text": "\uf042", "id": "fa-adjust"}, {"text": "\uf043", "id": "fa-tint"}, {"text": "\uf044", "id": "fa-pencil-square-o"}, {"text": "\uf045", "id": "fa-share-square-o"}, {"text": "\uf046", "id": "fa-check-square-o"}, {"text": "\uf047", "id": "fa-arrows"}, {"text": "\uf048", "id": "fa-step-backward"}, {"text": "\uf049", "id": "fa-fast-backward"}, {"text": "\uf04a", "id": "fa-backward"}, {"text": "\uf04b", "id": "fa-play"}, {"text": "\uf04c", "id": "fa-pause"}, {"text": "\uf04d", "id": "fa-stop"}, {"text": "\uf04e", "id": "fa-forward"}, {"text": "\uf050", "id": "fa-fast-forward"}, {"text": "\uf051", "id": "fa-step-forward"}, {"text": "\uf052", "id": "fa-eject"}, {"text": "\uf053", "id": "fa-chevron-left"}, {"text": "\uf054", "id": "fa-chevron-right"}, {"text": "\uf055", "id": "fa-plus-circle"}, {"text": "\uf056", "id": "fa-minus-circle"}, {"text": "\uf057", "id": "fa-times-circle"}, {"text": "\uf058", "id": "fa-check-circle"}, {"text": "\uf059", "id": "fa-question-circle"}, {"text": "\uf05a", "id": "fa-info-circle"}, {"text": "\uf05b", "id": "fa-crosshairs"}, {"text": "\uf05c", "id": "fa-times-circle-o"}, {"text": "\uf05d", "id": "fa-check-circle-o"}, {"text": "\uf05e", "id": "fa-ban"}, {"text": "\uf060", "id": "fa-arrow-left"}, {"text": "\uf061", "id": "fa-arrow-right"}, {"text": "\uf062", "id": "fa-arrow-up"}, {"text": "\uf063", "id": "fa-arrow-down"}, {"text": "\uf064", "id": "fa-share"}, {"text": "\uf065", "id": "fa-expand"}, {"text": "\uf066", "id": "fa-compress"}, {"text": "\uf067", "id": "fa-plus"}, {"text": "\uf068", "id": "fa-minus"}, {"text": "\uf069", "id": "fa-asterisk"}, {"text": "\uf06a", "id": "fa-exclamation-circle"}, {"text": "\uf06b", "id": "fa-gift"}, {"text": "\uf06c", "id": "fa-leaf"}, {"text": "\uf06d", "id": "fa-fire"}, {"text": "\uf06e", "id": "fa-eye"}, {"text": "\uf070", "id": "fa-eye-slash"}, {"text": "\uf071", "id": "fa-exclamation-triangle"}, {"text": "\uf072", "id": "fa-plane"}, {"text": "\uf073", "id": "fa-calendar"}, {"text": "\uf074", "id": "fa-random"}, {"text": "\uf075", "id": "fa-comment"}, {"text": "\uf076", "id": "fa-magnet"}, {"text": "\uf077", "id": "fa-chevron-up"}, {"text": "\uf078", "id": "fa-chevron-down"}, {"text": "\uf079", "id": "fa-retweet"}, {"text": "\uf07a", "id": "fa-shopping-cart"}, {"text": "\uf07b", "id": "fa-folder"}, {"text": "\uf07c", "id": "fa-folder-open"}, {"text": "\uf07d", "id": "fa-arrows-v"}, {"text": "\uf07e", "id": "fa-arrows-h"}, {"text": "\uf080", "id": "fa-bar-chart-o"}, {"text": "\uf081", "id": "fa-twitter-square"}, {"text": "\uf082", "id": "fa-facebook-square"}, {"text": "\uf083", "id": "fa-camera-retro"}, {"text": "\uf084", "id": "fa-key"}, {"text": "\uf085", "id": "fa-cogs"}, {"text": "\uf086", "id": "fa-comments"}, {"text": "\uf087", "id": "fa-thumbs-o-up"}, {"text": "\uf088", "id": "fa-thumbs-o-down"}, {"text": "\uf089", "id": "fa-star-half"}, {"text": "\uf08a", "id": "fa-heart-o"}, {"text": "\uf08b", "id": "fa-sign-out"}, {"text": "\uf08c", "id": "fa-linkedin-square"}, {"text": "\uf08d", "id": "fa-thumb-tack"}, {"text": "\uf08e", "id": "fa-external-link"}, {"text": "\uf090", "id": "fa-sign-in"}, {"text": "\uf091", "id": "fa-trophy"}, {"text": "\uf092", "id": "fa-github-square"}, {"text": "\uf093", "id": "fa-upload"}, {"text": "\uf094", "id": "fa-lemon-o"}, {"text": "\uf095", "id": "fa-phone"}, {"text": "\uf096", "id": "fa-square-o"}, {"text": "\uf097", "id": "fa-bookmark-o"}, {"text": "\uf098", "id": "fa-phone-square"}, {"text": "\uf099", "id": "fa-twitter"}, {"text": "\uf09a", "id": "fa-facebook"}, {"text": "\uf09b", "id": "fa-github"}, {"text": "\uf09c", "id": "fa-unlock"}, {"text": "\uf09d", "id": "fa-credit-card"}, {"text": "\uf09e", "id": "fa-rss"}, {"text": "\uf0a0", "id": "fa-hdd-o"}, {"text": "\uf0a1", "id": "fa-bullhorn"}, {"text": "\uf0f3", "id": "fa-bell"}, {"text": "\uf0a3", "id": "fa-certificate"}, {"text": "\uf0a4", "id": "fa-hand-o-right"}, {"text": "\uf0a5", "id": "fa-hand-o-left"}, {"text": "\uf0a6", "id": "fa-hand-o-up"}, {"text": "\uf0a7", "id": "fa-hand-o-down"}, {"text": "\uf0a8", "id": "fa-arrow-circle-left"}, {"text": "\uf0a9", "id": "fa-arrow-circle-right"}, {"text": "\uf0aa", "id": "fa-arrow-circle-up"}, {"text": "\uf0ab", "id": "fa-arrow-circle-down"}, {"text": "\uf0ac", "id": "fa-globe"}, {"text": "\uf0ad", "id": "fa-wrench"}, {"text": "\uf0ae", "id": "fa-tasks"}, {"text": "\uf0b0", "id": "fa-filter"}, {"text": "\uf0b1", "id": "fa-briefcase"}, {"text": "\uf0b2", "id": "fa-arrows-alt"}, {"text": "\uf0c0", "id": "fa-users"}, {"text": "\uf0c1", "id": "fa-link"}, {"text": "\uf0c2", "id": "fa-cloud"}, {"text": "\uf0c3", "id": "fa-flask"}, {"text": "\uf0c4", "id": "fa-scissors"}, {"text": "\uf0c5", "id": "fa-files-o"}, {"text": "\uf0c6", "id": "fa-paperclip"}, {"text": "\uf0c7", "id": "fa-floppy-o"}, {"text": "\uf0c8", "id": "fa-square"}, {"text": "\uf0c9", "id": "fa-bars"}, {"text": "\uf0ca", "id": "fa-list-ul"}, {"text": "\uf0cb", "id": "fa-list-ol"}, {"text": "\uf0cc", "id": "fa-strikethrough"}, {"text": "\uf0cd", "id": "fa-underline"}, {"text": "\uf0ce", "id": "fa-table"}, {"text": "\uf0d0", "id": "fa-magic"}, {"text": "\uf0d1", "id": "fa-truck"}, {"text": "\uf0d2", "id": "fa-pinterest"}, {"text": "\uf0d3", "id": "fa-pinterest-square"}, {"text": "\uf0d4", "id": "fa-google-plus-square"}, {"text": "\uf0d5", "id": "fa-google-plus"}, {"text": "\uf0d6", "id": "fa-money"}, {"text": "\uf0d7", "id": "fa-caret-down"}, {"text": "\uf0d8", "id": "fa-caret-up"}, {"text": "\uf0d9", "id": "fa-caret-left"}, {"text": "\uf0da", "id": "fa-caret-right"}, {"text": "\uf0db", "id": "fa-columns"}, {"text": "\uf0dc", "id": "fa-sort"}, {"text": "\uf0dd", "id": "fa-sort-asc"}, {"text": "\uf0de", "id": "fa-sort-desc"}, {"text": "\uf0e0", "id": "fa-envelope"}, {"text": "\uf0e1", "id": "fa-linkedin"}, {"text": "\uf0e2", "id": "fa-undo"}, {"text": "\uf0e3", "id": "fa-gavel"}, {"text": "\uf0e4", "id": "fa-tachometer"}, {"text": "\uf0e5", "id": "fa-comment-o"}, {"text": "\uf0e6", "id": "fa-comments-o"}, {"text": "\uf0e7", "id": "fa-bolt"}, {"text": "\uf0e8", "id": "fa-sitemap"}, {"text": "\uf0e9", "id": "fa-umbrella"}, {"text": "\uf0ea", "id": "fa-clipboard"}, {"text": "\uf0eb", "id": "fa-lightbulb-o"}, {"text": "\uf0ec", "id": "fa-exchange"}, {"text": "\uf0ed", "id": "fa-cloud-download"}, {"text": "\uf0ee", "id": "fa-cloud-upload"}, {"text": "\uf0f0", "id": "fa-user-md"}, {"text": "\uf0f1", "id": "fa-stethoscope"}, {"text": "\uf0f2", "id": "fa-suitcase"}, {"text": "\uf0a2", "id": "fa-bell-o"}, {"text": "\uf0f4", "id": "fa-coffee"}, {"text": "\uf0f5", "id": "fa-cutlery"}, {"text": "\uf0f6", "id": "fa-file-text-o"}, {"text": "\uf0f7", "id": "fa-building-o"}, {"text": "\uf0f8", "id": "fa-hospital-o"}, {"text": "\uf0f9", "id": "fa-ambulance"}, {"text": "\uf0fa", "id": "fa-medkit"}, {"text": "\uf0fb", "id": "fa-fighter-jet"}, {"text": "\uf0fc", "id": "fa-beer"}, {"text": "\uf0fd", "id": "fa-h-square"}, {"text": "\uf0fe", "id": "fa-plus-square"}, {"text": "\uf100", "id": "fa-angle-double-left"}, {"text": "\uf101", "id": "fa-angle-double-right"}, {"text": "\uf102", "id": "fa-angle-double-up"}, {"text": "\uf103", "id": "fa-angle-double-down"}, {"text": "\uf104", "id": "fa-angle-left"}, {"text": "\uf105", "id": "fa-angle-right"}, {"text": "\uf106", "id": "fa-angle-up"}, {"text": "\uf107", "id": "fa-angle-down"}, {"text": "\uf108", "id": "fa-desktop"}, {"text": "\uf109", "id": "fa-laptop"}, {"text": "\uf10a", "id": "fa-tablet"}, {"text": "\uf10b", "id": "fa-mobile"}, {"text": "\uf10c", "id": "fa-circle-o"}, {"text": "\uf10d", "id": "fa-quote-left"}, {"text": "\uf10e", "id": "fa-quote-right"}, {"text": "\uf110", "id": "fa-spinner"}, {"text": "\uf111", "id": "fa-circle"}, {"text": "\uf112", "id": "fa-reply"}, {"text": "\uf113", "id": "fa-github-alt"}, {"text": "\uf114", "id": "fa-folder-o"}, {"text": "\uf115", "id": "fa-folder-open-o"}, {"text": "\uf118", "id": "fa-smile-o"}, {"text": "\uf119", "id": "fa-frown-o"}, {"text": "\uf11a", "id": "fa-meh-o"}, {"text": "\uf11b", "id": "fa-gamepad"}, {"text": "\uf11c", "id": "fa-keyboard-o"}, {"text": "\uf11d", "id": "fa-flag-o"}, {"text": "\uf11e", "id": "fa-flag-checkered"}, {"text": "\uf120", "id": "fa-terminal"}, {"text": "\uf121", "id": "fa-code"}, {"text": "\uf122", "id": "fa-reply-all"}, {"text": "\uf122", "id": "fa-mail-reply-all"}, {"text": "\uf123", "id": "fa-star-half-o"}, {"text": "\uf124", "id": "fa-location-arrow"}, {"text": "\uf125", "id": "fa-crop"}, {"text": "\uf126", "id": "fa-code-fork"}, {"text": "\uf127", "id": "fa-chain-broken"}, {"text": "\uf128", "id": "fa-question"}, {"text": "\uf129", "id": "fa-info"}, {"text": "\uf12a", "id": "fa-exclamation"}, {"text": "\uf12b", "id": "fa-superscript"}, {"text": "\uf12c", "id": "fa-subscript"}, {"text": "\uf12d", "id": "fa-eraser"}, {"text": "\uf12e", "id": "fa-puzzle-piece"}, {"text": "\uf130", "id": "fa-microphone"}, {"text": "\uf131", "id": "fa-microphone-slash"}, {"text": "\uf132", "id": "fa-shield"}, {"text": "\uf133", "id": "fa-calendar-o"}, {"text": "\uf134", "id": "fa-fire-extinguisher"}, {"text": "\uf135", "id": "fa-rocket"}, {"text": "\uf136", "id": "fa-maxcdn"}, {"text": "\uf137", "id": "fa-chevron-circle-left"}, {"text": "\uf138", "id": "fa-chevron-circle-right"}, {"text": "\uf139", "id": "fa-chevron-circle-up"}, {"text": "\uf13a", "id": "fa-chevron-circle-down"}, {"text": "\uf13b", "id": "fa-html5"}, {"text": "\uf13c", "id": "fa-css3"}, {"text": "\uf13d", "id": "fa-anchor"}, {"text": "\uf13e", "id": "fa-unlock-alt"}, {"text": "\uf140", "id": "fa-bullseye"}, {"text": "\uf141", "id": "fa-ellipsis-h"}, {"text": "\uf142", "id": "fa-ellipsis-v"}, {"text": "\uf143", "id": "fa-rss-square"}, {"text": "\uf144", "id": "fa-play-circle"}, {"text": "\uf145", "id": "fa-ticket"}, {"text": "\uf146", "id": "fa-minus-square"}, {"text": "\uf147", "id": "fa-minus-square-o"}, {"text": "\uf148", "id": "fa-level-up"}, {"text": "\uf149", "id": "fa-level-down"}, {"text": "\uf14a", "id": "fa-check-square"}, {"text": "\uf14b", "id": "fa-pencil-square"}, {"text": "\uf14c", "id": "fa-external-link-square"}, {"text": "\uf14d", "id": "fa-share-square"}, {"text": "\uf14e", "id": "fa-compass"}, {"text": "\uf150", "id": "fa-caret-square-o-down"}, {"text": "\uf151", "id": "fa-caret-square-o-up"}, {"text": "\uf152", "id": "fa-caret-square-o-right"}, {"text": "\uf153", "id": "fa-eur"}, {"text": "\uf154", "id": "fa-gbp"}, {"text": "\uf155", "id": "fa-usd"}, {"text": "\uf156", "id": "fa-inr"}, {"text": "\uf157", "id": "fa-jpy"}, {"text": "\uf158", "id": "fa-rub"}, {"text": "\uf159", "id": "fa-krw"}, {"text": "\uf15a", "id": "fa-btc"}, {"text": "\uf15b", "id": "fa-file"}, {"text": "\uf15c", "id": "fa-file-text"}, {"text": "\uf15d", "id": "fa-sort-alpha-asc"}, {"text": "\uf15e", "id": "fa-sort-alpha-desc"}, {"text": "\uf160", "id": "fa-sort-amount-asc"}, {"text": "\uf161", "id": "fa-sort-amount-desc"}, {"text": "\uf162", "id": "fa-sort-numeric-asc"}, {"text": "\uf163", "id": "fa-sort-numeric-desc"}, {"text": "\uf164", "id": "fa-thumbs-up"}, {"text": "\uf165", "id": "fa-thumbs-down"}, {"text": "\uf166", "id": "fa-youtube-square"}, {"text": "\uf167", "id": "fa-youtube"}, {"text": "\uf168", "id": "fa-xing"}, {"text": "\uf169", "id": "fa-xing-square"}, {"text": "\uf16a", "id": "fa-youtube-play"}, {"text": "\uf16b", "id": "fa-dropbox"}, {"text": "\uf16c", "id": "fa-stack-overflow"}, {"text": "\uf16d", "id": "fa-instagram"}, {"text": "\uf16e", "id": "fa-flickr"}, {"text": "\uf170", "id": "fa-adn"}, {"text": "\uf171", "id": "fa-bitbucket"}, {"text": "\uf172", "id": "fa-bitbucket-square"}, {"text": "\uf173", "id": "fa-tumblr"}, {"text": "\uf174", "id": "fa-tumblr-square"}, {"text": "\uf175", "id": "fa-long-arrow-down"}, {"text": "\uf176", "id": "fa-long-arrow-up"}, {"text": "\uf177", "id": "fa-long-arrow-left"}, {"text": "\uf178", "id": "fa-long-arrow-right"}, {"text": "\uf179", "id": "fa-apple"}, {"text": "\uf17a", "id": "fa-windows"}, {"text": "\uf17b", "id": "fa-android"}, {"text": "\uf17c", "id": "fa-linux"}, {"text": "\uf17d", "id": "fa-dribbble"}, {"text": "\uf17e", "id": "fa-skype"}, {"text": "\uf180", "id": "fa-foursquare"}, {"text": "\uf181", "id": "fa-trello"}, {"text": "\uf182", "id": "fa-female"}, {"text": "\uf183", "id": "fa-male"}, {"text": "\uf184", "id": "fa-gittip"}, {"text": "\uf185", "id": "fa-sun-o"}, {"text": "\uf186", "id": "fa-moon-o"}, {"text": "\uf187", "id": "fa-archive"}, {"text": "\uf188", "id": "fa-bug"}, {"text": "\uf189", "id": "fa-vk"}, {"text": "\uf18a", "id": "fa-weibo"}, {"text": "\uf18b", "id": "fa-renren"}, {"text": "\uf18c", "id": "fa-pagelines"}, {"text": "\uf18d", "id": "fa-stack-exchange"}, {"text": "\uf18e", "id": "fa-arrow-circle-o-right"}, {"text": "\uf190", "id": "fa-arrow-circle-o-left"}, {"text": "\uf191", "id": "fa-caret-square-o-left"}, {"text": "\uf192", "id": "fa-dot-circle-o"}, {"text": "\uf193", "id": "fa-wheelchair"}, {"text": "\uf194", "id": "fa-vimeo-square"}, {"text": "\uf195", "id": "fa-try"}, {"text": "\uf196", "id": "fa-plus-square-o"}],
- init: function (editor, element) {
- this._super(editor);
- this.element = element;
- },
/**
* Initializes select2: in Chrome and Safari,
diff --git a/addons/website_blog/__openerp__.py b/addons/website_blog/__openerp__.py
index 97c163945af..831c180d51f 100644
--- a/addons/website_blog/__openerp__.py
+++ b/addons/website_blog/__openerp__.py
@@ -30,7 +30,7 @@ OpenERP Blog
""",
'author': 'OpenERP SA',
- 'depends': ['knowledge', 'website_mail'],
+ 'depends': ['knowledge', 'website_mail', 'website_partner'],
'data': [
'data/website_blog_data.xml',
'views/website_blog_views.xml',
diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py
index 5cb678b5475..e2eac59678e 100644
--- a/addons/website_blog/controllers/main.py
+++ b/addons/website_blog/controllers/main.py
@@ -1,23 +1,4 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2013-Today OpenERP SA ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
import datetime
import werkzeug
@@ -29,6 +10,7 @@ from openerp.addons.website.models.website import slug
from openerp.osv.orm import browse_record
from openerp.tools.translate import _
from openerp import SUPERUSER_ID
+from openerp.tools import html2plaintext
class QueryURL(object):
@@ -62,12 +44,13 @@ class QueryURL(object):
class WebsiteBlog(http.Controller):
- _blog_post_per_page = 6
- _post_comment_per_page = 6
+ _blog_post_per_page = 20
+ _post_comment_per_page = 10
def nav_list(self):
blog_post_obj = request.registry['blog.post']
- groups = blog_post_obj.read_group(request.cr, request.uid, [], ['name', 'create_date'],
+ groups = blog_post_obj.read_group(
+ request.cr, request.uid, [], ['name', 'create_date'],
groupby="create_date", orderby="create_date asc", context=request.context)
for group in groups:
begin_date = datetime.datetime.strptime(group['__domain'][0][2], tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
@@ -108,35 +91,27 @@ class WebsiteBlog(http.Controller):
def blog(self, blog=None, tag=None, page=1, **opt):
""" Prepare all values to display the blog.
- :param blog: blog currently browsed.
- :param tag: tag that is currently used to filter blog posts
- :param integer page: current page of the pager. Can be the blog or
- post pager.
- :param date: date currently used to filter blog posts (dateBegin_dateEnd)
-
:return dict values: values for the templates, containing
- - 'blog_posts': list of browse records that are the posts to display
- in a given blog, if not blog_post_id
- - 'blog': browse of the current blog, if blog_id
- - 'blogs': list of browse records of blogs
- - 'pager': the pager to display posts pager in a blog
- - 'tag': current tag, if tag_id
+ - 'blog': current blog
+ - 'blogs': all blogs for navigation
+ - 'pager': pager of posts
+ - 'tag': current tag
+ - 'tags': all tags, for navigation
- 'nav_list': a dict [year][month] for archives navigation
+ - 'date': date_begin optional parameter, used in archives navigation
+ - 'blog_url': help object to create URLs
"""
date_begin, date_end = opt.get('date_begin'), opt.get('date_end')
cr, uid, context = request.cr, request.uid, request.context
blog_post_obj = request.registry['blog.post']
- blog_posts = None
-
blog_obj = request.registry['blog.blog']
blog_ids = blog_obj.search(cr, uid, [], order="create_date asc", context=context)
blogs = blog_obj.browse(cr, uid, blog_ids, context=context)
domain = []
-
if blog:
domain += [('blog_id', '=', blog.id)]
if tag:
@@ -176,35 +151,29 @@ class WebsiteBlog(http.Controller):
'post_url': post_url,
'date': date_begin,
}
- return request.website.render("website_blog.blog_post_short", values)
+ response = request.website.render("website_blog.blog_post_short", values)
+ return response
@http.route([
- '/blogpost/',
+ '/blog//post/',
], type='http', auth="public", website=True, multilang=True)
- def blog_post(self, blog_post, tag_id=None, page=1, enable_editor=None, **post):
+ def blog_post(self, blog, blog_post, tag_id=None, page=1, enable_editor=None, **post):
""" Prepare all values to display the blog.
- :param blog_post: blog post currently browsed. If not set, the user is
- browsing the blog and a post pager is calculated.
- If set the user is reading the blog post and a
- comments pager is calculated.
- :param blog: blog currently browsed.
- :param tag: tag that is currently used to filter blog posts
- :param integer page: current page of the pager. Can be the blog or
- post pager.
- :param date: date currently used to filter blog posts (dateBegin_dateEnd)
-
- - 'enable_editor': editor control
-
:return dict values: values for the templates, containing
- - 'blog_post': browse of the current post, if blog_post_id
- - 'blog': browse of the current blog, if blog_id
+ - 'blog_post': browse of the current post
+ - 'blog': browse of the current blog
- 'blogs': list of browse records of blogs
- - 'pager': the pager to display comments pager in a blog post
- - 'tag': current tag, if tag_id
+ - 'tag': current tag, if tag_id in parameters
+ - 'tags': all tags, for tag-based navigation
+ - 'pager': a pager on the comments
- 'nav_list': a dict [year][month] for archives navigation
+ - 'next_post': next blog post, to direct the user towards the next interesting post
"""
+ cr, uid, context = request.cr, request.uid, request.context
+ tag_obj = request.registry['blog.tag']
+ blog_post_obj = request.registry['blog.post']
date_begin, date_end = post.get('date_begin'), post.get('date_end')
pager_url = "/blogpost/%s" % blog_post.id
@@ -226,62 +195,125 @@ class WebsiteBlog(http.Controller):
post_url = QueryURL('', ['blogpost'], blogpost=blog_post, tag_id=tag_id, date_begin=date_begin, date_end=date_end)
blog_url = QueryURL('', ['blog', 'tag'], blog=blog_post.blog_id, tag=tag, date_begin=date_begin, date_end=date_end)
- cr, uid, context = request.cr, request.uid, request.context
- blog_obj = request.registry['blog.blog']
- blog_ids = blog_obj.search(cr, uid, [], context=context)
- blogs = blog_obj.browse(cr, uid, blog_ids, context=context)
+ if not blog_post.blog_id.id == blog.id:
+ return request.redirect("/blog/%s/post/%s" % (slug(blog_post.blog_id), slug(blog_post)))
- tag_obj = request.registry['blog.tag']
- tag_ids = tag_obj.search(cr, uid, [], context=context)
- tags = tag_obj.browse(cr, uid, tag_ids, context=context)
+ tags = tag_obj.browse(cr, uid, tag_obj.search(cr, uid, [], context=context), context=context)
+
+ # Find next Post
+ visited_blogs = request.httprequest.cookies.get('visited_blogs') or ''
+ visited_ids = filter(None, visited_blogs.split(','))
+ visited_ids = map(lambda x: int(x), visited_ids)
+ if blog_post.id not in visited_ids:
+ visited_ids.append(blog_post.id)
+ next_post_id = blog_post_obj.search(cr, uid, [
+ ('id', 'not in', visited_ids),
+ ], order='ranking desc', limit=1, context=context)
+ if not next_post_id:
+ next_post_id = blog_post_obj.search(cr, uid, [('id', '!=', blog.id)], order='ranking desc', limit=1, context=context)
+ next_post = next_post_id and blog_post_obj.browse(cr, uid, next_post_id[0], context=context) or False
values = {
- 'blog': blog_post.blog_id,
- 'blogs': blogs,
'tags': tags,
'tag': tag,
+ 'blog': blog,
'blog_post': blog_post,
'main_object': blog_post,
- 'pager': pager,
'nav_list': self.nav_list(),
'enable_editor': enable_editor,
+ 'next_post': next_post,
'date': date_begin,
'post_url': post_url,
'blog_url': blog_url,
+ 'pager': pager,
}
- return request.website.render("website_blog.blog_post_complete", values)
+ response = request.website.render("website_blog.blog_post_complete", values)
+ response.set_cookie('visited_blogs', ','.join(map(str, visited_ids)))
+
+ request.session[request.session_id] = request.session.get(request.session_id, [])
+ if not (blog_post.id in request.session[request.session_id]):
+ request.session[request.session_id].append(blog_post.id)
+ # Increase counter
+ blog_post_obj.write(cr, SUPERUSER_ID, [blog_post.id], {
+ 'visits': blog_post.visits+1,
+ },context=context)
+ return response
+
+ def _blog_post_message(self, user, blog_post_id=0, **post):
+ cr, uid, context = request.cr, request.uid, request.context
+ blog_post = request.registry['blog.post']
+ partner_obj = request.registry['res.partner']
+ thread_obj = request.registry['mail.thread']
+ website = request.registry['website']
+
+ public_id = website.get_public_user(cr, uid, context)
+ if uid != public_id:
+ partner_ids = [user.partner_id.id]
+ else:
+ partner_ids = blog_post._find_partner_from_emails(
+ cr, SUPERUSER_ID, 0, [post.get('email')], context=context)
+ if not partner_ids or not partner_ids[0]:
+ partner_ids = [partner_obj.create(cr, SUPERUSER_ID, {'name': post.get('name'), 'email': post.get('email')}, context=context)]
+
+ message_id = blog_post.message_post(
+ cr, SUPERUSER_ID, int(blog_post_id),
+ body=post.get('comment'),
+ type='comment',
+ subtype='mt_comment',
+ author_id=partner_ids[0],
+ path=post.get('path', False),
+ context=dict(context, mail_create_nosubcribe=True))
+ return message_id
@http.route(['/blogpost/comment'], type='http', auth="public", methods=['POST'], website=True)
def blog_post_comment(self, blog_post_id=0, **post):
cr, uid, context = request.cr, request.uid, request.context
if post.get('comment'):
- user = request.registry['res.users'].browse(cr, SUPERUSER_ID, uid, context=context)
- group_ids = user.groups_id
- group_id = request.registry["ir.model.data"].get_object_reference(cr, uid, 'website_mail', 'group_comment')[1]
- if group_id in [group.id for group in group_ids]:
- blog_post = request.registry['blog.post']
- blog_post.check_access_rights(cr, uid, 'read')
- blog_post.message_post(
- cr, SUPERUSER_ID, int(blog_post_id),
- body=post.get('comment'),
- type='comment',
- subtype='mt_comment',
- author_id=user.partner_id.id,
- context=dict(context, mail_create_nosubcribe=True))
+ user = request.registry['res.users'].browse(cr, uid, uid, context=context)
+ blog_post = request.registry['blog.post']
+ blog_post.check_access_rights(cr, uid, 'read')
+ self._blog_post_message(user, blog_post_id, **post)
return werkzeug.utils.redirect(request.httprequest.referrer + "#comments")
+ def _get_discussion_detail(self, ids, publish=False, **post):
+ cr, uid, context = request.cr, request.uid, request.context
+ values = []
+ mail_obj = request.registry.get('mail.message')
+ for message in mail_obj.browse(cr, SUPERUSER_ID, ids, context=context):
+ values.append({
+ "id": message.id,
+ "author_name": message.author_id.name,
+ "author_image": message.author_id.image and \
+ ("data:image/png;base64,%s" % message.author_id.image) or \
+ '/website_blog/static/src/img/anonymous.png',
+ "date": message.date,
+ 'body': html2plaintext(message.body),
+ 'website_published' : message.website_published,
+ 'publish' : publish,
+ })
+ return values
+
+ @http.route(['/blogpost/post_discussion'], type='json', auth="public", website=True)
+ def post_discussion(self, blog_post_id=0, **post):
+ cr, uid, context = request.cr, request.uid, request.context
+ publish = request.registry['res.users'].has_group(cr, uid, 'base.group_website_publisher')
+ user = request.registry['res.users'].browse(cr, uid, uid, context=context)
+ id = self._blog_post_message(user, blog_post_id, **post)
+ return self._get_discussion_detail([id], publish, **post)
+
@http.route('/blogpost/new', type='http', auth="public", website=True, multilang=True)
def blog_post_create(self, blog_id, **post):
cr, uid, context = request.cr, request.uid, request.context
create_context = dict(context, mail_create_nosubscribe=True)
- new_blog_post_id = request.registry['blog.post'].create(
- request.cr, request.uid, {
- 'blog_id': blog_id,
- 'name': _("Blog Post Title"),
- 'content': '',
- 'website_published': False,
- }, context=create_context)
- return werkzeug.utils.redirect("/blogpost/%s?enable_editor=1" % new_blog_post_id)
+ new_blog_post_id = request.registry['blog.post'].create(cr, uid, {
+ 'blog_id': blog_id,
+ 'name': _("Blog Post Title"),
+ 'subtitle': _("Subtitle"),
+ 'content': '',
+ 'website_published': False,
+ }, context=create_context)
+ new_blog_post = request.registry['blog.post'].browse(cr, uid, new_blog_post_id, context=context)
+ return werkzeug.utils.redirect("/blog/%s/post/%s?enable_editor=1" % (slug(new_blog_post.blog_id), slug(new_blog_post)))
@http.route('/blogpost/duplicate', type='http', auth="public", website=True)
def blog_post_copy(self, blog_post_id, **post):
@@ -293,5 +325,31 @@ class WebsiteBlog(http.Controller):
"""
cr, uid, context = request.cr, request.uid, request.context
create_context = dict(context, mail_create_nosubscribe=True)
- new_blog_post_id = request.registry['blog.post'].copy(cr, uid, blog_post_id, {}, context=create_context)
- return werkzeug.utils.redirect("/blogpost/%s?enable_editor=1" % new_blog_post_id)
+ nid = request.registry['blog.post'].copy(cr, uid, blog_post_id, {}, context=create_context)
+ new_blog_post = request.registry['blog.post'].browse(cr, uid, nid, context=context)
+ post = request.registry['blog.post'].browse(cr, uid, nid, context)
+ return werkzeug.utils.redirect("/blog/%s/post/%s?enable_editor=1" % (slug(post.blog_id), slug(new_blog_post)))
+
+ @http.route('/blogpost/get_discussion/', type='json', auth="public", website=True)
+ def discussion(self, post_id=0, path=None, count=False, **post):
+ cr, uid, context = request.cr, request.uid, request.context
+ mail_obj = request.registry.get('mail.message')
+ domain = [('res_id', '=', int(post_id)), ('model', '=', 'blog.post'), ('path', '=', path)]
+ #check current user belongs to website publisher group
+ publish = request.registry['res.users'].has_group(cr, uid, 'base.group_website_publisher')
+ if not publish:
+ domain.append(('website_published', '=', True))
+ ids = mail_obj.search(cr, SUPERUSER_ID, domain, count=count)
+ if count:
+ return ids
+ return self._get_discussion_detail(ids, publish, **post)
+
+ @http.route('/blogpost/change_background', type='json', auth="public", website=True)
+ def change_bg(self, post_id=0, image=None, **post):
+ if not post_id:
+ return False
+ return request.registry['blog.post'].write(request.cr, request.uid, [int(post_id)], {'background_image': image}, request.context)
+
+ @http.route('/blog/get_user/', type='json', auth="public", website=True)
+ def get_user(self, **post):
+ return [False if request.session.uid else True]
diff --git a/addons/website_blog/data/website_blog_data.xml b/addons/website_blog/data/website_blog_data.xml
index a5a3aa3d8c9..efc25264633 100644
--- a/addons/website_blog/data/website_blog_data.xml
+++ b/addons/website_blog/data/website_blog_data.xml
@@ -2,7 +2,8 @@
- News
+ Our News
+ Sharing our evolution with passionPresentation of new OpenERP features
diff --git a/addons/website_blog/data/website_blog_demo.xml b/addons/website_blog/data/website_blog_demo.xml
index 913c2ddc9b1..d6827220414 100644
--- a/addons/website_blog/data/website_blog_demo.xml
+++ b/addons/website_blog/data/website_blog_demo.xml
@@ -8,329 +8,199 @@
functional
- technical
-
- website
-
- pos
-
- OpenERP v8 New Features
+ The Future of Emails
+ Ideas behing the OpenERP communication tools.
- OpenERP, Point of Sale, Hardware, Interface, Payment Terminal, Store
- Open source Point of Sale with no installation required that runs online and offline.
+ OpenERP, email
+ The Future of Emails
+ /website_blog/static/src/img/post1.jpg
-
-
-
-
-
-
-
- OpenERP's Point of Sale introduces a super clean
- interface with no installation required that runs
- online and offline on modern hardwares.
-
- It's full integration with the company inventory
- and accounting, gives you real time statistics
- without the hassle of integrating several applications.
-
-
-
-
+
+
+
+ Emails are broken.
+
+ Emails make me waste my time. But I need them.
+ Given the importance that emails have in our lives,
+ it's incredible it's still one of the only software
+ areas that did not evolve in the past 20 years!
+
+ Reading my inbox is the most unproductive task I do
+ on a daily basis. I have to spend one full hour a
+ day to process my emails. All the junk flows in the
+ same inbox; spams, information that doesn't matter,
+ quoted answers of quoted answers, etc. At the end
+ of the hour, only 10 emails actually requested an
+ answer from me. With a good tool, I could have done
+ my job in 10 minutes!
+
-
-
-
-
-
- Linked with Project Management
-
-
Infinitely flexible. Incredibly easy to use.
-
-
-
- OpenERP's collaborative and realtime project
- management helps your team get work done. Keep
- track of everything, from the big picture to the
- minute details, from the customer contract to the
- billing.
-
- Organize projects around your own processes. Work
- on tasks and issues using the kanban view, schedule
- tasks using the gantt chart and control deadlines
- in the calendar view. Every project may have it's
- own stages allowing teams to optimize their job.
-
-
-
-
+
+
+ At OpenERP, we build tools to bring productivity to
+ enterprises. As emails and information flows are one of
+ the biggest wastes of time in companies, we have to fix
+ this.
+
+ To disrupt emails, you need more than just another user
+ interface. We need to rethink the whole communication flow.
+
+
The Communication Mechanism of OpenERP
+
+ Here are the ideas behing the OpenERP communication tools:
+
+
+
+ Get Things Done: your inbox is a
+ todo list. You should be able to process (not only
+ read) the inbox and easily mark messages for future
+ actions. Every inbox should be empty after having
+ been processed; no more overload of information.
+
+
+ Keep control of what you want to receive or don't want
+ to receive. People should never receive spam. You
+ should follow/unfollow any kind of information in one
+ click.
+
+ Productivity is key: our smart user
+ interface does not require you to click on every mail
+ to read a thread. Reading a full thread, replying,
+ attaching documents is super fast.
+
+
+ A mix of push & pull: Today, people
+ are victims of what others decide to push to them.
+ OpenERP differentiates:
+
+
+ Messages "for information":
+ you can pull them when you need some specific
+ information; they are not required to be read
+ every day.You receive only what you decided
+ to follow.This accounts for 90% of your daily
+ emails.Use the "Inbox" menu for these.
+
+ Messages "for action": they
+ require your immediate attention and you need
+ to process them all. This accounts for 10%
+ of your daily emails. Use the "To: me" menu
+ for these.
+
+
+
+ Focus on the Content: Everything is
+ stripped to emphasize on the real message. No more
+ welcome introductions, greetings, signatures and legal
+ notes.We standardize the layout of each message.
+ (signatures are on the profile of a contact, not in
+ every message)
+
+ Folders and mailing lists are great tools but too
+ complex in traditional email clients. In OpenERP, a
+ group of contacts that share a discussion can be
+ created with one click. Every group should have it's
+ own email address.
+
+
-
-
-
-
-
Work with the hardware you already have...
-
-
-
-
-
-
- No installation required
-
-
- OpenERP's Point of Sale introduces a super clean
- interface with no installation required that runs
- online and offline on modern hardware. Laptops,
- tablets, industrial POS, it runs on everything.
-
-
-
]]>
- New Hardware Integration
+ Integrating your CMS and E-Commerce
+ Building your company's website and selling your products online easy.
-
-
-
-
-
-
-
-
-
-
- New Features Launched
-
-
- OpenERP's Point of Sale introduces a super clean
- interface with no installation required that runs
- online and offline on modern hardware. Laptops,
- tablets, industrial POS, it runs on everything.
-
-
-
-
-
-
-
-
-
-
Our Offers
-
-
-
-
-
-
-
Beginner
-
- Starter package
-
-
-
-
$450.00
-
per month
-
-
-
-
-
Battery: 8 hours
-
Screen: 2.5 inch
-
Weight: 1.1 ounces
-
No support
-
-
-
-
-
-
-
-
-
Professional
-
- Enterprise package
-
-
-
-
$590.00
-
per month
-
-
-
-
-
Battery: 12 hours
-
Screen: 2.8 inch
-
Weight: 1.2 ounces
-
Limited support
-
-
-
-
-
-
-
-
-
Expert
-
- The top of the top
-
-
-
-
$890.00
-
per month
-
-
-
-
-
Battery: 20 hours
-
Screen: 2.8 inch
-
Weight: 1.2 ounces
-
Unlimited support
-
-
-
-
-
-
-
-
-
-
-]]>
-
-
-
-
- Touchscreen Point of Sale for 6.1
-
-
- Point of Sale, Hardware, Interface, Payment Terminal, Store
- Point of Sale with no installation required that runs online and offline.
-
-The brand new OpenERP touchscreen point of sale is available with 6.1 which allows you
-to manage your shop sales very easily. It's fully web based so that you don't
-have to install or deploy any software and all the sales shops can be easily
-consolidated. It works in connected and disconnected modes so that you can
-continue to sell even if you lose your internet connection.
-
-
Here's a summary of its main features and benefits:
-
-
100% WEB based
-
available for any touchscreen device (ipod, ipad, any tablet)mobile (with portable devices)
-
no installation required
-
no synchronization needed, completely integrated
-
continue working even when your connection is down or if you close your browser, data won't be lost
-
fully web based with a clean interface smart interface
-
-
You have different options to select your products. You can do it through the
-barcode reader, just browse through the categories you have put in place (ie.
-drinks, snacks, meals, etc.), or text search in case neither of the other
-options work for you. For example, if you need to use the POS for your restaurant,
-your employees can record multiple tickets at the same time without having to wait
-to process one transaction at a time. In addition, you can facilitate payments,
-the application allows multiple payment methods.
-
The POS application is so simple and accessible to use that your shop or
-restaurant will never need any other tool to manage orders. Due to its smart
-and user-friendly interface you don't need any training to learn how to use it.
-Think of it as an out-of-the-box solution to boost your business' productivity.
-
-]]>
-
-
-
-
- Announcing a New Partnership
-
-
- OpenERP, Partnership, News, Accounting
- Our company partners with OpenERP to develop accounting best practices.
-
-
-
-
-
-
-
-
- We are proud to announce a new partnership with
- the company OpenERP. Their open source application suite
- will allow us to reach new markets, specifically in
- the accounting area.
-
- The full integration with the company inventory
- and accounting, will give our customers real time statistics
- without the hassle of integrating several applications.
-
-
-
+
+ /website_blog/static/src/img/post2.jpg
+
+
+
+
+
+
+
+ New Features Launched
+
+
+ To add to an already comprehensive set of OpenERP
+ features, a website content management system (CMS
+ or WMS) has been developed and a beta release is
+ available from today, 31st January 2014.
+
-
-
-
-
-
- OpenERP Project Management
-
-
Infinitely flexible. Incredibly easy to use.
-
-
-
- OpenERP's collaborative and realtime project
- management helps your team get work done. Keep
- track of everything, from the big picture to the
- minute details, from the customer contract to the
- billing.
-
- Organize projects around your own processes. Work
- on tasks and issues using the kanban view, schedule
- tasks using the gantt chart and control deadlines
- in the calendar view. Every project may have it's
- own stages allowing teams to optimize their job.
-
-
-
-
+
+
+ OpenERP claims to be 'the Open Source software that makes
+ building your company's website and selling your products
+ online easy'. So how true is this statement?
+
+ "OpenERP's latest launch will allow a business to go from
+ zero to trading online quicker than ever before,” Stuart
+ Mackintosh, MD of Open Source specialist and OpenERP
+ integration partner, OpusVL, explains. “The investment
+ required to have a fully automated business system is
+ dramatically reduced, enabling the small and medium
+ enterprise to compete at a level of functionality and
+ performance previously reserved for the big IT investors."
+
+
+
+ "Finally, the leading edge is being brought to the masses.
+ It will now be the turn of the big players to catch up to
+ the superior technologies of the SME."
+
+
+
+ "This is another clever and highly disruptive move by
+ OpenERP,which will force other technology providers to
+ take another look at the value they are providing to ensure
+ that their 'solutions' can still compete."
+
+ "OpenERP now competes on many fronts, with no real
+ competition out there to knock them off the top spot.
+ With the launch of their integrated CMS and Ecommerce
+ systems,it only elevates their position as one of the leading
+ lights in the open source revolution. It will be at least 5
+ years before another ERP or CMS provider will be able to
+ compete at this level due to the technology currently
+ employed by most industry providers."
+
+
Adding to industry leading technology
+
+ Like many modern website editors, with OpenERP you can edit
+ content in-line, enabling you to see exactly what you are
+ changing and ensure your changes suit the context.
+
+ However, unlike other web content management systems, it
+ fully integrates into the back-end database. This means
+ that when you edit a product description, image or price,
+ it updates the product database in real time, providing a
+ true self-service window into the business.
+
+ This provides a single source of data for your company and
+ removes the need to create offline synchronisation between
+ website and product database.
+
+ As it comes, there is a default website based on Bootstrap
+ 3, the latest industry standard for rapid development of
+ multi-device websites backed by Twitter, so can be directly
+ integrated with many web tools and works across all devices
+ by default.
+
]]>
-
-
diff --git a/addons/website_blog/doc/blog_blog.rst b/addons/website_blog/doc/blog_blog.rst
new file mode 100644
index 00000000000..8c5f80ab8e4
--- /dev/null
+++ b/addons/website_blog/doc/blog_blog.rst
@@ -0,0 +1,20 @@
+_blog_blog:
+
+blog.blog
+=========
+In ``blog.blog``, added field ``subtitle`` which Indicates the subtitle of blogs.
+ - ``subtitle``: fields.char('Blog Subtitle')
+
+mail.message
+============
+In ``mail.message``, added field ``discussion`` which Indicates the unique identification
+of paragraph on blog post.
+ - ``discussion``: fields.char('Discussion Unique Name')
+
+blog.post
+=========
+Fields
+++++++
+ - ``sub_title`` : contains the subtitle of every blog post.
+ - ``visits`` : Indicates the number of visits on evry blog post.
+ - ``ranking`` : Indicates the ranking on every blog post.
diff --git a/addons/website_blog/doc/changelog.rst b/addons/website_blog/doc/changelog.rst
index 8b6131b5806..42f2fd7e170 100644
--- a/addons/website_blog/doc/changelog.rst
+++ b/addons/website_blog/doc/changelog.rst
@@ -7,3 +7,29 @@ Changelog
----------------
- created ``website_blog`` menu, build on defunct document_page module.
+ - added new feature ``Inline Discussion`` , that will allow a user to comment
+ on every paragraph on blog post
+ - added new feature ``Select to Tweet``, that will alllow a user tweet a selected
+ text from blog to post , directly on twitter.
+
+
+
+WebsiteBlog(controller)
+=======================
+Methods
++++++++
+ - ``blog`` : remove routing related to date.
+ - ``blog_post`` : updated with , suggestion of next post to the user based on
+ cookie and number of views.
+ - ``discussion`` : added method , contains a detail of discussion on every paragraph,
+ if count is true it only return len of ids else return full detail.
+ def discussion(self, post_id=0, discussion=None, count=False, **post)
+ - ``post_discussion`` : added methodt, that allow to post discussion on any paragraph.
+ def post_discussion(self, blog_post_id=0, **post)
+ - ``change_bg`` : added method allow a user to change background image on blog
+ post from front-end.
+ def change_bg(self, post_id=0, image=None, **post)
+ - ``get_user`` : added method , that will return True if user is public else False.
+ def get_user(self, **post):
+ return [False if request.session.uid else True]
+
diff --git a/addons/website_blog/doc/controller.rst b/addons/website_blog/doc/controller.rst
new file mode 100644
index 00000000000..727368aab8c
--- /dev/null
+++ b/addons/website_blog/doc/controller.rst
@@ -0,0 +1,21 @@
+.. _controller:
+
+WebsiteBlog(controller)
+=======================
+Methods
++++++++
+ - ``blog`` : remove routing related to date.
+ - ``blog_post`` : updated with , suggestion of next post to the user based on
+ cookie and number of views.
+ - ``discussion`` : added method , contains a detail of discussion on every paragraph,
+ if count is true it only return len of ids else return full detail.
+ def discussion(self, post_id=0, discussion=None, count=False, **post)
+ - ``post_discussion`` : added methodt, that allow to post discussion on any paragraph.
+ def post_discussion(self, blog_post_id=0, **post)
+ - ``change_bg`` : added method allow a user to change background image on blog
+ post from front-end.
+ def change_bg(self, post_id=0, image=None, **post)
+ - ``get_user`` : added method , that will return True if user is public else False.
+ def get_user(self, **post):
+ return [False if request.session.uid else True]
+
diff --git a/addons/website_blog/models/__init__.py b/addons/website_blog/models/__init__.py
index fb5368a71c5..083e1ef6660 100644
--- a/addons/website_blog/models/__init__.py
+++ b/addons/website_blog/models/__init__.py
@@ -1 +1,2 @@
+import mail_message
import website_blog
diff --git a/addons/website_blog/models/mail_message.py b/addons/website_blog/models/mail_message.py
new file mode 100644
index 00000000000..e1b60f6ad20
--- /dev/null
+++ b/addons/website_blog/models/mail_message.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+from openerp.osv import osv, fields
+
+
+class MailMessage(osv.Model):
+ _inherit = 'mail.message'
+
+ _columns = {
+ 'path': fields.char(
+ 'Discussion Path', select=1,
+ help='Used to display messages in a paragraph-based chatter using a unique path;'),
+ }
diff --git a/addons/website_blog/models/website_blog.py b/addons/website_blog/models/website_blog.py
index e132e4c5e15..031856fff10 100644
--- a/addons/website_blog/models/website_blog.py
+++ b/addons/website_blog/models/website_blog.py
@@ -1,32 +1,15 @@
# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-Today OpenERP SA ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
+from datetime import datetime
+import difflib
+import lxml
+import random
from openerp import tools
from openerp import SUPERUSER_ID
from openerp.osv import osv, fields
from openerp.tools.translate import _
-import difflib
-
class Blog(osv.Model):
_name = 'blog.blog'
@@ -35,12 +18,9 @@ class Blog(osv.Model):
_order = 'name'
_columns = {
- 'name': fields.char('Name', required=True),
+ 'name': fields.char('Blog Name', required=True),
+ 'subtitle': fields.char('Blog Subtitle'),
'description': fields.text('Description'),
- 'blog_post_ids': fields.one2many(
- 'blog.post', 'blog_id',
- 'Blogs',
- ),
}
@@ -52,9 +32,6 @@ class BlogTag(osv.Model):
_columns = {
'name': fields.char('Name', required=True),
- 'blog_post_ids': fields.many2many(
- 'blog.post', string='Posts',
- ),
}
@@ -62,37 +39,20 @@ class BlogPost(osv.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['mail.thread', 'website.seo.metadata']
- _order = 'write_date DESC'
- # maximum number of characters to display in summary
- _shorten_max_char = 250
+ _order = 'id DESC'
- def get_shortened_content(self, cr, uid, ids, name, arg, context=None):
+ def _compute_ranking(self, cr, uid, ids, name, arg, context=None):
res = {}
- for page in self.browse(cr, uid, ids, context=context):
- try:
- body_short = tools.html_email_clean(
- page.content,
- remove=True,
- shorten=True,
- max_length=self._shorten_max_char,
- expand_options={
- 'oe_expand_container_tag': 'div',
- 'oe_expand_container_class': 'oe_mail_expand text-center',
- 'oe_expand_container_content': '',
- 'oe_expand_a_href': '/blogpost/%d' % page.id,
- 'oe_expand_a_class': 'oe_mail_expand btn btn-info',
- 'oe_expand_separator_node': 'br',
- },
- protect_sections=True,
- )
- except Exception:
- body_short = False
- res[page.id] = body_short
+ for blog_post in self.browse(cr, uid, ids, context=context):
+ age = datetime.now() - datetime.strptime(blog_post.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ res[blog_post.id] = blog_post.visits * (0.5+random.random()) / max(3, age.days)
return res
_columns = {
'name': fields.char('Title', required=True, translate=True),
- 'content_image': fields.binary('Background Image'),
+ 'subtitle': fields.char('Sub Title', translate=True),
+ 'author_id': fields.many2one('res.partner', 'Author'),
+ 'background_image': fields.binary('Background Image'),
'blog_id': fields.many2one(
'blog.blog', 'Blog',
required=True, ondelete='cascade',
@@ -101,32 +61,22 @@ class BlogPost(osv.Model):
'blog.tag', string='Tags',
),
'content': fields.html('Content', translate=True),
- 'shortened_content': fields.function(
- get_shortened_content,
- type='html',
- string='Shortened Content',
- help="Shortened content of the page that serves as a summary"
- ),
# website control
'website_published': fields.boolean(
'Publish', help="Publish on the website"
),
- 'website_published_datetime': fields.datetime(
- 'Publish Date'
- ),
- # TDE TODO FIXME: when website_mail/mail_thread.py inheritance work -> this field won't be necessary
'website_message_ids': fields.one2many(
'mail.message', 'res_id',
domain=lambda self: [
- '&', ('model', '=', self._name), ('type', '=', 'comment')
+ '&', '&', ('model', '=', self._name), ('type', '=', 'comment'), ('path', '=', False)
],
string='Website Messages',
help="Website communication history",
),
- # technical stuff: history, menu (to keep ?)
'history_ids': fields.one2many(
'blog.post.history', 'post_id',
- 'History', help='Last post modifications'
+ 'History', help='Last post modifications',
+ deprecated='This field will be removed for OpenERP v9.'
),
# creation / update stuff
'create_date': fields.datetime(
@@ -145,11 +95,71 @@ class BlogPost(osv.Model):
'res.users', 'Last Contributor',
select=True, readonly=True,
),
+ 'visits': fields.integer('No of Views'),
+ 'ranking': fields.function(_compute_ranking, string='Ranking', type='float'),
}
+
_defaults = {
- 'website_published': False
+ 'name': _('Blog Post Title'),
+ 'subtitle': _('Subtitle'),
+ 'author_id': lambda self, cr, uid, ctx=None: self.pool['res.users'].browse(cr, uid, uid, context=ctx).partner_id.id,
}
+ def html_tag_nodes(self, html, attribute=None, tags=None, context=None):
+ """ Processing of html content to tag paragraphs and set them an unique
+ ID.
+ :return result: (html, mappin), where html is the updated html with ID
+ and mapping is a list of (old_ID, new_ID), where old_ID
+ is None is the paragraph is a new one. """
+ mapping = []
+ if not html:
+ return html, mapping
+ if tags is None:
+ tags = ['p']
+ if attribute is None:
+ attribute = 'data-unique-id'
+ counter = 0
+
+ # form a tree
+ root = lxml.html.fragment_fromstring(html, create_parent='div')
+ if not len(root) and root.text is None and root.tail is None:
+ return html, mapping
+
+ # check all nodes, replace :
+ # - img src -> check URL
+ # - a href -> check URL
+ for node in root.iter():
+ if not node.tag in tags:
+ continue
+ ancestor_tags = [parent.tag for parent in node.iterancestors()]
+ if ancestor_tags:
+ ancestor_tags.pop()
+ ancestor_tags.append('counter_%s' % counter)
+ new_attribute = '/'.join(reversed(ancestor_tags))
+ old_attribute = node.get(attribute)
+ node.set(attribute, new_attribute)
+ mapping.append((old_attribute, counter))
+ counter += 1
+
+ html = lxml.html.tostring(root, pretty_print=False, method='html')
+ # this is ugly, but lxml/etree tostring want to put everything in a 'div' that breaks the editor -> remove that
+ if html.startswith('
+
+
+
+
+
+
@@ -155,5 +161,6 @@
name="Page History"
res_model="blog.post.history"
src_model="blog.post"/>
+
diff --git a/addons/website_event_track/__openerp__.py b/addons/website_event_track/__openerp__.py
index 338477eeb8e..418e049cec9 100644
--- a/addons/website_event_track/__openerp__.py
+++ b/addons/website_event_track/__openerp__.py
@@ -28,7 +28,8 @@ Adds support for:
],
'qweb': ['static/src/xml/*.xml'],
'demo': [
- 'data/event_demo.xml'
+ 'data/event_demo.xml',
+ 'data/website_event_track_demo.xml'
],
'installable': True,
}
diff --git a/addons/website_event_track/controllers/event.py b/addons/website_event_track/controllers/event.py
index 466e3d37a40..60d4bbd390d 100644
--- a/addons/website_event_track/controllers/event.py
+++ b/addons/website_event_track/controllers/event.py
@@ -23,11 +23,14 @@ import openerp
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp.addons.website.controllers.main import Website as controllers
+import datetime
import re
import werkzeug.utils
controllers = controllers()
+import pytz
+from pytz import timezone
class website_event(http.Controller):
@http.route(['/event//track/'], type='http', auth="public", website=True, multilang=True)
@@ -37,14 +40,65 @@ class website_event(http.Controller):
values = { 'track': track, 'event': track.event_id, 'main_object': track }
return request.website.render("website_event_track.track_view", values)
+ def _prepare_calendar(self, event, event_track_ids):
+ local_tz = pytz.timezone(event.timezone_of_event or 'UTC')
+ locations = {} # { location: [track, start_date, end_date, rowspan]}
+ dates = [] # [ (date, {}) ]
+ for track in event_track_ids:
+ locations.setdefault(track.location_id or False, [])
+
+ forcetr = True
+ for track in event_track_ids:
+ start_date = (datetime.datetime.strptime(track.date, '%Y-%m-%d %H:%M:%S')).replace(tzinfo=pytz.utc).astimezone(local_tz)
+ end_date = start_date + datetime.timedelta(hours = (track.duration or 30))
+ location = track.location_id or False
+ locations.setdefault(location, [])
+
+ # New TR, align all events
+ if forcetr or (start_date>dates[-1][0]) or not location:
+ dates.append((start_date, {}, bool(location)))
+ for loc in locations.keys():
+ if locations[loc] and (locations[loc][-1][2] > start_date):
+ locations[loc][-1][3] += 1
+ elif not locations[loc] or locations[loc][-1][2] < start_date:
+ locations[loc].append([False, locations[loc] and locations[loc][-1][2] or dates[0][0], start_date, 1])
+ dates[-1][1][loc] = locations[loc][-1]
+ forcetr = not bool(location)
+
+ # Add event
+ if locations[location] and locations[location][-1][1] > start_date:
+ locations[location][-1][3] -= 1
+ locations[location].append([track, start_date, end_date, 1])
+ dates[-1][1][location] = locations[location][-1]
+ return {
+ 'locations': locations,
+ 'dates': dates
+ }
+
+
# TODO: not implemented
@http.route(['/event//agenda'], type='http', auth="public", website=True, multilang=True)
def event_agenda(self, event, tag=None, **post):
- values = {
+ comp = lambda x: (x.date, bool(x.location_id))
+ event.track_ids.sort(lambda x,y: cmp(comp(x), comp(y)))
+
+ days = {}
+ days_nbr = {}
+ for track in event.track_ids:
+ if not track.date: continue
+ days.setdefault(track.date[:10], [])
+ days[track.date[:10]].append(track)
+
+ for d in days:
+ days_nbr[d] = len(days[d])
+ days[d] = self._prepare_calendar(event, days[d])
+
+ return request.website.render("website_event_track.agenda", {
'event': event,
- 'main_object': event,
- }
- return request.website.render("website_event_track.agenda", values)
+ 'days': days,
+ 'days_nbr': days_nbr,
+ 'tag': tag
+ })
@http.route([
'/event//track',
diff --git a/addons/website_event_track/data/event_demo.xml b/addons/website_event_track/data/event_demo.xml
index 5bdd302649b..784d962e471 100644
--- a/addons/website_event_track/data/event_demo.xml
+++ b/addons/website_event_track/data/event_demo.xml
@@ -24,20 +24,6 @@
Round Table
-
-
- Room 1
-
-
- Room 2
-
-
- Room 3
-
-
- Room 4
-
-
@@ -83,68 +69,6 @@
-
-
- A Better Future With OpenERP eCommerce
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Apart from being an enterprise management software, OpenERP is
- used nowadays for a great variety of enterprise frontends.
-
- The talk illustrates the impact of OpenERP in areas such as the
- company website, online events management, eCommerce, online
- recruitments, social media marketing, etc.
-
-
-
-
-
-
-
-
-
- How To Drive Sales With OpenERP CRM
-
-
-
- 1
-
-
-
-
-
-
-
-
-
-
-
- Apart from being an enterprise management software, OpenERP is
- used nowadays for a great variety of enterprise frontends.
-
- The talk illustrates the impact of OpenERP in areas such as the
- company website, online events management, eCommerce, online
- recruitments, social media marketing, etc.
-
-
-
-
-
-
-
-
http://facebook.com/openerp
@@ -160,35 +84,5 @@
-
-
- Social Marketing As a Source of Leads
-
-
-
- 0
-
-
-
-
-
-
-
-
-
-
- Apart from being an enterprise management software, OpenERP is
- used nowadays for a great variety of enterprise frontends.
-
- The talk illustrates the impact of OpenERP in areas such as the
- company website, online events management, eCommerce, online
- recruitments, social media marketing, etc.
-
-
-
-
-
-
-
diff --git a/addons/website_event_track/data/website_event_track_demo.xml b/addons/website_event_track/data/website_event_track_demo.xml
new file mode 100644
index 00000000000..b54d114e127
--- /dev/null
+++ b/addons/website_event_track/data/website_event_track_demo.xml
@@ -0,0 +1,325 @@
+
+
+
+
+ Le Foyer du lac
+
+
+ Theatre
+
+
+ Lauzelle
+
+
+ Foyer Royal
+
+
+ Biereau
+
+
+ Bruyère
+
+
+ How to develop a website module.
+
+
+
+
+
+
+ 3
+
+
+
+
+ How to integrate hardware materials with the OpenERP point of sale.
+
+
+
+
+
+
+
+
+
+ How to develop real time apps, the live chat module explained.
+
+
+
+
+
+
+
+
+
+ How to develop automated tests in the OpenERP web client.
+
+
+
+
+
+
+
+
+
+ The new way to promote your modules in the Apps platform and OpenERP website.
+
+
+
+
+
+
+
+
+
+ Detailed roadmap of accounting new modules and improvements for version 8.
+
+
+
+
+
+
+
+
+
+ A technical explanation of OpenERP as a CMS and a eCommerce platform for version 8.
+
+
+
+
+
+
+
+
+
+ Discover OpenERP CRM: How to optimize your sales, from leads to sales orders.
+
+
+
+
+
+ 2
+
+
+
+
+ How to use OpenERP for your HR process: recruitment, leaves management, appraisals, expenses, etc.
+
+
+
+
+
+
+ 2
+
+
+
+
+ Raising qualitive insights with the survey app
+
+
+
+
+
+ 5
+
+
+
+ Discover OpenERP Point-of-Sale: Your shop ready to use in 30 minutes.
+
+
+
+
+
+
+
+
+
+ Manage your events with OpenERP, the new training modules.
+
+
+
+
+
+
+
+
+
+ Advanced reporting with Google Spreadsheets integration.
+
+
+
+
+
+
+
+
+
+ New Paypal modules (portal, handling, installments).
+
+
+
+
+
+
+
+
+
+ OpenERP Mobile for Notes, Meetings and Messages.
+
+
+
+
+
+
+
+
+
+ OpenERP as your Enterprise Social Network.
+
+
+
+
+
+
+
+
+
+ The Art of Making an OpenERP Demo.
+
+
+
+
+
+
+
+
+
+ How to build your marketing strategy for the purpose of generating leads with OpenERP.
+
+
+
+
+
+ 5
+
+
+
+
+ Advanced lead management with OpenERP: tips and tricks from the fields
+
+
+
+
+
+ 5
+
+
+
+
+ New Certification Program (valid from Oct. 2013).
+
+
+
+
+
+
+
+
+
+ Recruiting high skilled talents with OpenERP HR apps
+
+
+
+
+
+ 7
+
+
+
+ Manage your KPIs (recomended to openERP partners).
+
+
+
+
+
+
+
+
+
+ Key Success factors selling OpenERP.
+
+
+
+
+
+
+
+
+
+ Merge proposals review, code sprint (entire day).
+
+
+
+
+
+
+
+
+
+ Merge proposals review, code sprint (entire afternoon)
+
+
+
+
+
+
+
+
+
+ OpenERP in 2014
+
+
+
+
+
+
+ 3
+
+
+ OpenERP Status & Strategy 2014
+
+
+
+
+
+
+
+
+ The new marketing strategy.
+
+
+
+
+
+
+ 6
+
+
+ Morning break
+
+
+
+
+
+
+
+ Lunch
+
+
+
+
+
+
+
+
diff --git a/addons/website_event_track/models/event.py b/addons/website_event_track/models/event.py
index 716aac6bff4..c9d590d7068 100644
--- a/addons/website_event_track/models/event.py
+++ b/addons/website_event_track/models/event.py
@@ -23,18 +23,20 @@ from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp.addons.website.models.website import slug
+import pytz
+
class event_track_tag(osv.osv):
_name = "event.track.tag"
_order = 'name'
_columns = {
- 'name': fields.char('Event Track Tag')
+ 'name': fields.char('Event Track Tag', translate=True)
}
class event_tag(osv.osv):
_name = "event.tag"
_order = 'name'
_columns = {
- 'name': fields.char('Event Tag')
+ 'name': fields.char('Event Tag', translate=True)
}
#
@@ -45,7 +47,7 @@ class event_track_stage(osv.osv):
_name = "event.track.stage"
_order = 'sequence'
_columns = {
- 'name': fields.char('Track Stage'),
+ 'name': fields.char('Track Stage', translate=True),
'sequence': fields.integer('Sequence')
}
_defaults = {
@@ -79,7 +81,7 @@ class event_track(osv.osv):
'stage_id': fields.many2one('event.track.stage', 'Stage'),
'description': fields.html('Track Description', translate=True),
'date': fields.datetime('Track Date'),
- 'duration': fields.integer('Duration'),
+ 'duration': fields.float('Duration', digits=(16,2)),
'location_id': fields.many2one('event.track.location', 'Location'),
'event_id': fields.many2one('event.event', 'Event', required=True),
'color': fields.integer('Color Index'),
@@ -99,10 +101,11 @@ class event_track(osv.osv):
_defaults = {
'user_id': lambda self, cr, uid, ctx: uid,
'website_published': lambda self, cr, uid, ctx: False,
- 'duration': lambda *args: 60,
+ 'duration': lambda *args: 1.5,
'stage_id': _default_stage_id,
'priority': '2'
}
+
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
stage_obj = self.pool.get('event.track.stage')
result = stage_obj.name_search(cr, uid, '', context=context)
@@ -117,6 +120,10 @@ class event_track(osv.osv):
#
class event_event(osv.osv):
_inherit = "event.event"
+ def _tz_get(self,cr,uid, context=None):
+ # put POSIX 'Etc/*' entries at the end to avoid confusing users - see bug 1086728
+ return [(tz,tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')]
+
def _get_tracks_tag_ids(self, cr, uid, ids, field_names, arg=None, context=None):
res = dict.fromkeys(ids, [])
for event in self.browse(cr, uid, ids, context=context):
@@ -134,11 +141,13 @@ class event_event(osv.osv):
'show_blog': fields.boolean('News'),
'tracks_tag_ids': fields.function(_get_tracks_tag_ids, type='one2many', relation='event.track.tag', string='Tags of Tracks'),
'allowed_track_tag_ids': fields.many2many('event.track.tag', string='Accepted Tags', help="List of available tags for track proposals."),
+ 'timezone_of_event': fields.selection(_tz_get, 'Event Timezone', size=64),
}
_defaults = {
'show_track_proposal': False,
'show_tracks': False,
'show_blog': False,
+ 'timezone_of_event':lambda self,cr,uid,c: self.pool.get('res.users').browse(cr, uid, uid, c).tz,
}
def _get_new_menu_pages(self, cr, uid, event, context=None):
context = context or {}
@@ -160,7 +169,7 @@ class event_sponsors_type(osv.osv):
_name = "event.sponsor.type"
_order = "sequence"
_columns = {
- "name": fields.char('Sponsor Type', required=True),
+ "name": fields.char('Sponsor Type', required=True, translate=True),
"sequence": fields.integer('Sequence')
}
diff --git a/addons/website_event_track/static/src/css/website_event_track.css b/addons/website_event_track/static/src/css/website_event_track.css
index 7862175ec27..f6c01f8f19e 100644
--- a/addons/website_event_track/static/src/css/website_event_track.css
+++ b/addons/website_event_track/static/src/css/website_event_track.css
@@ -52,3 +52,43 @@
background-image: -ms-linear-gradient(top, #C2792A, #DB9141);
background-image: -o-linear-gradient(top, #C2792A, #DB9141);
}
+.event_color_0 {
+ background-color: white;
+ color: #5a5a5a;
+}
+.event_color_1 {
+ background-color: #cccccc;
+ color: #424242;
+}
+.event_color_2 {
+ background-color: #ffc7c7;
+ color: #7a3737;
+}
+.event_color_3 {
+ background-color: #fff1c7;
+ color: #756832;
+}
+.event_color_4 {
+ background-color: #e3ffc7;
+ color: #5d6937;
+}
+.event_color_5 {
+ background-color: #c7ffd5;
+ color: #1a7759;
+}
+.event_color_6 {
+ background-color: #c7ffff;
+ color: #1a5d83;
+}
+.event_color_7 {
+ background-color: #c7d5ff;
+ color: #3b3e75;
+}
+.event_color_8 {
+ background-color: #e3c7ff;
+ color: #4c3668;
+}
+.event_color_9 {
+ background-color: #ffc7f1;
+ color: #6d2c70;
+}
diff --git a/addons/website_event_track/static/src/js/website_event_track.js b/addons/website_event_track/static/src/js/website_event_track.js
new file mode 100644
index 00000000000..8093b9e7bca
--- /dev/null
+++ b/addons/website_event_track/static/src/js/website_event_track.js
@@ -0,0 +1,24 @@
+$(document).ready(function() {
+
+ jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
+ return function( elem ) {
+ return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
+ };
+ });
+
+ $("#event_track_search").bind('keyup', function(e){
+ var change_text = $(this).val();
+ $('.event_track').removeClass('invisible');
+
+ $("#search_summary").removeClass('invisible');
+ if (change_text) {
+ $("#search_number").text($(".event_track:Contains("+change_text+")").length);
+ $(".event_track:not(:Contains("+change_text+"))").addClass('invisible');
+ } else {
+ $("#search_number").text(30);
+ }
+
+ event.preventDefault();
+ });
+
+});
diff --git a/addons/website_event_track/views/event_backend.xml b/addons/website_event_track/views/event_backend.xml
index ad262b6122e..c028b8b47d4 100644
--- a/addons/website_event_track/views/event_backend.xml
+++ b/addons/website_event_track/views/event_backend.xml
@@ -162,6 +162,9 @@
+
+
+
@@ -226,7 +229,7 @@
Regular Talks. These are standard talks with slides,
- alocated in slots of 30 minutes.
+ alocated in slots of 60 minutes.
- Lightning Talks. These are 10 minutes talks on many
+ Lightning Talks. These are 30 minutes talks on many
different topics. Most topics are accepted in lightning talks.
Set your Twitter API access below to be able to use the Twitter Scroller Website snippet.
+ You can get your API credentials from https://apps.twitter.com/app/new
+
- on -
- -Read Next
+- Apart from being an enterprise management software, OpenERP is - used nowadays for a great variety of enterprise frontends. -
- The talk illustrates the impact of OpenERP in areas such as the - company website, online events management, eCommerce, online - recruitments, social media marketing, etc. -
-- Apart from being an enterprise management software, OpenERP is - used nowadays for a great variety of enterprise frontends. -
- The talk illustrates the impact of OpenERP in areas such as the - company website, online events management, eCommerce, online - recruitments, social media marketing, etc. -
-- Apart from being an enterprise management software, OpenERP is - used nowadays for a great variety of enterprise frontends. -
- The talk illustrates the impact of OpenERP in areas such as the - company website, online events management, eCommerce, online - recruitments, social media marketing, etc. -
-You can click on cells to highlight your interests.
-- Monday, 3rd June - 23 talks -
-10:00
- Room 5 -
10:30
11:00
12:00
15:00
10:30
11:00
+
+ talks
+
++
Duration
- minutes
+ hours
Location
-
Regular Talks. These are standard talks with slides,
- alocated in slots of 30 minutes.
+ alocated in slots of 60 minutes.
-
- Lightning Talks. These are 10 minutes talks on many
+ Lightning Talks. These are 30 minutes talks on many
different topics. Most topics are accepted in lightning talks.
diff --git a/addons/website_gengo/controllers/main.py b/addons/website_gengo/controllers/main.py index b1776f9974a..f80c7cbb34b 100644 --- a/addons/website_gengo/controllers/main.py +++ b/addons/website_gengo/controllers/main.py @@ -4,9 +4,6 @@ import openerp from openerp.addons.web import http from openerp.addons.web.http import request import time -import json -from openerp.tools.translate import _ - GENGO_DEFAULT_LIMIT = 20 @@ -40,21 +37,3 @@ class website_gengo(http.Controller): def post_gengo_jobs(self): request.registry['base.gengo.translations']._sync_request(request.cr, request.uid, limit=GENGO_DEFAULT_LIMIT, context=request.context) return True - - @http.route('/website/gengo_callback/--
-
-
-
-
-
++-
+
+
+
+
+ -
+
+
+
+ Add a language...
+
+
+
++
+
+
+
+
+Set your Twitter API access below to be able to use the Twitter Scroller Website snippet.
++ You can get your API credentials from https://apps.twitter.com/app/new +