Merge remote-tracking branch 'odoo/saas-5' into saas-5-report-translation-fix-sle
This commit is contained in:
commit
42263b814e
|
@ -1,2 +0,0 @@
|
|||
.*
|
||||
**/node_modules
|
|
@ -580,7 +580,14 @@ class account_account(osv.osv):
|
|||
pass
|
||||
if name:
|
||||
if operator not in expression.NEGATIVE_TERM_OPERATORS:
|
||||
ids = self.search(cr, user, ['|', ('code', '=like', name+"%"), '|', ('shortcut', '=', name), ('name', operator, name)]+args, limit=limit)
|
||||
plus_percent = lambda n: n+'%'
|
||||
code_op, code_conv = {
|
||||
'ilike': ('=ilike', plus_percent),
|
||||
'like': ('=like', plus_percent),
|
||||
}.get(operator, (operator, lambda n: n))
|
||||
|
||||
ids = self.search(cr, user, ['|', ('code', code_op, code_conv(name)), '|', ('shortcut', '=', name), ('name', operator, name)]+args, limit=limit)
|
||||
|
||||
if not ids and len(name.split()) >= 2:
|
||||
#Separating code and name of account for searching
|
||||
operand1,operand2 = name.split(' ',1) #name can contain spaces e.g. OpenERP S.A.
|
||||
|
@ -2918,7 +2925,7 @@ class account_fiscal_position_template(osv.osv):
|
|||
'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True),
|
||||
'account_ids': fields.one2many('account.fiscal.position.account.template', 'position_id', 'Account Mapping'),
|
||||
'tax_ids': fields.one2many('account.fiscal.position.tax.template', 'position_id', 'Tax Mapping'),
|
||||
'note': fields.text('Notes', translate=True),
|
||||
'note': fields.text('Notes'),
|
||||
}
|
||||
|
||||
def generate_fiscal_position(self, cr, uid, chart_temp_id, tax_template_ref, acc_template_ref, company_id, context=None):
|
||||
|
|
|
@ -295,7 +295,8 @@ class account_invoice(osv.osv):
|
|||
},
|
||||
multi='all'),
|
||||
'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}, track_visibility='always'),
|
||||
'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]},
|
||||
domain="[('type', 'in', {'out_invoice': ['sale'], 'out_refund': ['sale_refund'], 'in_refund': ['purchase_refund'], 'in_invoice': ['purchase']}.get(type, [])), ('company_id', '=', company_id)]"),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}),
|
||||
'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean',
|
||||
|
|
|
@ -579,6 +579,9 @@ class account_move_line(osv.osv):
|
|||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'account_move_line_journal_id_period_id_index\'')
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX account_move_line_journal_id_period_id_index ON account_move_line (journal_id, period_id)')
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('account_move_line_date_id_index',))
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX account_move_line_date_id_index ON account_move_line (date DESC, id desc)')
|
||||
return res
|
||||
|
||||
def _check_no_view(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
<field name="period_id" ref="account.period_5"/>
|
||||
<field name="date" eval="time.strftime('%Y')+'-01-01'"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="reference_type">none</field>
|
||||
<field name="name">BNK/2014/001</field>
|
||||
<field name="balance_end" eval="2040.0"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
|
|
|
@ -33,7 +33,7 @@ class account_fiscal_position(osv.osv):
|
|||
'company_id': fields.many2one('res.company', 'Company'),
|
||||
'account_ids': fields.one2many('account.fiscal.position.account', 'position_id', 'Account Mapping'),
|
||||
'tax_ids': fields.one2many('account.fiscal.position.tax', 'position_id', 'Tax Mapping'),
|
||||
'note': fields.text('Notes', translate=True),
|
||||
'note': fields.text('Notes'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
|
|
|
@ -139,6 +139,13 @@
|
|||
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
|
||||
</record>
|
||||
|
||||
<record id="account_subscription_line_comp_rule" model="ir.rule">
|
||||
<field name="name">Account subscription line company rule</field>
|
||||
<field name="model_id" ref="model_account_subscription_line"/>
|
||||
<field name="global" eval="True"/>
|
||||
<field name="domain_force">['|',('subscription_id.model_id.company_id','=',False),('subscription_id.model_id.company_id','child_of',[user.company_id.id])]</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="account_invoice_line_comp_rule">
|
||||
<field name="name">Invoice Line company rule</field>
|
||||
<field name="model_id" ref="model_account_invoice_line"/>
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
</tr>
|
||||
<tr t-foreach="get_lines_with_out_partner(data['form'])" t-as="not_partner">
|
||||
<td>
|
||||
<span t-esc="partner['name']"/>
|
||||
<span t-esc="not_partner['name']"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="formatLang(not_partner['direction'], currency_obj=res_company.currency_id)"/>
|
||||
|
|
|
@ -36,11 +36,11 @@ class account_subscription_generate(osv.osv_memory):
|
|||
def action_generate(self, cr, uid, ids, context=None):
|
||||
mod_obj = self.pool.get('ir.model.data')
|
||||
act_obj = self.pool.get('ir.actions.act_window')
|
||||
sub_line_obj = self.pool.get('account.subscription.line')
|
||||
moves_created=[]
|
||||
for data in self.read(cr, uid, ids, context=context):
|
||||
cr.execute('select id from account_subscription_line where date<%s and move_id is null', (data['date'],))
|
||||
line_ids = map(lambda x: x[0], cr.fetchall())
|
||||
moves = self.pool.get('account.subscription.line').move_create(cr, uid, line_ids, context=context)
|
||||
line_ids = sub_line_obj.search(cr, uid, [('date', '<', data['date']), ('move_id', '=', False)], context=context)
|
||||
moves = sub_line_obj.move_create(cr, uid, line_ids, context=context)
|
||||
moves_created.extend(moves)
|
||||
result = mod_obj.get_object_reference(cr, uid, 'account', 'action_move_line_form')
|
||||
id = result and result[1] or False
|
||||
|
|
|
@ -22,7 +22,6 @@ from dateutil.relativedelta import relativedelta
|
|||
import datetime
|
||||
import logging
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.osv.orm import intersect, except_orm
|
||||
|
@ -73,6 +72,7 @@ class account_analytic_invoice_line(osv.osv):
|
|||
|
||||
result = {}
|
||||
res = self.pool.get('product.product').browse(cr, uid, product, context=local_context)
|
||||
price = False
|
||||
if price_unit is not False:
|
||||
price = price_unit
|
||||
elif pricelist_id:
|
||||
|
@ -746,29 +746,32 @@ class account_analytic_account(osv.osv):
|
|||
contract_ids = ids
|
||||
else:
|
||||
contract_ids = self.search(cr, uid, [('recurring_next_date','<=', current_date), ('state','=', 'open'), ('recurring_invoices','=', True), ('type', '=', 'contract')])
|
||||
for contract in self.browse(cr, uid, contract_ids, context=context):
|
||||
try:
|
||||
invoice_values = self._prepare_invoice(cr, uid, contract, context=context)
|
||||
invoice_ids.append(self.pool['account.invoice'].create(cr, uid, invoice_values, context=context))
|
||||
next_date = datetime.datetime.strptime(contract.recurring_next_date or current_date, "%Y-%m-%d")
|
||||
interval = contract.recurring_interval
|
||||
if contract.recurring_rule_type == 'daily':
|
||||
new_date = next_date+relativedelta(days=+interval)
|
||||
elif contract.recurring_rule_type == 'weekly':
|
||||
new_date = next_date+relativedelta(weeks=+interval)
|
||||
elif contract.recurring_rule_type == 'monthly':
|
||||
new_date = next_date+relativedelta(months=+interval)
|
||||
else:
|
||||
new_date = next_date+relativedelta(years=+interval)
|
||||
self.write(cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, context=context)
|
||||
if automatic:
|
||||
cr.commit()
|
||||
except Exception:
|
||||
if automatic:
|
||||
cr.rollback()
|
||||
_logger.error(traceback.format_exc())
|
||||
else:
|
||||
raise
|
||||
if contract_ids:
|
||||
cr.execute('SELECT company_id, array_agg(id) as ids FROM account_analytic_account WHERE id IN %s GROUP BY company_id', (tuple(contract_ids),))
|
||||
for company_id, ids in cr.fetchall():
|
||||
for contract in self.browse(cr, uid, ids, context=dict(context, company_id=company_id, force_company=company_id)):
|
||||
try:
|
||||
invoice_values = self._prepare_invoice(cr, uid, contract, context=context)
|
||||
invoice_ids.append(self.pool['account.invoice'].create(cr, uid, invoice_values, context=context))
|
||||
next_date = datetime.datetime.strptime(contract.recurring_next_date or current_date, "%Y-%m-%d")
|
||||
interval = contract.recurring_interval
|
||||
if contract.recurring_rule_type == 'daily':
|
||||
new_date = next_date+relativedelta(days=+interval)
|
||||
elif contract.recurring_rule_type == 'weekly':
|
||||
new_date = next_date+relativedelta(weeks=+interval)
|
||||
elif contract.recurring_rule_type == 'monthly':
|
||||
new_date = next_date+relativedelta(months=+interval)
|
||||
else:
|
||||
new_date = next_date+relativedelta(years=+interval)
|
||||
self.write(cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, context=context)
|
||||
if automatic:
|
||||
cr.commit()
|
||||
except Exception:
|
||||
if automatic:
|
||||
cr.rollback()
|
||||
_logger.exception('Fail to create recurring invoice for contract %s', contract.code)
|
||||
else:
|
||||
raise
|
||||
return invoice_ids
|
||||
|
||||
class account_analytic_account_summary_user(osv.osv):
|
||||
|
|
|
@ -334,7 +334,7 @@
|
|||
<field name="writeoff_amount" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="payment_option" required="1" attrs="{'invisible':[('writeoff_amount','=',0)]}"/>
|
||||
<field name="writeoff_acc_id"
|
||||
attrs="{'invisible':['|', ('payment_option','!=','with_writeoff'), ('writeoff_amount','=',0)], 'required':[('payment_option','=','with_writeoff')]}"
|
||||
attrs="{'invisible':['|', ('payment_option','!=','with_writeoff'), ('writeoff_amount','=',0)], 'required':[('payment_option','=','with_writeoff'), ('writeoff_amount','!=',0)]}"
|
||||
domain="[('type','=','other')]"/>
|
||||
<field name="comment"
|
||||
attrs="{'invisible':['|', ('payment_option','!=','with_writeoff'), ('writeoff_amount','=',0)]}"/>
|
||||
|
|
|
@ -248,7 +248,7 @@
|
|||
<page string="Bill Information">
|
||||
<field name="line_dr_ids" on_change="onchange_price(line_dr_ids, tax_id, partner_id)" context="{'journal_id':journal_id,'partner_id':partner_id}">
|
||||
<tree string="Expense Lines" editable="bottom">
|
||||
<field name="account_id" widget="selection" domain="[('user_type.report_type','=','expense'), ('type','!=','view')]"/>
|
||||
<field name="account_id" domain="[('user_type.report_type','=','expense'), ('type','!=','view')]"/>
|
||||
<field name="name"/>
|
||||
<field name="amount"/>
|
||||
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
|
||||
|
|
|
@ -117,10 +117,22 @@ def sh256crypt(cls, password, salt, magic=magic_sha256):
|
|||
class res_users(osv.osv):
|
||||
_inherit = "res.users"
|
||||
|
||||
def init(self, cr):
|
||||
"""Encrypt all passwords at module installation"""
|
||||
cr.execute("SELECT id, password FROM res_users WHERE password IS NOT NULL and password != ''")
|
||||
for user in cr.fetchall():
|
||||
self._set_encrypted_password(cr, user[0], user[1])
|
||||
|
||||
def _set_encrypted_password(self, cr, uid, plain_password):
|
||||
"""Set an encrypted password for a given user"""
|
||||
salt = gen_salt()
|
||||
stored_password_crypt = md5crypt(plain_password, salt)
|
||||
cr.execute("UPDATE res_users SET password = '', password_crypt = %s WHERE id = %s",
|
||||
(stored_password_crypt, uid))
|
||||
|
||||
def set_pw(self, cr, uid, id, name, value, args, context):
|
||||
if value:
|
||||
encrypted = md5crypt(value, gen_salt())
|
||||
cr.execute("update res_users set password='', password_crypt=%s where id=%s", (encrypted, id))
|
||||
self._set_encrypted_password(cr, id, value)
|
||||
del value
|
||||
|
||||
def get_pw( self, cr, uid, ids, name, args, context ):
|
||||
|
@ -144,9 +156,7 @@ class res_users(osv.osv):
|
|||
if cr.rowcount:
|
||||
stored_password, stored_password_crypt = cr.fetchone()
|
||||
if stored_password and not stored_password_crypt:
|
||||
salt = gen_salt()
|
||||
stored_password_crypt = md5crypt(stored_password, salt)
|
||||
cr.execute("UPDATE res_users SET password='', password_crypt=%s WHERE id=%s", (stored_password_crypt, uid))
|
||||
self._set_encrypted_password(cr, uid, stored_password)
|
||||
try:
|
||||
return super(res_users, self).check_credentials(cr, uid, password)
|
||||
except openerp.exceptions.AccessDenied:
|
||||
|
|
|
@ -74,7 +74,7 @@ class OAuthLogin(Home):
|
|||
state = dict(
|
||||
d=request.session.db,
|
||||
p=provider['id'],
|
||||
r=redirect,
|
||||
r=werkzeug.url_quote_plus(redirect),
|
||||
)
|
||||
token = request.params.get('token')
|
||||
if token:
|
||||
|
@ -143,7 +143,7 @@ class OAuthController(http.Controller):
|
|||
cr.commit()
|
||||
action = state.get('a')
|
||||
menu = state.get('m')
|
||||
redirect = state.get('r')
|
||||
redirect = werkzeug.url_unquote_plus(state['r']) if state.get('r') else False
|
||||
url = '/web'
|
||||
if redirect:
|
||||
url = redirect
|
||||
|
|
|
@ -214,6 +214,7 @@ class base_action_rule(osv.osv):
|
|||
""" Wrap the methods `create` and `write` of the models specified by
|
||||
the rules given by `ids` (or all existing rules if `ids` is `None`.)
|
||||
"""
|
||||
updated = False
|
||||
if ids is None:
|
||||
ids = self.search(cr, SUPERUSER_ID, [])
|
||||
for action_rule in self.browse(cr, SUPERUSER_ID, ids):
|
||||
|
@ -223,20 +224,21 @@ class base_action_rule(osv.osv):
|
|||
model_obj.create = self._wrap_create(model_obj.create, model)
|
||||
model_obj.write = self._wrap_write(model_obj.write, model)
|
||||
model_obj.base_action_ruled = True
|
||||
return True
|
||||
updated = True
|
||||
return updated
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
res_id = super(base_action_rule, self).create(cr, uid, vals, context=context)
|
||||
self._register_hook(cr, [res_id])
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
if self._register_hook(cr, [res_id]):
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res_id
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
super(base_action_rule, self).write(cr, uid, ids, vals, context=context)
|
||||
self._register_hook(cr, ids)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
if self._register_hook(cr, ids):
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return True
|
||||
|
||||
def onchange_model_id(self, cr, uid, ids, model_id, context=None):
|
||||
|
@ -266,7 +268,10 @@ class base_action_rule(osv.osv):
|
|||
action_ids = self.search(cr, uid, action_dom, context=context)
|
||||
for action in self.browse(cr, uid, action_ids, context=context):
|
||||
now = datetime.now()
|
||||
last_run = get_datetime(action.last_run) if action.last_run else False
|
||||
if action.last_run:
|
||||
last_run = get_datetime(action.last_run)
|
||||
else:
|
||||
last_run = datetime.utcfromtimestamp(0)
|
||||
|
||||
# retrieve all the records that satisfy the action's condition
|
||||
model = self.pool[action.model_id.model]
|
||||
|
@ -297,11 +302,16 @@ class base_action_rule(osv.osv):
|
|||
if not record_dt:
|
||||
continue
|
||||
action_dt = self._check_delay(cr, uid, action, record, record_dt, context=context)
|
||||
if last_run and (last_run <= action_dt < now) or (action_dt < now):
|
||||
if last_run <= action_dt < now:
|
||||
try:
|
||||
context = dict(context or {}, action=True)
|
||||
self._process(cr, uid, action, [record.id], context=context)
|
||||
except Exception:
|
||||
import traceback
|
||||
_logger.error(traceback.format_exc())
|
||||
|
||||
action.write({'last_run': now.strftime(DEFAULT_SERVER_DATETIME_FORMAT)})
|
||||
|
||||
if automatic:
|
||||
# auto-commit for batch processing
|
||||
cr.commit()
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<field eval="False" name="doall"/>
|
||||
<field eval="'base.action.rule'" name="model"/>
|
||||
<field eval="'_check'" name="function"/>
|
||||
<field eval="'()'" name="args"/>
|
||||
<field eval="'(True,)'" name="args"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
|
|
@ -1,22 +1,53 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import openerp
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
|
||||
from werkzeug.wrappers import BaseResponse as Response
|
||||
|
||||
import json
|
||||
|
||||
|
||||
class website_gengo(http.Controller):
|
||||
|
||||
def get_gengo_key(self, cr):
|
||||
icp = request.registry['ir.config_parameter']
|
||||
return icp.get_param(cr, SUPERUSER_ID, request.registry['base.gengo.translations'].GENGO_KEY, default="")
|
||||
|
||||
@http.route('/website/gengo_callback', type='http', auth='none')
|
||||
def gengo_callback(self,**post):
|
||||
def gengo_callback(self, **post):
|
||||
cr, uid, context = request.cr, openerp.SUPERUSER_ID, request.context
|
||||
translation_pool = request.registry['ir.translation']
|
||||
if post and post.get('job'):
|
||||
job = json.loads(post['job'])
|
||||
if post and post.get('job') and post.get('pgk'):
|
||||
if post.get('pgk') != self.get_gengo_key(cr):
|
||||
return Response("Bad authentication - 403/412", status=412)
|
||||
job = json.loads(post['job'], 'utf-8')
|
||||
tid = job.get('custom_data', False)
|
||||
if (job.get('status') == 'approved') and tid:
|
||||
term = translation_pool.browse(cr, uid, int(tid), context=context)
|
||||
if term.job_id <> job.get('job_id'):
|
||||
raise 'Error'
|
||||
vals = {'state': 'translated', 'value': job.get('body_tgt')}
|
||||
translation_pool.write(cr, uid, [int(tid)], vals, context=context)
|
||||
if term.src != job.get('body_src'):
|
||||
return Response("Text Altered - Not saved", status=100)
|
||||
domain = [
|
||||
'|',
|
||||
('id', "=", int(tid)),
|
||||
'&', '&', '&', '&', '&',
|
||||
('state', '=', term.state),
|
||||
('gengo_translation', '=', term.gengo_translation),
|
||||
('src', "=", term.src),
|
||||
('type', "=", term.type),
|
||||
('name', "=", term.name),
|
||||
('lang', "=", term.lang),
|
||||
#('order_id', "=", term.order_id),
|
||||
]
|
||||
|
||||
all_ir_tanslations = translation_pool.search(cr, uid, domain, context=context or {})
|
||||
|
||||
if all_ir_tanslations:
|
||||
vals = {'state': 'translated', 'value': job.get('body_tgt')}
|
||||
translation_pool.write(cr, uid, all_ir_tanslations, vals, context=context)
|
||||
return Response("OK", status=200)
|
||||
else:
|
||||
return Response("No terms found", status=104)
|
||||
return Response("Not saved", status=100)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<field name="interval_number">6</field>
|
||||
<field name="interval_type">hours</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall">0</field>
|
||||
<field eval="'base.gengo.translations'" name="model"></field>
|
||||
<field eval="'_sync_response'" name="function"/>
|
||||
<field eval="'(20,)'" name="args"/>
|
||||
|
@ -20,6 +21,7 @@
|
|||
<field name="interval_number">6</field>
|
||||
<field name="interval_type">hours</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall">0</field>
|
||||
<field eval="'base.gengo.translations'" name="model"></field>
|
||||
<field eval="'_sync_request'" name="function"/>
|
||||
<field eval="'(20,)'" name="args"/>
|
||||
|
|
|
@ -56,16 +56,18 @@ LANG_CODE_MAPPING = {
|
|||
'fi_FI': ('fi', 'Finnish')
|
||||
}
|
||||
|
||||
|
||||
class ir_translation(osv.Model):
|
||||
_name = "ir.translation"
|
||||
_inherit = "ir.translation"
|
||||
|
||||
_columns = {
|
||||
'gengo_comment': fields.text("Comments & Activity Linked to Gengo"),
|
||||
'job_id': fields.char('Gengo Job ID', size=32),
|
||||
'order_id': fields.char('Gengo Order ID', size=32),
|
||||
"gengo_translation": fields.selection([('machine', 'Translation By Machine'),
|
||||
('standard', 'Standard'),
|
||||
('pro', 'Pro'),
|
||||
('ultra', 'Ultra')], "Gengo Translation Service Level", help='You can select here the service level you want for an automatic translation using Gengo.'),
|
||||
('standard', 'Standard'),
|
||||
('pro', 'Pro'),
|
||||
('ultra', 'Ultra')], "Gengo Translation Service Level", help='You can select here the service level you want for an automatic translation using Gengo.'),
|
||||
}
|
||||
|
||||
def _get_all_supported_languages(self, cr, uid, context=None):
|
||||
|
@ -83,3 +85,19 @@ class ir_translation(osv.Model):
|
|||
|
||||
def _get_gengo_corresponding_language(cr, lang):
|
||||
return lang in LANG_CODE_MAPPING and LANG_CODE_MAPPING[lang][0] or lang
|
||||
|
||||
def _get_source_query(self, cr, uid, name, types, lang, source, res_id):
|
||||
query, params = super(ir_translation, self)._get_source_query(cr, uid, name, types, lang, source, res_id)
|
||||
|
||||
query += """
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN gengo_translation=%s then 10
|
||||
WHEN gengo_translation=%s then 20
|
||||
WHEN gengo_translation=%s then 30
|
||||
WHEN gengo_translation=%s then 40
|
||||
ELSE 0
|
||||
END DESC
|
||||
"""
|
||||
params += ('machine', 'standard', 'ultra', 'pro',)
|
||||
return (query, params)
|
||||
|
|
|
@ -19,12 +19,13 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
import uuid
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
from openerp import tools
|
||||
from openerp import tools, SUPERUSER_ID
|
||||
from openerp.tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
@ -36,7 +37,9 @@ except ImportError:
|
|||
|
||||
GENGO_DEFAULT_LIMIT = 20
|
||||
|
||||
|
||||
class base_gengo_translations(osv.osv_memory):
|
||||
GENGO_KEY = "Gengo.UUID"
|
||||
|
||||
_name = 'base.gengo.translations'
|
||||
_columns = {
|
||||
|
@ -46,9 +49,20 @@ class base_gengo_translations(osv.osv_memory):
|
|||
'lang_id': fields.many2one('res.lang', 'Language', required=True),
|
||||
'sync_limit': fields.integer("No. of terms to sync"),
|
||||
}
|
||||
_defaults = {'sync_type' : 'both',
|
||||
'sync_limit' : 20
|
||||
}
|
||||
_defaults = {
|
||||
'sync_type': 'both',
|
||||
'sync_limit': 20
|
||||
}
|
||||
|
||||
def init(self, cr):
|
||||
icp = self.pool['ir.config_parameter']
|
||||
if not icp.get_param(cr, SUPERUSER_ID, self.GENGO_KEY, default=None):
|
||||
icp.set_param(cr, SUPERUSER_ID, self.GENGO_KEY, str(uuid.uuid4()))
|
||||
|
||||
def get_gengo_key(self, cr):
|
||||
icp = self.pool['ir.config_parameter']
|
||||
return icp.get_param(cr, SUPERUSER_ID, self.GENGO_KEY, default="Undefined")
|
||||
|
||||
def gengo_authentication(self, cr, uid, context=None):
|
||||
'''
|
||||
This method tries to open a connection with Gengo. For that, it uses the Public and Private
|
||||
|
@ -113,48 +127,68 @@ class base_gengo_translations(osv.osv_memory):
|
|||
_logger.warning("%s", gengo)
|
||||
else:
|
||||
offset = 0
|
||||
all_translation_ids = translation_pool.search(cr, uid, [('state', '=', 'inprogress'), ('gengo_translation', 'in', ('machine', 'standard', 'pro', 'ultra')), ('job_id', "!=", False)], context=context)
|
||||
all_translation_ids = translation_pool.search(cr, uid, [('state', '=', 'inprogress'), ('gengo_translation', 'in', ('machine', 'standard', 'pro', 'ultra')), ('order_id', "!=", False)], context=context)
|
||||
while True:
|
||||
translation_ids = all_translation_ids[offset:offset + limit]
|
||||
offset += limit
|
||||
if not translation_ids:
|
||||
break
|
||||
|
||||
terms_progress = {
|
||||
'gengo_order_ids': set(),
|
||||
'ir_translation_ids': set(),
|
||||
}
|
||||
translation_terms = translation_pool.browse(cr, uid, translation_ids, context=context)
|
||||
gengo_job_id = [term.job_id for term in translation_terms]
|
||||
if gengo_job_id:
|
||||
gengo_ids = ','.join(gengo_job_id)
|
||||
for term in translation_terms:
|
||||
terms_progress['gengo_order_ids'].add(term.order_id)
|
||||
terms_progress['ir_translation_ids'].add(tools.ustr(term.id))
|
||||
|
||||
for order_id in terms_progress['gengo_order_ids']:
|
||||
order_response = gengo.getTranslationOrderJobs(id=order_id)
|
||||
jobs_approved = order_response.get('response', []).get('order', []).get('jobs_approved', [])
|
||||
gengo_ids = ','.join(jobs_approved)
|
||||
|
||||
if gengo_ids: # Need to check, because getTranslationJobBatch don't catch this case and so call the getTranslationJobs because no ids in url
|
||||
try:
|
||||
job_response = gengo.getTranslationJobBatch(id=gengo_ids)
|
||||
except:
|
||||
continue
|
||||
if job_response['opstat'] == 'ok':
|
||||
for job in job_response['response'].get('jobs', []):
|
||||
self._update_terms_job(cr, uid, job, context=context)
|
||||
if job.get('custom_data') in terms_progress['ir_translation_ids']:
|
||||
self._update_terms_job(cr, uid, job, context=context)
|
||||
return True
|
||||
|
||||
def _update_terms_job(self, cr, uid, job, context=None):
|
||||
translation_pool = self.pool.get('ir.translation')
|
||||
tid = int(job['custom_data'])
|
||||
vals = {}
|
||||
if job.get('job_id', False):
|
||||
vals['job_id'] = job['job_id']
|
||||
if job.get('status', False) in ('queued', 'available', 'pending', 'reviewable'):
|
||||
vals['state'] = 'inprogress'
|
||||
if job.get('status', False) in ('queued','available','pending','reviewable'):
|
||||
vals['state'] = 'inprogress'
|
||||
if job.get('body_tgt', False) and job.get('status', False)=='approved':
|
||||
if job.get('body_tgt', False) and job.get('status', False) == 'approved':
|
||||
vals['value'] = job['body_tgt']
|
||||
if job.get('status', False) in ('approved', 'canceled'):
|
||||
vals['state'] = 'translated'
|
||||
if vals:
|
||||
translation_pool.write(cr, uid, [tid], vals, context=context)
|
||||
|
||||
def _update_terms(self, cr, uid, response, context=None):
|
||||
def _update_terms(self, cr, uid, response, term_ids, context=None):
|
||||
"""
|
||||
Update the terms after their translation were requested to Gengo
|
||||
"""
|
||||
for jobs in response.get('jobs', []):
|
||||
translation_pool = self.pool.get('ir.translation')
|
||||
|
||||
vals = {
|
||||
'order_id': response.get('order_id', ''),
|
||||
'state': 'inprogress'
|
||||
}
|
||||
|
||||
translation_pool.write(cr, uid, term_ids, vals, context=context)
|
||||
jobs = response.get('jobs', [])
|
||||
if jobs:
|
||||
for t_id, res in jobs.items():
|
||||
self._update_terms_job(cr, uid, res, context=context)
|
||||
|
||||
return
|
||||
|
||||
def pack_jobs_request(self, cr, uid, term_ids, context=None):
|
||||
|
@ -164,7 +198,7 @@ class base_gengo_translations(osv.osv_memory):
|
|||
'term2.id': {...}
|
||||
}
|
||||
}'''
|
||||
|
||||
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
|
||||
translation_pool = self.pool.get('ir.translation')
|
||||
jobs = {}
|
||||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
|
@ -173,7 +207,7 @@ class base_gengo_translations(osv.osv_memory):
|
|||
if re.search(r"\w", term.src or ""):
|
||||
comment = user.company_id.gengo_comment or ''
|
||||
if term.gengo_comment:
|
||||
comment+='\n' + term.gengo_comment
|
||||
comment += '\n' + term.gengo_comment
|
||||
jobs[time.strftime('%Y%m%d%H%M%S') + '-' + str(term.id)] = {
|
||||
'type': 'text',
|
||||
'slug': 'Single :: English to ' + term.lang,
|
||||
|
@ -184,10 +218,9 @@ class base_gengo_translations(osv.osv_memory):
|
|||
'lc_tgt': translation_pool._get_gengo_corresponding_language(term.lang),
|
||||
'auto_approve': auto_approve,
|
||||
'comment': comment,
|
||||
'callback_url': self.pool.get('ir.config_parameter').get_param(cr, uid,'web.base.url') + '/website/gengo_callback'
|
||||
'callback_url': "%s/website/gengo_callback?pgk=%s&db=%s" % (base_url, self.get_gengo_key(cr), cr.dbname)
|
||||
}
|
||||
return {'jobs': jobs, 'as_group': 1}
|
||||
|
||||
return {'jobs': jobs, 'as_group': 0}
|
||||
|
||||
def _send_translation_terms(self, cr, uid, term_ids, context=None):
|
||||
"""
|
||||
|
@ -200,7 +233,7 @@ class base_gengo_translations(osv.osv_memory):
|
|||
if request['jobs']:
|
||||
result = gengo.postTranslationJobs(jobs=request)
|
||||
if result['opstat'] == 'ok':
|
||||
self._update_terms(cr, uid, result['response'], context=context)
|
||||
self._update_terms(cr, uid, result['response'], term_ids, context=context)
|
||||
else:
|
||||
_logger.error(gengo)
|
||||
return True
|
||||
|
@ -218,10 +251,10 @@ class base_gengo_translations(osv.osv_memory):
|
|||
context = {}
|
||||
language_pool = self.pool.get('res.lang')
|
||||
translation_pool = self.pool.get('ir.translation')
|
||||
domain = [('state', '=', 'to_translate'), ('gengo_translation', 'in', ('machine', 'standard', 'pro', 'ultra')), ('job_id', "=", False)]
|
||||
domain = [('state', '=', 'to_translate'), ('gengo_translation', 'in', ('machine', 'standard', 'pro', 'ultra')), ('order_id', "=", False)]
|
||||
if context.get('gengo_language', False):
|
||||
lc = language_pool.browse(cr, uid, context['gengo_language'], context=context).code
|
||||
domain.append( ('lang', '=', lc) )
|
||||
domain.append(('lang', '=', lc))
|
||||
|
||||
all_term_ids = translation_pool.search(cr, uid, domain, context=context)
|
||||
try:
|
||||
|
|
|
@ -102,7 +102,7 @@ openerp.calendar = function(instance) {
|
|||
var self = this;
|
||||
var action_url = '';
|
||||
|
||||
action_url = _.str.sprintf('/?db=%s#id=%s&view_type=form&model=calendar.event', db, meeting_id);
|
||||
action_url = _.str.sprintf('/web?db=%s#id=%s&view_type=form&model=calendar.event', db, meeting_id);
|
||||
|
||||
var reload_page = function(){
|
||||
return location.replace(action_url);
|
||||
|
|
|
@ -164,6 +164,13 @@ class crm_claim(osv.osv):
|
|||
# context: no_log, because subtype already handle this
|
||||
return super(crm_claim, self).create(cr, uid, vals, context=context)
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
claim = self.browse(cr, uid, id, context=context)
|
||||
default = dict(default or {},
|
||||
stage_id = self._get_default_stage_id(cr, uid, context=context),
|
||||
name = _('%s (copy)') % claim.name)
|
||||
return super(crm_claim, self).copy(cr, uid, id, default, context=context)
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Mail gateway
|
||||
# -------------------------------------------------------
|
||||
|
|
|
@ -32,7 +32,7 @@ class report_event_registration(osv.osv):
|
|||
'draft_state': fields.integer(' # No of Draft Registrations', size=20),
|
||||
'confirm_state': fields.integer(' # No of Confirmed Registrations', size=20),
|
||||
'seats_max': fields.integer('Max Seats'),
|
||||
'nbevent': fields.integer('Number Of Events'),
|
||||
'nbevent': fields.integer('Number of Registrations'),
|
||||
'event_type': fields.many2one('event.type', 'Event Type'),
|
||||
'registration_state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Attended'), ('cancel', 'Cancelled')], 'Registration State', readonly=True, required=True),
|
||||
'event_state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Event State', readonly=True, required=True),
|
||||
|
@ -59,7 +59,7 @@ class report_event_registration(osv.osv):
|
|||
r.name AS name_registration,
|
||||
e.company_id AS company_id,
|
||||
e.date_begin AS event_date,
|
||||
count(e.id) AS nbevent,
|
||||
count(r.id) AS nbevent,
|
||||
CASE WHEN r.state IN ('draft') THEN r.nb_register ELSE 0 END AS draft_state,
|
||||
CASE WHEN r.state IN ('open','done') THEN r.nb_register ELSE 0 END AS confirm_state,
|
||||
e.type AS event_type,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.oe_event_date{
|
||||
border-top-left-radius:3px;
|
||||
border-top-right-radius:3px;
|
||||
font-size: 48px;
|
||||
font-size: 36px;
|
||||
height: auto;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
|
|
|
@ -245,7 +245,8 @@ class event_ticket(osv.osv):
|
|||
]
|
||||
|
||||
def onchange_product_id(self, cr, uid, ids, product_id=False, context=None):
|
||||
return {'value': {'price': self.pool.get("product.product").browse(cr, uid, product_id).list_price or 0}}
|
||||
price = self.pool.get("product.product").browse(cr, uid, product_id).list_price if product_id else 0
|
||||
return {'value': {'price': price}}
|
||||
|
||||
|
||||
class event_registration(osv.osv):
|
||||
|
|
|
@ -58,7 +58,7 @@ def start_end_date_for_period(period, default_start_date=False, default_end_date
|
|||
end_date = default_end_date
|
||||
|
||||
if start_date and end_date:
|
||||
return (start_date.strftime(DF), end_date.strftime(DF))
|
||||
return (datetime.strftime(start_date, DF), datetime.strftime(end_date, DF))
|
||||
else:
|
||||
return (start_date, end_date)
|
||||
|
||||
|
|
|
@ -129,6 +129,36 @@ class gamification_goal_definition(osv.Model):
|
|||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
||||
return self.pool.get('mail.followers').search(cr, uid, [('res_model', '=', model_name), ('partner_id', '=', user.partner_id.id)], count=True, context=context)
|
||||
|
||||
def _check_domain_validity(self, cr, uid, ids, context=None):
|
||||
# take admin as should always be present
|
||||
superuser = self.pool['res.users'].browse(cr, uid, SUPERUSER_ID, context=context)
|
||||
for definition in self.browse(cr, uid, ids, context=context):
|
||||
if definition.computation_mode not in ('count', 'sum'):
|
||||
continue
|
||||
|
||||
obj = self.pool[definition.model_id.model]
|
||||
try:
|
||||
domain = safe_eval(definition.domain, {'user': superuser})
|
||||
# demmy search to make sure the domain is valid
|
||||
obj.search(cr, uid, domain, context=context, count=True)
|
||||
except (ValueError, SyntaxError), e:
|
||||
msg = e.message or (e.msg + '\n' + e.text)
|
||||
raise osv.except_osv(_('Error!'),_("The domain for the definition %s seems incorrect, please check it.\n\n%s" % (definition.name, msg)))
|
||||
return True
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
res_id = super(gamification_goal_definition, self).create(cr, uid, vals, context=context)
|
||||
if vals.get('computation_mode') in ('count', 'sum'):
|
||||
self._check_domain_validity(cr, uid, [res_id], context=context)
|
||||
|
||||
return res_id
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
res = super(gamification_goal_definition, self).write(cr, uid, ids, vals, context=context)
|
||||
if vals.get('computation_mode', 'count') in ('count', 'sum') and (vals.get('domain') or vals.get('model_id')):
|
||||
self._check_domain_validity(cr, uid, ids, context=context)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class gamification_goal(osv.Model):
|
||||
|
|
|
@ -699,7 +699,7 @@ class google_calendar(osv.AbstractModel):
|
|||
for att in att_obj.browse(cr, uid, my_att_ids, context=context):
|
||||
event = att.event_id
|
||||
|
||||
base_event_id = att.google_internal_event_id.split('_')[0]
|
||||
base_event_id = att.google_internal_event_id.rsplit('_', 1)[0]
|
||||
|
||||
if base_event_id not in event_to_synchronize:
|
||||
event_to_synchronize[base_event_id] = {}
|
||||
|
@ -721,7 +721,7 @@ class google_calendar(osv.AbstractModel):
|
|||
|
||||
for event in all_event_from_google.values():
|
||||
event_id = event.get('id')
|
||||
base_event_id = event_id.split('_')[0]
|
||||
base_event_id = event_id.rsplit('_', 1)[0]
|
||||
|
||||
if base_event_id not in event_to_synchronize:
|
||||
event_to_synchronize[base_event_id] = {}
|
||||
|
@ -786,7 +786,7 @@ class google_calendar(osv.AbstractModel):
|
|||
if actSrc == 'OE':
|
||||
self.delete_an_event(cr, uid, current_event[0], context=context)
|
||||
elif actSrc == 'GG':
|
||||
new_google_event_id = event.GG.event['id'].split('_')[1]
|
||||
new_google_event_id = event.GG.event['id'].rsplit('_', 1)[1]
|
||||
if 'T' in new_google_event_id:
|
||||
new_google_event_id = new_google_event_id.replace('T', '')[:-1]
|
||||
else:
|
||||
|
@ -795,7 +795,8 @@ class google_calendar(osv.AbstractModel):
|
|||
if event.GG.status:
|
||||
parent_event = {}
|
||||
if not event_to_synchronize[base_event][0][1].OE.event_id:
|
||||
event_to_synchronize[base_event][0][1].OE.event_id = att_obj.search_read(cr, uid, [('google_internal_event_id', '=', event.GG.event['id'].split('_')[0])], ['event_id'], context=context_novirtual)[0].get('event_id')[0]
|
||||
main_ev = att_obj.search_read(cr, uid, [('google_internal_event_id', '=', event.GG.event['id'].rsplit('_', 1)[0])], fields=['event_id'], context=context_novirtual)
|
||||
event_to_synchronize[base_event][0][1].OE.event_id = main_ev[0].get('event_id')[0]
|
||||
|
||||
parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id)
|
||||
res = self.update_from_google(cr, uid, parent_event, event.GG.event, "copy", context)
|
||||
|
|
|
@ -225,7 +225,7 @@ class hr_employee(osv.osv):
|
|||
"resized as a 128x128px image, with aspect ratio preserved. "\
|
||||
"Use this field in form views or some kanban views."),
|
||||
'image_small': fields.function(_get_image, fnct_inv=_set_image,
|
||||
string="Smal-sized photo", type="binary", multi="_get_image",
|
||||
string="Small-sized photo", type="binary", multi="_get_image",
|
||||
store = {
|
||||
'hr.employee': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
|
||||
},
|
||||
|
|
|
@ -40,7 +40,9 @@
|
|||
<page string="Public Information">
|
||||
<group>
|
||||
<group string="Contact Information">
|
||||
<field name="address_id" on_change="onchange_address_id(address_id)" context="{'show_address': 1}" options='{"always_reload": True, "highlight_first_line": True}'/>
|
||||
<field name="address_id" on_change="onchange_address_id(address_id)"
|
||||
context="{'show_address': 1, 'default_customer': False}"
|
||||
options='{"always_reload": True, "highlight_first_line": True}'/>
|
||||
<field name="mobile_phone"/>
|
||||
<field name="work_location"/>
|
||||
</group>
|
||||
|
@ -68,7 +70,9 @@
|
|||
<field name="otherid" groups="base.group_hr_user"/>
|
||||
</group>
|
||||
<group string="Contact Information">
|
||||
<field name="address_home_id" context="{'show_address': 1}" options='{"always_reload": True, "highlight_first_line": True}'/>
|
||||
<field name="address_home_id"
|
||||
context="{'show_address': 1, 'default_customer': False}"
|
||||
options='{"always_reload": True, "highlight_first_line": True}'/>
|
||||
</group>
|
||||
<group string="Status">
|
||||
<field name="gender"/>
|
||||
|
|
|
@ -99,7 +99,7 @@ class hr_holidays_status(osv.osv):
|
|||
for record in self.browse(cr, uid, ids, context=context):
|
||||
name = record.name
|
||||
if not record.limit:
|
||||
name = name + (' (%d/%d)' % (record.leaves_taken or 0.0, record.max_leaves or 0.0))
|
||||
name = name + (' (%g/%g)' % (record.leaves_taken or 0.0, record.max_leaves or 0.0))
|
||||
res.append((record.id, name))
|
||||
return res
|
||||
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
</page>
|
||||
<page string="Attendances" groups="base.group_hr_attendance">
|
||||
<group>
|
||||
<field context="{'employee_id': employee_id, 'user_id':user_id}" name="attendances_ids" nolabel="1">
|
||||
<field context="{'default_employee_id': employee_id, 'user_id':user_id}" name="attendances_ids" nolabel="1">
|
||||
<tree string="Attendances" editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="action"/>
|
||||
|
|
|
@ -225,7 +225,7 @@ class partner_vat_intra(osv.osv_memory):
|
|||
data_head = """<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<ns2:IntraConsignment xmlns="http://www.minfin.fgov.be/InputCommon" xmlns:ns2="http://www.minfin.fgov.be/IntraConsignment" IntraListingsNbr="1">
|
||||
<ns2:Representative>
|
||||
<RepresentativeID identificationType="NVAT" issuedBy="%(issued_by)s">%(company_vat)s</RepresentativeID>
|
||||
<RepresentativeID identificationType="NVAT" issuedBy="%(issued_by)s">%(vatnum)s</RepresentativeID>
|
||||
<Name>%(company_name)s</Name>
|
||||
<Street>%(street)s</Street>
|
||||
<PostCode>%(post_code)s</PostCode>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -141,7 +141,7 @@ class account_invoice(osv.osv):
|
|||
elif algorithm == 'random':
|
||||
if not self.check_bbacomm(reference):
|
||||
base = random.randint(1, 9999999999)
|
||||
bbacomm = str(base).rjust(7, '0')
|
||||
bbacomm = str(base).rjust(10, '0')
|
||||
base = int(bbacomm)
|
||||
mod = base % 97 or 97
|
||||
mod = str(mod).rjust(2, '0')
|
||||
|
|
|
@ -72,6 +72,7 @@ class account_fiscal_position(osv.osv):
|
|||
_inherit = 'account.fiscal.position'
|
||||
_columns = {
|
||||
'name': fields.char('Fiscal Position', size=64, required=True, translate=True),
|
||||
'note': fields.text('Notes', translate=True),
|
||||
}
|
||||
|
||||
|
||||
|
@ -79,6 +80,7 @@ class account_fiscal_position_template(osv.osv):
|
|||
_inherit = 'account.fiscal.position.template'
|
||||
_columns = {
|
||||
'name': fields.char('Fiscal Position Template', size=64, required=True, translate=True),
|
||||
'note': fields.text('Notes', translate=True),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ class mail_notification(osv.Model):
|
|||
references = message.parent_id.message_id if message.parent_id else False
|
||||
|
||||
# create email values
|
||||
max_recipients = 100
|
||||
max_recipients = 50
|
||||
chunks = [email_pids[x:x + max_recipients] for x in xrange(0, len(email_pids), max_recipients)]
|
||||
email_ids = []
|
||||
for chunk in chunks:
|
||||
|
@ -188,7 +188,7 @@ class mail_notification(osv.Model):
|
|||
'references': references,
|
||||
}
|
||||
email_ids.append(self.pool.get('mail.mail').create(cr, uid, mail_values, context=context))
|
||||
if force_send and len(chunks) < 6: # for more than 500 followers, use the queue system
|
||||
if force_send and len(chunks) < 2: # for more than 50 followers, use the queue system
|
||||
self.pool.get('mail.mail').send(cr, uid, email_ids, context=context)
|
||||
return True
|
||||
|
||||
|
|
|
@ -211,3 +211,16 @@ class mail_group(osv.Model):
|
|||
return []
|
||||
else:
|
||||
return super(mail_group, self).get_suggested_thread(cr, uid, removed_suggested_threads, context)
|
||||
|
||||
def message_get_email_values(self, cr, uid, id, notif_mail=None, context=None):
|
||||
res = super(mail_group, self).message_get_email_values(cr, uid, id, notif_mail=notif_mail, context=context)
|
||||
group = self.browse(cr, uid, id, context=context)
|
||||
res.update({
|
||||
'headers': {
|
||||
'Precedence': 'list',
|
||||
}
|
||||
})
|
||||
if group.alias_domain:
|
||||
res['headers']['List-Id'] = '%s.%s' % (group.alias_name, group.alias_domain)
|
||||
res['headers']['List-Post'] = '<mailto:%s@%s>' % (group.alias_name, group.alias_domain)
|
||||
return res
|
||||
|
|
|
@ -204,12 +204,15 @@ class mail_mail(osv.Model):
|
|||
"""
|
||||
body = self.send_get_mail_body(cr, uid, mail, partner=partner, context=context)
|
||||
body_alternative = tools.html2plaintext(body)
|
||||
return {
|
||||
res = {
|
||||
'body': body,
|
||||
'body_alternative': body_alternative,
|
||||
'subject': self.send_get_mail_subject(cr, uid, mail, partner=partner, context=context),
|
||||
'email_to': self.send_get_mail_to(cr, uid, mail, partner=partner, context=context),
|
||||
}
|
||||
if mail.model and mail.res_id and self.pool.get(mail.model) and hasattr(self.pool[mail.model], 'message_get_email_values'):
|
||||
res.update(self.pool[mail.model].message_get_email_values(cr, uid, mail.res_id, mail, context=context))
|
||||
return res
|
||||
|
||||
def send(self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None):
|
||||
""" Sends the selected emails immediately, ignoring their current
|
||||
|
@ -268,6 +271,9 @@ class mail_mail(osv.Model):
|
|||
# build an RFC2822 email.message.Message object and send it without queuing
|
||||
res = None
|
||||
for email in email_list:
|
||||
email_headers = dict(headers)
|
||||
if email.get('headers'):
|
||||
email_headers.update(email['headers'])
|
||||
msg = ir_mail_server.build_email(
|
||||
email_from=mail.email_from,
|
||||
email_to=email.get('email_to'),
|
||||
|
@ -282,7 +288,7 @@ class mail_mail(osv.Model):
|
|||
object_id=mail.res_id and ('%s-%s' % (mail.res_id, mail.model)),
|
||||
subtype='html',
|
||||
subtype_alternative='plain',
|
||||
headers=headers)
|
||||
headers=email_headers)
|
||||
res = ir_mail_server.send_email(cr, uid, msg,
|
||||
mail_server_id=mail.mail_server_id.id,
|
||||
context=context)
|
||||
|
|
|
@ -34,6 +34,7 @@ import pytz
|
|||
import socket
|
||||
import time
|
||||
import xmlrpclib
|
||||
import re
|
||||
from email.message import Message
|
||||
from urllib import urlencode
|
||||
|
||||
|
@ -48,6 +49,8 @@ from openerp.tools.translate import _
|
|||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
mail_header_msgid_re = re.compile('<[^<>]+>')
|
||||
|
||||
def decode_header(message, header, separator=' '):
|
||||
return separator.join(map(decode, filter(None, message.get_all(header, []))))
|
||||
|
||||
|
@ -694,6 +697,16 @@ class mail_thread(osv.AbstractModel):
|
|||
if record.alias_domain and record.alias_name else False
|
||||
for record in self.browse(cr, SUPERUSER_ID, ids, context=context)]
|
||||
|
||||
def message_get_email_values(self, cr, uid, id, notif_mail=None, context=None):
|
||||
""" Temporary method to create custom notification email values for a given
|
||||
model and document. This should be better to have a headers field on
|
||||
the mail.mail model, computed when creating the notification email, but
|
||||
this cannot be done in a stable version.
|
||||
|
||||
TDE FIXME: rethink this ulgy thing. """
|
||||
res = dict()
|
||||
return res
|
||||
|
||||
#------------------------------------------------------
|
||||
# Mail gateway
|
||||
#------------------------------------------------------
|
||||
|
@ -1301,13 +1314,13 @@ class mail_thread(osv.AbstractModel):
|
|||
msg_dict['date'] = stored_date.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
if message.get('In-Reply-To'):
|
||||
parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', '=', decode(message['In-Reply-To']))])
|
||||
parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', '=', decode(message['In-Reply-To'].strip()))])
|
||||
if parent_ids:
|
||||
msg_dict['parent_id'] = parent_ids[0]
|
||||
|
||||
if message.get('References') and 'parent_id' not in msg_dict:
|
||||
parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', 'in',
|
||||
[x.strip() for x in decode(message['References']).split()])])
|
||||
msg_list = mail_header_msgid_re.findall(decode(message['References']))
|
||||
parent_ids = self.pool.get('mail.message').search(cr, uid, [('message_id', 'in', [x.strip() for x in msg_list])])
|
||||
if parent_ids:
|
||||
msg_dict['parent_id'] = parent_ids[0]
|
||||
|
||||
|
|
|
@ -267,10 +267,7 @@ class mail_compose_message(osv.TransientModel):
|
|||
# mass mailing: rendering override wizard static values
|
||||
if mass_mail_mode and wizard.model:
|
||||
# always keep a copy, reset record name (avoid browsing records)
|
||||
mail_values.update(notification=True, record_name=False)
|
||||
if hasattr(self.pool[wizard.model], 'message_new'):
|
||||
mail_values['model'] = wizard.model
|
||||
mail_values['res_id'] = res_id
|
||||
mail_values.update(notification=True, model=wizard.model, res_id=res_id, record_name=False)
|
||||
# auto deletion of mail_mail
|
||||
if 'mail_auto_delete' in context:
|
||||
mail_values['auto_delete'] = context.get('mail_auto_delete')
|
||||
|
|
|
@ -84,7 +84,8 @@ class MailMail(osv.Model):
|
|||
def send_get_email_dict(self, cr, uid, mail, partner=None, context=None):
|
||||
res = super(MailMail, self).send_get_email_dict(cr, uid, mail, partner, context=context)
|
||||
if mail.mailing_id and res.get('body') and res.get('email_to'):
|
||||
email_to = tools.email_split(res.get('email_to')[0])
|
||||
emails = tools.email_split(res.get('email_to')[0])
|
||||
email_to = emails and emails[0] or False
|
||||
unsubscribe_url = self._get_unsubscribe_url(cr, uid, mail, email_to, context=context)
|
||||
if unsubscribe_url:
|
||||
res['body'] = tools.append_content_to_html(res['body'], unsubscribe_url, plaintext=False, container_tag='p')
|
||||
|
|
|
@ -591,6 +591,7 @@
|
|||
<field name="mail_mail_id"/>
|
||||
<field name="message_id"/>
|
||||
<field name="sent"/>
|
||||
<field name="exception"/>
|
||||
<field name="opened"/>
|
||||
<field name="replied"/>
|
||||
<field name="bounced"/>
|
||||
|
|
|
@ -1071,8 +1071,8 @@ class mrp_production(osv.osv):
|
|||
return False
|
||||
# Take routing location as a Source Location.
|
||||
source_location_id = production.location_src_id.id
|
||||
if production.bom_id.routing_id and production.bom_id.routing_id.location_id:
|
||||
source_location_id = production.bom_id.routing_id.location_id.id
|
||||
if production.routing_id and production.routing_id.location_id:
|
||||
source_location_id = production.routing_id.location_id.id
|
||||
|
||||
destination_location_id = production.product_id.property_stock_production.id
|
||||
if not source_location_id:
|
||||
|
|
|
@ -675,6 +675,17 @@ class product_template(osv.osv):
|
|||
if not context or "create_product_product" not in context:
|
||||
self.create_variant_ids(cr, uid, [product_template_id], context=context)
|
||||
self._set_standard_price(cr, uid, product_template_id, vals.get('standard_price', 0.0), context=context)
|
||||
|
||||
# TODO: this is needed to set given values to first variant after creation
|
||||
# these fields should be moved to product as lead to confusion
|
||||
related_vals = {}
|
||||
if vals.get('ean13'):
|
||||
related_vals['ean13'] = vals['ean13']
|
||||
if vals.get('default_code'):
|
||||
related_vals['default_code'] = vals['default_code']
|
||||
if related_vals:
|
||||
self.write(cr, uid, product_template_id, related_vals, context=context)
|
||||
|
||||
return product_template_id
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
|
|
|
@ -27,7 +27,7 @@ class account_invoice(osv.Model):
|
|||
template_values = Composer.onchange_template_id(
|
||||
cr, uid, composer_id, line.product_id.email_template_id.id, 'comment', 'account.invoice', invoice.id
|
||||
)['value']
|
||||
template_values['attachment_ids'] = [(4, id) for id in template_values.get('attachment_ids', '[]')]
|
||||
template_values['attachment_ids'] = [(4, id) for id in template_values.get('attachment_ids', [])]
|
||||
Composer.write(cr, uid, [composer_id], template_values, context=context)
|
||||
Composer.send_mail(cr, uid, [composer_id], context=context)
|
||||
return True
|
||||
|
|
|
@ -50,8 +50,8 @@
|
|||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="alias_model">project.task</field>
|
||||
<field name="message_follower_ids" eval="[(6, 0, [
|
||||
ref('base.user_root'),
|
||||
ref('base.user_demo')])]"/>
|
||||
ref('base.partner_root'),
|
||||
ref('base.partner_demo')])]"/>
|
||||
</record>
|
||||
|
||||
<!-- We assign after so that default values applies -->
|
||||
|
|
|
@ -19,9 +19,6 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import time
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
@ -55,7 +52,7 @@ class purchase_requisition(osv.osv):
|
|||
'procurement_id': fields.many2one('procurement.order', 'Procurement', ondelete='set null'),
|
||||
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
|
||||
'state': fields.selection([('draft', 'Draft'), ('in_progress', 'Confirmed'), ('open', 'Bid Selection'), ('done', 'PO Created'), ('cancel', 'Cancelled')],
|
||||
'Status', track_visibility='onchange', required=True),
|
||||
'Status', track_visibility='onchange', required=True),
|
||||
'multiple_rfq_per_supplier': fields.boolean('Multiple RFQ per supplier'),
|
||||
'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', required=True),
|
||||
|
@ -63,7 +60,7 @@ class purchase_requisition(osv.osv):
|
|||
|
||||
def _get_picking_in(self, cr, uid, context=None):
|
||||
obj_data = self.pool.get('ir.model.data')
|
||||
return obj_data.get_object_reference(cr, uid, 'stock','picking_type_in')[1]
|
||||
return obj_data.get_object_reference(cr, uid, 'stock', 'picking_type_in')[1]
|
||||
|
||||
_defaults = {
|
||||
'state': 'draft',
|
||||
|
@ -85,8 +82,7 @@ class purchase_requisition(osv.osv):
|
|||
|
||||
def tender_cancel(self, cr, uid, ids, context=None):
|
||||
purchase_order_obj = self.pool.get('purchase.order')
|
||||
#try to set all associated quotations to cancel state
|
||||
purchase_ids = []
|
||||
# try to set all associated quotations to cancel state
|
||||
for tender in self.browse(cr, uid, ids, context=context):
|
||||
for purchase_order in tender.purchase_ids:
|
||||
purchase_order_obj.action_cancel(cr, uid, [purchase_order.id], context=context)
|
||||
|
@ -163,8 +159,11 @@ class purchase_requisition(osv.osv):
|
|||
date_order = requisition.ordering_date or fields.date.context_today(self, cr, uid, context=context)
|
||||
qty = product_uom._compute_qty(cr, uid, requisition_line.product_uom_id.id, requisition_line.product_qty, default_uom_po_id)
|
||||
supplier_pricelist = supplier.property_product_pricelist_purchase and supplier.property_product_pricelist_purchase.id or False
|
||||
vals = po_line_obj.onchange_product_id(cr, uid, [], supplier_pricelist, product.id, qty, default_uom_po_id,
|
||||
supplier.id, date_order=date_order, fiscal_position_id=supplier.property_account_position, date_planned=requisition_line.schedule_date,
|
||||
vals = po_line_obj.onchange_product_id(
|
||||
cr, uid, [], supplier_pricelist, product.id, qty, default_uom_po_id,
|
||||
supplier.id, date_order=date_order,
|
||||
fiscal_position_id=supplier.property_account_position,
|
||||
date_planned=requisition_line.schedule_date,
|
||||
name=False, price_unit=False, state='draft', context=context)['value']
|
||||
vals.update({
|
||||
'order_id': purchase_id,
|
||||
|
@ -233,8 +232,6 @@ class purchase_requisition(osv.osv):
|
|||
"""
|
||||
Generate all purchase order based on selected lines, should only be called on one tender at a time
|
||||
"""
|
||||
if context is None:
|
||||
contex = {}
|
||||
po = self.pool.get('purchase.order')
|
||||
poline = self.pool.get('purchase.order.line')
|
||||
id_per_supplier = {}
|
||||
|
@ -267,8 +264,7 @@ class purchase_requisition(osv.osv):
|
|||
id_per_supplier[po_line.partner_id.id] = [po_line]
|
||||
|
||||
#generate po based on supplier and cancel all previous RFQ
|
||||
ctx = context.copy()
|
||||
ctx['force_requisition_id'] = True
|
||||
ctx = dict(context or {}, force_requisition_id=True)
|
||||
for supplier, product_line in id_per_supplier.items():
|
||||
#copy a quotation for this supplier and change order_line then validate it
|
||||
quotation_id = po.search(cr, uid, [('requisition_id', '=', tender.id), ('partner_id', '=', supplier)], limit=1)[0]
|
||||
|
@ -429,12 +425,9 @@ class procurement_order(osv.osv):
|
|||
return super(procurement_order, self)._run(cr, uid, procurement, context=context)
|
||||
|
||||
def _check(self, cr, uid, procurement, context=None):
|
||||
requisition_obj = self.pool.get('purchase.requisition')
|
||||
if procurement.rule_id and procurement.rule_id.action == 'buy' and procurement.product_id.purchase_requisition:
|
||||
if procurement.requisition_id.state == 'done':
|
||||
if any([purchase.shipped for purchase in procurement.requisition_id.purchase_ids]):
|
||||
return True
|
||||
return False
|
||||
return super(procurement_order, self)._check(cr, uid, procurement, context=context)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
<tr t-foreach="o.invoice_line" t-as="l">
|
||||
<td><span t-field="l.name"/></td>
|
||||
<td class="text-right"><span t-esc="', '.join(map(lambda x: x.name, l.invoice_line_tax_id))"/></td>
|
||||
<td class="text-right"><span t-esc="l.product_id.intrastat_id"/></td>
|
||||
<td class="text-right"><span t-esc="l.product_id.intrastat_id.name"/></td>
|
||||
<td class="text-right"><span t-esc="l.product_id.weight"/></td>
|
||||
<td class="text-right"><span t-field="l.quantity"/></td>
|
||||
<td groups="product.group_uom"><span t-field="l.uos_id"/></td>
|
||||
|
|
|
@ -128,13 +128,12 @@ class sale_order(osv.osv):
|
|||
sale_clause = ''
|
||||
no_invoiced = False
|
||||
for arg in args:
|
||||
if arg[1] == '=':
|
||||
if arg[2]:
|
||||
clause += 'AND inv.state = \'paid\''
|
||||
else:
|
||||
clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\' AND inv.state <> \'paid\' AND rel.order_id = sale.id '
|
||||
sale_clause = ', sale_order AS sale '
|
||||
no_invoiced = True
|
||||
if (arg[1] == '=' and arg[2]) or (arg[1] == '!=' and not arg[2]):
|
||||
clause += 'AND inv.state = \'paid\''
|
||||
else:
|
||||
clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\' AND inv.state <> \'paid\' AND rel.order_id = sale.id '
|
||||
sale_clause = ', sale_order AS sale '
|
||||
no_invoiced = True
|
||||
|
||||
cursor.execute('SELECT rel.order_id ' \
|
||||
'FROM sale_order_invoice_rel AS rel, account_invoice AS inv '+ sale_clause + \
|
||||
|
|
|
@ -747,7 +747,7 @@
|
|||
</tree>
|
||||
</field>
|
||||
-->
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'view_move_picking_tree', 'default_picking_type_id': picking_type_id,'default_picking_id': active_id}"/>
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id,'default_picking_id': active_id}"/>
|
||||
<field name="pack_operation_exist" invisible="1"/>
|
||||
<field name="note" placeholder="Add an internal note..." class="oe_inline"/>
|
||||
</page>
|
||||
|
@ -1286,7 +1286,7 @@
|
|||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain" eval="['|','&',('picking_id','=',False),('location_dest_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.picking_type_id.code','=','outgoing')]"/>
|
||||
<field name="domain" eval="[('picking_id.picking_type_id.code','=','incoming'), ('location_id.usage','!=','internal'), ('location_dest_id.usage', '=', 'internal')]"/>
|
||||
<field name="view_id" ref="view_move_tree_reception_picking"/>
|
||||
<field name="context">{'product_receive': True, 'search_default_future': True}</field>
|
||||
<field name="help" type="html">
|
||||
|
|
|
@ -46,7 +46,7 @@ class WebsiteSurvey(http.Controller):
|
|||
|
||||
# In case of auth required, block public user
|
||||
if survey.auth_required and uid == request.website.user_id.id:
|
||||
return request.website.render("website.403")
|
||||
return request.website.render("survey.auth_required", {'survey': survey})
|
||||
|
||||
# In case of non open surveys
|
||||
if survey.stage_id.closed:
|
||||
|
|
|
@ -14,18 +14,7 @@
|
|||
</record>
|
||||
|
||||
<!-- Record rules -->
|
||||
<record id="survey_public_access" model="ir.rule">
|
||||
<field name="name">Public access to surveys</field>
|
||||
<field name="model_id" ref="survey.model_survey_survey"/>
|
||||
<field name="domain_force">[('auth_required', '=', False)]</field>
|
||||
<field name="groups" eval="[(4, ref('base.group_public'))]"/>
|
||||
<field eval="0" name="perm_unlink"/>
|
||||
<field eval="0" name="perm_write"/>
|
||||
<field eval="1" name="perm_read"/>
|
||||
<field eval="0" name="perm_create"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_rule" model="ir.rule">
|
||||
<record id="survey_users_access" model="ir.rule">
|
||||
<field name="name">Access to survey for regular users</field>
|
||||
<field name="model_id" ref="survey.model_survey_survey"/>
|
||||
<field name="domain_force">[('stage_id.closed', '=', False)]</field>
|
||||
|
@ -36,7 +25,7 @@
|
|||
<field eval="0" name="perm_create"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_rule_manager" model="ir.rule">
|
||||
<record id="survey_manager_access" model="ir.rule">
|
||||
<field name="name">Survey Manager access rights</field>
|
||||
<field name="model_id" ref="survey.model_survey_survey"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
|
@ -47,7 +36,7 @@
|
|||
<field eval="1" name="perm_create"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_public_access" model="ir.rule">
|
||||
<record id="survey_input_public_access" model="ir.rule">
|
||||
<field name="name">Public access to user_input</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input"/>
|
||||
<field name="domain_force">[('create_uid', '=', user.id)]</field>
|
||||
|
@ -58,7 +47,7 @@
|
|||
<field eval="0" name="perm_create"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_rule" model="ir.rule">
|
||||
<record id="survey_input_users_access" model="ir.rule">
|
||||
<field name="name">Access to user_input for regular users</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input"/>
|
||||
<field name="domain_force">[('create_uid', '=', user.id)]</field>
|
||||
|
@ -69,7 +58,7 @@
|
|||
<field eval="0" name="perm_create"/>
|
||||
</record>
|
||||
|
||||
<record id="survey_rule_manager" model="ir.rule">
|
||||
<record id="survey_input_manager_access" model="ir.rule">
|
||||
<field name="name">Survey Manager access rights</field>
|
||||
<field name="model_id" ref="survey.model_survey_user_input"/>
|
||||
<field name="domain_force">[(1, '=', 1)]</field>
|
||||
|
|
|
@ -26,7 +26,6 @@ $(document).ready(function () {
|
|||
var question_id = $(pagination).attr("data-question_id");
|
||||
var limit = $(pagination).attr("data-record_limit"); //Number of Record Par Page. If you want to change number of record per page, change record_limit in pagination template.
|
||||
$('#table_question_'+ question_id +' tbody tr:lt('+limit+')').removeClass('hidden');
|
||||
$('#pagination_'+question_id+' li:first').addClass('active');
|
||||
$('#pagination_'+question_id+' li a').click(function(event){
|
||||
event.preventDefault();
|
||||
$('#pagination_'+question_id+' li').removeClass('active');
|
||||
|
@ -41,6 +40,7 @@ $(document).ready(function () {
|
|||
$('#table_question_'+question_id+' tbody tr:lt('+ limit * num +'):gt('+min+')').removeClass('hidden');
|
||||
}
|
||||
});
|
||||
$('#pagination_'+question_id+' li:first').addClass('active').find('a').click();
|
||||
});
|
||||
|
||||
//initialize MultiBar Chart
|
||||
|
|
|
@ -31,6 +31,20 @@
|
|||
</t>
|
||||
</template>
|
||||
|
||||
<!-- Message when a login is required -->
|
||||
<template id="auth_required" name="Login required for this survey">
|
||||
<t t-call="website.layout">
|
||||
<div class="wrap">
|
||||
<div class="container">
|
||||
<div class="jumbotron mt32">
|
||||
<h1>Login required</h1>
|
||||
<p>This survey is open only to registered people. Please <a t-attf-href="/web/login?redirect=%2Fsurvey%2Fstart%2F#{ slug(survey) }">log in</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- Message when the survey has no pages -->
|
||||
<template id="nopages" name="Survey has no pages">
|
||||
<t t-call="website.layout">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@charset "utf-8";
|
||||
@charset "UTF-8";
|
||||
@font-face {
|
||||
font-family: "mnmliconsRegular";
|
||||
src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot");
|
||||
|
@ -2350,7 +2350,7 @@
|
|||
}
|
||||
.openerp .oe_fileupload .oe_add button.oe_attach .oe_e {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
top: -10px;
|
||||
left: -9px;
|
||||
}
|
||||
.openerp .oe_fileupload .oe_add input.oe_form_binary_file {
|
||||
|
@ -2624,11 +2624,11 @@
|
|||
.openerp .oe_list_editable .oe_list_content td.oe_list_field_cell {
|
||||
padding: 4px 6px 3px;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) {
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell {
|
||||
color: transparent;
|
||||
text-shadow: none;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) * {
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell * {
|
||||
visibility: hidden;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_drop_down_button {
|
||||
|
@ -2644,6 +2644,13 @@
|
|||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_list_field_handle {
|
||||
color: transparent;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_readonly {
|
||||
padding: 4px 6px 3px;
|
||||
text-align: left;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea {
|
||||
height: 27px;
|
||||
-moz-border-radius: 0;
|
||||
|
@ -2655,9 +2662,14 @@
|
|||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field select {
|
||||
min-width: 0;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float.oe_readonly, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer.oe_readonly {
|
||||
padding: 6px 0px 0px;
|
||||
text-align: right;
|
||||
max-width: 100px;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
|
||||
width: 100% !important;
|
||||
text-align: right;
|
||||
}
|
||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime input.oe_datepicker_master, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date input.oe_datepicker_master {
|
||||
width: 100% !important;
|
||||
|
@ -3301,6 +3313,9 @@ body.oe_single_form .oe_single_form_container {
|
|||
.openerp_ie ul.oe_form_status li.oe_active > .arrow span, .openerp_ie ul.oe_form_status_clickable li.oe_active > .arrow span {
|
||||
background-color: #729fcf !important;
|
||||
}
|
||||
.openerp_ie .oe_webclient {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.openerp {
|
||||
|
@ -3351,6 +3366,7 @@ body.oe_single_form .oe_single_form_container {
|
|||
background: white;
|
||||
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
|
||||
background-color: transparent;
|
||||
z-index: 1800;
|
||||
}
|
||||
.tooltip .tooltip-inner {
|
||||
text-align: left !important;
|
||||
|
|
|
@ -1918,7 +1918,7 @@ $sheet-padding: 16px
|
|||
text-shadow: none
|
||||
.oe_e
|
||||
position: relative
|
||||
top: -1px
|
||||
top: -10px
|
||||
left: -9px
|
||||
input.oe_form_binary_file
|
||||
display: inline-block
|
||||
|
@ -2117,7 +2117,7 @@ $sheet-padding: 16px
|
|||
.oe_list_editable .oe_list_content td.oe_list_field_cell
|
||||
padding: 4px 6px 3px
|
||||
.oe_list.oe_list_editable.oe_editing
|
||||
.oe_edition .oe_list_field_cell:not(.oe_readonly)
|
||||
.oe_edition .oe_list_field_cell
|
||||
*
|
||||
visibility: hidden
|
||||
color: transparent
|
||||
|
@ -2129,6 +2129,11 @@ $sheet-padding: 16px
|
|||
.oe_input_icon
|
||||
margin-top: 5px
|
||||
.oe_form_field
|
||||
&.oe_list_field_handle
|
||||
color: transparent
|
||||
&.oe_readonly
|
||||
padding: 4px 6px 3px
|
||||
text-align: left
|
||||
min-width: 0
|
||||
max-width: none
|
||||
input, textarea
|
||||
|
@ -2139,9 +2144,13 @@ $sheet-padding: 16px
|
|||
input, textarea, select
|
||||
min-width: 0
|
||||
&.oe_form_field_float,&.oe_form_view_integer
|
||||
input
|
||||
&.oe_readonly
|
||||
padding: 6px 0px 0px
|
||||
text-align: right
|
||||
max-width: 100px
|
||||
input
|
||||
width: 100% !important
|
||||
text-align: right
|
||||
&.oe_form_field_datetime,&.oe_form_field_date
|
||||
input.oe_datepicker_master
|
||||
width: 100% !important
|
||||
|
@ -2670,6 +2679,8 @@ body.oe_single_form
|
|||
> .arrow span
|
||||
background-color: #729fcf !important
|
||||
|
||||
.oe_webclient
|
||||
height: auto !important
|
||||
// }}}
|
||||
|
||||
// @media print {{{
|
||||
|
@ -2719,6 +2730,7 @@ body.oe_single_form
|
|||
background: white
|
||||
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5)
|
||||
background-color: transparent
|
||||
z-index: 1800
|
||||
.tooltip-inner
|
||||
text-align: left !important
|
||||
max-width: 350px
|
||||
|
|
|
@ -183,6 +183,7 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
*/
|
||||
close: function(reason) {
|
||||
if (this.dialog_inited && !this.__tmp_dialog_hiding) {
|
||||
$('.tooltip').remove(); //remove open tooltip if any to prevent them staying when modal has disappeared
|
||||
this.trigger("closing", reason);
|
||||
if (this.$el.is(":data(bs.modal)")) { // may have been destroyed by closing signal
|
||||
this.__tmp_dialog_hiding = true;
|
||||
|
@ -493,7 +494,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
|
|||
size: 'medium',
|
||||
title: error.title,
|
||||
buttons: [
|
||||
{text: _t("Ok"), click: function() { this.$el.parents('.modal').modal('hide'); }}
|
||||
{text: _t("Ok"), click: function() { this.parents('.modal').modal('hide'); }}
|
||||
]
|
||||
}, $('<div>').html(error.error)).open();
|
||||
},
|
||||
|
@ -706,7 +707,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({
|
|||
var $button = self.$el.find('.oe_form_button');
|
||||
$button.appendTo(this.getParent().$buttons);
|
||||
$button.eq(2).click(function(){
|
||||
self.getParent().close();
|
||||
self.$el.parents('.modal').modal('hide');
|
||||
});
|
||||
$button.eq(0).click(function(){
|
||||
self.rpc("/web/session/change_password",{
|
||||
|
@ -726,7 +727,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({
|
|||
size: 'medium',
|
||||
title: error.title,
|
||||
buttons: [
|
||||
{text: _t("Ok"), click: function() { this.$el.parents('.modal').modal('hide'); }}
|
||||
{text: _t("Ok"), click: function() { this.parents('.modal').modal('hide'); }}
|
||||
]
|
||||
}, $('<div>').html(error.error)).open();
|
||||
},
|
||||
|
|
|
@ -773,15 +773,18 @@ instance.web.unblockUI = function() {
|
|||
$.fn.tooltip.Constructor.DEFAULTS.placement = 'auto top';
|
||||
$.fn.tooltip.Constructor.DEFAULTS.html = true;
|
||||
$.fn.tooltip.Constructor.DEFAULTS.container = 'body';
|
||||
//overwrite bootstrap tooltip method to fix bug when using placement
|
||||
//auto and the parent element does not exist anymore resulting in
|
||||
//an error. This should be remove once bootstrap fix the bug
|
||||
//overwrite bootstrap tooltip method to prevent showing 2 tooltip at the same time
|
||||
var bootstrap_show_function = $.fn.tooltip.Constructor.prototype.show;
|
||||
$.fn.tooltip.Constructor.prototype.show = function (e) {
|
||||
if (this.$element.parent().length === 0){
|
||||
return;
|
||||
}
|
||||
return bootstrap_show_function.call(this, e);
|
||||
$.fn.tooltip.Constructor.prototype.show = function () {
|
||||
$('.tooltip').remove();
|
||||
//the following fix the bug when using placement
|
||||
//auto and the parent element does not exist anymore resulting in
|
||||
//an error. This should be remove once we updade bootstrap to a version that fix the bug
|
||||
//edit: bug has been fixed here : https://github.com/twbs/bootstrap/pull/13752
|
||||
var e = $.Event('show.bs.' + this.type);
|
||||
var inDom = $.contains(document.documentElement, this.$element[0]);
|
||||
if (e.isDefaultPrevented() || !inDom) return;
|
||||
return bootstrap_show_function.call(this);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -233,7 +233,8 @@ instance.web.parse_value = function (value, descriptor, value_if_empty) {
|
|||
value = value.replace(instance.web._t.database.parameters.thousands_sep, "");
|
||||
} while(tmp !== value);
|
||||
tmp = Number(value);
|
||||
if (isNaN(tmp))
|
||||
// do not accept not numbers or float values
|
||||
if (isNaN(tmp) || tmp % 1)
|
||||
throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value));
|
||||
return tmp;
|
||||
case 'float':
|
||||
|
@ -268,6 +269,11 @@ instance.web.parse_value = function (value, descriptor, value_if_empty) {
|
|||
case 'datetime':
|
||||
var datetime = Date.parseExact(
|
||||
value, (date_pattern + ' ' + time_pattern));
|
||||
if (datetime !== null)
|
||||
return instance.web.datetime_to_str(datetime);
|
||||
datetime = Date.parseExact(value.toString().replace(/\d+/g, function(m){
|
||||
return m.length === 1 ? "0" + m : m ;
|
||||
}), (date_pattern + ' ' + time_pattern));
|
||||
if (datetime !== null)
|
||||
return instance.web.datetime_to_str(datetime);
|
||||
datetime = Date.parse(value);
|
||||
|
@ -276,6 +282,11 @@ instance.web.parse_value = function (value, descriptor, value_if_empty) {
|
|||
throw new Error(_.str.sprintf(_t("'%s' is not a correct datetime"), value));
|
||||
case 'date':
|
||||
var date = Date.parseExact(value, date_pattern);
|
||||
if (date !== null)
|
||||
return instance.web.date_to_str(date);
|
||||
date = Date.parseExact(value.toString().replace(/\d+/g, function(m){
|
||||
return m.length === 1 ? "0" + m : m ;
|
||||
}), date_pattern);
|
||||
if (date !== null)
|
||||
return instance.web.date_to_str(date);
|
||||
date = Date.parse(value);
|
||||
|
|
|
@ -345,11 +345,11 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
|||
'keydown .oe_searchview_input, .oe_searchview_facet': function (e) {
|
||||
switch(e.which) {
|
||||
case $.ui.keyCode.LEFT:
|
||||
this.focusPreceding(this);
|
||||
this.focusPreceding(e.target);
|
||||
e.preventDefault();
|
||||
break;
|
||||
case $.ui.keyCode.RIGHT:
|
||||
this.focusFollowing(this);
|
||||
this.focusFollowing(e.target);
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1869,25 +1869,9 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
|
|||
do_attach_tooltip: function(widget, trigger, options) {
|
||||
widget = widget || this;
|
||||
trigger = trigger || this.$el;
|
||||
var container = 'body';
|
||||
/*TODO: need to be refactor
|
||||
in the case we can find the view form in the parent,
|
||||
attach the element to it (to prevent tooltip to keep showing
|
||||
when switching view) or if we have a modal currently showing,
|
||||
attach tooltip to the modal to prevent the tooltip to show in the body in the
|
||||
case we close the modal too fast*/
|
||||
if ($(trigger).parents('.oe_view_manager_view_form').length > 0){
|
||||
container = $(trigger).parents('.oe_view_manager_view_form');
|
||||
}
|
||||
else {
|
||||
if (window.$('.modal.in').length>0){
|
||||
container = window.$('.modal.in:last()');
|
||||
}
|
||||
}
|
||||
options = _.extend({
|
||||
delay: { show: 500, hide: 0 },
|
||||
trigger: 'hover',
|
||||
container: container,
|
||||
title: function() {
|
||||
var template = widget.template + '.tooltip';
|
||||
if (!QWeb.has_template(template)) {
|
||||
|
@ -2399,6 +2383,7 @@ instance.web.form.KanbanSelection = instance.web.form.FieldChar.extend({
|
|||
if (li.length) {
|
||||
var value = {};
|
||||
value[self.name] = String(li.data('value'));
|
||||
self.record_id = self.view.datarecord.id;
|
||||
if (self.record_id) {
|
||||
return self.view.dataset._model.call('write', [[self.record_id], value, self.view.dataset.get_context()]).done(self.reload_record.bind(self));
|
||||
} else {
|
||||
|
@ -2632,6 +2617,7 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({
|
|||
type_of_date: "datetime",
|
||||
events: {
|
||||
'change .oe_datepicker_master': 'change_datetime',
|
||||
'keypress .oe_datepicker_master': 'change_datetime',
|
||||
},
|
||||
init: function(parent) {
|
||||
this._super(parent);
|
||||
|
@ -2750,8 +2736,8 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({
|
|||
format_client: function(v) {
|
||||
return instance.web.format_value(v, {"widget": this.type_of_date});
|
||||
},
|
||||
change_datetime: function() {
|
||||
if (this.is_valid_()) {
|
||||
change_datetime: function(e) {
|
||||
if ((e.type !== "keypress" || e.which === 13) && this.is_valid_()) {
|
||||
this.set_value_from_ui_();
|
||||
this.trigger("datetime_changed");
|
||||
}
|
||||
|
@ -2809,7 +2795,9 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc
|
|||
},
|
||||
set_dimensions: function (height, width) {
|
||||
this._super(height, width);
|
||||
this.datewidget.$input.css('height', height);
|
||||
if (!this.get("effective_readonly")) {
|
||||
this.datewidget.$input.css('height', height);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -3509,6 +3497,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
|||
this.floating = false;
|
||||
this.current_display = null;
|
||||
this.is_started = false;
|
||||
this.ignore_focusout = false;
|
||||
},
|
||||
reinit_value: function(val) {
|
||||
this.internal_set_value(val);
|
||||
|
@ -3640,6 +3629,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
|||
var ed_delay = 200;
|
||||
var ed_duration = 15000;
|
||||
var anyoneLoosesFocus = function (e) {
|
||||
if (self.ignore_focusout) { return; }
|
||||
var used = false;
|
||||
if (self.floating) {
|
||||
if (self.last_search.length > 0) {
|
||||
|
@ -3843,11 +3833,17 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
|||
_search_create_popup: function() {
|
||||
this.no_ed = true;
|
||||
this.ed_def.reject();
|
||||
return instance.web.form.CompletionFieldMixin._search_create_popup.apply(this, arguments);
|
||||
this.ignore_focusout = true;
|
||||
this.reinit_value(false);
|
||||
var res = instance.web.form.CompletionFieldMixin._search_create_popup.apply(this, arguments);
|
||||
this.ignore_focusout = false;
|
||||
this.no_ed = false;
|
||||
return res;
|
||||
},
|
||||
set_dimensions: function (height, width) {
|
||||
this._super(height, width);
|
||||
this.$input.css('height', height);
|
||||
if (!this.get("effective_readonly") && this.$input)
|
||||
this.$input.css('height', height);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1108,7 +1108,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
ids = value;
|
||||
}
|
||||
new instance.web.Model(column.relation)
|
||||
.call('name_get', [ids]).done(function (names) {
|
||||
.call('name_get', [ids, this.dataset.context]).done(function (names) {
|
||||
// FIXME: nth horrible hack in this poor listview
|
||||
record.set(column.id + '__display',
|
||||
_(names).pluck(1).join(', '));
|
||||
|
|
|
@ -130,15 +130,7 @@
|
|||
if (this.editable()) {
|
||||
this.$el.find('table:first').show();
|
||||
this.$el.find('.oe_view_nocontent').remove();
|
||||
this.start_edition().then(function(){
|
||||
var fields = self.editor.form.fields;
|
||||
self.editor.form.fields_order.some(function(field){
|
||||
if (fields[field].$el.is(':visible')){
|
||||
fields[field].$el.find("input").select();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
this.start_edition();
|
||||
} else {
|
||||
this._super();
|
||||
}
|
||||
|
@ -243,6 +235,7 @@
|
|||
return this.ensure_saved().then(function () {
|
||||
var $recordRow = self.groups.get_row_for(record);
|
||||
var cells = self.get_cells_for($recordRow);
|
||||
var fields = {};
|
||||
self.fields_for_resize.splice(0, self.fields_for_resize.length);
|
||||
return self.with_event('edit', {
|
||||
record: record.attributes,
|
||||
|
@ -256,10 +249,16 @@
|
|||
|
||||
// FIXME: need better way to get the field back from bubbling (delegated) DOM events somehow
|
||||
field.$el.attr('data-fieldname', field_name);
|
||||
fields[field_name] = field;
|
||||
self.fields_for_resize.push({field: field, cell: cell});
|
||||
}, options).then(function () {
|
||||
$recordRow.addClass('oe_edition');
|
||||
self.resize_fields();
|
||||
var focus_field = options && options.focus_field ? options.focus_field : undefined;
|
||||
if (!focus_field){
|
||||
focus_field = _.find(self.editor.form.fields_order, function(field){ return fields[field] && fields[field].$el.is(':visible:has(input)'); });
|
||||
}
|
||||
if (focus_field) fields[focus_field].$el.find('input').select();
|
||||
return record.attributes;
|
||||
});
|
||||
}).fail(function () {
|
||||
|
@ -286,9 +285,7 @@
|
|||
if (!this.editor.is_editing()) { return; }
|
||||
for(var i=0, len=this.fields_for_resize.length; i<len; ++i) {
|
||||
var item = this.fields_for_resize[i];
|
||||
if (!item.field.get('effective_invisible')) {
|
||||
this.resize_field(item.field, item.cell);
|
||||
}
|
||||
this.resize_field(item.field, item.cell);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
@ -307,6 +304,11 @@
|
|||
at: 'left top',
|
||||
of: $cell
|
||||
});
|
||||
if (field.get('effective_readonly')) {
|
||||
field.$el.addClass('oe_readonly');
|
||||
}
|
||||
if(field.widget == "handle")
|
||||
field.$el.addClass('oe_list_field_handle');
|
||||
},
|
||||
/**
|
||||
* @return {jQuery.Deferred}
|
||||
|
@ -452,13 +454,7 @@
|
|||
setup_events: function () {
|
||||
var self = this;
|
||||
_.each(this.editor.form.fields, function(field, field_name) {
|
||||
var set_invisible = function() {
|
||||
field.set({'force_invisible': field.get('effective_readonly')});
|
||||
};
|
||||
field.on("change:effective_readonly", self, set_invisible);
|
||||
set_invisible();
|
||||
field.on('change:effective_invisible', self, function () {
|
||||
if (field.get('effective_invisible')) { return; }
|
||||
field.on("change:effective_readonly", self, function(){
|
||||
var item = _(self.fields_for_resize).find(function (item) {
|
||||
return item.field === field;
|
||||
});
|
||||
|
@ -749,31 +745,6 @@
|
|||
throw new Error("is_editing's state filter must be either `new` or" +
|
||||
" `edit` if provided");
|
||||
},
|
||||
_focus_setup: function (focus_field) {
|
||||
var form = this.form;
|
||||
|
||||
var field;
|
||||
// If a field to focus was specified
|
||||
if (focus_field
|
||||
// Is actually in the form
|
||||
&& (field = form.fields[focus_field])
|
||||
// And is visible
|
||||
&& field.$el.is(':visible')) {
|
||||
// focus it
|
||||
field.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
_(form.fields_order).detect(function (name) {
|
||||
// look for first visible field in fields_order, focus it
|
||||
var field = form.fields[name];
|
||||
if (!field.$el.is(':visible')) {
|
||||
return false;
|
||||
}
|
||||
// Stop as soon as a field got focused
|
||||
return field.focus() !== false;
|
||||
});
|
||||
},
|
||||
edit: function (record, configureField, options) {
|
||||
// TODO: specify sequence of edit calls
|
||||
var self = this;
|
||||
|
@ -788,7 +759,6 @@
|
|||
_(form.fields).each(function (field, name) {
|
||||
configureField(name, field);
|
||||
});
|
||||
self._focus_setup(options && options.focus_field);
|
||||
return form;
|
||||
});
|
||||
},
|
||||
|
|
|
@ -218,7 +218,12 @@ openerp.web_calendar = function(instance) {
|
|||
this.info_fields.push(fv.arch.children[fld].attrs.name);
|
||||
}
|
||||
|
||||
return (new instance.web.Model(this.dataset.model))
|
||||
var edit_check = new instance.web.Model(this.dataset.model)
|
||||
.call("check_access_rights", ["write", false])
|
||||
.then(function (write_right) {
|
||||
self.write_right = write_right;
|
||||
});
|
||||
var init = new instance.web.Model(this.dataset.model)
|
||||
.call("check_access_rights", ["create", false])
|
||||
.then(function (create_right) {
|
||||
self.create_right = create_right;
|
||||
|
@ -228,6 +233,7 @@ openerp.web_calendar = function(instance) {
|
|||
self.ready.resolve();
|
||||
});
|
||||
});
|
||||
return $.when(edit_check, init);
|
||||
},
|
||||
|
||||
get_fc_init_options: function () {
|
||||
|
@ -657,17 +663,13 @@ openerp.web_calendar = function(instance) {
|
|||
date_start_day = new Date(event.start.getFullYear(),event.start.getMonth(),event.start.getDate(),7);
|
||||
date_stop_day = new Date(event_end.getFullYear(),event_end.getMonth(),event_end.getDate(),19);
|
||||
}
|
||||
data[this.date_start] = instance.web.parse_value(date_start_day, this.fields[this.date_start]);
|
||||
if (this.date_stop) {
|
||||
data[this.date_stop] = instance.web.parse_value(date_stop_day, this.fields[this.date_stop]);
|
||||
}
|
||||
diff_seconds = Math.round((date_stop_day.getTime() - date_start_day.getTime()) / 1000);
|
||||
|
||||
}
|
||||
else {
|
||||
data[this.date_start] = instance.web.parse_value(event.start, this.fields[this.date_start]);
|
||||
data[this.date_start] = event.start;
|
||||
if (this.date_stop) {
|
||||
data[this.date_stop] = instance.web.parse_value(event_end, this.fields[this.date_stop]);
|
||||
data[this.date_stop] = event_end;
|
||||
}
|
||||
diff_seconds = Math.round((event_end.getTime() - event.start.getTime()) / 1000);
|
||||
}
|
||||
|
@ -841,7 +843,11 @@ openerp.web_calendar = function(instance) {
|
|||
if (! this.open_popup_action) {
|
||||
var index = this.dataset.get_id_index(id);
|
||||
this.dataset.index = index;
|
||||
this.do_switch_view('form', null, { mode: "edit" });
|
||||
if (this.write_right) {
|
||||
this.do_switch_view('form', null, { mode: "edit" });
|
||||
} else {
|
||||
this.do_switch_view('form', null, { mode: "view" });
|
||||
}
|
||||
}
|
||||
else {
|
||||
var pop = new instance.web.form.FormOpenPopup(this);
|
||||
|
|
|
@ -926,10 +926,6 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({
|
|||
var self = this;
|
||||
this.setup_color_picker();
|
||||
this.$el.find('[title]').each(function(){
|
||||
//in case of kanban, attach tooltip to the element itself
|
||||
//otherwise it might stay on screen when kanban view reload
|
||||
//since default container is body.
|
||||
//(when clicking on ready for next stage for example)
|
||||
$(this).tooltip({
|
||||
delay: { show: 500, hide: 0},
|
||||
container: $(this),
|
||||
|
|
|
@ -413,7 +413,7 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
action = ServerActions.browse(cr, uid, action_id, context=context)
|
||||
if action.state == 'code' and action.website_published:
|
||||
action_res = ServerActions.run(cr, uid, [action_id], context=context)
|
||||
if isinstance(action_res, Response):
|
||||
if isinstance(action_res, werkzeug.wrappers.Response):
|
||||
res = action_res
|
||||
if res:
|
||||
return res
|
||||
|
|
|
@ -74,7 +74,6 @@ class ir_http(orm.AbstractModel):
|
|||
# to url without language so google doesn't see duplicate content
|
||||
return request.redirect(path + '?' + request.httprequest.query_string)
|
||||
return self.reroute(path)
|
||||
return self._handle_exception(code=404)
|
||||
return super(ir_http, self)._dispatch()
|
||||
|
||||
def reroute(self, path):
|
||||
|
@ -103,8 +102,8 @@ class ir_http(orm.AbstractModel):
|
|||
try:
|
||||
_, path = rule.build(arguments)
|
||||
assert path is not None
|
||||
except Exception:
|
||||
return self._handle_exception(werkzeug.exceptions.NotFound())
|
||||
except Exception, e:
|
||||
return self._handle_exception(e, code=404)
|
||||
|
||||
if request.httprequest.method in ('GET', 'HEAD'):
|
||||
generated_path = werkzeug.url_unquote_plus(path)
|
||||
|
@ -136,55 +135,70 @@ class ir_http(orm.AbstractModel):
|
|||
if response.status_code == 304:
|
||||
return response
|
||||
|
||||
response.mimetype = attach[0]['mimetype']
|
||||
response.mimetype = attach[0]['mimetype'] or 'application/octet-stream'
|
||||
response.data = datas.decode('base64')
|
||||
return response
|
||||
|
||||
def _handle_exception(self, exception=None, code=500):
|
||||
try:
|
||||
def _handle_exception(self, exception, code=500):
|
||||
# This is done first as the attachment path may
|
||||
# not match any HTTP controller, so the request
|
||||
# may not be website-enabled.
|
||||
attach = self._serve_attachment()
|
||||
if attach:
|
||||
return attach
|
||||
|
||||
is_website_request = bool(getattr(request, 'website_enabled', False) and request.website)
|
||||
if not is_website_request:
|
||||
# Don't touch non website requests exception handling
|
||||
return super(ir_http, self)._handle_exception(exception)
|
||||
except Exception:
|
||||
else:
|
||||
try:
|
||||
response = super(ir_http, self)._handle_exception(exception)
|
||||
if isinstance(response, Exception):
|
||||
exception = response
|
||||
else:
|
||||
# if parent excplicitely returns a plain response, then we don't touch it
|
||||
return response
|
||||
except Exception, e:
|
||||
exception = e
|
||||
|
||||
attach = self._serve_attachment()
|
||||
if attach:
|
||||
return attach
|
||||
values = dict(
|
||||
exception=exception,
|
||||
traceback=traceback.format_exc(exception),
|
||||
)
|
||||
code = getattr(exception, 'code', code)
|
||||
|
||||
if getattr(request, 'website_enabled', False) and request.website:
|
||||
values = dict(
|
||||
exception=exception,
|
||||
traceback=traceback.format_exc(exception),
|
||||
)
|
||||
if exception:
|
||||
code = getattr(exception, 'code', code)
|
||||
if isinstance(exception, ir_qweb.QWebException):
|
||||
values.update(qweb_exception=exception)
|
||||
if isinstance(exception.qweb.get('cause'), openerp.exceptions.AccessError):
|
||||
code = 403
|
||||
if code == 500:
|
||||
logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
|
||||
if 'qweb_exception' in values:
|
||||
view = request.registry.get("ir.ui.view")
|
||||
views = view._views_get(request.cr, request.uid, exception.qweb['template'], request.context)
|
||||
to_reset = [v for v in views if v.model_data_id.noupdate is True]
|
||||
values['views'] = to_reset
|
||||
elif code == 403:
|
||||
logger.warn("403 Forbidden:\n\n%s", values['traceback'])
|
||||
if isinstance(exception, openerp.exceptions.AccessError):
|
||||
code = 403
|
||||
|
||||
values.update(
|
||||
status_message=werkzeug.http.HTTP_STATUS_CODES[code],
|
||||
status_code=code,
|
||||
)
|
||||
if isinstance(exception, ir_qweb.QWebException):
|
||||
values.update(qweb_exception=exception)
|
||||
if isinstance(exception.qweb.get('cause'), openerp.exceptions.AccessError):
|
||||
code = 403
|
||||
|
||||
if not request.uid:
|
||||
self._auth_method_public()
|
||||
if code == 500:
|
||||
logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
|
||||
if 'qweb_exception' in values:
|
||||
view = request.registry.get("ir.ui.view")
|
||||
views = view._views_get(request.cr, request.uid, exception.qweb['template'], request.context)
|
||||
to_reset = [v for v in views if v.model_data_id.noupdate is True]
|
||||
values['views'] = to_reset
|
||||
elif code == 403:
|
||||
logger.warn("403 Forbidden:\n\n%s", values['traceback'])
|
||||
|
||||
try:
|
||||
html = request.website._render('website.%s' % code, values)
|
||||
except Exception:
|
||||
html = request.website._render('website.http_error', values)
|
||||
return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
|
||||
values.update(
|
||||
status_message=werkzeug.http.HTTP_STATUS_CODES[code],
|
||||
status_code=code,
|
||||
)
|
||||
|
||||
raise
|
||||
if not request.uid:
|
||||
self._auth_method_public()
|
||||
|
||||
try:
|
||||
html = request.website._render('website.%s' % code, values)
|
||||
except Exception:
|
||||
html = request.website._render('website.http_error', values)
|
||||
return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
|
||||
|
||||
class ModelConverter(ir.ir_http.ModelConverter):
|
||||
def __init__(self, url_map, model=False, domain='[]'):
|
||||
|
|
|
@ -25,6 +25,7 @@ import openerp.modules
|
|||
import openerp
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools import ustr, DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from openerp.tools import html_escape as escape
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.addons.base.ir import ir_qweb
|
||||
|
||||
|
@ -276,7 +277,7 @@ class Image(orm.AbstractModel):
|
|||
url_params[options_key] = options[options_key]
|
||||
|
||||
return ir_qweb.HTMLSafe('<img class="%s" src="/website/image?%s"/>' % (
|
||||
' '.join(itertools.imap(werkzeug.utils.escape, classes)),
|
||||
' '.join(itertools.imap(escape, classes)),
|
||||
werkzeug.urls.url_encode(url_params)
|
||||
))
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ except ImportError:
|
|||
|
||||
import openerp
|
||||
from openerp.osv import orm, osv, fields
|
||||
from openerp.tools import html_escape as escape
|
||||
from openerp.tools.safe_eval import safe_eval
|
||||
from openerp.addons.web.http import request
|
||||
|
||||
|
@ -710,7 +711,7 @@ class ir_attachment(osv.osv):
|
|||
for attachment in self.browse(cr, uid, ids, context=context):
|
||||
# in-document URLs are html-escaped, a straight search will not
|
||||
# find them
|
||||
url = werkzeug.utils.escape(attachment.website_url)
|
||||
url = escape(attachment.website_url)
|
||||
ids = Views.search(cr, uid, ["|", ('arch', 'like', '"%s"' % url), ('arch', 'like', "'%s'" % url)], context=context)
|
||||
|
||||
if ids:
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -468,7 +468,7 @@
|
|||
}
|
||||
);
|
||||
});
|
||||
menu.on('click', 'a[data-action!=ace]', function (event) {
|
||||
menu.on('click', 'a[data-view-id]', function (event) {
|
||||
var view_id = $(event.currentTarget).data('view-id');
|
||||
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
|
||||
model: 'ir.ui.view',
|
||||
|
@ -1504,7 +1504,7 @@
|
|||
url: this.link
|
||||
});
|
||||
this.media.renameNode("img");
|
||||
this.media.$.attributes.src = this.link;
|
||||
$(this.media).attr('src', this.link);
|
||||
return this._super();
|
||||
},
|
||||
clear: function () {
|
||||
|
@ -1988,6 +1988,11 @@
|
|||
// a/@href, ...)
|
||||
_(mutations).chain()
|
||||
.filter(function (m) {
|
||||
// ignore any SVG target, these blokes are like weird mon
|
||||
if (m.target && m.target instanceof SVGElement) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore any change related to mundane image-edit-button
|
||||
if (m.target && m.target.className
|
||||
&& m.target.className.indexOf('image-edit-button') !== -1) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
if (!window.location.origin) { // fix for ie9
|
||||
window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
|
||||
}
|
||||
document.getElementById("mobile-viewport").src = window.location.origin + window.location.pathname + "#mobile-preview";
|
||||
document.getElementById("mobile-viewport").src = window.location.origin + window.location.pathname + window.location.search + "#mobile-preview";
|
||||
this.$el.modal();
|
||||
},
|
||||
destroy: function () {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
var nodialog = 'website_translator_nodialog';
|
||||
|
||||
website.EditorBar.include({
|
||||
do_not_translate : ['-','*','!'],
|
||||
events: _.extend({}, website.EditorBar.prototype.events, {
|
||||
'click a[data-action=edit_master]': 'edit_master',
|
||||
}),
|
||||
|
@ -134,7 +135,7 @@
|
|||
node.className += ' oe_translatable_inprogress';
|
||||
}
|
||||
} else {
|
||||
node.className += ' oe_translatable_todo';
|
||||
node.className += this.do_not_translate.indexOf(node.textContent.trim()) ? ' oe_translatable_todo' : '';
|
||||
}
|
||||
node.contentEditable = true;
|
||||
var nid = _.uniqueId();
|
||||
|
|
|
@ -24,9 +24,9 @@ import datetime
|
|||
import re
|
||||
|
||||
import pytz
|
||||
import werkzeug.utils
|
||||
|
||||
import openerp
|
||||
import openerp.tools
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
|
||||
|
@ -150,7 +150,7 @@ class website_event(http.Controller):
|
|||
if post.get('tag_'+str(tag.id)):
|
||||
tags.append(tag.id)
|
||||
|
||||
e = werkzeug.utils.escape
|
||||
e = openerp.tools.escape
|
||||
track_description = '''<section data-snippet-id="text-block">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
|
|
@ -120,7 +120,7 @@ class WebsiteForum(http.Controller):
|
|||
|
||||
question_count = Post.search(cr, uid, domain, count=True, context=context)
|
||||
if tag:
|
||||
url = "/forum/%s/%s/questions" % (slug(forum), slug(tag))
|
||||
url = "/forum/%s/tag/%s/questions" % (slug(forum), slug(tag))
|
||||
else:
|
||||
url = "/forum/%s" % slug(forum)
|
||||
|
||||
|
@ -208,12 +208,16 @@ class WebsiteForum(http.Controller):
|
|||
}, context=context)
|
||||
return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), new_question_id))
|
||||
|
||||
@http.route(['''/forum/<model("forum.forum"):forum>/question/<model("forum.post", "[('forum_id','=',forum[0])]"):question>'''], type='http', auth="public", website=True)
|
||||
@http.route(['''/forum/<model("forum.forum"):forum>/question/<model("forum.post", "[('forum_id','=',forum[0]),('parent_id','=',False)]"):question>'''], type='http', auth="public", website=True)
|
||||
def question(self, forum, question, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
# increment view counter
|
||||
request.registry['forum.post'].set_viewed(cr, SUPERUSER_ID, [question.id], context=context)
|
||||
|
||||
if question.parent_id:
|
||||
redirect_url = "/forum/%s/question/%s" % (slug(forum), slug(question.parent_id))
|
||||
return werkzeug.utils.redirect(redirect_url, 301)
|
||||
|
||||
filters = 'question'
|
||||
values = self._prepare_forum_values(forum=forum, searches=post)
|
||||
values.update({
|
||||
|
|
|
@ -148,7 +148,7 @@ function IsKarmaValid(eventNumber,minKarma){
|
|||
CKEDITOR.tools.callFunction(eventNumber,this);
|
||||
return false;
|
||||
} else {
|
||||
alert("Sorry you need more than 30 Karma.");
|
||||
alert("Sorry you need more than " + minKarma + " Karma.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -156,14 +156,14 @@
|
|||
<!-- Display a post -->
|
||||
<template id="display_post">
|
||||
<div class="question clearfix">
|
||||
<div class="pull-left text-center">
|
||||
<div class="col-md-2 hidden-xs text-center">
|
||||
<div t-attf-class="box #{question.is_correct and 'oe_green' or 'oe_grey'} #{(question.child_count == 0) and 'text-muted' or ''}">
|
||||
<span t-esc="question.child_count"/>
|
||||
<div t-if="question.child_count>1" class="subtitle">Answers</div>
|
||||
<div t-if="question.child_count<=1" class="subtitle">Answer</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-left: 95px; margin-top:-8px;">
|
||||
<div class="col-md-10">
|
||||
<div class="question-name">
|
||||
<a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
|
||||
<span t-if="not question.active"><b> [Deleted]</b></span>
|
||||
|
@ -180,6 +180,11 @@
|
|||
t-field="question.create_uid" t-field-options='{"widget": "contact", "country_image": true, "fields": ["name", "country_id"]}'
|
||||
style="display: inline-block;"/>
|
||||
on <span t-field="question.write_date" t-field-options='{"format":"short"}'/>
|
||||
<span class="visible-xs">
|
||||
<b t-esc="question.child_count or 0"/>
|
||||
<t t-if="question.child_count>1">answers</t>
|
||||
<t t-if="question.child_count==1">answers</t>
|
||||
</span>
|
||||
with <b t-field="question.views"/> views
|
||||
<span t-if="question.vote_count>0"> and
|
||||
<b t-esc="question.vote_count or 0"/>
|
||||
|
@ -432,7 +437,7 @@
|
|||
<template id="post_description_full" name="Question Navigation">
|
||||
<t t-call="website_forum.header">
|
||||
<div t-attf-class="question #{not question.active and 'alert alert-danger' or ''}">
|
||||
<div class="text-center pull-left">
|
||||
<div class="col-md-2 hidden-xs text-center">
|
||||
<t t-call="website_forum.vote">
|
||||
<t t-set="post" t-value="question"/>
|
||||
</t>
|
||||
|
@ -444,7 +449,7 @@
|
|||
t-attf-class="favourite_question no-decoration fa fa-2x fa-star #{question.user_favourite and 'forum_favourite_question' or ''}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-left: 95px;">
|
||||
<div style="col-md-10">
|
||||
<h1 class="mt0">
|
||||
<a t-attf-href="/forum/#{ slug(forum) }/question/#{ slug(question) }" t-field="question.name"/>
|
||||
<span t-if="not question.active"><b> [Deleted]</b></span>
|
||||
|
@ -470,7 +475,6 @@
|
|||
</div>
|
||||
</div>
|
||||
<t t-raw="question.content"/>
|
||||
|
||||
<div class="mt16 clearfix">
|
||||
<div class="pull-right">
|
||||
<div class="text-right">
|
||||
|
@ -533,6 +537,18 @@
|
|||
<span class="text-muted">Asked on <span t-field="question.create_date" t-field-options='{"format":"short"}'/></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="visible-xs text-center">
|
||||
<t t-call="website_forum.vote">
|
||||
<t t-set="post" t-value="question"/>
|
||||
</t>
|
||||
<div class="text-muted text-center">
|
||||
<span t-field="question.views"/> Views
|
||||
</div>
|
||||
<div class="mt4">
|
||||
<a t-attf-data-href="/forum/#{slug(question.forum_id)}/question/#{slug(question)}/toggle_favourite"
|
||||
t-attf-class="favourite_question no-decoration fa fa-2x fa-star #{question.user_favourite and 'forum_favourite_question' or ''}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<t t-call="website_forum.post_comment">
|
||||
<t t-set="object" t-value="question"/>
|
||||
|
@ -544,7 +560,7 @@
|
|||
<div t-foreach="question.child_ids" t-as="answer" class="mt16 mb32">
|
||||
<a t-attf-id="answer-#{str(answer.id)}"/>
|
||||
<div t-attf-class="forum_answer" t-attf-id="answer_#{answer.id}" >
|
||||
<div class="text-center pull-left">
|
||||
<div class="col-md-2 hidden-xs text-center">
|
||||
<t t-call="website_forum.vote">
|
||||
<t t-set="post" t-value="answer"/>
|
||||
</t>
|
||||
|
@ -553,7 +569,7 @@
|
|||
t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-left: 95px;" class="clearfix">
|
||||
<div class="col-md-10 clearfix">
|
||||
<t t-raw="answer.content"/>
|
||||
<div class="mt16">
|
||||
<ul class="list-inline pull-right">
|
||||
|
@ -589,6 +605,15 @@
|
|||
<div t-field="answer.create_uid" t-field-options='{"widget": "contact", "badges": true, "fields": ["karma"]}'/>
|
||||
<span class="text-muted">Answered on <span t-field="answer.create_date" t-field-options='{"format":"short"}'/></span>
|
||||
</div>
|
||||
<div class="visible-xs text-center">
|
||||
<t t-call="website_forum.vote">
|
||||
<t t-set="post" t-value="answer"/>
|
||||
</t>
|
||||
<div class="text-muted mt8">
|
||||
<a t-attf-class="accept_answer fa fa-2x fa-check-circle no-decoration #{answer.is_correct and 'oe_answer_true' or 'oe_answer_false'}"
|
||||
t-attf-data-href="/forum/#{slug(question.forum_id)}/post/#{slug(answer)}/toggle_correct"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<t t-call="website_forum.post_comment">
|
||||
<t t-set="object" t-value="answer"/>
|
||||
|
|
|
@ -92,9 +92,8 @@ class WebsiteMail(http.Controller):
|
|||
values['is_follower'] = len(
|
||||
request.registry['mail.followers'].search(
|
||||
cr, SUPERUSER_ID, [
|
||||
('res_model', '=', 'mail.group'),
|
||||
('res_model', '=', model),
|
||||
('res_id', '=', obj_ids[0]),
|
||||
('partner_id', '=', partner_id.id)
|
||||
], context=context)) == 1
|
||||
|
||||
return values
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
|
||||
website.snippet.animationRegistry.follow = website.snippet.Animation.extend({
|
||||
selector: ".js_follow",
|
||||
start: function (editable_mode) {
|
||||
var self = this;
|
||||
this.is_user = false;
|
||||
|
||||
openerp.jsonRpc('/website_mail/is_follower', 'call', {
|
||||
model: this.$target.data('object'),
|
||||
id: this.$target.data('id'),
|
||||
}).always(function (data) {
|
||||
self.is_user = data.is_user;
|
||||
self.email = data.email;
|
||||
self.toggle_subscription(data.is_follower, data.email);
|
||||
self.$target.removeClass("hidden");
|
||||
});
|
||||
|
||||
// not if editable mode to allow designer to edit alert field
|
||||
if (!editable_mode) {
|
||||
$('.js_follow > .alert').addClass("hidden");
|
||||
$('.js_follow > .input-group-btn.hidden').removeClass("hidden");
|
||||
this.$target.find('.js_follow_btn, .js_unfollow_btn').on('click', function (event) {
|
||||
event.preventDefault();
|
||||
self.on_click();
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
on_click: function () {
|
||||
var self = this;
|
||||
var $email = this.$target.find(".js_follow_email");
|
||||
|
||||
if ($email.length && !$email.val().match(/.+@.+/)) {
|
||||
this.$target.addClass('has-error');
|
||||
return false;
|
||||
}
|
||||
this.$target.removeClass('has-error');
|
||||
|
||||
openerp.jsonRpc('/website_mail/follow', 'call', {
|
||||
'id': +this.$target.data('id'),
|
||||
'object': this.$target.data('object'),
|
||||
'message_is_follower': this.$target.attr("data-follow") || "off",
|
||||
'email': $email.length ? $email.val() : false,
|
||||
}).then(function (follow) {
|
||||
self.toggle_subscription(follow, self.email);
|
||||
});
|
||||
},
|
||||
toggle_subscription: function(follow, email) {
|
||||
console.log(follow, email);
|
||||
if (follow) {
|
||||
this.$target.find(".js_follow_btn").addClass("hidden");
|
||||
this.$target.find(".js_unfollow_btn").removeClass("hidden");
|
||||
}
|
||||
else {
|
||||
this.$target.find(".js_follow_btn").removeClass("hidden");
|
||||
this.$target.find(".js_unfollow_btn").addClass("hidden");
|
||||
}
|
||||
this.$target.find('input.js_follow_email')
|
||||
.val(email ? email : "")
|
||||
.attr("disabled", follow || (email.length && this.is_user) ? "disabled" : false);
|
||||
this.$target.attr("data-follow", follow ? 'on' : 'off');
|
||||
},
|
||||
});
|
||||
})();
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
<template id="head" inherit_id="website.assets_frontend" name="Mail customization">
|
||||
<xpath expr="/t" position="inside">
|
||||
<script type="text/javascript" src="/website_mail/static/src/js/follow.js"></script>
|
||||
<link rel='stylesheet' href='/website_mail/static/src/css/website_mail.css'/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
import controllers
|
||||
import models
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import datetime
|
||||
from dateutil import relativedelta
|
||||
|
||||
from openerp import tools
|
||||
from openerp import tools, SUPERUSER_ID
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.website.models.website import slug
|
||||
from openerp.addons.web.http import request
|
||||
|
@ -28,12 +29,23 @@ class MailGroup(http.Controller):
|
|||
def view(self, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
group_obj = request.registry.get('mail.group')
|
||||
mail_message_obj = request.registry.get('mail.message')
|
||||
group_ids = group_obj.search(cr, uid, [('alias_id', '!=', False), ('alias_id.alias_name', '!=', False)], context=context)
|
||||
values = {'groups': group_obj.browse(cr, uid, group_ids, context)}
|
||||
groups = group_obj.browse(cr, uid, group_ids, context)
|
||||
# compute statistics
|
||||
month_date = datetime.datetime.today() - relativedelta.relativedelta(months=1)
|
||||
group_data = dict.fromkeys(group_ids, dict())
|
||||
for group in groups:
|
||||
group_data[group.id]['monthly_message_nbr'] = mail_message_obj.search(
|
||||
cr, SUPERUSER_ID,
|
||||
[('model', '=', 'mail.group'), ('res_id', '=', group.id), ('date', '>=', month_date.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT))],
|
||||
count=True, context=context)
|
||||
values = {'groups': groups, 'group_data': group_data}
|
||||
return request.website.render('website_mail_group.mail_groups', values)
|
||||
|
||||
@http.route(["/groups/subscription/"], type='json', auth="user")
|
||||
def subscription(self, group_id=0, action=False, **post):
|
||||
""" TDE FIXME: seems dead code """
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
group_obj = request.registry.get('mail.group')
|
||||
if action:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
import mail_group
|
|
@ -0,0 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from openerp.osv import osv
|
||||
|
||||
|
||||
class MailGroup(osv.Model):
|
||||
_inherit = 'mail.group'
|
||||
|
||||
def message_get_email_values(self, cr, uid, id, notif_mail=None, context=None):
|
||||
res = super(MailGroup, self).message_get_email_values(cr, uid, id, notif_mail=notif_mail, context=context)
|
||||
group = self.browse(cr, uid, id, context=context)
|
||||
base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url')
|
||||
res['headers'].update({
|
||||
'List-Archive': '<%s/groups/%s>' % (base_url, group.id),
|
||||
'List-Subscribe': '<%s/groups>' % (base_url),
|
||||
'List-Unsubscribe': '<%s/groups>' % (base_url),
|
||||
})
|
||||
return res
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
var website = openerp.website;
|
||||
|
||||
website.snippet.animationRegistry.follow = website.snippet.Animation.extend({
|
||||
selector: ".js_follow",
|
||||
website.snippet.animationRegistry.follow_alias = website.snippet.Animation.extend({
|
||||
selector: ".js_follow_alias",
|
||||
start: function (editable_mode) {
|
||||
var self = this;
|
||||
this.is_user = false;
|
||||
|
@ -23,8 +23,8 @@
|
|||
|
||||
// not if editable mode to allow designer to edit alert field
|
||||
if (!editable_mode) {
|
||||
$('.js_follow > .alert').addClass("hidden");
|
||||
$('.js_follow > .input-group-btn.hidden').removeClass("hidden");
|
||||
$('.js_follow_alias > .alert').addClass("hidden");
|
||||
$('.js_follow_alias > .input-group-btn.hidden').removeClass("hidden");
|
||||
this.$target.find('.js_follow_btn, .js_unfollow_btn').on('click', function (event) {
|
||||
event.preventDefault();
|
||||
self.on_click();
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<span class="oe_snippet_thumbnail_title">Discussion Group</span>
|
||||
</div>
|
||||
|
||||
<div class="oe_snippet_body js_follow"
|
||||
<div class="oe_snippet_body js_follow_alias"
|
||||
data-id="0"
|
||||
data-object="mail.group"
|
||||
data-follow="off">
|
||||
|
@ -38,7 +38,7 @@
|
|||
|
||||
<xpath expr="//div[@id='snippet_options']" position="inside">
|
||||
<div data-snippet-option-id='subscribe'
|
||||
data-selector=".js_follow"
|
||||
data-selector=".js_follow_alias"
|
||||
data-selector-siblings="p, h1, h2, h3, blockquote, .well, .panel"
|
||||
>
|
||||
<li>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
</div>
|
||||
<div class="col-md-2">
|
||||
<i class='fa fa-user'/> <t t-esc="len(group.message_follower_ids)"/> participants<br />
|
||||
<i class='fa fa-envelope-o'/> <t t-esc="len(group.message_ids)"/> messages
|
||||
<i class='fa fa-envelope-o'/> <t t-raw="group_data[group.id]['monthly_message_nbr']"/> messages / month
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<t t-call="website_mail.follow"><t t-set="object" t-value="group"/></t>
|
||||
|
|
|
@ -55,7 +55,7 @@ class table_compute(object):
|
|||
self.table[(pos/PPR)+y2][(pos%PPR)+x2] = False
|
||||
self.table[pos/PPR][pos%PPR] = {
|
||||
'product': p, 'x':x, 'y': y,
|
||||
'class': " ".join(map(lambda x: x.html_class, p.website_style_ids))
|
||||
'class': " ".join(map(lambda x: x.html_class or '', p.website_style_ids))
|
||||
}
|
||||
if index<=PPG:
|
||||
maxy=max(maxy,y+(pos/PPR))
|
||||
|
@ -169,7 +169,7 @@ class website_sale(http.Controller):
|
|||
|
||||
values = {
|
||||
'search': search,
|
||||
'category': category and int(category),
|
||||
'category': category,
|
||||
'attrib_values': attrib_values,
|
||||
'attrib_set': attrib_set,
|
||||
'pager': pager,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue